JEMRIS  2.8.3
open-source MRI simulations
Sequence development tool: JEMRIS_seq


JEMRIS sequence development does not require any programming. The sequence is determined through an XML tree structure. A dedicated GUI allows to construct the tree structure for arbitrary complex MRI sequences.


Sections:


The Sequence tree concept

The MRI sequence is specified utilizing a tree structure. As a simple example, the EPI sequence below is eqivalently described by left-right ordered tree structure. This is very suitable and efficient for computer-aided design and access of the pulse sequence: The MRI simulator JEMRIS utilizes such a tree structure to represent the MRI sequence. The MatLab tool JEMRIS_seq is used to design the sequences.

seq_tree.jpg


Introduction to the sequence GUI JEMRIS_seq

Change to the directory in which the sequence xml-files are located and type JEMRIS_seq to start the sequence GUI. The following picture shows a screenshot of the GUI with the EPI sequence loaded into it:

epi_jemris_screenshot.png

The Sequence GUI allows to interactively build the sequence tree. The tools for this are given in the top panel. The "File" dialog allows to load and save sequences (XML format). The "write XML" button overwrites the current sequence file according to the changes made.

Further, this button executes a run through the sequence and draws the sequence diagram. Possible error output during sequence execution is thrown to the "jemris tree dump" at the bottom. Instead, the "read XML" button discards all changes made to the sequence tree and rereads the current sequence from its XML file. To view the sequence diagram, i.e. the ADC, RF, and gradient events, use the "Sequence Diagram" check box. There are several features to visualize the sequence diagram:

  • The "interval [ms]" dialog changes the time axis for all pulses, whereas the "zoom" button allows to interactively zoom into a specific axis.
    epi_jemsdg_screenshot.png
  • The "draw moment" flag draws the gradient moments, i.e. the time integral along the gradient axes, instead of the gradient waveforms. The moment is set to zero after each RF pulse, however, it is inverted for 180° pulses. Thus, it shows the correct k-space position encoding for gradient echo and spin echo sequences.
    epi_jemsmo_screenshot.png
  • A different view of the gradient moments is given by switching the "k-space trajectory" flag: it draws the k-space trajectory. Once it is drawn, it is possible to further add the ADC events to this plot by another check box or have a look at the travelling order by the activation of the ''Continous'' flag.
    epi_jemksp_screenshot.png

By clicking on a module, its individual attributes are displayed in the upper half of the GUI and may be edited. The attribute values can not only be actual numbers, analytical expressions are also accepted (see section Analytical expressions ). Often attribute values from other modules are needed for an analytical expression, e.g. the area under a previous gradient pulse or the duration of a previous pulse. This is handled using the "observe" attribute (see also section Analytical expressions ).


Sequence Building Modules

This section briefly summarizes all sequence building modules and its attributes. The interaction of these modules is explained in the next section Analytical expressions.

The Parameters Module

The parameter module (orange circle) is always present in a sequence. It is not a part of the sequence tree but exists only once. The module contains global attributes such as field of view $ FOV_{x/y/z} $, the size of the image matrix, the gradient slew rate etc.

Sequence Modules

