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.
In addition to any user-defined objectives, the developer might also have defined secondary objectives (e.g., minimise the total sequence duration). These objective functions will only be considered if they do not affect the result of the user-defined primary objective. More details on these developer-defined secondary objectives can be found in the section on defining new sequences
Summary variables
All variables are available as members of the variables structure.
MRIBuilder.Variables.variables — Module
Main 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 — Function
N_left(sinc_pulse)Number of zero-crossings of the SincPulse before the maximum.
Also, see variables.N_right
MRIBuilder.Variables.variables.N_right — Function
N_left(sinc_pulse)Number of zero-crossings of the SincPulse after the maximum.
Also, see variables.N_left
MRIBuilder.Variables.variables.TE — Function
echo_time(sequence)Returns the echo time of a sequence in ms.
This is typically defined as the time between the excitation pulse and the crossing of k=0 during the MRI readout.
echo_time(sequence)Computes the echo time(s) of a sequence in ms.
MRIBuilder.Variables.variables.TR — Function
repetition_time(sequence)Computes the repetition_times of a sequence in ms.
MRIBuilder.Variables.variables.all_gradient_strengths — Function
all_gradient_strengths(spoilt_slice_select)Returns the gradient strength before, during, and after the pulse in SpoiltSliceSelect.
MRIBuilder.Variables.variables.amplitude — Function
amplitude(pulse)Return the amplitude of an RFPulseComponent in kHz.
MRIBuilder.Variables.variables.area_under_curve — Function
area_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 — Function
bandwidth(pulse)Return the bandwidth of an RFPulseComponent in kHz.
MRIBuilder.Variables.variables.bmat — Function
bmat(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 — Function
bmat_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 — Function
bval(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 — Function
delay(sequence)Returns the offset beetween the readout and the spin echo in ms.
MRIBuilder.Variables.variables.diffusion_time — Function
diffusion_time(diffusion_sequence)Returns the diffusion time of a DiffusionSpinEcho in ms.
diffusion_time(sequence)Computes the diffusion time of a sequence in ms.
MRIBuilder.Variables.variables.duration — Function
duration(block)Duration of the sequence or building block in ms.
MRIBuilder.Variables.variables.duration_dephase — Function
duration_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 — Function
duration_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 — Function
duration_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 — Function
dwell_time(adc)The dwell time of the ADC readout in ms.
MRIBuilder.Variables.variables.echo_time — Function
echo_time(sequence)Returns the echo time of a sequence in ms.
This is typically defined as the time between the excitation pulse and the crossing of k=0 during the MRI readout.
echo_time(sequence)Computes the echo time(s) of a sequence in ms.
MRIBuilder.Variables.variables.effective_time — Function
effective_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 — Function
flat_time(trapezoid)Returns the flat time of a Trapezoid gradient profile in ms.
MRIBuilder.Variables.variables.flip_angle — Function
flip_angle(pulse)Return the flip angle of an RFPulseComponent in degrees.
MRIBuilder.Variables.variables.fov — Function
fov(readout)Defines the field of view of a readout in mm.
MRIBuilder.Variables.variables.frequency — Function
frequency(pulse)Return the off-resonance frequency of an RFPulseComponent in kHz.
MRIBuilder.Variables.variables.gradient_strength — Function
gradient_strength(gradient)Maximum 3D gradient strength of the gradient in kHz/um.
MRIBuilder.Variables.variables.gradient_strength_norm — Function
gradient_strength_norm(gradient)The norm of the variables.gradient_strength.
MRIBuilder.Variables.variables.lobe_duration — Function
lobe_duration(sinc_pulse)Time between two zero-crossings of a SincPulse.
MRIBuilder.Variables.variables.net_dephasing — Function
net_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 — Function
nsamples(adc)Number of samples in an ADC.
MRIBuilder.Variables.variables.oversample — Function
oversample(adc)The oversampling rate of the ADC readout.
MRIBuilder.Variables.variables.phase — Function
phase(pulse)Return the phase of an RFPulseComponent in degrees.
MRIBuilder.Variables.variables.qval — Function
qval(gradient)The norm of the variables.qvec.
MRIBuilder.Variables.variables.qvec — Function
qvec(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.
qvec(gradient)The total integral of the area under the gradient curve as a length-3 vector.
The norm of this vector is available as qval.
MRIBuilder.Variables.variables.ramp_overlap — Function
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.
MRIBuilder.Variables.variables.readout_times — Function
readout_times(sequence)Returns all the times that the sequence will readout.
MRIBuilder.Variables.variables.resolution — Function
resolution(readout)Resolution of the readout.
MRIBuilder.Variables.variables.rise_time — Function
rise_time(trapezoid)Returns the rise time of a Trapezoid gradient profile in ms.
MRIBuilder.Variables.variables.slew_rate — Function
slew_rate(gradient)Maximum 3D slew rate of the gradient in kHz/um/ms.
MRIBuilder.Variables.variables.slew_rate_norm — Function
slew_rate_norm(gradient)The norm of the variables.slew_rate.
MRIBuilder.Variables.variables.slice_thickness — Function
slice_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 — Function
spoiler(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 — Function
time_to_center(adc)The time of the ADC readout to reach the center of k-space.
MRIBuilder.Variables.variables.voxel_size — Function
voxel_size(readout)Defines the voxel size of a readout in mm.
MRIBuilder.Variables.variables.Δ — Function
diffusion_time(diffusion_sequence)Returns the diffusion time of a DiffusionSpinEcho in ms.
diffusion_time(sequence)Computes the diffusion time of a sequence in ms.
MRIBuilder.Variables.variables.δ — Function
δ(trapezoid)Returns the effective duration of a Trapezoid gradient profile in ms.
Defined as variables.rise_time + variables.flat_time.
Variables interface
MRIBuilder.Variables — Module
Defines 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.add_cost_function!: add a specific term to the model cost functions.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 — Type
Parent 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.alternative_variables — Constant
Mapping of variable names to alternative ways to compute that variables.
MRIBuilder.Variables.default_generic_method — Constant
Contains for each variable the default, generic method.
This is the method that checks for alternative_functions or uses one of the getters.
MRIBuilder.Variables.getter_functions — Constant
Mapping of symbols to actual getter functions.
Used in set_getter!.
MRIBuilder.Variables.getters — Constant
Assigns getters to specific variables.
MRIBuilder.Variables.AbstractBlock — Type
Parent type of all components, building block, and sequences that form an MRI sequence.
MRIBuilder.Variables.InvalidRoute — Type
Raised if there is no way to reach a valid function by the default, generic method (default_generic_method).
MRIBuilder.Variables._get_mult_variable — Method
Helper to call the variable for a result of a getter. Used in _get_variable.
MRIBuilder.Variables._get_variable — Method
_get_variable(name, tried_names, args...; kwargs...)Tries to find a route to get values for the variable name with the given args and kwargs.
The route through tried_names has already been attempted.
This function returns the route to get to the value and the value itself.
MRIBuilder.Variables.add_alternative_variable! — Method
add_alternative_variable!(name, other_func, conversion)Defines an alternative way to compute the variable with given name.
If the variable name is not defined and other_name is, then the value of name is computed by applying conversion to the value of other_name.
MRIBuilder.Variables.add_cost_function! — Function
add_cost_function!(function, level=2)Adds an additional term to the cost function.
This term will be minimised together with any other terms in the cost function. Terms added at a lower level will be optimised before any terms with a higher level.
By default, the term is added to the level=2, which is appropriate for any cost functions added by the developer, which will generally only be optimised after any user-defined cost functions (which are added at level=1 by add_simple_constraint! or set_simple_constraints!.
Any sequence will also have a level=3 cost function, which minimises the total sequence duration.
MRIBuilder.Variables.add_new_variable! — Method
add_new_variable!(name)Adds a new variable in variables.
This is a helper function, which is called by @defvar for any variable that does not exist yet.
MRIBuilder.Variables.adjust_groups — Method
adjust_groups(block)Returns an array of keywords in adjust that should affect a specfic block.
If any of these keywords are present in adjust, then adjust_internal will be called.
Some standard keywords are:
:gradient: expects gradient adjustment parameters:pulse: expects RF pulse adjustment parameters
MRIBuilder.Variables.adjust_internal — Function
adjust_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. It should be defined for any block that is adjustable (as defined by adjust_groups).
MRIBuilder.Variables.apply_simple_constraint! — Method
apply_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
apply_simple_constraint!(variable, :>=/:<=, value)Set an inequality constraint to the variable.
value can be a number of an equation.
MRIBuilder.Variables.base_variables — Method
base_variables([T])Return dictionary with all Variable objects defined for a specific sequence component/block T.
This only returns those Variable directly defined for this component/block, not for any sub-components (through get_pulse, [get_gradient][(@ref), etc.)
If T is not provided, all Variable objects are returned.
MRIBuilder.Variables.get_free_variable — Method
get_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 — Function
get_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 — Function
gradient_orientation(building_block)Returns the gradient orientation.
MRIBuilder.Variables.make_generic — Function
make_generic(sequence/building_block/component)Returns a generic version of the BaseSequence, BaseBuildingBlock, or BaseComponent
- Sequences are all flattened and returned as a single
Sequencecontaining onlyBuildingBlockobjects. - Any
BaseBuildingBlockis converted into aBuildingBlock. - Pulses are replaced with
GenericPulse(except for instant pulses). - Instant readouts are replaced with
ADC.
MRIBuilder.Variables.scanner_constraints! — Method
scanner_constraints!(block)Constraints variables.gradient_strength and variables.slew_rate to be less than the global_scanner maximum.
MRIBuilder.Variables.set_getter! — Method
set_getter!(variable_name, getter)Set the getter function for variable_name.
If the value for variable is not defined for a sequence, the value for the result of the getter function is returned instead.
Possible values for the getter function are:
:pulse:get_pulse:gradient:get_gradient:readout:get_readout:pathway:get_pathway
MRIBuilder.Variables.set_simple_constraints! — Method
set_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 — Method
variable_defined_for(variable, args...; kwargs...)MRIBuilder.Variables.@defvar — Macro
@defvar(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