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.variablesModule

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).

source
MRIBuilder.Variables.variables.TEFunction
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.

source
echo_time(sequence)

Computes the echo time(s) of a sequence in ms.

source
MRIBuilder.Variables.variables.area_under_curveFunction
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.

source
MRIBuilder.Variables.variables.bmatFunction
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.

source
MRIBuilder.Variables.variables.bmat_gradientFunction
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.

source
MRIBuilder.Variables.variables.bvalFunction
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.

source
MRIBuilder.Variables.variables.duration_stateFunction
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: +longitudinal
  • transverse=true, positive=true: +transverse
  • transverse=false, positive=false: -longitudinal
  • transverse=true, positive=false: -transverse
source
MRIBuilder.Variables.variables.echo_timeFunction
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.

source
echo_time(sequence)

Computes the echo time(s) of a sequence in ms.

source
MRIBuilder.Variables.variables.net_dephasingFunction
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.

source
MRIBuilder.Variables.variables.qvecFunction
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.

source
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.

source
MRIBuilder.Variables.variables.ramp_overlapFunction
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.

source

Variables interface

MRIBuilder.VariablesModule

Defines the functions that can be called on parts of an MRI sequence to query or constrain any variables.

In addition this defines:

source
MRIBuilder.Variables._get_variableMethod
_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.

source
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.

source
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.

source
MRIBuilder.Variables.adjust_groupsMethod
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
source
MRIBuilder.Variables.adjust_internalFunction
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).

source
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 variable
  • number: fix variable to this value
  • equation: 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.

source
MRIBuilder.Variables.base_variablesMethod
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.

source
MRIBuilder.Variables.get_readoutFunction
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.

source
MRIBuilder.Variables.make_genericFunction
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 Sequence containing only BuildingBlock objects.
  • Any BaseBuildingBlock is converted into a BuildingBlock.
  • Pulses are replaced with GenericPulse (except for instant pulses).
  • Instant readouts are replaced with ADC.
source
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.

source
MRIBuilder.Variables.@defvarMacro
@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
source