KSFoundation  [October2024]
A platform for structured EPIC programming on GE MR systems
HowTo - Plotting (HTML)

Home

There are two types of plot modes for sequence drawing in the KSFoundation library: HOST (ks_plot_host()) and TGT/IPG (ks_plot_tgt_addframe()).
In addition, there is a slice-timing graph that is generated on HOST via ks_plot_slicetime().



Requirements to make plotting work

  1. Download Anaconda3 (python 3): https://www.anaconda.com/download/#linux
  2. Install Anaconda3 in e.g. /usr/local/anaconda3
  3. Update to a newer version of Bokeh (need 2.0+) to make mouse hovering work: https://anaconda.org/bokeh/bokeh
  4. Make symbolic link (so ks_plot_host() works): /usr/local/bin/apython -> /usr/local/anaconda3/bin/python

Where are your HTML plots?

  • Simulation (WTools): In the plot subdirectory of the current psd folder
  • Hardware (MR-scanner): In /usr/g/mrraw/plot/<psdname>



Sequence plotting on HOST (dry-running before TGT)



ksfsemain.gif
HOST plotting



Plotting your sequence on HOST allows you to visualize the sequence after the pulsegen function(s) have been dry-runned on HOST, before any waveforms have been created on TGT. The benefit with this is that one can look for e.g. gradients that may overlap before the related hardware (TGT) error occurs. Please note that a call to the sequence's seqstate function should be done to set the amplitudes of the phase encoding gradients for a given shot:

Note that the acquisition windows are presented together with the frequency encoding gradient, and will also show up differently if ramp sampling is applied.

Plotting in HOST mode is supported on both Sim (WTools) and on the MR scanner. Plotting in TGT mode is supported on Sim only to avoid end-of-sequence (EOS) interrupts while scanning.

In addition, there is a slice-time plotting feature using ks_plot_slicetime() (see Slice-time plotting of multiple sequence modules below).

All plot calls below are already set up for the provided psds, please see:

Sequence plotting on TGT



ksfsemain_tgt.png
TGT plotting



The benefit of plotting on TGT is to see scan time changes such as phase encoding variations. Depending on what you want to visualize, place the call to ks_plot_tgt_addframe(...) in the corresponding scan function.

  1. Add in predownload
  2. Add in the scan function you want to visualize. ctrl is the sequence module you want to plot
  3. Setup a protocol in WTools and run the scan in MGDSim. There is a bug in MGDSim that sometimes requires you to press the PlotPulse button several times in order for all scan loops to be executed. Setting opslquant = 1 may help.
  4. Run psdplot.py with the generated JSON file as input argument

Example

if you want to visualize the phase encoding steps in the ksfse sequence:

  1. Set up TGT plot by calling
  2. Write the frames by adding a call to ks_plot_tgt_addframe() in ksfse_scan_acqloop():
    for (shot = -ksfse_dda; shot < ksfse_numshots; shot++) {
    ...
    if (shot > 0) {
    }
    ...
    }
  3. Setup a protocol in WTools and run the scan in MGDSim.
  4. Run the following from inside the plot sub directory:
    cd plot/tgt/
    apython ../../../KSFoundation/psdplot/psdplot.py psdplot_tgt_ksfse_ksfsemain.json

Customizing the plot

You can change the fileformat (default PNG) with the CV ks_plot_filefmt.
Helvetica font is the default font but is not required.
Check psdplot.py for further customization.




Slice-time plotting of multiple sequence modules

plot_slicetime.png
Slicetime plot

Adding a ks_plot_slicetime() command right after every ks_scan_playsequence() call in the sequence (main sequence + optional additional sequence modules) provides the information necessary to automatically create a slice-time plot for the sequence with time on the X-axis and slice location on the Y-axis. In this slice-time plot, each sequence module is plotted as a box, where the box height corresponds to the slice thickness of the current sequence module and the box width corresponds to the duration of the sequence module (<module>.seqctrl.duration). If .seqctrl.duration is larger than .seqctrl.min_duration for a sequence module, the time after .seqctrl.min_duration up to .seqctrl.duration will be shown in pale color (as for ksinv1 in the figure above). This indicates that the sequence module has some dead time after the last waveforms in the sequence module.

Note that this plot is generated on HOST in predownload() and works both for SIM and HW.

For SIM (WTools), the slice-time plot will be placed in ./plot/slicetime/<psdname>_slicetime.png. For HW (MR-scanner), the plot will be generated when SaveRx is pressed and placed in /usr/g/mrraw/plot/<psdname>/slicetime/<psdname>_slicetime.png.

ks_plot_slicetime() can take multiple slice locations (in [mm]) as arguments to be able to plot SMS (multi-band) sequence data.

See also ks_plot_slicetime_begin() and ks_plot_slicetime_end()

Example

In ksgre.e, ksfse.e and ksepi.e, and the provided sequence modules, ks_plot_slicetime() is already added after each call to ks_scan_playsequence().

Here is an example for ksfse with inversion:

From the main sequence (ksfse_implementation.e):

time += ks_scan_playsequence(&ksfse.seqctrl); // important to call ks_scan_playsequence() right before each ks_plot_slicetime() to reflect what is happening
ks_plot_slicetime(ks_perform_slicetimeplot, // = FALSE = 0 (plotting disabled) or TRUE = 1 (plotting enabled)
&ksfse.seqctrl, // the sequence module to be added as the next box in the growing plot
1, // number of simulaneous slices (normally 1 unless SMS)
&slice_pos->optloc, // mm location(s) for the slice, if SMS then this is a float array
opslthick, // slice thickness in mm from the UI menu
slice_pos == NULL ? KS_PLOT_NO_EXCITATION : KS_PLOT_STANDARD); // plot steering flag indicating real or dummy slice

From the @include'd inversion code (KSInversion.e), a bit simplified without SMS:

time += ks_scan_playsequence(&ksinv->seqctrl); // important to call ks_scan_playsequence() right before each ks_plot_slicetime() to reflect what is happening
ks_plot_slicetime(ks_perform_slicetimeplot, // = FALSE = 0 (plotting disabled) or TRUE = 1 (plotting enabled)
&ksinv->seqctrl, // the sequence module to be added as the next box in the growing plot
1, // number of simulaneous slices (normally 1 unless SMS)
&slice_pos->optloc, // mm location(s) for the slice, if SMS then this is a float array
ksinv->selrfinv.slthick, // slice thickness in mm, being thicker than opslthick as often with inversion pulses
slice_pos == NULL ? KS_PLOT_NO_EXCITATION : KS_PLOT_STANDARD);


See also how the following calls are placed in ksfse_scan_acqloop() to mark end of slice-groups & passes for the slice timing plot:

...
ks_plot_slicetime_endofpass(KS_PLOT_PASS_WAS_DUMMY);
...
ks_plot_slicetime_endofslicegroup("ksfse shots");
...
ks_plot_slicetime_endofpass(KS_PLOT_PASS_WAS_STANDARD);

From ksfse_predownload_plot():

/* Sequence plot */
ks_plot_host(seqcollection, &ksfse.phaseenc_plan);
/* Sequence timing plot: */



Home