Signal Modelling#

The steady-state longitudinal magnetization of an inversion recovery experiment can be derived from the Bloch equations for the pulse sequence {θ180 – TI – θ90 – (TR-TI)}, and is given by:

(1)#\[\begin{equation}\label{eq:1} M_{z}(TI) = M_0 \frac{1-\text{cos}(\theta_{180})e^{- \frac{TR}{T_1}} -[1-\text{cos}(\theta_{180})]e^{- \frac{TI}{T_1}}}{1 - \text{cos}(\theta_{180}) \text{cos}(\theta_{90}) e^{- \frac{TR}{T_1}}} \end{equation}\]

where Mz is the longitudinal magnetization prior to the θ90 pulse. If the in-phase real signal is desired, it can be calculated by multiplying Eq. 1 by ksin(θ90)e-TE/T2, where k is a constant. This general equation can be simplified by grouping together the constants for each measurements regardless of their values (i.e. at each TI, same TE and θ90 are used) and assuming an ideal inversion pulse:

(2)#\[\begin{equation}\label{eq:2} M_z(TI) = C(1-2e^{- \frac{TI}{T_1}} + e^{- \frac{TR}{T_1}}) \end{equation}\]

where the first three terms and the denominator of Eq. 1 have been grouped together into the constant C. If the experiment is designed such that TR is long enough to allow for full relaxation of the magnetization (TR > 5T1), we can do an additional approximation by dropping the last term in Eq. 2:

(3)#\[\begin{equation}\label{eq:3} M_z(TI) = C(1-2e^{- \frac{TI}{T_1}}) \end{equation}\]

The simplicity of the signal model described by Eq. 3, both in its equation and experimental implementation, has made it the most widely used equation to describe the signal evolution in an inversion recovery T1 mapping experiment. The magnetization curves are plotted in Figure 2 for approximate T1 values of three different tissues in the brain. Note that in many practical implementations, magnitude-only images are acquired, so the signal measured would be proportional to the absolute value of Eq. 3.

Hide code cell source
from repo2data.repo2data import Repo2Data
import os 
import pickle
import matplotlib.pyplot as plt
import chart_studio.plotly as py
import plotly.graph_objs as go
import numpy as np
from plotly import __version__
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
from IPython.display import display, HTML
from plotly import tools

data_req_path = os.path.join("..","..", "binder", "data_requirement.json")
repo2data = Repo2Data(data_req_path)
DATA_ROOT = os.path.join(repo2data.install()[0],"t1-book-neurolibre")
Hide code cell output
---- repo2data starting ----
/srv/conda/envs/notebook/lib/python3.8/site-packages/repo2data
Config from file :
../../binder/data_requirement.json
Destination:
../../data/qmrlab-t1-book

Info : ../../data/qmrlab-t1-book already downloaded

Figure 2. Inversion recovery curves (Eq. 2) for three different T1 values, approximating the main types of tissue in the brain.

Hide code cell source
filename = os.path.join(DATA_ROOT,"01",'figure_2.pkl')

with open(filename, 'rb') as f:
    params, signal_WM, signal_GM, signal_CSF = pickle.load(f)

config={'showLink': False, 'displayModeBar': False}

init_notebook_mode(connected=True)

wm = go.Scatter(
    x = params["TI"],
    y = signal_WM,
    name = 'T<sub>1</sub> = 0.9 s (White Matter)',
    text = 'T<sub>1</sub> = 0.9 s (White Matter)',
    hoverinfo = 'x+y+text'
)

gm = go.Scatter(
    x = params["TI"],
    y = signal_GM,
    name = 'T<sub>1</sub> = 1.5 s (Grey Matter)',
    text = 'T<sub>1</sub> = 1.5 s (Grey Matter)',
    hoverinfo = 'x+y+text'
)

csf = go.Scatter(
    x = params["TI"],
    y = signal_CSF,
    name = 'T<sub>1</sub> = 4.0 s (Cerebrospinal Fluid)',
    text = 'T<sub>1</sub> = 4.0 s (Cerebrospinal Fluid)',
    hoverinfo = 'x+y+text'
)

data = [wm, gm, csf]