So far, there exist five modules which are of type sequence:

  • The ConcatSequence module (yellow circle) represents a loop which can be controlled by its attribute "repetitions" (default value is 1). It may contain other ConcatSequences, AtomicSequences or DelayAtomicSequences, but no pulses. A ConcatSequence also has Hidden Attributes, which are not set by the user but in runtime:

    1. "Counter" is the loop counter through which other modules get information of the current value of the loop counter.
    2. "Duration" specifies the duration of the ConcatSequence, which of course depends on the duration of its children.

    Additionally, the attributes "aux1,aux2,aux3" define auxiliary variables. These values are not used in the calculation of the ConcatSequence. However they can be observed by other modules, thus lengthy expressions can be displayed in a more structured way.

  • The AtomicSequence module (purple circles) contains the actual pulses. Each AtomicSequence can contain multiple pulses which are then applied simultaneously. Additionally, the attributes "Inclination", "Azimut", "RotAngle" define the rotation matrix for gradient pulses inside this AtomicSequence, e.g. for oblique slices or radial imaging. As the ConcatSequence, the AtomicSequence has auxiliary attributes for convenience, and a hidden attribute specifying its duration. Note: it is possible to insert one AtomicSequence into the Parameter module. This is a "static atom" which acts throughout the complete sequence lifetime. Its pulses, however, need to be prepared accordingly, similar as for pulses within the sequence.

  • The DelayAtomicSequence (green circles) is a specialised AtomicSequence to define delays. It can either be defined as a simple delay by setting the parameter: "delay = 30", which will simple wait for 30ms. However, for e.g. a echo time delay, the echo time is defined from the center of "A_RF" to the center of "A_G_RO". The needed dead-time is TE minus the duration of "A_G_DP" and half the durations of "A_RF" and "A_G_RO". This can be automatically inserted by the DelayAtom. The required parameters for a TE of 30ms are then: "delay = 30 ", "StartSeq = A_RF", "StopSeq = A_G_RO", "DelayType = C2C ". Apart from the "center-to-center" ( "C2C") delay type, following other delay types are defined:

    1. "C2E" (center-to-end)
    2. "B2C" (beginning-to-center)
    3. "B2E" (beginning-to-end; default, if no delaytype is specified)
    ug_TE_delay.jpg

  • The Container (red circles) is a specialised module to include pre-defined building blocks, which are defined by the ContainerSequence module (see below). Thus, the Container module itself does not define sequence events. It only defines the ContainerSequence and it is responsible for I/O of attributes between the main sequence and the building block. For that, the Container may observe several other modules (see section Analytical expressions) in the sequence tree which are then atuomatically passed to the ContainerSequence - this is concept of importing attributes to the ContainerSequence. Further, it is possible to observe attributes of other modules within the ContainerSequence - this is the concept of exporting attributes from the ContainerSequence. The attribute "Filename" specifies the xml-file of the ContainerSequence. Further, the Container has the following additional attributes:
    1. "Imp1,...,Imp8" Eight attributes which can be used to import information into the ContainerSequence.
    2. "Exp1,Exp2,Exp3" Three hidden attributes for export. They are defined by the ContainerSequence (see below) and passed to the Container.
    Note: A double-click on the Container node in the GUI opens a new Sequence-GUI for editing the ContainerSequence.
  • The ContainerSequence (red circles) is the root sequence of an encapsulated building block. It can not be inserted as a module into the sequence tree (this is done by the Container, see above). The ContainerSequence has similar functionality to a ConcatSequence (with the difference that it is not loopable) i.e. it is possible to insert other sequence modules as child-modules. The ContainerSequence has the following additional attributes:
    1. "Exp1,Exp2,Imp3" Three attributes which can be used for export from the ContainerSequence.
    2. "Imp1,...,Imp8" Eight hidden attributes for import. They are defined by the Container (see above) and passed to the ContainerSequence.
    3. "Info_Imp1,...,Info_Imp8" Eight (unobservable) attributes with no specific purpose, but may be used to provide information about any possible usage of the import attributes.
    4. "Info_Exp1,Info_Exp2,Info_Exp3" Three (unobservable) attributes with no specific purpose, but may be used to provide information about any possible usage of the export attributes.
    Note: See the JEMRIS example directory (typically "/usr/local/share/jemris/example") for the sample sequence "epi_modular.xml" utlizing the Container and ContainerSequence.


Pulse Shape Modules

There exist several pulses which may be inserted into an AtomicSequence. All pulses have several attributes in common:

  • Duration: Every pulse has a duration, which is explicitly set. (In some cases, it is however calculated from other attributes.)
  • InitialDelay: A pulse may start at a later time within the atom, e.g. an RF pulse may wait for the ramp-up time of a gradient pulse.
  • ADC: The ADCs specify the number of readout events. These events are equidistantly distributed over the length of the pulse, and the simulator will save the acquired signal at each of these time points. It is possible to specify a negative number of ADCs : in this case the time points are not ADC events, but so called TPOIs (Time Points Of Interest), at which the pulse is evaluated but no signal acquired, as specified by ADCs. This concept is good for a) better visualization of certain pulse shapes and b) force the simulator to evaluate the solution at these time points. However, keep in mind that many TPOIs (or ADCs) will slow down the simulation.
  • Phaselock: Locks the phase of the ADCs inside the module to the phase of the last RF pulse. Can be 1 (true) or 0 (false).

The most simple pulse-shape module is the

  • EmptyPulse: It represents a dead-time according its specified duration in which, if ADCs are present, simulated data is acquired. (The above introduced DelayAtomicSequence is a special implementation of an AtomicSequence which has a single EmptyPulse as a child.) The EmptyPulse has one new attribute
    1. "Shape" An analytic expression to specify non-equidistant ADC sampling during the pulse length. See Exceptional Attributes for explanation. The analytic expression is automatically scaled over the total length of the pulse.

