KSFoundation  [April-2021]
A platform for simpler EPIC programming on GE MR systems
KS_SEQ_COLLECTION Struct Reference

#include <KSFoundation.h>

Data Fields

int numseq
 
int mode
 
int evaltrdone
 
KS_SEQ_CONTROLseqctrlptr [KS_MAXUNIQUE_SEQUENCES]
 

Detailed Description

Collection handle of all sequence modules involved in the pulse sequence, used for RF scaling, TR timing and SAR calculations

A pulse sequence consists of one or more sequence modules - one of them being the main pulse sequence, while the other ones may be add-on features like e.g. FatSat, Inversion etc. These sequence modules, each having its own KS_SEQ_CONTROL struct, are played in scan() according to some pattern. As a collection they produce some:

  • total duration for the given number of slices
  • SAR given the various RF pulses involved in the sequence modules

To perform an overarching RF scaling (accounting also for the prescan RF pulses), TR timing and SAR calculation, the collection of sequence modules (KS_SEQ_COLLECTION) involved is used.

While the KS_SEQ_CONTROL struct could be viewed as the administrator for each sequence module, there is a single KS_SEQ_COLLECTION struct administrating the entire pulse sequence. This KS_SEQ_COLLECTION handle is only needed in cveval() and is passed to GEReq_eval_rfscaling(), GEReq_eval_TR(), and GEReq_eval_checkTR_SAR(), which should all be called in cveval().

A sequence module (KS_SEQ_CONTROL) is added to the sequence collection (KS_SEQ_COLLECTION) by calling ks_eval_addtoseqcollection(), but it will be added only if seqctrl.duration > 0 . Setting mypluginmodule.duration = 0 indicates don't use, and hence it will neither be a part of RF scaling or SAR calculations. Moreover, since ks_scan_playsequence() in scan() checks if seqctrl.duration = 0 (returns 0 duration and skips playing the module in scan()), the TR timing follows automatically.

This is also analogous to how KS_TRAP, KS_RF and other sequence objects, on a smaller scale, are ignored by the ks_pg_***() functions if the .duration field is set to 0.

Example use of KS_SEQ_COLLECTION with one main sequence module and one fatsat sequence module

@ipgexport
KS_SEQ_CONTROL mymainseqctrl;
KS_SEQ_CONTROL fatsatseqctrl;
@host
cveval() {
// Collection (handle) of all sequence modules
ks_init_seqcollection(&seqcollection); // Important: Reset the collection every time
// ... set up the sequence objects in each sequence module here using ks_eval_***() functions etc. (not shown in this example) ...
// call each <seqmodule>_pg() function here and make sure each seqctrl.duration field > 0 for those sequence modules that should be used
mypsd_pg(); // also updates mymainseqctrl.gradrf, mymainseqctrl.min_duration = mymainseqctrl.duration > 0
myfatsat_pg(); // some fatsat sequence module that also updates fatsatseqctrl.gradrf, fatsatseqctrl.min_duration = fatsatseqctrl.duration > 0
// Add the KS_SEQ_CONTROLs to be used to the KS_SEQ_COLLECTION
ks_eval_addtoseqcollection(&seqcollection, &mymainseqctrl); // add the main sequence to the collection (ignored if mymainseqctrl.duration = 0)
ks_eval_addtoseqcollection(&seqcollection, &fatsatseqctrl); // add the fatsat sequence module to the collection (ignored if fatsatseqctrl.duration = 0)
// Perform the RF scaling (across all sequence modules as well as the prescan RF pulses) to get the correct actual FA
// [2D multi-slice specific] Get the number of slices per TR (slperpass) and the necessary padding time to reach the desired TR (timetoadd_perTR)
// mysliceloop_nargs() is a slice loop wrapper function with standardized input args, passed as a function pointer to GEReq_eval_TR()
// For more details on this function pointer, see documentation for GEReq_eval_TR() and GEReq_eval_checkTR_SAR()
GEReq_eval_TR(&slperpass, &timetoadd_perTR, 0, seqcollection, mysliceloop_nargs, 0, NULL);
// Calculate the slice plan (slice ordering and number of passes (acqs))
// ks_slice_plan (declared in GERequired.e) is passed to GEReq_predownload_store_sliceplan() in predownload()
ks_calc_sliceplan(&ks_slice_plan, exist(opslquant), slperpass);
// Spread the available timetoadd_perTR evenly, by increasing the .duration field for the main sequence by timetoadd_perTR/ks_slice_plan.nslices_per_pass
mymainseqctrl.duration = RUP_GRD(mymainseqctrl.duration + CEIL_DIV(timetoadd_perTR, ks_slice_plan.nslices_per_pass));
// Check the actual TR and update SAR values in the UI (error will occur if the sum of sequence durations differs from optr)
// Scan clock
pitscan = scanloop();
}
@pg
void mypsd_pg() {
// ... calls to various ks_pg_***() functions here
tmploc.pos += myreadout.grad.duration; // time for the end of last sequence entry (in this case the end of the readout gradient)
#ifndef IPG // On HOST only (i.e. not IPG/TGT)
ks_eval_seqctrl_setminduration(&mymainseqctrl, tmploc.pos); // sets mymainseqctrl.duration = mymainseqctrl.min_duration = tmploc.pos + mymainseqctrl.ssi_time
#endif
}
mypsd_pg();
KS_SEQLENGTH(seqcore, mymainseqctrl);
myfatsat_pg();
KS_SEQLENGTH(seqfat, fatsatseqctrl);
}
// play one slice loop, i.e. one TR
int mysliceloop(int slperpass, int ky) {
for (i = 0; i < slperpass; i++) {
// ... do some phase encoding etc based on value of 'ky' (not shown)...
time += ks_scan_playsequence(&fatsatseqctrl); // if fatsatseqctrl.duration > 0, it plays sequence in real time (on TGT) and returns the time in [us] equal to value of fatsatseqctrl.duration
time += ks_scan_playsequence(&mymainseqctrl); // if mymainseqctrl.duration > 0, it plays sequence in real time (on TGT) and returns the time in [us] equal to value of mymainseqctrl.duration
}
}
// wrapper function to sliceloop with standardized input args to be used as function pointer to GEReq_eval_TR() and GEReq_eval_checkTR_SAR()
// nargs and args aren't used in this case
int mysliceloop_nargs(int slperpass, int nargs, void **args) {
mysliceloop(slperpass, 0);
}
// declare scanloop in @pg section instead of @rsp to allow it to be called on HOST too. This, to figure out how long one TR is.
float scanloop() {
float time = 0.0;
for (ky = 0; ky < opyres; ky++) {
time += mysliceloop();
}
return time; // return the time in [us] for the scan
}
@rsp
scan() {
scanloop();
}

Field Documentation

◆ numseq

int numseq

Number of sequence modules

◆ mode

int mode

Flag to determine if new sequence modules may be added. Initialized to KS_SEQ_COLLECTION_ALLOWNEW, and set to KS_SEQ_COLLECTION_LOCKED by GEReq_eval_TR() and GEReq_eval_rfscaling() to prevent accidental adding of modules too late

◆ evaltrdone

int evaltrdone

Flag to determine if TR timing has been done. The sequence may have more than one function that could be responsible for the TR timing, but only one should be called for any given configuration. As an example is the inversion module (KSInversion.e), which takes over the TR timing if the inversion flag is set

◆ seqctrlptr


The documentation for this struct was generated from the following file: