From 361d98e5711d8fcdf040af465389d1d564fec3fe Mon Sep 17 00:00:00 2001
From: Michiel Cottaar <michiel.cottaar@ndcn.ox.ac.uk>
Date: Sun, 28 Jan 2024 13:43:33 +0000
Subject: [PATCH] Define effective_time

---
 src/building_blocks.jl           | 23 ++++++++++++++++++++---
 src/pulses/constant_pulses.jl    |  3 ++-
 src/pulses/fixed_pulses.jl       |  9 ++++++---
 src/pulses/instant_pulses.jl     |  5 +++--
 src/pulses/pulses.jl             |  1 +
 src/pulses/sinc_pulses.jl        |  3 ++-
 src/readouts/instant_readouts.jl |  5 +++--
 src/variables.jl                 |  2 ++
 8 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/src/building_blocks.jl b/src/building_blocks.jl
index 51f7842..76912bd 100644
--- a/src/building_blocks.jl
+++ b/src/building_blocks.jl
@@ -2,7 +2,7 @@ module BuildingBlocks
 import JuMP: has_values, value, Model, @constraint, @objective, owner_model, objective_function, optimize!, AbstractJuMPScalar
 import Printf: @sprintf
 import ..Scanners: Scanner
-import ..Variables: variables, start_time, duration, end_time, gradient_strength, slew_rate
+import ..Variables: variables, start_time, duration, end_time, gradient_strength, slew_rate, effective_time
 
 """
 Parent type for all individual components out of which a sequence can be built.
@@ -18,6 +18,9 @@ abstract type BuildingBlock end
 Parent type for all RF pulses.
 
 RF pulses combined with gradients, should be childrent of [`ContainerBlock`](@ref) instead.
+
+Required methods:
+- [`effective_time`](@ref)(pulse): Best approximation of time the RF pulse is applied. This is defined relative to the start of the pulse.
 """
 abstract type RFPulseBlock <: BuildingBlock end
 
@@ -54,6 +57,21 @@ The [`BuildingBlock`](@ref) is defined by one or more indices as defined below.
 start_time(bb::BuildingBlock) = 0.
 start_time(container::ContainerBlock, index1, index2, more_indices...) = start_time(container, index1) + start_time(container[index1], index2, more_indices)
 
+"""
+    effective_time(pulse)
+    effective_time(readout)
+    effective_time(container, indices...)
+
+Returns the effective time of a pulse or readout.
+
+For a pulse, this means the timepoint at which one would place an [`InstantRFPulseBlock`](@ref) if one would want to have a similar effect.
+
+For a reaodut, this is the time the readout passes through the zero-point in k-space (or the minimum in k-space if it does not go through zero).
+
+The time is given with respect to the start of the pulse or readout, or to the start of a container if the pulse/readout is identified using indices.
+"""
+effective_time(bb::ContainerBlock, index, indices...) = start_time(bb, index) + effective_time(bb[index], indices...)
+
 """
     end_time(container, args...)
 
@@ -61,8 +79,7 @@ Returns the end time of the specific [`BuildingBlock`](@ref) within the containe
 The [`BuildingBlock`](@ref) is defined by one or more indices as defined below.
 """
 end_time(bb::BuildingBlock) = duration(bb::BuildingBlock)
-end_time(container::ContainerBlock, index1) = start_time(container, index1) + duration(container[index1])
-end_time(container::ContainerBlock, index1, index2, more_indices...) = start_time(container, index1) + end_time(container[index1], index2, more_indices)
+end_time(container::ContainerBlock, index1, indices...) = start_time(container, index1) + end_time(container[index1], indices...)
 
 
 """
diff --git a/src/pulses/constant_pulses.jl b/src/pulses/constant_pulses.jl
index 6e5625c..8275ce6 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, Model
 import ...BuildingBlocks: RFPulseBlock, set_simple_constraints!, fixed
-import ...Variables: variables, get_free_variable, flip_angle, phase, amplitude, frequency, bandwidth, start_time, end_time, VariableType, duration
+import ...Variables: variables, get_free_variable, flip_angle, phase, amplitude, frequency, bandwidth, start_time, end_time, VariableType, duration, effective_time
 import ...BuildSequences: @global_model_constructor
 import ..FixedPulses: FixedPulse
 
@@ -43,6 +43,7 @@ phase(pulse::ConstantPulse) = pulse.phase
 frequency(pulse::ConstantPulse) = pulse.frequency
 flip_angle(pulse::ConstantPulse) = amplitude(pulse) * duration(pulse) * 360
 bandwidth(pulse::ConstantPulse) = 3.79098854 / duration(pulse)
+effective_time(pulse::ConstantPulse) = duration(pulse) / 2
 
 variables(::Type{<:ConstantPulse}) = [amplitude, duration, phase, frequency, flip_angle, bandwidth]
 
diff --git a/src/pulses/fixed_pulses.jl b/src/pulses/fixed_pulses.jl
index d7dd240..49de3fe 100644
--- a/src/pulses/fixed_pulses.jl
+++ b/src/pulses/fixed_pulses.jl
@@ -1,7 +1,7 @@
 module FixedPulses
 
-import ...BuildingBlocks: RFPulseBlock, fixed
-import ...Variables: variables, duration
+import ...BuildingBlocks: RFPulseBlock, fixed, BuildingBlockPrinter
+import ...Variables: variables, duration, amplitude, effective_time
 
 
 """