All other pulses subdived into two main pulse classes, RF Pulses and Gradient Pulses, respectively. The following RF pulse Modules are defined in JEMRIS:

  • HardRFPulse: A nonselective rectangular RF pulse. It has the following specific attributes

    1. "FlipAngle" The applied flip angle
    2. "InitialPhase" The constant phase during the duration of the pulse (the rotation axis)
    3. "Channel" An optional channel number, in case of parallel RF transmit.

  • GaussianRFPulse: A Gaussian-shaped RF pulse. It has all the attributes of the HardRFPulse, and additionally

    1. "Bandwidth" The bandwidth of the Gaussian
    2. "Frequency" The frequency-shift applied for this RF pulse

  • SincRFPulse: A sinc-shaped ( $sin(t)/t$) RF pulse. It has all the attributes of the GaussianRFPulse, and additionally

    1. "Zeros" The number of positive zero-crossings (or side lobes) of the sinc function (default 3)
    2. "Apodization" The apodization factor of the Hanning window (default 0.5)

  • AnalyticRFPulse: An arbitrary RF pulse specified through an analytic expression. It has all the attributes of the GaussianRFPulse, and additionally

    1. "Shape" The analytic expression for the RF pulse shape, using the time variable "T". The expression can be complex to define the magnitude and phase of the RF-pulse. Use "I" as imaginary unit, e.g. "Shape=sech(T)^(1.0+I)" generates a hyperbolic secant pulse.
    2. "Diff" If set to 1, then the actual pulse shape is defined by the first derivative of the analytic expression. In this case, the flip angle is set via the analytic expression rather than numerical integration.
    3. "Constants" A comma-sparated list of floating point numbers, which may be used as variables "c1,c2,..." in the Shape expression.

  • ExternalRFPulse: An arbitrary RF pulse specified through an external file. It has all the attributes of the GaussianRFPulse, and additionally

    1. "Filename" A HDF5-file with a single dataset "extpulse" of size $N \times 3$ where the 1st column holds the time points, and 2nd and 3rd column hold amplitudes and phases, respectively. Phase units should be radiants. Time is assumed to increase and start at zero. The last time point defines the length of the pulse.
    2. "Scale" An optional scale of the amplitudes (default 1)
    3. "Interpolate" if true, linear interpolation is performed, otherwise nearest neighbor look-up. Can be 1 (true) or 0 (false).

    Note, that such external HDF5-files are easily constructed with the GUI: generate a global matlab variable of size $N \times 3$, where the first column are the time points and the remaining columns are RF amplitudes and phases. Then write the name of the variable to the attribute "Filename". The GUI will automatically construct the necessary HDF5-file for JEMRIS.

Note that in terms of numerical efficiency the built-in RF pulses (HardRFPulse,GaussianRFPulse,SincRFPulse) perform best, the evaluation of an AnalyticRFPulse is slower, and an ExternalRFPulse is slowest, if it is specified through many data points.

Next, there exist several Gradient Pulse Modules in JEMRIS. All gradients can induce eddy currents, for which three additional attributes are in common for these pulses:

  • EddyCurrents: An analytic expression, similar to the Shape attribute of AnalyticGradPulse (see section Exceptional Attributes). It specifies the shape of the eddy current, e.g. "EddyCurrent=01.*exp(-5*T)". The expression then gets compiled in runtime for fast evaluation of the eddy currents.
  • EddyConvLength: The number of points to compute the convolution integral for the actual eddy current. Note that eddy currents are computationally expensive, therefore a short convolution length increases simulation speed. Eddy currents are displayed in the GUI, thus you can visually check if the length is sufficient.
  • Hide: A boolean to hide the gradient event itself, but not its eddy current. If "Hide=1" the actual gradient is not played out. Thus, by copying a gradient and hiding the copies, it is possible to individually specify arbirtrary eddy current cross terms.

Eddy currents are implemented in a very flexible way: the eddy currents will adapt automatically, if the gradient module changes its shape during the sequence runtime (e.g. a phase-encoding gradient). Note, that Eddy currents can be combined with nonlinear gradient fields Further, eddy currents typically last longer than the gradient event itself, i.e. beyond the AtomicSequence in which it was originated. In this case the transients are taken over to the next AtomicSequence. Caution: the latter may fail in a loop, if the remaining eddy current overlaps with the same AtomicSequence in which it was orginally generated. Then, only original eddy current is played out and the remaining eddy current is lost. In order to correctly simulate such long lasting eddy currents, the loop structure must be changed accordingly.

