From bdb966ee0a8cff9849c578e869896fcf31a7c302 Mon Sep 17 00:00:00 2001 From: Michiel Cottaar <michiel.cottaar@ndcn.ox.ac.uk> Date: Fri, 26 Jan 2024 14:47:46 +0000 Subject: [PATCH] Add sequence back into ConcretBlocks to fix timings --- src/concrete_blocks.jl | 31 ++++++++++++++++++------------ src/gradients/instant_gradients.jl | 7 ++++--- src/gradients/pulsed_gradients.jl | 6 +++--- src/pulses/constant_pulses.jl | 6 +++--- src/pulses/instant_pulses.jl | 7 ++++--- src/pulses/sinc_pulses.jl | 6 +++--- 6 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/concrete_blocks.jl b/src/concrete_blocks.jl index fa0559c..9d0e2f8 100644 --- a/src/concrete_blocks.jl +++ b/src/concrete_blocks.jl @@ -50,35 +50,38 @@ ConcreteGradient(values::Tuple{<:Vector, <:Vector, <:Vector, <:Vector}) = Concre A [`BuildingBlock`](@ref) that is fully defined (i.e., there are no variables to be optimised). """ struct ConcreteBlock <: AbstractConcreteBlock + builder :: AbstractSequence duration :: Float64 pulse :: Union{ConcreteRFPulse, Nothing} gradient :: Union{ConcreteGradient, Nothing} readout_times :: Vector{Float64} end -function ConcreteBlock(duration::Number; pulse=nothing, gradient=nothing, readout_times=Number[]) +ConcreteBlock(args...; kwargs...) = BuildingBlockPlaceholder{ConcreteBlock}(args...; kwargs...) + +function ConcreteBlock(builder::AbstractSequence, duration::Number; pulse=nothing, gradient=nothing, readout_times=Number[]) ConcreteBlock(builder, duration, ConcreteRFPulse(pulse), ConcreteGradient(gradient), Float64.(readout_times)) end -to_block(::SequenceBuilder, concrete::AbstractConcreteBlock) = concrete + has_values(::AbstractConcreteBlock) = true duration(c::AbstractConcreteBlock) = 0. duration(c::ConcreteBlock) = c.duration """ - ConcreteBlock(other_building_block) + ConcreteBlock(sequence, other_building_block) Creates a [`ConcreteBlock`](@ref) from another [`BuildingBlock`](@ref). This will raise an error if the other [`BuildingBlock`](@ref) has not been optimised yet. If it has been optimised, then [`to_concrete_block`](@ref) will be called. """ -function ConcreteBlock(block::BuildingBlock) +function ConcreteBlock(sequence::AbstractSequence, block::BuildingBlock) if !has_values(block) error("Making `BuildingBlock` objects concrete is only possible after optimisation.") end - return to_concrete_block(block) + return to_concrete_block(sequence, block) end @@ -89,13 +92,13 @@ Internal function used to create [`ConcreteBlock`](@ref) from any [`BuildingBloc This needs to be defined for every [`BuildingBlock`](@ref) """ -function to_concrete_block(cb::AbstractConcreteBlock) - return cb +function to_concrete_block(sequence, cb::ConcreteBlock) + return ConcreteBlock(sequence, cb.duration, cb.pulse, cb.gradient, cb.readout_times) end properties(::Type{<:ConcreteBlock}) = [] -has_values(c::ConcreteBlock) = true +has_values(c::AbstractConcreteBlock) = has_values(c.builder) """ @@ -111,11 +114,15 @@ struct Sequence <: AbstractSequence TR :: Number end -function Sequence(seq::SequenceBuilder) - if !has_values(seq) - optimize!(seq.model) +function Sequence(builder::SequenceBuilder) + if !has_values(builder) + optimize!(builder.model) + end + seq = Sequence(AbstractConcreteBlock[], value(TR)) + for block in builder.blocks + push!(seq.blocks, ConcreteBlock(seq, block)) end - return Sequence(ConcreteBlock.(seq.blocks), value(seq.TR)) + return seq end end \ No newline at end of file diff --git a/src/gradients/instant_gradients.jl b/src/gradients/instant_gradients.jl index ff8cd6a..114cd56 100644 --- a/src/gradients/instant_gradients.jl +++ b/src/gradients/instant_gradients.jl @@ -2,7 +2,7 @@ module InstantGradients import JuMP: @constraint, @variable, VariableRef import ...BuildingBlocks: BuildingBlock, properties, BuildingBlockPlaceholder, set_simple_constraints!, duration import ...SequenceBuilders: SequenceBuilder, owner_model, start_time -import ...ConcreteBlocks: to_concrete_block, AbstractConcreteBlock +import ...ConcreteBlocks: to_concrete_block, AbstractConcreteBlock, Sequence, AbstractSequence import ..IntegrateGradients: qval, bval """ @@ -51,12 +51,13 @@ Instantaneous MR gradient with no free variables. See [`InstantGradientBlock`](@ref) for a version where [`qval`](@ref) is variable. """ struct ConcreteInstantGradient <: AbstractConcreteBlock + builder :: AbstractSequence orientation :: Any qval :: Number end -function to_concrete_block(block::InstantGradientBlock) - return ConcreteInstantGradient(block.orientation, value(qval(block))) +function to_concrete_block(builder::Sequence, block::InstantGradientBlock) + return ConcreteInstantGradient(builder, block.orientation, value(qval(block))) end diff --git a/src/gradients/pulsed_gradients.jl b/src/gradients/pulsed_gradients.jl index 08f7f81..e0a955b 100644 --- a/src/gradients/pulsed_gradients.jl +++ b/src/gradients/pulsed_gradients.jl @@ -7,7 +7,7 @@ import JuMP: @constraint, @variable, Model, VariableRef, owner_model, value import StaticArrays: SVector import ...BuildingBlocks: BuildingBlock, duration, properties, set_simple_constraints!, BuildingBlockPlaceholder, gradient_strength, slew_rate import ...SequenceBuilders: SequenceBuilder, start_time -import ...ConcreteBlocks: ConcreteBlock, to_concrete_block +import ...ConcreteBlocks: ConcreteBlock, to_concrete_block, AbstractSequence import ..IntegrateGradients: qval, bval @@ -116,7 +116,7 @@ end properties(::Type{<:PulsedGradient}) = [qval, δ, gradient_strength, duration, rise_time, flat_time, slew_rate] -function to_concrete_block(block::PulsedGradient) +function to_concrete_block(s::AbstractSequence, block::PulsedGradient) if block.orientation == :bvec rotate = true qvec = [value(qval(block)), 0., 0.] @@ -132,7 +132,7 @@ function to_concrete_block(block::PulsedGradient) t_start = value(start_time(block)) t_rise = value(rise_time(block)) t_d = value(δ(block)) - return ConcreteBlock(t_start + t_d + t_rise, gradients=[ + return ConcreteBlock(s, t_start + t_d + t_rise, gradients=[ (t_start, zeros(3)), (t_start + t_rise, qvec), (t_start + t_d, qvec), diff --git a/src/pulses/constant_pulses.jl b/src/pulses/constant_pulses.jl index 1468e45..992caa5 100644 --- a/src/pulses/constant_pulses.jl +++ b/src/pulses/constant_pulses.jl @@ -1,7 +1,7 @@ module ConstantPulses import JuMP: VariableRef, @constraint, @variable, value import ...BuildingBlocks: BuildingBlock, properties, BuildingBlockPlaceholder, set_simple_constraints!, duration -import ...SequenceBuilders: SequenceBuilder, owner_model, start_time, end_time +import ...SequenceBuilders: SequenceBuilder, owner_model, start_time, end_time, AbstractSequence import ...ConcreteBlocks: ConcreteBlock, to_concrete_block import ..Properties: flip_angle, phase, amplitude, frequency, bandwidth @@ -49,10 +49,10 @@ bandwidth(pulse::ConstantPulse) = 3.79098854 / duration(pulse) properties(::Type{<:ConstantPulse}) = [amplitude, duration, phase, frequency, flip_angle, bandwidth] -function to_concrete_block(block::ConstantPulse) +function to_concrete_block(s::AbstractSequence, block::ConstantPulse) d = value(duration(block)) final_phase = value(phase(block)) + d * value(frequency(block)) * 360 - return ConcreteBlock(value(duration(block)), pulse=[ + return ConcreteBlock(s, value(duration(block)), pulse=[ ([0., d]), value.([amplitude(block), amplitude(block)]), value.([phase(block), final_phase]) diff --git a/src/pulses/instant_pulses.jl b/src/pulses/instant_pulses.jl index 0c018b1..58a2c6f 100644 --- a/src/pulses/instant_pulses.jl +++ b/src/pulses/instant_pulses.jl @@ -2,7 +2,7 @@ module InstantPulses import JuMP: @constraint, @variable, VariableRef, value import ...BuildingBlocks: BuildingBlock, properties, BuildingBlockPlaceholder, set_simple_constraints!, duration import ...SequenceBuilders: SequenceBuilder, owner_model, start_time -import ...ConcreteBlocks: to_concrete_block, AbstractConcreteBlock +import ...ConcreteBlocks: to_concrete_block, AbstractConcreteBlock, Sequence, AbstractSequence import ..Properties: flip_angle, phase struct InstantRFPulseBlock <: BuildingBlock @@ -38,12 +38,13 @@ Instantaneous RF pulse with no free variables. See [`InstantRFPulseBlock`](@ref) for a version where [`flip_angle`](@ref) and [`phase`](@ref) are variables. """ struct ConcreteInstantRFPulse <: AbstractConcreteBlock + builder :: AbstractSequence flip_angle :: Number phase :: Number end -function to_concrete_block(block::InstantRFPulseBlock) - return ConcreteInstantRFPulse(value(flip_angle(block)), value(phase(block))) +function to_concrete_block(builder::Sequence, block::InstantRFPulseBlock) + return ConcreteInstantRFPulse(builder, value(flip_angle(block)), value(phase(block))) end end \ No newline at end of file diff --git a/src/pulses/sinc_pulses.jl b/src/pulses/sinc_pulses.jl index dcdce06..2fa8e96 100644 --- a/src/pulses/sinc_pulses.jl +++ b/src/pulses/sinc_pulses.jl @@ -4,7 +4,7 @@ import JuMP: VariableRef, @constraint, @variable, value import QuadGK: quadgk import Polynomials: fit, Polynomial import ...BuildingBlocks: BuildingBlock, properties, BuildingBlockPlaceholder, set_simple_constraints!, duration -import ...SequenceBuilders: SequenceBuilder, owner_model, start_time, end_time +import ...SequenceBuilders: SequenceBuilder, owner_model, start_time, end_time, AbstractSequence import ...ConcreteBlocks: ConcreteBlock, to_concrete_block import ..Properties: flip_angle, phase, amplitude, frequency, bandwidth @@ -100,13 +100,13 @@ lobe_duration(pulse::SincPulse) = pulse.lobe_duration bandwidth(pulse::SincPulse) = 1 / lobe_duration(pulse) properties(::Type{<:SincPulse}) = [amplitude, N_left, N_right, duration, phase, frequency, flip_angle, lobe_duration, bandwidth] -function to_concrete_block(block::SincPulse) +function to_concrete_block(s::AbstractSequence, block::SincPulse) normed_times = -value(N_left(block)):0.1:value(N_right(block)) + 1e-5 times = ((normed_times .+ value(N_left(block))) .* value(lobe_duration(block))) amplitudes = value(amplitude(block)) .* (normalised_function.(normed_times; apodise=block.apodise)) phases = (value(frequency(block)) .* value(lobe_duration(block))) .* normed_times * 360 return ConcreteBlock( - value(duration(block)); + s, value(duration(block)); pulse_amplitude=Shape(times, amplitudes), pulse_phase=Shape(times, phases), ) -- GitLab