@@ -33,8 +33,11 @@ end
 variables(::Type{<:FixedPulse}) = []
 
 duration(fp::FixedPulse) = maximum(fp.time)
+amplitude(fp::FixedPulse) = maximum(abs.(fp.amplitude))
+effective_time(pulse::FixedPulse) = pulse.time[argmax(abs(pulse.amplitude))]
+
+Base.show(io::IO, fp::FixedPulse) = print(io, "FixedPulse for $(duration(fp)) ms (peak at $(effective_time(fp)) ms)")
 
-Base.show(io::IO, fp::FixedPulse) = print(io, "FixedPulse for $(duration(fp)) ms")
 
 
 """
diff --git a/src/pulses/instant_pulses.jl b/src/pulses/instant_pulses.jl
index 9390d5f..5230306 100644
--- a/src/pulses/instant_pulses.jl
+++ b/src/pulses/instant_pulses.jl
@@ -1,7 +1,7 @@
 module InstantPulses
 import JuMP: @constraint, @variable, VariableRef, value, Model
 import ...BuildingBlocks: RFPulseBlock, fixed
-import ...Variables: flip_angle, phase, start_time, variables, duration, get_free_variable, VariableType
+import ...Variables: flip_angle, phase, start_time, variables, duration, get_free_variable, VariableType, effective_time
 import ...BuildSequences: @global_model_constructor
 import ..FixedPulses: FixedInstantPulse
 
@@ -25,8 +25,9 @@ end
 
 flip_angle(instant::InstantRFPulseBlock) = instant.flip_angle
 phase(instant::InstantRFPulseBlock) = instant.phase
-duration(instant::InstantRFPulseBlock) = 0.
+duration(::InstantRFPulseBlock) = 0.
 variables(::Type{<:InstantRFPulseBlock}) = [flip_angle, phase]
+effective_time(::InstantRFPulseBlock) = 0.
 
 
 function fixed(block::InstantRFPulseBlock)
diff --git a/src/pulses/pulses.jl b/src/pulses/pulses.jl
index ae19637..8eabf44 100644
--- a/src/pulses/pulses.jl
+++ b/src/pulses/pulses.jl
@@ -4,6 +4,7 @@ include("instant_pulses.jl")
 include("constant_pulses.jl")
 include("sinc_pulses.jl")
 
+import ..Variables: effective_time
 import .FixedPulses: FixedPulse
 import .InstantPulses: InstantRFPulseBlock
 import .ConstantPulses: ConstantPulse
diff --git a/src/pulses/sinc_pulses.jl b/src/pulses/sinc_pulses.jl
index c771a48..90b7988 100644
--- a/src/pulses/sinc_pulses.jl
+++ b/src/pulses/sinc_pulses.jl
@@ -4,7 +4,7 @@ import JuMP: VariableRef, @constraint, @variable, value, Model
 import QuadGK: quadgk
 import Polynomials: fit, Polynomial
 import ...BuildingBlocks: RFPulseBlock, set_simple_constraints!, fixed
-import ...Variables: flip_angle, phase, amplitude, frequency, bandwidth, VariableType, variables, get_free_variable, duration
+import ...Variables: flip_angle, phase, amplitude, frequency, bandwidth, VariableType, variables, get_free_variable, duration, effective_time
 import ...BuildSequences: @global_model_constructor
 import ..FixedPulses: FixedPulse
 
@@ -101,6 +101,7 @@ flip_angle(pulse::SincPulse) = (pulse.nlobe_integral(N_left(pulse)) + pulse.nlob
 lobe_duration(pulse::SincPulse) = pulse.lobe_duration
 bandwidth(pulse::SincPulse) = 1 / lobe_duration(pulse)
 variables(::Type{<:SincPulse}) = [amplitude, N_left, N_right, duration, phase, frequency, flip_angle, lobe_duration, bandwidth]
+effective_time(pulse::SincPulse) = N_left(pulse) * lobe_duration(pulse)
 
 function fixed(block::SincPulse)
     normed_times = -value(N_left(block)):0.1:value(N_right(block)) + 1e-5
diff --git a/src/readouts/instant_readouts.jl b/src/readouts/instant_readouts.jl
index d1ee57e..21e74a6 100644
--- a/src/readouts/instant_readouts.jl
+++ b/src/readouts/instant_readouts.jl
@@ -1,5 +1,5 @@
 module InstantReadouts
-import ...BuildingBlocks: BuildingBlock, to_block, fixed
+import ...BuildingBlocks: BuildingBlock, to_block, fixed, effective_time
 import ...Variables: variables, duration
 
 """
@@ -14,6 +14,7 @@ end
 
 variables(::Type{<:InstantReadout}) = []
 to_block(::Type{<:InstantReadout}) = InstantReadout()
-duration(::InstantReadout) =0.
+duration(::InstantReadout) = 0.
 fixed(i::InstantReadout) = i
+effective_time(::InstantReadout) = 0.
 end
\ No newline at end of file
diff --git a/src/variables.jl b/src/variables.jl
index 51cb70b..f47f85c 100644
--- a/src/variables.jl
+++ b/src/variables.jl
@@ -54,8 +54,10 @@ variables() = [values(symbol_to_func)...]
 area_under_curve(bb) = qval(bb)
 
 
+# These functions are more fully defined in building_blocks.jl
 function start_time end
 function end_time end
+function effective_time end
 
 
 const VariableType = Union{Number, AbstractJuMPScalar}
-- 
GitLab