layout = go.Layout(
    width=600,
    height=350,
    margin=go.layout.Margin(
        l=100,
        r=50,
        b=60,
        t=0,
    ),
    annotations=[
        dict(
            x=0.5004254919715793,
            y=-0.175,
            showarrow=False,
            text='Inversion Time – TI (ms)',
            font=dict(
                family='Times New Roman',
                size=22
            ),
            xref='paper',
            yref='paper'
        ),
        dict(
            x=-0.15,
            y=0.50,
            showarrow=False,
            text='Long. Magnetization (M<sub>z</sub>)',
            font=dict(
                family='Times New Roman',
                size=22
            ),
            textangle=-90,
            xref='paper',
            yref='paper'
        ),
    ],
    xaxis=dict(
        showgrid=False,
        linecolor='black',
        linewidth=2
    ),
    yaxis=dict(
        showgrid=False,
        linecolor='black',
        linewidth=2
    ),
    legend=dict(
        x=0.55,
        y=0.15,
        traceorder='normal',
        font=dict(
            family='Times New Roman',
            size=12,
            color='#000'
        ),
        bordercolor='#000000',
        borderwidth=2
    ),
    plot_bgcolor='white'
)

fig = dict(data=data, layout=layout)

plot(fig, filename = 'ir_fig_2.html', config = config)

display(HTML('ir_fig_2.html'))

Practically, Eq. 1 is the better choice for simulating the signal of an inversion recovery experiment, as the TRs are often chosen to be greater than 5T1 of the tissue-of-interest, which rarely coincides with the longest T1 present (e.g. TR may be sufficiently long for white matter, but not for CSF which could also be present in the volume). Equation 3 also assumes ideal inversion pulses, which is rarely the case due to slice profile effects. Figure 3 displays the inversion recovery signal magnitude (complete relaxation normalized to 1) of an experiment with TR = 5 s and T1 values ranging between 250 ms to 5 s, calculated using both equations.

Figure 3. Signal recovery curves simulated using Eq. 3 (solid) and Eq. 1 (dotted) with a TR = 5 s for T1 values ranging between 0.25 to 5 s.

Hide code cell source
filename = os.path.join(DATA_ROOT,"01",'figure_3.pkl')

with open(filename, 'rb') as f:
    T1_range, signal_T1_Eq1, signal_T1_Eq3 = pickle.load(f)

config={'showLink': False, 'displayModeBar': False}

init_notebook_mode(connected=True)

data1 = [dict(
        visible = False,
        x = params["TI"],
        y = abs(signal_T1_Eq3[ii]),
        name = '[Eq. 3] – Long TR approximation',
        text = '[Eq. 3] – Long TR approximation',
        hoverinfo = 'x+y+text') for ii in range(len(T1_range))]

data1[3]['visible'] = True

data2 = [dict(
        visible = False,
        x = params["TI"],
        y = abs(signal_T1_Eq1[ii]),
        line = dict(
            color = ('rgb(22, 96, 167)'),
            dash = 'dash'),
        name = '[Eq. 1] – General Equation',
        text = '[Eq. 1] – General Equation',
        hoverinfo = 'x+y+text') for ii in range(len(T1_range))]

data2[3]['visible'] = True

data = data1 + data2

steps = []
for i in range(len(T1_range)):
    step = dict(
        method = 'restyle',  
        args = ['visible', [False] * len(data1)],
        label = str(T1_range[i])
    )
    step['args'][1][i] = True # Toggle i'th trace to "visible"
    steps.append(step)

sliders = [dict(
    x = 0,
    y = -0.0,
    active = 3,
    currentvalue = {"prefix": "T<sub>1</sub> value (s): <b>"},
    pad = {"t": 50, "b": 10},
    steps = steps
)]

layout = go.Layout(
    width=580,
    height=400,
    margin=go.layout.Margin(
        l=80,
        r=40,
        b=60,
        t=10,
    ),
    annotations=[
        dict(
            x=0.5004254919715793,
            y=-0.2,
            showarrow=False,
            text='Inversion Time – TI (ms)',
            font=dict(
                family='Times New Roman',
                size=22
            ),
            xref='paper',
            yref='paper'
        ),
        dict(
            x=-0.14,
            y=0.5,
            showarrow=False,
            text='Signal (magnitude)',
            font=dict(
                family='Times New Roman',
                size=22
            ),
            textangle=-90,
            xref='paper',
            yref='paper'
        ),
    ],
    xaxis=dict(
        autorange=False,
        range=[0, 5],
        showgrid=False,
        linecolor='black',
        linewidth=2
    ),
    yaxis=dict(
        autorange=False,
        range=[0, 1],
        showgrid=False,
        linecolor='black',
        linewidth=2
    ),
    legend=dict(
        x=0.5,
        y=0.5,
        traceorder='normal',
        font=dict(
            family='Times New Roman',
            size=12,
            color='#000'
        ),
        bordercolor='#000000',
        borderwidth=2
    ), 
    sliders=sliders,
    plot_bgcolor='white'
)

fig = dict(data=data, layout=layout)

plot(fig, filename = 'ir_fig_3.html', config = config)
display(HTML('ir_fig_3.html'))