Further, each gradient can define a non-linear spatial field dependence, instead of the default linear gradient fields.

  • "NLG_field": an analytical expression for non-linear gradient field can be specified to every gradient module. Use "X,Y,Z" as space variables, and "G" as the gradient value, e.g. "NLG_field=X*G+0.001*G*X^2" will simulate a linear x-gradient field with a small quadratic term. Take into account that
    1. the defined axis of this gradient (GX, GY, or GZ) has no meaning,
    2. the Sequence GUI can not display nonlinear-gradients, thus these pulses are displayed as standard gradients,
    3. nonlinear gradient events get rotated, if the according AtomicSequence defines a rotation of the coordinate system.
    4. nonlinear gradient events can be used to simualte time-dependent frequency shifts (zero oder). For that, simple use "NLG_field=G" i.e. omit any spatial dependence in the analytical expression.
    5. nonlinear gradient events can be combined with eddy currents to generate higher or zero order eddy currents.

The following gradient pulse modules exist in JEMRIS:

  • TrapGradPulse: A trapezoidal-shaped gradient pulse, which is the by far most used gradient pulse in JEMRIS. If no duration is specified, the pulse prepares in shortest possbile time according to the hardware limits.

    ug_trapgrad.jpg

    The TrapGradPulse has several attributes inherited from the basic GradientPulse class, i.e. these attributes are common for all gradient pulses:

    1. "Slewrate" and "MaxAmplitude": Hardware limits, specific only for the particular gradient. If not set, the limits from the Parameter module are used.
    2. "Axis" The spatial axis of the gradient pulse; one of "GX", "GY", or "GZ".
    3. "Area" The area of the pulse shape, i.e. the total k-space encoding.
    4. "NLG_field" An optional attribute specifying an analytic expression for nonlinear gradients. See Exceptional Attributes for explanation.

    Further, the TrapGradPulse has attributes specific to its trapezoidal shape:

    1. "FlatTopArea" Instead of specifying the area, only the area under the flat top may be specified.
    2. "FlatTopTime" Defines the duration of the flat top. In this case, ADCs are automatically set under the flat top (readout gradient). Therefore, the "FlatTopTime" is in case of ADCs the reciprocal of the readout bandwidth.
    3. "Asymetric" Ratio of the slew rates between the ramp up and the ramp down.

    Finally, the TrapGradPulse has several hidden attributes which may provide important information to other modules: "Amplitude", "RampUpTime", "RampDnTime", "EndOfFlatTop".

  • ConstantGradPulse: A constant gradient, for which the attribute, "Amplidue" needs to be specified.

  • TriangleGradPulse: A ramp-up or down gradient, for which the attributes, "Amplidue" and "TriangleType" (can be either "UP" or "DN") need to be specified.

  • AnalyticGradPulse: An arbitrary gradient pulse specified through an analytic expression. It has similar functionality as the AnalyticRFPulse. By setting the attribute "Diff=1", the attribute Shape directly allows specification of the k-space trajectory (analytic differentiation of the Shape).

  • SpiralGradPulse: A priliminary very simple spiral gradient, only intended for usage on the "GX" and the "GY" axis. It has two attributes, "pitch" and "alpha" for scaling and spiral denisity, respecitvely.

  • ExternalGradPulse: An arbitrary Gradient pulse specified through an external HDF5-file, again simliar to the definition of the ExternalRFPulse. Here, the HDF5-file has a single dataset "extpulse" of size $N \times 2$ specifying the time-points $t_i$ and gradient-values $G_i$ of the pulse.

    Note, that such external HDF5-files are easily constructed by the GUI: generate a global matlab variable of size $N \times 2$, where the first column are the time points and the second column are the gradient values. Then write the name of the variable to the attribute "Filename". The GUI will automatically construct the necessary HDF5-file for JEMRIS.

Analytical expressions

The "Observe" Attribute

As mentioned above, instead of being numbers module attributes values can also be analytical expressions. For that variables are needed which contain the attribute values of other modules. Consider a gradient pulse with name "P1" which is dependent on the area of another gradient pulse "P2". To have access to this value, the pulse "P1" needs the special attribute Observe="A=P2.Area". Now the area of pulse "P2" can be accessed in every attribute definition of "P1" with the variable "A". For instance, "P1" sets its area to minus half the area of "P2" through Area="-0.5*A". Multiple attributes can be observed separated by commas, e.g.: Observe="A=P4.Area, NX=P.Nx, NY=P.Ny, C=C2.Repetitions" . Then, the observed attributes are accessed with the user-defined variables "A", "NX", "NY", "C", etc. The variable names can be freely chosen. It is recommended to use only uppercase letters to prevent undesired string replacements in formulas. Further the letters "I", and "P" should not be used as variables as this might conflict with other pre-defined epxressions (see below). (It is valid to use this letters within a variable name, e.g. Observe="C.Counter=INDEX" or Observe="P.FOVx=PFOVX")

