Skip to content
Snippets Groups Projects
Unverified Commit d72c03b3 authored by Michiel Cottaar's avatar Michiel Cottaar
Browse files

Document all the variables

parent c86d3118
No related branches found
No related tags found
1 merge request!2Define variables through new @defvar macro
...@@ -5,7 +5,7 @@ In most MR sequence building software, the user will have to set all of these fr ...@@ -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. In MRIBuilder the internal free parameters are not set directly.
Instead, they are inferred using a non-linear, constrained optimisation. 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). 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). 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. 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. ...@@ -16,8 +16,52 @@ All variables are available as members of the [`variables`](@ref) structure.
```@meta ```@meta
CollapsedDocStrings = true CollapsedDocStrings = true
``` ```
```@docs ```@docs; canonical=false
variables. 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 ## Variables interface
......
module AbstractTypes 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). 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. ...@@ -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 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. 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. ...@@ -30,6 +48,48 @@ Super type for all RF pulses.
""" """
abstract type RFPulseComponent <: EventComponent end 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. Super type for all readout events.
""" """
......
...@@ -85,12 +85,37 @@ end ...@@ -85,12 +85,37 @@ end
N_right(pulse::SincPulse) = pulse.Nzeros[2] N_right(pulse::SincPulse) = pulse.Nzeros[2]
end 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 @defvar pulse begin
phase(pulse::SincPulse) = pulse.phase phase(pulse::SincPulse) = pulse.phase
frequency(pulse::SincPulse) = pulse.frequency frequency(pulse::SincPulse) = pulse.frequency
lobe_duration(pulse::SincPulse) = pulse.lobe_duration lobe_duration(pulse::SincPulse) = pulse.lobe_duration
end end
"""
lobe_duration(sinc_pulse)
Time between two zero-crossings of a [`SincPulse`](@ref).
"""
variables.lobe_duration
@defvar begin @defvar begin
duration(pulse::SincPulse) = (variables.N_left(pulse) + variables.N_right(pulse)) * variables.lobe_duration(pulse) 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 flip_angle(pulse::SincPulse) = (pulse.norm_flip_angle[1] + pulse.norm_flip_angle[2]) * variables.amplitude(pulse) * variables.lobe_duration(pulse) * 360
......
...@@ -55,8 +55,43 @@ end ...@@ -55,8 +55,43 @@ end
resolution(adc::ADC) = adc.resolution resolution(adc::ADC) = adc.resolution
end 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) @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 readout_times(adc::ADC) = ((1:Int(variables.nsamples(adc))) .- 0.5) .* variables.dwell_time(adc)
@defvar begin @defvar begin
duration(adc::ADC) = variables.nsamples(adc) * variables.dwell_time(adc) duration(adc::ADC) = variables.nsamples(adc) * variables.dwell_time(adc)
......
...@@ -234,6 +234,18 @@ Similarly, if `last_event` is set to something else than `nothing`, only the gra ...@@ -234,6 +234,18 @@ Similarly, if `last_event` is set to something else than `nothing`, only the gra
""" """
variables.qvec 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) function edge_times(bb::BaseBuildingBlock)
res = Float64[] res = Float64[]
for (key, event) in events(bb) for (key, event) in events(bb)
......
...@@ -4,7 +4,7 @@ import LinearAlgebra: norm ...@@ -4,7 +4,7 @@ import LinearAlgebra: norm
import StaticArrays: SVector import StaticArrays: SVector
import JuMP: @constraint, @objective, objective_function import JuMP: @constraint, @objective, objective_function
import ...BuildSequences: global_model, global_scanner 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 ...Components: ChangingGradient, ConstantGradient, RFPulseComponent
import ...Containers: BaseBuildingBlock import ...Containers: BaseBuildingBlock
...@@ -83,8 +83,9 @@ function SpoiltSliceSelect(pulse::RFPulseComponent; orientation=[0, 0, 1], group ...@@ -83,8 +83,9 @@ function SpoiltSliceSelect(pulse::RFPulseComponent; orientation=[0, 0, 1], group
return res return res
end end
gradient_orientation(spoilt::SpoiltSliceSelect) = spoilt.orientation
@defvar begin @defvar begin
gradient_orientation(spoilt::SpoiltSliceSelect) = spoilt.orientation
duration_trap1(spoilt::SpoiltSliceSelect) = 2 * spoilt.rise_time1 + spoilt.flat_time1 - spoilt.diff_time 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 duration_trap2(spoilt::SpoiltSliceSelect) = 2 * spoilt.fall_time2 + spoilt.flat_time2 - spoilt.diff_time
end end
...@@ -121,5 +122,19 @@ get_pulse(spoilt::SpoiltSliceSelect) = spoilt.pulse ...@@ -121,5 +122,19 @@ get_pulse(spoilt::SpoiltSliceSelect) = spoilt.pulse
return [grad1, grad2, grad3] return [grad1, grad2, grad3]
end 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 end
\ No newline at end of file
...@@ -113,11 +113,34 @@ get_group(pg::BaseTrapezoid) = get_group(get_gradient(pg)) ...@@ -113,11 +113,34 @@ get_group(pg::BaseTrapezoid) = get_group(get_gradient(pg))
slew_rate(g::Trapezoid3D) = g.slew_rate slew_rate(g::Trapezoid3D) = g.slew_rate
end 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 @defvar gradient begin
gradient_strength(g::Trapezoid) = variables.slew_rate(g) .* variables.rise_time(g) gradient_strength(g::Trapezoid) = variables.slew_rate(g) .* variables.rise_time(g)
δ(g::Trapezoid) = variables.rise_time(g) + variables.flat_time(g) δ(g::Trapezoid) = variables.rise_time(g) + variables.flat_time(g)
end 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 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π @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) ...@@ -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) @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_pulse(ss::SliceSelect) = ss.pulse
get_gradient(ss::SliceSelect) = ss.trapezoid get_gradient(ss::SliceSelect) = ss.trapezoid
@defvar effective_time(ss::SliceSelect) = variables.effective_time(ss, :pulse) @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)) ...@@ -234,6 +266,29 @@ Base.getindex(lr::LineReadout, ::Val{:adc}) = ((1 - variables.ramp_overlap(lr))
effective_time(lr::LineReadout) = variables.effective_time(lr, :adc) effective_time(lr::LineReadout) = variables.effective_time(lr, :adc)
end 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_readout(lr::LineReadout) = lr.adc
get_gradient(lr::LineReadout) = lr.trapezoid get_gradient(lr::LineReadout) = lr.trapezoid
......
...@@ -71,4 +71,18 @@ get_pathway(ge::DiffusionSpinEcho) = Pathway(ge, [90, 180], 1, group=:diffusion) ...@@ -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) diffusion_time(ge::DiffusionSpinEcho) = start_time(ge, :gradient2) - start_time(ge, :gradient)
end 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 end
\ No newline at end of file
...@@ -57,5 +57,12 @@ get_pathway(ge::SpinEcho) = Pathway(ge, [90, 180], 1) ...@@ -57,5 +57,12 @@ get_pathway(ge::SpinEcho) = Pathway(ge, [90, 180], 1)
delay(ge::SpinEcho) = variables.duration_transverse(ge) - variables.echo_time(ge) delay(ge::SpinEcho) = variables.duration_transverse(ge) - variables.echo_time(ge)
end end
"""
echo_time(sequence)
Returns the echo time of a sequence in ms.
"""
variables.echo_time
end end
\ No newline at end of file
...@@ -249,6 +249,23 @@ def_alternate_variable!(:spoiler_scale, :qval, q->1e-3 * 2π/q, l->1e-3 * 2π/l, ...@@ -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, :qval_square, sqrt, q -> q * q, false)
def_alternate_variable!(:qval_square, :qvec, qv -> sum(q -> q * q, qv), nothing, 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] for vec_variable in [:gradient_strength, :slew_rate]
vec_square = Symbol(string(vec_variable) * "_square") vec_square = Symbol(string(vec_variable) * "_square")
vec_norm = Symbol(string(vec_variable) * "_norm") vec_norm = Symbol(string(vec_variable) * "_norm")
...@@ -256,6 +273,20 @@ for vec_variable in [:gradient_strength, :slew_rate] ...@@ -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) def_alternate_variable!(vec_square, vec_variable, v -> v[1] * v[1] + v[2] * v[2] + v[3] * v[3], nothing, false)
end 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] for name in [:slice_thickness, :bandwidth, :fov, :voxel_size]
inv_name = Symbol("inverse_" * string(name)) inv_name = Symbol("inverse_" * string(name))
def_alternate_variable!(name, inv_name, inv, inv, true) def_alternate_variable!(name, inv_name, inv, inv, true)
...@@ -269,6 +300,27 @@ for (name, alt_name) in [ ...@@ -269,6 +300,27 @@ for (name, alt_name) in [
def_alternate_variable!(name, alt_name, identity, identity, false) def_alternate_variable!(name, alt_name, identity, identity, false)
end 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. Parent type for any variable in the MRI sequence.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment