*
Let us now extend the framework to achieve the same results as in the previous example In depth tutorial - Part 1: analytic pulse shapes
This tutorial assumes that you are familiar with compiling JEMRIS. The necessary steps for recompiling will not be explained in detail.
The workflow will involve the creation of two new classes one for the RF pulse and one for the gradient pulses.
Let us start with defining the template for an RF pulse, which inherits from the RF pulse functionality implemented in framework. This is done with a few lines for the object declaration.
Enter the directory
[jemris-dir]/src. Create a new header file for the STA RF pulse by the name
STARFPulse.h. And type in the lines below.
#ifndef STARFPULSE_H_
#define STARFPULSE_H_
class STARFPulse :
public RFPulse {
public:
STARFPulse() {};
STARFPulse (const STARFPulse&) {};
~STARFPulse () {};
inline STARFPulse*
Clone()
const {
return (
new STARFPulse(*
this)); };
private:
double m_Gamma;
double m_Alpha;
double m_A;
double m_Beta;
int m_N;
};
#endif
mode
Definition Declarations.h:112
Implementation of JEMRIS RFPulse.
virtual Module * Clone() const =0
Clone a module.
Super class for all RF pulses.
Definition RFPulse.h:36
virtual bool Prepare(const PrepareMode mode)
see Module::Prepare()
Definition RFPulse.cpp:41
virtual double GetMagnitude(const double time)
Returns the Magnitidue of this pulse at a given time.
Definition RFPulse.h:121
The interesting part of the class will be regarding
Prepare and
GetMagnitude functions. The Prepare does as name suggests. It will prepare the pulse at start up. While the GetMagnitude function implementation will do the calculation of the pulse during runtime.
The rest of the object declaration is similar for any newly added framework element inherting from the virtual implementation RFPulse.
Let us now have a look at the implementation. Add
STARFPulse.cpp to the framework directory with the following content.
#include "STARFPulse.h"
bool STARFPulse::Prepare (PrepareMode
mode) {
bool btag = true;
ATTRIBUTE("Gamma", &m_Gamma);
ATTRIBUTE("Alpha", &m_Alpha);
ATTRIBUTE("A" , &m_A);
ATTRIBUTE("Beta" , &m_Beta);
ATTRIBUTE("N" , &m_N);
ATTRIBUTE("TPOIs", &m_more_tpois );
if (
mode == PREP_VERBOSE) {
if (!HasDOMattribute("Gamma")) {
btag = false;
cout << GetName() << "::Prepare() error: gamma required!!\n";
}
if (!HasDOMattribute("Alpha")) {
btag = false;
cout << GetName() << "::Prepare() error: Alpha required!!\n";
}
if (!HasDOMattribute("A")) {
btag = false;
cout << GetName() << "::Prepare() error: A required!!\n";
}
if (!HasDOMattribute("Beta")) {
btag = false;
cout << GetName() << "::Prepare() error: Beta required!!\n";
}
if (!HasDOMattribute("N")) {
btag = false;
cout << GetName() << "::Prepare() error: N required!!\n";
}
}
if (!btag &&
mode == PREP_VERBOSE)
cout << "\n warning in Prepare(1) of RFPULSE " << GetName() << endl;
return btag;
};
double STARFPulse::GetMagnitude (double const time) {
return m_Gamma * m_Alpha / GetDuration() * exp(-pow(m_Beta,2)*pow(1-time/GetDuration(),2)) *
sqrt( pow(2*PI*m_N*(1-time/GetDuration()),2) + 1);
}
Let us now implement the gradients. Since the two gradients only differ little we will implement both in the same class
<>STASpiralGradient<>. Let us start with the object declaration
STARGradPulse.h:
#ifndef _SPIRALGRADPULSE_H
#define _SPIRALGRADPULSE_H
#include <cmath>
class STASpiralGradPulse :
public GradPulse {
public:
STASpiralGradPulse () {};
STASpiralGradPulse*
Clone ()
const;
virtual bool Prepare (PrepareMode
mode);
virtual double GetGradient (double const time);
protected:
virtual string GetInfo ();
double m_A;
double m_N;
};
#endif
Implementation of JEMRIS GradPulse.
Base class and prototype for all gradient pulses.
Definition GradPulse.h:36
Definition SpiralGradPulse.h:37
The code of the implementation will now reflect the physics in
STAGradPulse.cpp
#include "STASpiralGradPulse.h"
double STASpiralGradPulse::GetGradient (double const time) {
double magn = 0.0;
double angle = 2*PI*m_N*time/GetDuration();
double ancos = cos(angle);
double ansin = sin(angle);
if (GetAxis() == AXIS_GX)
magn = - (m_A/GetDuration()) * ancos - 2*m_A*PI*m_N/GetDuration()*(1-time/GetDuration()) * ansin;
else if (GetAxis() == AXIS_GY)
magn = - (m_A/GetDuration()) * ansin + 2*m_A*PI*m_N/GetDuration()*(1-time/GetDuration()) * ancos;
return magn;
}
bool STASpiralGradPulse::Prepare (PrepareMode
mode) {
bool btag = true;
ATTRIBUTE("A" , &m_A);
ATTRIBUTE("N" , &m_N);
if (
mode == PREP_VERBOSE) {
if (!HasDOMattribute("Turns")) {
btag = false;
cout << GetName() << "::Prepare() error: Alpha required!!\n";
}
if (!HasDOMattribute("Pitch")) {
btag = false;
cout << GetName() << "::Prepare() error: Pitch required!!\n";
}
SetArea(0.0);
}
if (!btag &&
mode == PREP_VERBOSE)
cout << "\n warning in Prepare(1) of TRAPGRADPULSE " << GetName() << endl;
return btag;
}
string STASpiralGradPulse::GetInfo() {
stringstream s;
return s.str();
};
virtual string GetInfo()
Definition GradPulse.cpp:294
virtual bool Prepare(PrepareMode mode)
see Module::Prepare()
Definition GradPulse.cpp:160
We now have to introduce the two new modules to the framework factory by adding them to the
ModulePrototypeFactory.
Open
ModulePrototypeFactory.cpp and include the headers of the new modules:
...
#include "STASpiralGradPulse.h"
#include "STARFPulse.h"
Next, add the object prototypes to the factory constructor by appending the following two lines after the last entry in
ModulePrototypeFactory::ModulePrototypeFactory:
...
m_Clonables.insert( pair<string,Module*>( "STASPIRALGRADPULSE", new STASpiralGradPulse () ));
m_Clonables.insert( pair<string,Module*>( "STARFPULSE", new STARFPulse () ));
}
ModulePrototypeFactory()
Definition ModulePrototypeFactory.cpp:60
We are done with the framework extension. After recompile, the modules will be available for simulations. They also automatically appear in the Matlab sequence GUI.