Inversion recovery

[1]:
# This file is part of ILTpy examples.
# Author : Dr. Davis Thomas Daniel
# Last updated : 25.08.2025

This example shows steps to invert an EPR inversion recovery dataset using ILTpy. Data is loaded using numpy arrays.

Importing iltpy and loading data

[2]:
# import iltpy
import iltpy as ilt

# other libraries for handling data, plotting
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
plt.style.use('../../../examples/latex_style.mplstyle')

print(f"ILTpy version: {ilt.__version__}")
ILTpy version: 1.0.0
[3]:
# Load data using numpy arrays
data_EPR = np.loadtxt('../../../examples/EPR/inversion_recovery/data.txt') # intensity
t_EPR = np.loadtxt('../../../examples/EPR/inversion_recovery/dim1.txt') # time

coalIR = ilt.iltload(data=data_EPR,t=t_EPR)

Note

Alternatively, data and the sampling vector can be loaded from from text files directly by following a specific naming scheme for files.

For TXT data types, iltload looks for files named data.txt and dimN.txt files, where N is the number of dimensions. In this example, the data is stored as data.txt and the delays used during the inversion recovery experiment is stored as dim1.txt. The required folder structure and naming scheme is shown below.

examples/1d_epr_t1_coal/
├── data.txt
└── dim1.txt

Then, the folderpath can be given to iltload.

coalIR = ilt.iltload('../../../examples/1d_epr_t1_coal')
[4]:
# Plot the raw data
fig,ax = plt.subplots(figsize=(3,2.5))
ax.plot(coalIR.t[0].flatten(),coalIR.data,'k')
plt.xlabel('t [ns]')
a1 = plt.title('1D EPR inversion recovery',size=12)
../_images/Gallery_plot_1d_epr_7_0.png

Data preparation

[5]:
# Set noise variance to 1.
## estimate noise level using some points at the end of inversion recovery trace
noise_lvl  = np.std(coalIR.data[900:])

# Scale the data with the noise level
coalIR.data = coalIR.data/noise_lvl

Initialization and inversion

[6]:

# define output sampling tau = np.logspace(0,8.5,100) # Initialize the IltData object coalIR.init(tau=tau,kernel=ilt.Exponential()) # Invert coalIR.invert()
Starting iterations ...
100%|██████████| 100/100 [00:00<00:00, 693.67it/s]
Done.

Plotting results

[7]:
fig = plt.figure(figsize=(5, 5))
gs = gridspec.GridSpec(2, 2, width_ratios=[1, 1])

ax0 = fig.add_subplot(gs[0])
ax1 = fig.add_subplot(gs[1])
ax2 = fig.add_subplot(gs[1,0:])

# data and fit
ax0.plot(coalIR.t[0].flatten(), coalIR.data, color='#1f77b4', label='Exp.', linewidth=2)
ax0.plot(coalIR.t[0].flatten(), coalIR.fit, color='#ff7f0e', linestyle='dashed', label='Fit')
ax0.set_xlabel('t [ns]')
ax0.set_title('Data and fit')

# residuals
ax1.plot(coalIR.t[0].flatten(), coalIR.residuals, color='#7f7f7f')
ax1.set_title('Residuals')
ax1.set_xlabel('t [ns]')

# Distribution
ax2.semilogx(coalIR.tau[0].flatten(), -coalIR.g, color='#9467bd', label=r'$T_1$'+' distribution',linewidth=2)
ax2.set_title(r'$T_1$'+' distribution')
ax2.set_xlabel(r'$T_1$'+' [ns]')
ax2.legend()

plt.tight_layout()
plt.show()
../_images/Gallery_plot_1d_epr_13_0.png

Note

  • Inversion recovery curves may exhibit non-zero baselines, which may be interpreted by the algorithm as a slow relaxing component. This artefact manifests in the obtained relaxation distribution as peak with a long relaxation time constant as seen in the plot above at \(10^8\) ns. This baseline artefact can be removed by setting the sb parameter during initialization to True. See next example for usage.

  • Since it is an inversion recovery, the negative of g is plotted. If the residuals do not indicate any systematic features, the chosen kernel is compatible.

Appendix : Baseline artefact

[8]:
# We can test that the baseline feature is an artefact by increasing the maximum output sampling limit and checking if the feature shifts.
[9]:
from copy import deepcopy

coalIR2 = deepcopy(coalIR)

# change the upper limit of output sampling
tau2 = np.logspace(0,9,100)

# initialize
coalIR2.init(tau=tau2,kernel=ilt.Exponential())

coalIR2.invert()
Starting iterations ...
100%|██████████| 100/100 [00:00<00:00, 967.83it/s]
Done.

[10]:
np.log10(max(tau))
[10]:
np.float64(8.5)
[11]:
fig,ax = plt.subplots(figsize=(8, 3))

ax.semilogx(coalIR.tau[0].flatten(), -coalIR.g, color='#9467bd', label=r'$max(\tau) = 10^{8.5}$',linewidth=2)
ax.semilogx(coalIR2.tau[0].flatten(), -coalIR2.g, color="#254ade", label=r'$max(\tau) = 10^{9}$',linewidth=2)
ax.set_xlabel(r'$T_1$'+' [ns]')

ax.legend()


[11]:
<matplotlib.legend.Legend at 0x12120f250>
../_images/Gallery_plot_1d_epr_19_1.png

Note

  • Notice that the artefact shifts with higher output sampling limit, but the real feature remains the same.