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