Quick Start Tutorial

The core of generalized Bloch model is implemented in the function apply_hamiltonian_gbloch!(∂m∂t, m, mfun, p, t), which calculates the derivative ∂m/∂t for a given magnetization vector m and stores it in-place in the the variable ∂m∂t. The function interface is written in a way that we can directly feed it into a differential equation solver of the DifferentialEquations.jl package.

For this example, we need the following packages:

using MRIgeneralizedBloch
using DifferentialEquations
using Plots

We simulate the dynamics of a coupled spin system with the following parameters:

m0s = 0.15
R1 = 1 # 1/s
R2f = 15 # 1/s
T2s = 10e-6 # s
Rx = 30; # 1/s

and the thermal equilibrium of the magnetization m = [xf; yf; zf; zs; 1]:

m0 = [0; 0; 1-m0s; m0s; 1];

during a rectangular RF-pulse with the flip angle and pulse duration

α = π
TRF = 100e-6; # s

Further, we assume a perfectly calibrated, on-resonant RF-pulse:

B1 = 1
ω0 = 0; # rad/s

as well as a super-Lorentzian lineshape. We interpolate the corresponding Green's function to improve performance:

G = interpolate_greens_function(greens_superlorentzian, 0, TRF / T2s);

The generalized Bloch model is a so-called integro-differential equation where the derivative $∂m/∂t$ at the time $t_1$ does not just depend on $m(t_1)$, but on $m(t)$ for $t \in [0, t_1]$. This is solved with a delay differential equation (DDE) solver that stores an interpolated history function mfun(p, t), which we use in the apply_hamiltonian_gbloch! function to evaluate the integral. This history function has to be initialized with mfun(p, 0) = m0. Here, we use a slightly more complicated initialization that allows us to index the history function in apply_hamiltonian_gbloch!, which improves performance:

mfun(p, t; idxs=nothing) = typeof(idxs) <: Number ? m0[idxs] : m0;

With this, we are ready to formulate and solve the differential equation:

param = (α/TRF, B1, ω0, m0s, R1, R2f, T2s, Rx, G) # defined by apply_hamiltonian_gbloch!
prob = DDEProblem(apply_hamiltonian_gbloch!, m0, mfun, (0, TRF), param)
sol = solve(prob)
retcode: Success
Interpolation: automatic order switching interpolation
t: 12-element Vector{Float64}:
 0.0
 6.467225550500944e-8
 7.113948105551039e-7
 3.88977649246272e-6
 1.0349988165413888e-5
 1.9482146920742876e-5
 3.1140005366991184e-5
 4.558767188907695e-5
 6.165618816437438e-5
 7.85494362614568e-5
 9.933853901447032e-5
 0.0001
u: 12-element Vector{Vector{Float64}}:
 [0.0, 0.0, 0.85, 0.15, 1.0]
 [0.0017269759778720379, 0.0, 0.8499982456170762, 0.14999969040313105, 1.0]
 [0.018995075304755995, 0.0, 0.8497877291610881, 0.14996254346591958, 1.0]
 [0.10360944494400821, 0.0, 0.8436614714829197, 0.14888421766506987, 1.0]
 [0.271515827099733, 0.0, 0.805463641644652, 0.1422746913361597, 1.0]
 [0.4882931817405909, 0.0, 0.6957173301460509, 0.1242683708644349, 1.0]
 [0.704949732067321, 0.0, 0.474736545415402, 0.09160506101296072, 1.0]
 [0.8415650602454875, 0.0, 0.11762513881842868, 0.046466949757436614, 1.0]
 [0.7932965549263622, 0.0, -0.30398074263718977, 0.0023634201451245215, 1.0]
 [0.5301622383017996, 0.0, -0.6635974079565661, -0.028993409064871675, 1.0]
 [0.01775163709836658, 0.0, -0.849070958017044, -0.04453679776542625, 1.0]
 [0.00010497341348840103, 0.0, -0.8492535988155461, -0.04466510141094372, 1.0]

The plot function is implemented for such solution objects and we can plot the solution simply with

p = plot(sol, labels=["xᶠ" "yᶠ" "zᶠ" "zˢ" "1"], xlabel="t [s]", ylabel="m(t)")

More details on the interface, including the linear approximation of the generalized Bloch model can found in the following scripts that replicate all simulations, data analyses, and figures of the generalized Bloch paper.


This page was generated using Literate.jl.