Inversion recovery experiment

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

This example shows steps to invert a NMR inversion recovery dataset. The data was acquired using sample of tetra-\(\mathrm{Li}_{7}\mathrm{SiPS}_{8}\) at a magic angle spinning frequency of 20 KHz and \(\nu_{31_{\mathrm{P}}}\) = 323 MHz.

Imports

[2]:
# import iltpy
import iltpy as ilt

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

print(f"ILTpy version: {ilt.__version__}")
ILTpy version: 1.0.0
  • Load already processed data (i.e. after Fourier transformation and routine NMR processing steps)

Data preparation

Load data from text files into numpy arrays

[3]:
# load data from text files into numpy arrays
lsps_data = np.loadtxt('../../../examples/NMR/inversion_recovery/data.txt')
lsps_vdlist = np.loadtxt('../../../examples/NMR/inversion_recovery/dim1.txt')
print(f'Original data size : {lsps_data.shape}')


Original data size : (15, 65536)

Data reduction

[4]:
# Reduce data by a moving mean
lsps_data_red = np.reshape(lsps_data,(15,1024,64))
lsps_data_red = np.squeeze(np.mean(lsps_data_red,axis=2))
print(f'Reduced data size : {lsps_data_red.shape}')
Reduced data size : (15, 1024)
  • The data consists of 15 recovery delays and for each delay, a spectrum of 65536 points.

  • To reduce computational demands of the inversion, the data is reduced.

  • The data was reduced to (15,1024) by moving mean.

  • Alternatively, number of points can be reduced by decreasing the sampling.

lsps_data_red = lsps_data[:,0:65536:65] # selects spectrum points with a spacing of 65 for each delay

Set noise variance to unity

[5]:
## Setting noise variance to unity

# To estimate noise, select a region with no signal
fig,ax = plt.subplots(figsize=(6,2.5),ncols=2)
ax[0].plot(lsps_data_red[-1])
ax[1].plot(lsps_data_red[-1][220:350])
ax[0].set_title('NMR spectrum')
ax[1].set_title('Noise region')
[5]:
Text(0.5, 1.0, 'Noise region')
../_images/Gallery_plot_NMR_IR_14_1.png

Note

The noise should be estimated from a region with random noise and no systematic features. In case of spectra with distorted baselines, the noise region can be obtained by doing an additional baseline correction for the corresponding region and then estimating the noise level.

[6]:
# and calculate the noise level
noise_lvl = np.std(lsps_data_red[-1][220:350])

# scale data with the noise_lvl
lsps_data_red = lsps_data_red/noise_lvl

ILTpy workflow

Load data

[7]:
lspsILT = ilt.iltload(data=lsps_data_red,t=lsps_vdlist)
[8]:
# Plot the data
fig,ax = plt.subplots(figsize=(3,2.5))
a = ax.plot(lspsILT.data.flatten())
../_images/Gallery_plot_NMR_IR_20_0.png

Initialization and inversion

[9]:
# Intialization
tau = np.logspace(-2,5,100) ##user-defined tau
lspsILT.init(tau,kernel=ilt.Exponential())
[10]:
# Inversion
lspsILT.invert()
Starting iterations ...
100%|██████████| 100/100 [05:37<00:00,  3.38s/it]
Done.

Plot the results

[11]:
# In case of a compatible kernel, the residuals should be random, devoid of systematic features.
# check residuals for systematic features
fig,ax = plt.subplots(figsize=(4,1.2))
a = ax.plot(lspsILT.residuals)
../_images/Gallery_plot_NMR_IR_25_0.png
[12]:
# load the ppm axis for plotting

ppm_axis = np.loadtxt('../../../examples/NMR/inversion_recovery/chemical_shift_axis.txt')
ppm_axis = np.reshape(ppm_axis,(1024,64))
ppm_axis = np.mean(ppm_axis,axis=1)

# Generate x, y, z values from the lspsILT object
# use the ppm axis for plotting

x, y = ppm_axis[300:700], lspsILT.tau[0].flatten()
Z = -lspsILT.g[:,300:700]

fig,ax = plt.subplots()
a = ax.contourf(x,np.log10(y),Z,cmap='jet',levels=150)
ax.set_xlabel(r"$^{31}\mathrm{P}$"+' chemical shift [ppm]')
ax.set_ylabel('log('+r'$T_{1})$')

plt.colorbar(a)
[12]:
<matplotlib.colorbar.Colorbar at 0x119f3ea50>
../_images/Gallery_plot_NMR_IR_26_1.png

Note

  • Inversion recovery curves may exhibit non-zero baselines, which may be interpreted by the algorithm as a slow relaxing component (the negative peaks around \(10^{4}\)).

  • This artefact manifests in the obtained relaxation distribution as a negative peak with a long relaxation time constant, as seen above.

  • The artefact may be separated from the features of interest by choosing a tau with a higher maximum value.