From c86d3118b783021d5af848890fcdc1a26943ebde Mon Sep 17 00:00:00 2001 From: Michiel Cottaar <MichielCottaar@protonmail.com> Date: Thu, 30 May 2024 14:54:40 +0100 Subject: [PATCH] Fix tests --- .../constant_gradient_blocks.jl | 2 +- src/components/instant_gradients.jl | 4 +-- src/components/pulses/generic_pulses.jl | 10 +++--- src/components/pulses/sinc_pulses.jl | 2 +- src/containers/abstract.jl | 7 ++-- src/containers/building_blocks.jl | 2 +- src/parts/trapezoids.jl | 8 ++--- src/pathways.jl | 4 +-- src/variables.jl | 5 +-- test/test_IO.jl | 8 ++--- test/test_components.jl | 2 +- test/test_sequences.jl | 32 +++++++++---------- 12 files changed, 42 insertions(+), 44 deletions(-) diff --git a/src/components/gradient_waveforms/constant_gradient_blocks.jl b/src/components/gradient_waveforms/constant_gradient_blocks.jl index 7e6252f..0fb86ba 100644 --- a/src/components/gradient_waveforms/constant_gradient_blocks.jl +++ b/src/components/gradient_waveforms/constant_gradient_blocks.jl @@ -66,7 +66,7 @@ _mult(g1::AbstractVector, g2::AbstractVector) = g1 .* permutedims(g2) end function split_gradient(cgb::ConstantGradient, times::VariableType...) - durations = [times[1], [t[2] - t[1] for t in zip(times[1:end-1], times[2:end])]..., duration(cgb) - times[end]] + durations = [times[1], [t[2] - t[1] for t in zip(times[1:end-1], times[2:end])]..., variables.duration(cgb) - times[end]] if cgb isa ConstantGradient1D return [ConstantGradient1D(cgb.gradient_strength, cgb.orientation, d, cgb.group) for d in durations] else diff --git a/src/components/instant_gradients.jl b/src/components/instant_gradients.jl index ce1127b..ca2def1 100644 --- a/src/components/instant_gradients.jl +++ b/src/components/instant_gradients.jl @@ -57,8 +57,8 @@ end @defvar gradient qvec(ig::InstantGradient3D) = ig.qvec -variables.duration.f(::InstantGradient) = 0. -variables.effective_time.f(::InstantGradient) = 0. +@defvar duration(::InstantGradient) = 0. +@defvar effective_time(::InstantGradient) = 0. @defvar gradient bmat_gradient(::InstantGradient, qstart=nothing) = zero(SMatrix{3, 3, Float64, 3}) make_generic(ig::InstantGradient) = ig diff --git a/src/components/pulses/generic_pulses.jl b/src/components/pulses/generic_pulses.jl index 9696194..af88e4f 100644 --- a/src/components/pulses/generic_pulses.jl +++ b/src/components/pulses/generic_pulses.jl @@ -31,7 +31,7 @@ struct GenericPulse <: RFPulseComponent @assert all(time .>= 0) if isnothing(effective_time) test_res = new(Float64.(time), Float64.(amplitude), Float64.(phase), 0.) - effective_time = time_halfway_flip(test_res) + effective_time = variables.time_halfway_flip(test_res) end return new(Float64.(time), Float64.(amplitude), Float64.(phase), Float64(effective_time)) end @@ -56,12 +56,12 @@ function GenericPulse(pulse::GenericPulse, t1::Number, t2::Number) pnew = pulse.phase[use] if !(t1 ≈ tnew[1]) pushfirst!(tnew, t1) - pushfirst!(anew, amplitude(pulse, t1)) - pushfirst!(pnew, phase(pulse, t1)) + pushfirst!(anew, variables.amplitude(pulse, t1)) + pushfirst!(pnew, variables.phase(pulse, t1)) elseif !(t2 ≈ tnew[end]) push!(tnew, t2) - push!(anew, amplitude(pulse, t2)) - push!(pnew, phase(pulse, t2)) + push!(anew, variables.amplitude(pulse, t2)) + push!(pnew, variables.phase(pulse, t2)) end return GenericPulse(tnew .- t1, anew, pnew, pulse.effective_time - t1) end diff --git a/src/components/pulses/sinc_pulses.jl b/src/components/pulses/sinc_pulses.jl index 74efad5..5d92a74 100644 --- a/src/components/pulses/sinc_pulses.jl +++ b/src/components/pulses/sinc_pulses.jl @@ -99,7 +99,7 @@ end end @defvar begin - amplitude(pulse::SincPulse, time::Number) = variables.amplitude(pulse) * normalised_function(abs((time - variables.effective_time(pulse))) / variables.lobe_duration(pulse), N_left(pulse), N_right(pulse); apodise=pulse.apodise) + amplitude(pulse::SincPulse, time::Number) = variables.amplitude(pulse) * normalised_function(abs((time - variables.effective_time(pulse))) / variables.lobe_duration(pulse), variables.N_left(pulse), variables.N_right(pulse); apodise=pulse.apodise) phase(pulse::SincPulse, time::Number) = variables.phase(pulse) + variables.frequency(pulse) * (time - variables.effective_time(pulse)) * 360. frequency(pulse::SincPulse, time::Number) = variables.frequency(pulse) end diff --git a/src/containers/abstract.jl b/src/containers/abstract.jl index 16f66ff..195af76 100644 --- a/src/containers/abstract.jl +++ b/src/containers/abstract.jl @@ -37,6 +37,7 @@ end_time(block::Tuple{<:VariableType, <:AbstractBlock}) = variables.duration(blo effective_time(container::ContainerBlock, index, indices...) = start_time(container, index) + variables.effective_time(container[index], indices...) effective_time(block::Tuple{<:VariableType, <:AbstractBlock}) = block[1] + variables.effective_time(block[2]) end + """ effective_time(container, indices...) @@ -46,7 +47,7 @@ This will crash if the component does not have an [`effective_time`](@ref) (e.g. Also see [`variables.duration`](@ref), [`start_time`](@ref), and [`end_time`](@ref) """ -effective_time +variables.effective_time """ @@ -147,8 +148,8 @@ function get_gradient end Returns all the times that the sequence will readout. """ -readout_times +variables.readout_times -iter(component::Tuple{<:Number, <:ReadoutComponent}, ::Val{:readout}) = [(time, nothing) for time in readout_times(component[2])] +iter(component::Tuple{<:Number, <:ReadoutComponent}, ::Val{:readout}) = [(time, nothing) for time in variables.readout_times(component[2])] end \ No newline at end of file diff --git a/src/containers/building_blocks.jl b/src/containers/building_blocks.jl index 7d2db3d..b399f2e 100644 --- a/src/containers/building_blocks.jl +++ b/src/containers/building_blocks.jl @@ -232,7 +232,7 @@ Computes the area under the curve for the gradient waveform in [`BaseBuildingBlo If `first_event` is set to something else than `nothing`, only the gradient waveform after this RF pulse/Readout will be considered. Similarly, if `last_event` is set to something else than `nothing`, only the gradient waveform up to this RF pulse/Readout will be considered. """ -qvec +variables.qvec function edge_times(bb::BaseBuildingBlock) res = Float64[] diff --git a/src/parts/trapezoids.jl b/src/parts/trapezoids.jl index 9dd2b5d..2deab41 100644 --- a/src/parts/trapezoids.jl +++ b/src/parts/trapezoids.jl @@ -170,12 +170,12 @@ struct SliceSelect{N} <: BaseTrapezoid{N} pulse :: RFPulseComponent end -function SliceSelect(pulse::RFPulseComponent; orientation=nothing, rise_time=nothing, group=nothing, slew_rate=nothing, variables...) +function SliceSelect(pulse::RFPulseComponent; orientation=nothing, rise_time=nothing, group=nothing, slew_rate=nothing, vars...) res = SliceSelect( - Trapezoid(; orientation=orientation, rise_time=rise_time, flat_time=duration(pulse), group=group, slew_rate=slew_rate), + Trapezoid(; orientation=orientation, rise_time=rise_time, flat_time=variables.duration(pulse), group=group, slew_rate=slew_rate), pulse ) - set_simple_constraints!(res, variables) + set_simple_constraints!(res, vars) return res end @@ -225,7 +225,7 @@ function LineReadout(adc::ADC; ramp_overlap=nothing, orientation=nothing, group= end Base.keys(::LineReadout) = (Val(:rise), Val(:adc), Val(:flat), Val(:fall)) -Base.getindex(lr::LineReadout, ::Val{:adc}) = ((1 - ramp_overlap(lr)) * rise_time(lr), lr.adc) +Base.getindex(lr::LineReadout, ::Val{:adc}) = ((1 - variables.ramp_overlap(lr)) * variables.rise_time(lr), lr.adc) @defvar begin ramp_overlap(lr::LineReadout) = lr.ramp_overlap diff --git a/src/pathways.jl b/src/pathways.jl index 3283817..54ba18c 100644 --- a/src/pathways.jl +++ b/src/pathways.jl @@ -27,7 +27,7 @@ The RF pulses cause mappings between these different states as described below. - `:refocus`/`:invert`/180: Flips the sign of the spin state (i.e., +longitudinal <-> -longitudinal, +transverse <-> -transverse) - `:excite`/90: Takes spin state one step along the following sequence +longitudinal -> +transverse -> -longitudinal -> -transverse -> +longitudinal - `:neg_excite`/270/-90: Inverse step compared with `:excite`. -- `readout_index`: After encountering the number of pulses as defined in `pulse_effects`, continue the `PathWay` until the readout given by `index` is reached. If set to 0 the `PathWay` is terminated immediately after the last RF pulse. +- `readout_index`: After encountering the number of pulses as defined in `pulse_effects`, continue the `Pathway` until the readout given by `index` is reached. If set to 0 the `Pathway` is terminated immediately after the last RF pulse. - `group`: which gradient grouping to consider for the `qvec` and `bmat`. If not set, all gradients will be considered (using their current alignment). ## Attributes @@ -169,7 +169,7 @@ variables.bval """ get_pathway(sequence) -Gets the main [`PathWay`](@ref) that spins are expected to experience in the sequence. +Gets the main [`Pathway`](@ref) that spins are expected to experience in the sequence. Multiple pathways might be returned as an array or (named)tuple. """ diff --git a/src/variables.jl b/src/variables.jl index 29779ee..903497b 100644 --- a/src/variables.jl +++ b/src/variables.jl @@ -92,7 +92,7 @@ Main module containing all the MRIBuilder sequence variables. All variables are available as members of this module, e.g. `variables.echo_time` returns the echo time variable. -New variables can be defined using [`@defvar`](@ref). +New variables can be defined using `@defvar`. Set constraints on variables by passing them on as keywords during the sequence generation, e.g., `seq=SpinEcho(echo_time=70)`. @@ -360,9 +360,6 @@ Returns the gradient orientation. function gradient_orientation end -@defvar function effective_time end - - function (var::Variable)(block::AbstractBlock, args...; kwargs...) if !applicable(var.f, block, args...) && !isnothing(var.getter) apply_to = var.getter(block) diff --git a/test/test_IO.jl b/test/test_IO.jl index ce838f9..493161b 100644 --- a/test/test_IO.jl +++ b/test/test_IO.jl @@ -24,7 +24,7 @@ end # Single ADC event - @test length(readout_times(seq)) == 1024 + @test length(variables.readout_times(seq)) == 1024 @test variables.readout_times(seq)[1] ≈ 0.22 + 5 + 0.02 + 0.5 * 0.3125 @test variables.readout_times(seq)[end] ≈ 0.22 + 5 + 0.02 + 1023.5 * 0.3125 @@ -56,7 +56,7 @@ 0.02 + # RF ringdown time (added by block duration) 29.730 # Delay until ADC start (to get start at ADC at TE=30) ) - @test length(readout_times(seq)) == 8192 + @test length(variables.readout_times(seq)) == 8192 @test variables.readout_times(seq)[1] ≈ start_adc + 0.5 * 0.03125 @test variables.readout_times(seq)[end] ≈ start_adc + 8191.5 * 0.03125 end @@ -101,7 +101,7 @@ seq_json = read_sequence(io, format=:serialize) @test variables.duration(seq_orig) == variables.duration(seq_json) @test length(seq_orig) == length(seq_json) - @test all(duration.(seq_orig) .== duration.(seq_json)) + @test all(variables.duration.(seq_orig) .== variables.duration.(seq_json)) @test iszero(length(iter_instant_gradients(seq_json))) @test iszero(length(iter_instant_pulses(seq_json))) @test all(variables.readout_times(seq_json) .== variables.readout_times(seq_orig)) @@ -120,7 +120,7 @@ seq_json = read_sequence(io; format=:serialize) @test variables.duration(seq_orig) == variables.duration(seq_json) @test length(seq_orig) == length(seq_json) - @test all(duration.(seq_orig) .== duration.(seq_json)) + @test all(variables.duration.(seq_orig) .== variables.duration.(seq_json)) @test length(iter_instant_gradients(seq_json)) == length(iter_instant_gradients(seq_json)) @test length(iter_instant_pulses(seq_json)) == length(iter_instant_pulses(seq_json)) @test all(variables.readout_times(seq_json) .== variables.readout_times(seq_orig)) diff --git a/test/test_components.jl b/test/test_components.jl index f92736e..e8f6109 100644 --- a/test/test_components.jl +++ b/test/test_components.jl @@ -26,7 +26,7 @@ @testset "SincPulse" begin sp = SincPulse(amplitude=1., frequency=2., phase=0., lobe_duration=10., Nzeros=(2, 1)) - @test duration(sp) ≈ 30. + @test variables.duration(sp) ≈ 30. @test variables.amplitude(sp, 20) ≈ 1. @test variables.amplitude(sp, 0) ≈ 0. atol=1e-8 diff --git a/test/test_sequences.jl b/test/test_sequences.jl index 057b679..caa4834 100644 --- a/test/test_sequences.jl +++ b/test/test_sequences.jl @@ -27,7 +27,7 @@ seq = DiffusionSpinEcho(TE=:min, bval=1.) @test length(seq) == 9 grad_duration = variables.TE(seq) / 2 - @test all(isapprox.(duration.(seq), [0., 0., grad_duration, 0., 0., 0., grad_duration, 0., 0.], atol=1e-6)) + @test all(isapprox.(variables.duration.(seq), [0., 0., grad_duration, 0., 0., 0., grad_duration, 0., 0.], atol=1e-6)) @test length([iter_instant_pulses(seq)...]) == 2 @test length([iter_instant_gradients(seq)...]) == 0. @test variables.bval(seq) ≈ 1. @@ -38,24 +38,24 @@ @testset "Maximise b-value" begin seq = DiffusionSpinEcho(TE=80., bval=:max) @test length(seq) == 9 - @test all(isapprox.(duration.(seq), [0., 0., 40., 0., 0., 0., 40., 0., 0.], atol=1e-4, rtol=1e-4)) + @test all(isapprox.(variables.duration.(seq), [0., 0., 40., 0., 0., 0., 40., 0., 0.], atol=1e-4, rtol=1e-4)) @test length([iter_instant_pulses(seq)...]) == 2 @test length([iter_instant_gradients(seq)...]) == 0. @test variables.TE(seq) ≈ 80. @test variables.duration(seq) ≈ 80. - @test 4.8 < bval(seq) < 4.9 + @test 4.8 < variables.bval(seq) < 4.9 @test variables.rise_time(seq[:gradient]) ≈ min_rise_time rtol=1e-4 @test all(isapprox.(edge_times(seq, tol=1e-3), [0., min_rise_time, 40. - min_rise_time, 40, 40 + min_rise_time, 80 - min_rise_time, 80.], atol=1e-4)) # can also maximise q-value seq2 = DiffusionSpinEcho(TE=80., qval=:max) - @test all(isapprox.(duration.(seq), duration.(seq2), atol=1e-4, rtol=1e-4)) + @test all(isapprox.(variables.duration.(seq), variables.duration.(seq2), atol=1e-4, rtol=1e-4)) @test variables.TE(seq) ≈ variables.TE(seq2) atol=1e-4 rtol=1e-4 @test variables.bval(seq) ≈ variables.bval(seq2) atol=1e-4 rtol=1e-4 end @testset "Set diffusion time Δ" begin seq = DiffusionSpinEcho(TE=80., Δ=70., qval=:max) - @test all(isapprox.(duration.(seq), [0., 0., 10., 30., 0., 30., 10., 0., 0.], atol=1e-4, rtol=1e-4)) + @test all(isapprox.(variables.duration.(seq), [0., 0., 10., 30., 0., 30., 10., 0., 0.], atol=1e-4, rtol=1e-4)) @test variables.Δ(seq) ≈ 70. @test variables.TE(seq) ≈ 80. @test variables.duration(seq) ≈ 80. @@ -66,7 +66,7 @@ end @testset "Set gradient duration" begin seq = DiffusionSpinEcho(TE=80., gradient=(duration=10., ), bval=:max) - @test all(isapprox.(duration.(seq), [0., 0., 10., 30., 0., 30., 10., 0., 0.], atol=1e-4, rtol=1e-4)) + @test all(isapprox.(variables.duration.(seq), [0., 0., 10., 30., 0., 30., 10., 0., 0.], atol=1e-4, rtol=1e-4)) @test variables.Δ(seq) ≈ 70. rtol=1e-4 @test variables.TE(seq) ≈ 80. @test variables.duration(seq) ≈ 80. @@ -79,33 +79,33 @@ @testset "slice-select DW-SE" begin seq = DiffusionSpinEcho(duration=:min, bval=2., slice_thickness=2.) @test length(seq) == 9 - @test duration(seq[1]) > 1. + @test variables.duration(seq[1]) > 1. for index in 1:9 if index in [2, 4, 8, 9] - @test abs(duration(seq[index])) < 1e-6 + @test abs(variables.duration(seq[index])) < 1e-6 else - @test abs(duration(seq[index])) > 1 + @test abs(variables.duration(seq[index])) > 1 end end - @test duration(seq[:gradient]) ≈ duration(seq[:gradient2]) + @test variables.duration(seq[:gradient]) ≈ variables.duration(seq[:gradient2]) @test variables.bval(seq) ≈ 2. - @test length(readout_times(seq)) == 1 + @test length(variables.readout_times(seq)) == 1 @test variables.readout_times(seq)[1] > variables.TE(seq) end @testset "voxel-wise DW-SE" begin seq = DiffusionSpinEcho(duration=:min, bval=2., voxel_size=2., fov=(20, 20, 20)) @test length(seq) == 9 - @test duration(seq[1]) > 1. + @test variables.duration(seq[1]) > 1. for index in 1:9 if index in [2, 6, 8] - @test abs(duration(seq[index])) < 1e-6 + @test abs(variables.duration(seq[index])) < 1e-6 else - @test abs(duration(seq[index])) > 1 + @test abs(variables.duration(seq[index])) > 1 end end - @test duration(seq[:gradient]) ≈ duration(seq[:gradient2]) + @test variables.duration(seq[:gradient]) ≈ variables.duration(seq[:gradient2]) @test variables.bval(seq) ≈ 2. - @test length(readout_times(seq)) > 50 + @test length(variables.readout_times(seq)) > 50 @test 67 < variables.TE(seq) < 68 @test 72 < variables.duration(seq) < 73 -- GitLab