Sequence optimisation
In MRIBuilder an MR Sequence
is defined as a sequence of BuildingBlock
objects. Most BuildingBlock
objects will contain free parameters determining, for example, the duration of the block or the strength/orientation of the MR gradient. In most MR sequence building software, the user will have to set all of these free parameters by computing the appropriate values given a desired echo time, b-value, etc.
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 variables.echo_time
, variables.duration
, variables.resolution
, variables.gradient_strength
, variables.diffusion_time
, variables.duration_transverse
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. Internally, MRIBuilder will then optimise the BuildingBlock
free parameters to match any user-defined constraints and/or objectives. This optimisation uses the Ipopt optimiser accessed through the JuMP.jl library.
Summary variables
All variables are available as members of the variables
structure.
MRIBuilder.Variables.variables
— ModuleMain module containing all the MRIBuilder sequence variables.
All variables are available as members of this module, e.g. variables.echo_time
returns the echo time variable. New variables can be defined using @defvar
.
Set constraints on variables by passing them on as keywords during the sequence generation, e.g., seq=SpinEcho(echo_time=70)
.
After sequence generation you can get the variable values by calling variables.echo_time(seq)
. For the sequence defined above this would return 70. (or a number very close to that).
MRIBuilder.Variables.variables.N_left
— ConstantN_left(sinc_pulse)
Number of zero-crossings of the SincPulse
before the maximum.
Also, see variables.N_right
MRIBuilder.Variables.variables.N_right
— ConstantN_left(sinc_pulse)
Number of zero-crossings of the SincPulse
after the maximum.
Also, see variables.N_left
MRIBuilder.Variables.variables.TE
— ConstantTE(sequence)
Alternative name to compute the variables.echo_time
of a sequence in ms.
MRIBuilder.Variables.variables.TR
— ConstantTR(sequence)
Alternative name to compute the variables.repetition_time
of a sequence in ms.
MRIBuilder.Variables.variables.all_gradient_strengths
— Constantfall_time(spoilt_slice_select)
Returns the time of the SpoiltSliceSelect
to return to zero.
MRIBuilder.Variables.variables.amplitude
— Constantamplitude(pulse)
Return the amplitude of an RFPulseComponent
in kHz.
MRIBuilder.Variables.variables.area_under_curve
— Constantarea_under_curve(pathway::Pathway)
Return net displacement in k-space (i.e., spoiling) experienced by the spins following a specific Pathway
.
Only gradients active while the spins are in the transverse plane are considered.
Returns a NamedTuple with the area_under_curve
for all gradient groups.
MRIBuilder.Variables.variables.bandwidth
— Constantbandwidth(pulse)
Return the bandwidth of an RFPulseComponent
in kHz.
MRIBuilder.Variables.variables.bmat
— Constantbmat(pathway::Pathway)
Return 3x3 diffusion-weighted matrix experienced by the spins following a specific Pathway
in rad^2 ms/um^2.
Only gradients active while the spins are in the transverse plane are considered.
Returns a NamedTuple with the bmat
for all gradient groups.
MRIBuilder.Variables.variables.bmat_gradient
— Constantbmat_gradient(overlapping, qstart[, first_event, last_event])
Computes the addition to the variables.bmat
contributed by a specific building block or gradient.
qstart
represents the variables.qvec
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.
MRIBuilder.Variables.variables.bval
— Constantbval(pathway::Pathway)
Return size of diffusion-weighting experienced by the spins following a specific Pathway
in rad^2 ms/um^2.
Only gradients active while the spins are in the transverse plane will contribute to the diffusion weighting.
Returns a NamedTuple with the bval
for all gradient groups.
MRIBuilder.Variables.variables.delay
— Constantdelay(sequence)
Returns the offset beetween the readout and the spin echo in ms.
MRIBuilder.Variables.variables.diffusion_time
— Constantdiffusion_time(diffusion_sequence)
Returns the diffusion time of a DiffusionSpinEcho
in ms.
MRIBuilder.Variables.variables.duration
— Constantduration(block)
Duration of the sequence or building block in ms.
MRIBuilder.Variables.variables.duration_dephase
— Constantduration_dephase(pathway::Pathway)
Returns the net time that spins following the given Pathway
spent in the +transverse versus the -transverse state. This determines the amount of T2'-weighting as $e^{t/T_2'}$, where $t$ is the duration_dephase
.
Also see variables.duration_transverse
for T2-weighting.
MRIBuilder.Variables.variables.duration_state
— Constantduration_state(pathway::Pathway, transverse::Bool, positive::Bool)
Returns how long the Pathway
spent in a specific state.
The requested state can be set using transverse
and positive
as follows:
transverse=false
,positive=true
: +longitudinaltransverse=true
,positive=true
: +transversetransverse=false
,positive=false
: -longitudinaltransverse=true
,positive=false
: -transverse
MRIBuilder.Variables.variables.duration_transverse
— Constantduration_transverse(pathway::Pathway)
Returns the total amount of time that spins following the given Pathway
spent in the transverse plane. This determines the amount of T2-weighting as $e^{t/T_2}$, where $t$ is the duration_transverse
.
Also see variables.duration_dephase
for T2'-weighting.
MRIBuilder.Variables.variables.dwell_time
— Constantdwell_time(adc)
The dwell time of the ADC readout in ms.
MRIBuilder.Variables.variables.echo_time
— Constantecho_time(sequence)
Returns the echo time of a sequence in ms.
MRIBuilder.Variables.variables.effective_time
— Constanteffective_time(container, indices...)
Returns the start time of component with given indices
with respect to the start of the ContainerBlock
.
This will crash if the component does not have an variables.effective_time
(e.g., if it is (part of) a gradient waveform).
Also see variables.duration
, start_time
, and end_time
MRIBuilder.Variables.variables.flat_time
— Constantflat_time(trapezoid)
Returns the flat time of a Trapezoid
gradient profile in ms.
MRIBuilder.Variables.variables.flip_angle
— Constantflip_angle(pulse)
Return the flip angle of an RFPulseComponent
in degrees.
MRIBuilder.Variables.variables.fov
— Constantfov(readout)
Defines the field of view of a readout in mm.
MRIBuilder.Variables.variables.frequency
— Constantfrequency(pulse)
Return the off-resonance frequency of an RFPulseComponent
in kHz.
MRIBuilder.Variables.variables.gradient_strength
— Constantgradient_strength(gradient)
Maximum 3D gradient strength of the gradient in kHz/um.
MRIBuilder.Variables.variables.gradient_strength_norm
— Constantgradient_strength_norm(gradient)
The norm of the variables.gradient_strength
.
MRIBuilder.Variables.variables.lobe_duration
— Constantlobe_duration(sinc_pulse)
Time between two zero-crossings of a SincPulse
.
MRIBuilder.Variables.variables.net_dephasing
— Constantnet_dephasing(pathway::Pathway)
Return net displacement vector in k-space/q-space experienced by the spins following a specific Pathway
in kHz/um.
Only gradients active while the spins are in the transverse plane are considered.
Returns a NamedTuple with the qvec
for all gradient groups.
MRIBuilder.Variables.variables.nsamples
— Constantnsamples(adc)
Number of samples in an ADC.
MRIBuilder.Variables.variables.oversample
— Constantoversample(adc)
The oversampling rate of the ADC readout.
MRIBuilder.Variables.variables.phase
— Constantphase(pulse)
Return the phase of an RFPulseComponent
in degrees.
MRIBuilder.Variables.variables.qval
— Constantqval(gradient)
The norm of the variables.qvec
.
MRIBuilder.Variables.variables.qvec
— Constantqvec(overlapping[, first_event, last_event])
Computes the area under the curve for the gradient waveform in BaseBuildingBlock
.
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.
MRIBuilder.Variables.variables.ramp_overlap
— Constantramp_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.
MRIBuilder.Variables.variables.readout_times
— Constantreadout_times(sequence)
Returns all the times that the sequence will readout.
MRIBuilder.Variables.variables.resolution
— Constantresolution(readout)
Resolution of the readout.
MRIBuilder.Variables.variables.rise_time
— Constantrise_time(trapezoid)
Returns the rise time of a Trapezoid
gradient profile in ms.
MRIBuilder.Variables.variables.slew_rate
— Constantslew_rate(gradient)
Maximum 3D slew rate of the gradient in kHz/um/ms.
MRIBuilder.Variables.variables.slew_rate_norm
— Constantslew_rate_norm(gradient)
The norm of the variables.slew_rate
.
MRIBuilder.Variables.variables.slice_thickness
— Constantslice_thickness(slice_select)
Defines the slice thickness for a RF pulse with an active gradient in mm (e.g., SliceSelect
).
Defines as variables.gradient_strength_norm
(gradient) / variables.bandwidth
(pulse)
MRIBuilder.Variables.variables.spoiler_scale
— Constantspoiler_scale(gradient)
Spatial scale in mm over which the spoiler gradient will dephase by 2π.
Automatically computed based on variables.qvec
.
MRIBuilder.Variables.variables.time_to_center
— Constanttime_to_center(adc)
The time of the ADC readout to reach the center of k-space.
MRIBuilder.Variables.variables.voxel_size
— Constantvoxel_size(readout)
Defines the voxel size of a readout in mm.
MRIBuilder.Variables.variables.Δ
— ConstantΔ(sequence)
Alternative name to compute the variables.diffusion_time
of a sequence in ms.
MRIBuilder.Variables.variables.δ
— Constantδ(trapezoid)
Returns the effective duration of a Trapezoid
gradient profile in ms.
Defined as variables.rise_time
+ variables.flat_time
.
Variables interface
MRIBuilder.Variables
— ModuleDefines the functions that can be called on parts of an MRI sequence to query or constrain any variables.
In addition this defines:
variables
: module containing all variables.VariableType
: parent type for any variables (whether number or JuMP variable).get_free_variable
: helper function to create new JuMP variables.set_simple_constraints!
: callapply_simple_constraint!
for each keyword argument.apply_simple_constraint!
: set a simple equality constraint.get_pulse
/get_gradient
/get_readout
: Used to get the pulse/gradient/readout part of a building blockgradient_orientation
: returns the gradient orientation of a waveform if fixed.
MRIBuilder.Variables.VariableType
— TypeParent type for any variable in the MRI sequence.
Each variable can be one of:
- a new JuMP variable
- an expression linking this variable to other JuMP variable
- a number
Create these using get_free_variable
.
MRIBuilder.Variables.variables.TE
— ConstantTE(sequence)
Alternative name to compute the variables.echo_time
of a sequence in ms.
MRIBuilder.Variables.variables.TR
— ConstantTR(sequence)
Alternative name to compute the variables.repetition_time
of a sequence in ms.
MRIBuilder.Variables.variables.duration
— Constantduration(block)
Duration of the sequence or building block in ms.
MRIBuilder.Variables.variables.gradient_strength_norm
— Constantgradient_strength_norm(gradient)
The norm of the variables.gradient_strength
.
MRIBuilder.Variables.variables.qval
— Constantqval(gradient)
The norm of the variables.qvec
.
MRIBuilder.Variables.variables.slew_rate_norm
— Constantslew_rate_norm(gradient)
The norm of the variables.slew_rate
.
MRIBuilder.Variables.variables.spoiler_scale
— Constantspoiler_scale(gradient)
Spatial scale in mm over which the spoiler gradient will dephase by 2π.
Automatically computed based on variables.qvec
.
MRIBuilder.Variables.variables.Δ
— ConstantΔ(sequence)
Alternative name to compute the variables.diffusion_time
of a sequence in ms.
MRIBuilder.Variables.AbstractBlock
— TypeParent type of all components, building block, and sequences that form an MRI sequence.
MRIBuilder.Variables.Variable
— TypeA sequence property that can be constrained and/or optimised.
It acts as a function, so you can call it on a sequence or building block to get the actual values (e.g., v(sequence)
). It can return one of the following:
- a number
- a vector of number
- a NamedTuple with the values for individual sequence components
MRIBuilder.Variables.adjust_internal
— Functionadjust_internal(block, names_used; kwargs...)
Returns the adjusted blocks and add any keywords used in the process to names_used
.
This is a helper function used by adjust
.
MRIBuilder.Variables.adjustable
— Methodadjustable(block)
Returns whether a sequence, building block, or component can be adjusted
Can return one of:
:false
: not adjustable:gradient
: expects gradient adjustment parameters:pulse
: expects RF pulse adjustment parameters
MRIBuilder.Variables.apply_simple_constraint!
— Methodapply_simple_constraint!(variable, value)
Add a single constraint or objective to the variable
.
value
can be one of:
nothing
: do nothing:min
: minimise the variable:max
: maximise the variablenumber
: fix variable to this valueequation
: fix variable to the result of this equation
MRIBuilder.Variables.get_free_variable
— Methodget_free_variable(value; integer=false, start=0.01)
Get a representation of a given variable
given a user-defined constraint.
The result is guaranteed to be a VariableType
.
MRIBuilder.Variables.get_readout
— Functionget_readout(sequence)
Get the main readout events played out during the sequence.
This has to be defined for individual sequences to work.
Any readout
variables not explicitly defined for this sequence will be passed on to the readout.
MRIBuilder.Variables.gradient_orientation
— Functiongradient_orientation(building_block)
Returns the gradient orientation.
MRIBuilder.Variables.make_generic
— Functionmake_generic(sequence/building_block/component)
Returns a generic version of the BaseSequence
, BaseBuildingBlock
, or BaseComponent
- Sequences are all flattened and returned as a single
Sequence
containing onlyBuildingBlock
objects. - Any
BaseBuildingBlock
is converted into aBuildingBlock
. - Pulses are replaced with
GenericPulse
(except for instant pulses). - Instant readouts are replaced with
ADC
.
MRIBuilder.Variables.scanner_constraints!
— Methodscanner_constraints!(block)
Constraints variables.gradient_strength
and variables.slew_rate
to be less than the global_scanner
maximum.
MRIBuilder.Variables.set_simple_constraints!
— Methodset_simple_constraints!(block, kwargs)
Add any constraints or objective functions to the variables of a AbstractBlock
.
Each keyword argument has to match one of the functions in variables
(block). If set to a numeric value, a constraint will be added to fix the function value to that numeric value. If set to :min
or :max
, minimising or maximising this function will be added to the cost function.
MRIBuilder.Variables.variable_defined_for
— Methodvariable_defined_for(var, Val(type))
Check whether variable is defined for a specific sub-type.
MRIBuilder.Variables.@defvar
— Macro@defvar([getter, ], function(s))
Defines new variables
.
Each variable is defined as regular Julia functions embedded within a @defvar
macro. For example, to define a variables.echo_time
variable for a SpinEcho
sequence, one can use:
@defvar echo_time(ge::SpinEcho) = 2 * (variables.effective_time(ge, :refocus) - variables.effective_time(ge, :excitation))
Multiple variables can be defined in a single @defvar
by including them in a code block:
@defvar begin
function var1(seq::SomeSequenceType)
...
end
function var2(seq::SomeSequenceType)
...
end
end
Before the variable function definitions one can include a getter
. This getter
defines the type of the sequence component for which the variables will be defined. If the variable is not defined for the sequence, the variable will be extracted for those type of sequence components instead. The following sequence component types are provided:
pulse
: useget_pulse
gradient
: useget_gradient
readout
: useget_readout
pathway
: useget_pathway
e.g. the following defines a flip_angle
variable, which is marked as a property of an RF pulse.
@defvar pulse flip_angle(...) = ...
If after this definition, flip_angle
is not explicitly defined for any sequence, it will be extracted for the RF pulses in that sequence instead.