From d72c03b36da696fb617157cdd9eb0a44fb5447e6 Mon Sep 17 00:00:00 2001
From: Michiel Cottaar <MichielCottaar@protonmail.com>
Date: Thu, 30 May 2024 15:46:38 +0100
Subject: [PATCH] Document all the variables

---
 docs/src/sequence_optimisation.md      | 50 +++++++++++++++++++--
 src/components/abstract_types.jl       | 62 +++++++++++++++++++++++++-
 src/components/pulses/sinc_pulses.jl   | 25 +++++++++++
 src/components/readouts/ADCs.jl        | 35 +++++++++++++++
 src/containers/building_blocks.jl      | 12 +++++
 src/parts/spoilt_slice_selects.jl      | 19 +++++++-
 src/parts/trapezoids.jl                | 55 +++++++++++++++++++++++
 src/sequences/diffusion_spin_echoes.jl | 14 ++++++
 src/sequences/spin_echoes.jl           |  7 +++
 src/variables.jl                       | 52 +++++++++++++++++++++
 10 files changed, 325 insertions(+), 6 deletions(-)

diff --git a/docs/src/sequence_optimisation.md b/docs/src/sequence_optimisation.md
index 4a1b918..e2505c2 100644
--- a/docs/src/sequence_optimisation.md
+++ b/docs/src/sequence_optimisation.md
@@ -5,7 +5,7 @@ In most MR sequence building software, the user will have to set all of these fr
 
 In MRIBuilder the internal free parameters are not set directly. 
 Instead, they are inferred using a non-linear, constrained optimisation.
-For each sequence type, the developer defines how to compute various summary variables from the `BuildingBlock` free parameters, such as [`echo_time`](@ref), [`duration`](@ref), [`resolution`](@ref), [`gradient_strength`](@ref), [`diffusion_time`](@ref), etc.
+For each sequence type, the developer defines how to compute various summary variables from the `BuildingBlock` free parameters, such as [`variables.echo_time`](@ref), [`variables.duration`](@ref), [`variables.resolution`](@ref), [`variables.gradient_strength`](@ref), [`variables.diffusion_time`](@ref), [`variables.duration_transverse`](@ref) etc.
 A user can then create a specific instantiation of the sequence by fixing any of these summary variables to their desired values (or setting them to `:min`/`:max` to minimise/maximise them).
 In addition to the user-defined constraints, this optimisation will also take into account any [scanner-defined constraints](@ref scanners).
 Internally, MRIBuilder will then optimise the `BuildingBlock` free parameters to match any user-defined constraints and/or objectives.
@@ -16,8 +16,52 @@ All variables are available as members of the [`variables`](@ref) structure.
 ```@meta
 CollapsedDocStrings = true
 ```
-```@docs
-variables.
+```@docs; canonical=false
+variables.N_left                    
+variables.N_right                   
+variables.TE                        
+variables.TR                        
+variables.all_gradient_strengths
+variables.amplitude                 
+variables.area_under_curve          
+variables.bandwidth                 
+variables.bmat                      
+variables.bmat_gradient
+variables.bval                      
+variables.delay                     
+variables.diffusion_time            
+variables.duration                  
+variables.duration_dephase
+variables.duration_state            
+variables.duration_transverse       
+variables.dwell_time
+variables.echo_time                 
+variables.effective_time            
+variables.flat_time                 
+variables.flip_angle
+variables.fov                       
+variables.frequency                 
+variables.gradient_strength         
+variables.gradient_strength_norm    
+variables.lobe_duration             
+variables.net_dephasing             
+variables.nsamples
+variables.oversample                
+variables.phase                     
+variables.qval                      
+variables.qvec
+variables.ramp_overlap              
+variables.readout_times             
+variables.resolution                
+variables.rise_time                 
+variables.slew_rate
+variables.slew_rate_norm            
+variables.slice_thickness           
+variables.spoiler_scale             
+variables.time_to_center            
+variables.voxel_size                
+variables.Δ                         
+variables.δ
 ```
 
 ## Variables interface
diff --git a/src/components/abstract_types.jl b/src/components/abstract_types.jl
index b57d161..bb2f9c4 100644
--- a/src/components/abstract_types.jl
+++ b/src/components/abstract_types.jl
@@ -1,5 +1,5 @@
 module AbstractTypes
-import ...Variables: AbstractBlock, variables, adjustable, gradient_orientation
+import ...Variables: AbstractBlock, variables, adjustable, gradient_orientation, @defvar
 
 """
 Super-type for all individual components that form an MRI sequence (i.e., RF pulse, gradient waveform, or readout event).
@@ -17,6 +17,24 @@ N should be 1 for a 1D gradient waveform or 3 for a 3D one.
 """
 abstract type GradientWaveform{N} <: BaseComponent end
 
+@defvar begin
+    function slew_rate end
+    function gradient_strength end
+end
+
+"""
+    slew_rate(gradient)
+
+Maximum 3D slew rate of the gradient in kHz/um/ms.
+"""
+variables.slew_rate
+
+"""
+    gradient_strength(gradient)
+
+Maximum 3D gradient strength of the gradient in kHz/um.
+"""
+variables.gradient_strength
 
 """
 Super-type for all RF pulses, instant gradients and readouts that might play out during a gradient waveform.
@@ -30,6 +48,48 @@ Super type for all RF pulses.
 """
 abstract type RFPulseComponent <: EventComponent end
 
+@defvar pulse begin
+    function phase end
+    function amplitude end
+    function flip_angle end
+    function frequency end
+end
+
+"""
+    phase(pulse)
+
+Return the phase of an [`RFPulseComponent`](@ref) in degrees.
+"""
+variables.phase
+
+"""
+    amplitude(pulse)
+
+Return the amplitude of an [`RFPulseComponent`](@ref) in kHz.
+"""
+variables.amplitude
+
+"""
+    frequency(pulse)
+
+Return the off-resonance frequency of an [`RFPulseComponent`](@ref) in kHz.
+"""
+variables.frequency
+
+"""
+    flip_angle(pulse)
+
+Return the flip angle of an [`RFPulseComponent`](@ref) in degrees.
+"""
+variables.flip_angle
+
+"""
+    bandwidth(pulse)
+
+Return the bandwidth of an [`RFPulseComponent`](@ref) in kHz.
+"""
+variables.bandwidth
+
 """
 Super type for all readout events.
 """
diff --git a/src/components/pulses/sinc_pulses.jl b/src/components/pulses/sinc_pulses.jl
index 5d92a74..55f7878 100644
--- a/src/components/pulses/sinc_pulses.jl
+++ b/src/components/pulses/sinc_pulses.jl
@@ -85,12 +85,37 @@ end
     N_right(pulse::SincPulse) = pulse.Nzeros[2]
 end
 
+"""
+    N_left(sinc_pulse)
+
+Number of zero-crossings of the [`SincPulse`](@ref) before the maximum.
+
+Also, see [`variables.N_right`](@ref)
+"""
+variables.N_left
+
+"""
+    N_left(sinc_pulse)
+
+Number of zero-crossings of the [`SincPulse`](@ref) after the maximum.
+
+Also, see [`variables.N_left`](@ref)
+"""
+variables.N_right
+
 @defvar pulse begin
     phase(pulse::SincPulse) = pulse.phase
     frequency(pulse::SincPulse) = pulse.frequency
     lobe_duration(pulse::SincPulse) = pulse.lobe_duration
 end
 
+"""
+    lobe_duration(sinc_pulse)
+
+Time between two zero-crossings of a [`SincPulse`](@ref).
+"""
+variables.lobe_duration
+
 @defvar begin
     duration(pulse::SincPulse) = (variables.N_left(pulse) + variables.N_right(pulse)) * variables.lobe_duration(pulse)
     flip_angle(pulse::SincPulse) = (pulse.norm_flip_angle[1] + pulse.norm_flip_angle[2]) * variables.amplitude(pulse) * variables.lobe_duration(pulse) * 360
diff --git a/src/components/readouts/ADCs.jl b/src/components/readouts/ADCs.jl
index 0cfd1e5..d6b6de4 100644
--- a/src/components/readouts/ADCs.jl
+++ b/src/components/readouts/ADCs.jl
@@ -55,8 +55,43 @@ end
     resolution(adc::ADC) = adc.resolution
 end
 
+"""
+    oversample(adc)
+
+The oversampling rate of the ADC readout.
+"""
+variables.oversample
+
+"""
+    dwell_time(adc)
+
+The dwell time of the ADC readout in ms.
+"""
+variables.dwell_time
+
+"""
+    time_to_center(adc)
+
+The time of the ADC readout to reach the center of k-space.
+"""
+variables.time_to_center
+
+"""
+    resolution(readout)
+
+Resolution of the readout.
+"""
+variables.resolution
+
 @defvar readout nsamples(adc::ADC) = variables.resolution(adc) * variables.oversample(adc)
 
+"""
+    nsamples(adc)
+
+Number of samples in an ADC.
+"""
+variables.nsamples
+
 @defvar readout_times(adc::ADC) = ((1:Int(variables.nsamples(adc))) .- 0.5) .* variables.dwell_time(adc)
 @defvar begin
     duration(adc::ADC) = variables.nsamples(adc) * variables.dwell_time(adc)
diff --git a/src/containers/building_blocks.jl b/src/containers/building_blocks.jl
index b399f2e..0e3322f 100644
--- a/src/containers/building_blocks.jl
+++ b/src/containers/building_blocks.jl
@@ -234,6 +234,18 @@ Similarly, if `last_event` is set to something else than `nothing`, only the gra
 """
 variables.qvec
 
+"""
+    bmat_gradient(overlapping, qstart[, first_event, last_event])
+
+Computes the addition to the [`variables.bmat`](@ref) contributed by a specific building block or gradient.
+
+`qstart` represents the [`qvec`](@ref) at the start of this component.
+
+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.
+"""
+variables.bmat_gradient
+
 function edge_times(bb::BaseBuildingBlock)
     res = Float64[]
     for (key, event) in events(bb)
diff --git a/src/parts/spoilt_slice_selects.jl b/src/parts/spoilt_slice_selects.jl
index 2d2fe4d..f0665b1 100644
--- a/src/parts/spoilt_slice_selects.jl
+++ b/src/parts/spoilt_slice_selects.jl
@@ -4,7 +4,7 @@ import LinearAlgebra: norm
 import StaticArrays: SVector
 import JuMP: @constraint, @objective, objective_function
 import ...BuildSequences: global_model, global_scanner
-import ...Variables: VariableType, get_pulse, set_simple_constraints!, variables, @defvar
+import ...Variables: VariableType, get_pulse, set_simple_constraints!, variables, @defvar, gradient_orientation
 import ...Components: ChangingGradient, ConstantGradient, RFPulseComponent
 import ...Containers: BaseBuildingBlock
 
@@ -83,8 +83,9 @@ function SpoiltSliceSelect(pulse::RFPulseComponent; orientation=[0, 0, 1], group
     return res
 end
 
+gradient_orientation(spoilt::SpoiltSliceSelect) = spoilt.orientation
+
 @defvar begin
-    gradient_orientation(spoilt::SpoiltSliceSelect) = spoilt.orientation
     duration_trap1(spoilt::SpoiltSliceSelect) = 2 * spoilt.rise_time1 + spoilt.flat_time1 - spoilt.diff_time
     duration_trap2(spoilt::SpoiltSliceSelect) = 2 * spoilt.fall_time2 + spoilt.flat_time2 - spoilt.diff_time
 end
@@ -121,5 +122,19 @@ get_pulse(spoilt::SpoiltSliceSelect) = spoilt.pulse
     return [grad1, grad2, grad3]
 end
 
+"""
+    all_gradient_strengths(spoilt_slice_select)
+
+Returns the gradient strength before, during, and after the pulse in [`SpoiltSliceSelect`](@ref).
+"""
+variables.all_gradient_strengths
+
+
+"""
+    fall_time(spoilt_slice_select)
+
+Returns the time of the [`SpoiltSliceSelect`](@ref) to return to zero.
+"""
+variables.all_gradient_strengths
 
 end
\ No newline at end of file
diff --git a/src/parts/trapezoids.jl b/src/parts/trapezoids.jl
index 2deab41..8e078a2 100644
--- a/src/parts/trapezoids.jl
+++ b/src/parts/trapezoids.jl
@@ -113,11 +113,34 @@ get_group(pg::BaseTrapezoid) = get_group(get_gradient(pg))
     slew_rate(g::Trapezoid3D) = g.slew_rate
 end
 
+"""
+    rise_time(trapezoid)
+
+Returns the rise time of a [`Trapezoid`](@ref) gradient profile in ms.
+"""
+variables.rise_time
+
+"""
+    flat_time(trapezoid)
+
+Returns the flat time of a [`Trapezoid`](@ref) gradient profile in ms.
+"""
+variables.flat_time
+
 @defvar gradient begin
     gradient_strength(g::Trapezoid) = variables.slew_rate(g) .* variables.rise_time(g)
     δ(g::Trapezoid) = variables.rise_time(g) + variables.flat_time(g)
 end
 
+"""
+    δ(trapezoid)
+
+Returns the effective duration of a [`Trapezoid`](@ref) gradient profile in ms.
+
+Defined as [`variables.rise_time`](@ref) + [`variables.flat_time`](@ref).
+"""
+variables.δ
+
 @defvar duration(g::BaseTrapezoid) = 2 * variables.rise_time(g) + variables.flat_time(g)
 
 @defvar qvec(g::BaseTrapezoid, ::Nothing, ::Nothing) = variables.δ(g) .* variables.gradient_strength(g) .* 2π
@@ -184,6 +207,15 @@ Base.getindex(pg::SliceSelect, ::Val{:pulse}) = (0., pg.pulse)
 
 @defvar pulse inverse_slice_thickness(ss::SliceSelect) = 1e3 * variables.gradient_strength_norm(ss.trapezoid) .* variables.inverse_bandwidth(ss.pulse)
 
+"""
+    slice_thickness(slice_select)
+
+Defines the slice thickness for a RF pulse with an active gradient in mm (e.g., [`SliceSelect`](@ref)).
+
+Defines as [`variables.gradient_strength_norm`](@ref)(gradient) / [`variables.bandwidth`](@ref)(pulse)
+"""
+variables.slice_thickness
+
 get_pulse(ss::SliceSelect) = ss.pulse
 get_gradient(ss::SliceSelect) = ss.trapezoid
 @defvar effective_time(ss::SliceSelect) = variables.effective_time(ss, :pulse)
@@ -234,6 +266,29 @@ Base.getindex(lr::LineReadout, ::Val{:adc}) = ((1 - variables.ramp_overlap(lr))
     effective_time(lr::LineReadout) = variables.effective_time(lr, :adc)
 end
 
+"""
+    fov(readout)
+
+Defines the field of view of a readout in mm.
+"""
+variables.fov
+
+"""
+    voxel_size(readout)
+
+Defines the voxel size of a readout in mm.
+"""
+variables.voxel_size
+
+"""
+    ramp_overlap(line_readout)
+
+Return the fraction of the gradient ramp that overlaps with the ADC readout.
+
+Set to 0 to ensure that the ADC is only active during the flat time of the readout.
+"""
+variables.ramp_overlap
+
 get_readout(lr::LineReadout) = lr.adc
 get_gradient(lr::LineReadout) = lr.trapezoid
 
diff --git a/src/sequences/diffusion_spin_echoes.jl b/src/sequences/diffusion_spin_echoes.jl
index 2fc2355..80f741e 100644
--- a/src/sequences/diffusion_spin_echoes.jl
+++ b/src/sequences/diffusion_spin_echoes.jl
@@ -71,4 +71,18 @@ get_pathway(ge::DiffusionSpinEcho) = Pathway(ge, [90, 180], 1, group=:diffusion)
     diffusion_time(ge::DiffusionSpinEcho) = start_time(ge, :gradient2) - start_time(ge, :gradient)
 end
 
+"""
+    diffusion_time(diffusion_sequence)
+
+Returns the diffusion time of a [`DiffusionSpinEcho`](@ref) in ms.
+"""
+variables.diffusion_time
+
+"""
+    delay(sequence)
+
+Returns the offset beetween the readout and the spin echo in ms.
+"""
+variables.delay
+
 end
\ No newline at end of file
diff --git a/src/sequences/spin_echoes.jl b/src/sequences/spin_echoes.jl
index f1d8ff4..83fea19 100644
--- a/src/sequences/spin_echoes.jl
+++ b/src/sequences/spin_echoes.jl
@@ -57,5 +57,12 @@ get_pathway(ge::SpinEcho) = Pathway(ge, [90, 180], 1)
     delay(ge::SpinEcho) = variables.duration_transverse(ge) - variables.echo_time(ge)
 end
 
+"""
+    echo_time(sequence)
+
+Returns the echo time of a sequence in ms.
+"""
+variables.echo_time
+
 
 end
\ No newline at end of file
diff --git a/src/variables.jl b/src/variables.jl
index 903497b..ee824f3 100644
--- a/src/variables.jl
+++ b/src/variables.jl
@@ -249,6 +249,23 @@ def_alternate_variable!(:spoiler_scale, :qval, q->1e-3 * 2Ï€/q, l->1e-3 * 2Ï€/l,
 def_alternate_variable!(:qval, :qval_square, sqrt, q -> q * q, false)
 def_alternate_variable!(:qval_square, :qvec, qv -> sum(q -> q * q, qv), nothing, false)
 
+"""
+    qval(gradient)
+
+The norm of the [`qvec`](@ref).
+"""
+variables.qval
+
+"""
+    spoiler_scale(gradient)
+
+Spatial scale in mm over which the spoiler gradient will dephase by 2Ï€.
+
+Automatically computed based on [`qvec`](@ref).
+"""
+variables.spoiler_scale
+
+
 for vec_variable in [:gradient_strength, :slew_rate]
     vec_square = Symbol(string(vec_variable) * "_square")
     vec_norm = Symbol(string(vec_variable) * "_norm")
@@ -256,6 +273,20 @@ for vec_variable in [:gradient_strength, :slew_rate]
     def_alternate_variable!(vec_square, vec_variable, v -> v[1] * v[1] + v[2] * v[2] + v[3] * v[3], nothing, false)
 end
 
+"""
+    gradient_strength_norm(gradient)
+
+The norm of the [`gradient_strength`](@ref).
+"""
+variables.gradient_strength_norm
+
+"""
+    slew_rate_norm(gradient)
+
+The norm of the [`slew_rate`](@ref).
+"""
+variables.slew_rate_norm
+
 for name in [:slice_thickness, :bandwidth, :fov, :voxel_size]
     inv_name = Symbol("inverse_" * string(name))
     def_alternate_variable!(name, inv_name, inv, inv, true)
@@ -269,6 +300,27 @@ for (name, alt_name) in [
     def_alternate_variable!(name, alt_name, identity, identity, false)
 end
 
+"""
+    TE(sequence)
+
+Alternative name to compute the [`variables.echo_time`](@ref) of a sequence in ms.
+"""
+variables.TE
+
+"""
+    TR(sequence)
+
+Alternative name to compute the [`variables.repetition_time`](@ref) of a sequence in ms.
+"""
+variables.TR
+
+"""
+    Δ(sequence)
+
+Alternative name to compute the [`variables.diffusion_time`](@ref) of a sequence in ms.
+"""
+variables.Δ
+
 
 """
 Parent type for any variable in the MRI sequence.
-- 
GitLab