GrOpt

Overview

GrOpt is a toolbox for MRI Gradient Optimization (GrOpT). Rather than analytical combinations of trapezoids, the software uses optimization methods to generate arbitrary waveforms that are (hopefully) the most time optimal solution.

The main workhorse of the package are the optimization routines written in C, which for final applications are designed to be dropped into a pulse sequence as needed. Additionally, wrappers for Python and Matlab are provided, as well as some demo cases to show how the software can be used, and to prototype sequences and combinations of constraints.

Installation

GrOpt is written in C, with many wrapper and helper functions written in Matlab and Python.

C Compilation

The main optimization routines are written and can be directly compiled. Example usages are shown at the bottom of src/optimize_kernel.c. All of the functions that are would be called by the user are found in src/optimize_kernel.c

Python

The optimization routines have been wrapped with Cython, they can be built simply running in the python/ directory:

python setup.py build_ext --inplace

Which will build the python module named gropt in the python directory, which can then be imported into any python code.

Example jupyter notebooks are provided in the python/ directory that show many ways how the module can be used.

Matlab

There is set of mex functions to use for calling the optimization routines from Matlab. They can be compiled by running the file make.m from within the matlab directory.

The matlab/ directory then has sample scripts that show the verious ways to call the optimization functionss.

Constraints

Below are a list of constraints and documentation about their usage and input formats.

Where possible, the units are all Tesla, meters, and seconds (though occasionally ms will come up for helper functions where it might make more sense)

Most constraints are subject to a 1% cushion to help convergence of the optimization, so many constriants might be 99% of their actual value.

Gradient Strength

Maximum gradient strength \((G_{max})\) is defined in \(\dfrac{T}{s}\)

This constraint simply clips the gradient waveforms so that all values must be in the range \((-G_{max}, G_{max})\)

Slew Rate

Maximum slew rate \((SR_{max})\) is defined in \(\dfrac{T}{(m \cdot s)}\)

This constraint constrains \(\dfrac{dG}{dt}\) so that all values must be in the range \((-SR_{max}, SR_{max})\)

Moments

Moments are controlled with an array of doubles of length N_momentsx7, where N_moments is the number of moment constraints to be applied. Any number of constraints (N_moments) can be added in this array.

The 7 options in a constraint are (sequentially):

  • Axis of constraint

    0, 1, or 2 based on which axis to apply the constraint on, when more than one axis of gradients are being computed.

    Not currently implemented

  • Moment order

    What order of moment the constraint is. i.e. 0 = \(M_{0}\), 1 = \(M_{1}\), 2 = \(M_{2}\).

    Technically any number should work, though nothing higher than 2 is routinely tested.

  • t=0 offset

    This field gives the offset in seconds between the first point in the waveform, and the actual t=0 time for the moment integrals.

    e.g. If there is a 2 ms rf pulse before the waveform that is not included in the waveform, but you would still like to calculate moments from the excitation, this argument could = 1e-3 (1 ms)

  • Start time

    Defines the beginning of the range of the waveform that the moment should be constrained over

    -1 means the very beginning of the waveform (default behavior, to calculate for the entire waveform)

    (0, 1) gives a time in seconds relative to the start of the waveform (not the previous argument reference)

    >1 is cast to an integer and gives the index of the point to start the calculation

    Not currently implemented

  • End time

    Defines the end of the range of the waveform that the moment should be constrained over

    -1 means the very end of the waveform (default behavior, to calculate for the entire waveform with -1 above)

    (0, 1) gives a time in seconds relative to the start of the waveform (not the previous argument reference)

    >1 is cast to an integer and gives the index of the point to end the calculation

    Not currently implemented

  • Desired Moment

    The desired moment, with units \(\dfrac{mT \cdot ms^{N+1}}{m}\), where \(N\) is the moment order (see second argument)

  • Moment Tolerance

    How far off the waveform can be from the desired moment, in the same units.

    The optimization will very frequently use exactly all of this tolerance

Eddy Currents