Observer syntax in older versions of JEMRIS
As explained above, the current JEMRIS version requires an observer definition of the form: Observe="KEY1=Module1.Attribute1, KEY2=Module2.Attribute2, ...". Then the user-defined keywords "KEY1", "KEY2" can be used in analytic formulas. Older JEMRIS versions (<2.8.1) used a different syntax: Observe="Module1,Attribute1/Module2,Attribute2/...", then the according variables were automatically named "a1", "a2", etc. This syntax has the disadvantage that the user has to keep track which variable (a1,a2,...) belongs to which observed attribute. Therefore, the new syntax with user-defined keywords was introduced. The old syntax is still supported. (And used in some examples in the documentation.)

Hidden Attributes

Each module can have so-called hidden attributes which cannot be edited. Instead, these attributes are calculated from supplied values at runtime. The ConcatSequence, for example, not only has the attribute "Repetitions", it also has a hidden attribute "Counter" which gives the current loop-number. A hidden attribute can be observed by other modules. A list of all hidden attributes can be displayed using the pull-down menu in each module. Some frequently used hidden attributes are listed here:

  • Parameters module: "Dx", "Dy", "Dz" (pixel size in image space); "DKx", "DKy", "DKz" (increment in k-space); "KMAXx", "KMAXy", "KMAXz" (boundaries of k-space).

Hint: "Counter" is the most often observed attribute since it introduces the dynamic to the sequence. Attributes which observe a loop counter change their value in run time. (If these attributes are further observed by other attributes, the latter of course will also dynamically change their values accordingly.)

Expression syntax and predefined functions

The symbolic expression syntax is similar to scalar computation syntax in Matlab. For instance, if a gradient observes three attributes, e.g. Observe="KMX=P.KMAXx, KMY=P.KMAXy, C=C.Repetitions", then a valid formula for its area would be Area="sqrt(KMX^2+KMY^2)*(-1)^C"

Several predefined mathematical expressions may be used:

Pi area of a unit circle
abs(x) absolute value
step(x) step function
I imaginary unit
csgn(x) complex sign
conjugate(x) complex conjugation
real_part(x) real part
imag_part(x) imaginary part
sqrt(x) square root
sin(x) sine
cos(x) cosine
tan(x) tangent
asin(x) inverse sine
acos(x) inverse cosine
atan(x) inverse tangent
atan2(y,x) inverse tangent with two arguments
sinh(x) hyperbolic sine
cosh(x) hyperbolic cosine
tanh(x) hyperbolic tangent
asinh(x) inverse hyperbolic sine
acosh(x) inverse hyperbolic cosine
atanh(x) inverse hyperbolic tangent
exp(x) exponential function
log(x) natural logarithm
floor(x) round to integer
mod(x,y) Modulo
equal(x,y) returns 1, if x equals y, otherwise returns zero
gt(x,y) returns 1, if x is greater than y, otherwise returns zero
lt(x,y) returns 1, if x is less than y, otherwise returns zero
ite(x,y,a,b) returns a, if x equals y, otherwise returns b

There are further, less comonly used, mathematical functions. Please check the GiNaC documentation .

Exceptional Attributes

Every Module has its attributes which will have its internal special meaning. However, for the observer mechanism all these "standard attributes" may be arbitrarily linked to each other. For instance, the flip angle of an RF pulse may set its value through observation of the area of a gradient. However, there are some exceptional attributes for which this general behaviour does not work, i.e. these attributes can not be observed by other modules! These exceptions are listed below:

  • "Observe": Of course the Observe attribute itself can not be observed.
  • "Vector": Every pulse has the attribute "Vector". It is a space-separated list of floating point values which can be accessed by another attribute of this pulse. The values can be addressed by observation of loop counters, for instance an RF pulse may set Observe="C=C1.Counter" and FlipAngle="Vector(C)". Thus, the flip angle will loop through the vector as the loop-counter of the ConcatSequence increases. Note that in the Matlab GUI, "Vector" can also take any Matlab expression which results in a vector as input such as e.g. "rand(10,1)".
  • "Shape": For analytical RF / gradient pulses, this attribute defines the shape of the pulse
  • "NLG_field": the analytical expression for non-linear gradient fields

-- last change 11.06.2019 | Tony Stoecker | Imprint | Data Protection --