Eddy current constraints are controlled with an array of doubles of length N_eddyx4, where N_eddy is the number of eddy current constraints to be applied. Any number of constraints (N_eddy) can be added in this array.

The 4 options in a constraint are (sequentially):

  • Time constant of constraint
    The time constant in milliseconds of the constraint to be applied
  • Target eddy current magnitude
    The target value of the constraint (0 for nulling, but can be set to other values). the units here are basically arbitrary.
  • Tolerance
    The tolerance in the target value to be used. The units are somewhat arbitrary, but you can start around 1.0e-4
  • Mode
    0.0 to constrain the instantaneous eddy currents at the end of the waveform. 1.0 to constrain the sum of eddy current fields across the whole waveform.

Usage

Usage notes for the C, Python, and Matlab tools

C Usage

The two primary calls to the C library are the run_kernel_diff() and minTE_diff() functions.

Both take basically the same arguments, except run_kernel_diff() takes a fixed TE, and minTE_diff() takes a b-value to search for.

For usage examples, see the end of src/optimize_kernel.c, basic usages of both functions are shown.

Arguments

Note that this is just a list of all possible arguments, the order might be slightly different for a given function, see definitions in src/optimize_kernel.c

double **G_out
Pointer to the array where the output will be stored. This will be allocated within the function.
int *N_out
Pointer to an integer specifying the size of G_out after completion of the optimization.
double **ddebug
Pointer to the array where debug output information will be stored. It will have a fixed length 100 (but you should probably check that to see if it has changed)
int verbose
0 for no message, >0 for debug messages to console
int N
The size of the output gradient array
double dt
The raster time of the gradient waveform in seconds, i.e. the distance between timepoints.
double gmax
The maximum gradient amplitude in T/s.
double smax
The maximum slew rate in T/m/s.
double TE
The TE of the waveform in MILLIseconds. For diffusion waveforms, this takes into account T_readout. For non-diffusion it is just the length of the waveform, i.e. N*dt.
int N_moments
The number of moment constraints that will be used.
double *moments_params
An array describing the moment constraints, with 7 values per constraint as described here. So it is an array of N_moments*7 doubles.
double PNS_thresh
PNS threshold with the single exponential model, any decimal should work. To disable the constraint use -1.0 (or any negative number).
double T_readout, double T_90, double T_180
Timings of the readout prewinder, initial excitation pulse, and 180 pulse, in MILLIseconds.
double bval_weight, double slew_weight, double moments_weight
Initial weights for the various constraints, these can be kept at defaults, or all at 1.0, and everything will work well.
double bval_reduce
How much to change the above weights when convergence has slowed down too much. Dafault to around 10.
double dt_out
Adds a final linear interpolation to the waveform to reach dt_out in seconds.
int N_eddy
Number of eddy current constraints, can be 0 to disable
double *eddy_params
An array describing the eddy current constraints, with 4 values per constraint as described here. So it is an array of N_moments*4 doubles.
int is_Gin
1 if there is an input gradient waveform for warm starting the optimization, 0 to disable (default).
double *G_in
An array holding initialization values for the G vector in the optimization
double search_bval

When used in run_kernel, this gives a bvalue maximum, so iterations with a higher b-value are immediately stopped. It can be set to -1 to disable.

When referred to in minTE_diff(), it is the bvalue we are searching for.

int N_gfix
Number of fixed values in G. 0 is off (which technically enforces G=0 at the beginning and end), 2 informs the function that *gfix only has a start and end G value. N_gfix = N says that *gfix is a full array.
double *gfix
G values to set as fixed. Can be 2 entries for start and end. If full array, then big negative numbers mean NOT fixed, so fill array with -99999 and then fill in fixed values.
double slew_reg
This describes the slew rate minimization that is applied. The real amount is actually a multiplier of (1-slew_reg), so 1.0 is nothing, and 0.0 is complete regularization. Default is 1.0 right now but it might switch to 0.99 for smoother waveforms.

Citations

Citations for our work here