One-dimensional (1D) Fourier transformation
In this notebook, we are going to transform time-domain data into 1D or 2D spectra using SpectroChemPy processing tools
[1]:
import spectrochempy as scp
|
SpectroChemPy's API - v.0.8.2.dev7 ©Copyright 2014-2025 - A.Travert & C.Fernandez @ LCS |
FFT of 1D NMR spectra
First we open read some time domain data. Here is a NMD free induction decay (FID):
[2]:
path = scp.preferences.datadir / "nmrdata" / "bruker" / "tests" / "nmr" / "topspin_1d"
fid = scp.read_topspin(path)
fid
Running on GitHub Actions
MPL Configuration directory: /home/runner/.config/matplotlib
Stylelib directory: /home/runner/.config/matplotlib/stylelib
[2]:
NDDataset: [complex128] pp (size: 12411)[topspin_1d expno:1 procno:1 (FID)]
Summary
Data
I[ -1037 -2200 ... 0.06203 -0.05273] pp
Dimension `x`
The type of the data is complex:
[3]:
fid.dtype
[3]:
dtype('complex128')
We can represent both real and imaginary parts on the same plot using the show_complex
parameter.
[4]:
prefs = scp.preferences
prefs.figure.figsize = (6, 3)
fid.plot(show_complex=True, xlim=(0, 15000))
print("td = ", fid.size)
td = 12411

Now we perform a Fast Fourier Transform (FFT):
[5]:
spec = scp.fft(fid)
spec.plot(xlim=(100, -100))
print("si = ", spec.size)
spec
si = 16384
[5]:
NDDataset: [complex128] pp (size: 16384)[topspin_1d expno:1 procno:1 (FID)]
Summary
2025-04-27 01:47:22+00:00> Fft applied on dimension x
2025-04-27 01:47:22+00:00> Inplace binary op: imul with `[ 0.61+0.793j 0.61+0.793j ... 0.813+0.582j 0.813+0.582j]`
2025-04-27 01:47:22+00:00> `pk` applied to dimension `x` with parameters: {'phc0': 52.43836, 'phc1': -16.8366, 'pivot': 0.0, 'exptc': 0.0}
Data
I[ -229.7 -126.8 ... 187.2 216.1] pp
Dimension `x`

Alternative notation
[6]:
k = 1024
spec = fid.fft(size=32 * k)
spec.plot(xlim=(100, -100))
print("si = ", spec.size)
si = 32768

[7]:
newfid = spec.ifft()
# x coordinate is in second (base units) so lets transform it
newfid.plot(show_complex=True, xlim=(0, 15000))
[7]:

Let’s compare fid and newfid. There differs as a rephasing has been automatically applied after the first FFT (with the parameters found in the original fid metadata: PHC0 and PHC1).
First point in the time domain of the real part is at the maximum.
[8]:
newfid.real.plot(c="r", label="fft + ifft")
ax = fid.real.plot(clear=False, xlim=(0, 5000), ls="--", label="original real part")
_ = ax.legend()

First point in the time domain of the imaginary part is at the minimum.
[9]:
fid.imag.plot(ls="--", label="original imaginary part")
ax = newfid.imag.plot(clear=False, xlim=(0, 5000), c="r", label="fft + ifft")
_ = ax.legend(loc="lower right")

Preprocessing
Line broadening
Often before applying FFT, some exponential multiplication em
or other broadening filters such as gm
or sp
are applied. See the dedicated apodization tutorial.
[10]:
fid2 = fid.em(lb="50. Hz")
spec2 = fid2.fft()
spec2.plot()
spec.plot(
clear=False, xlim=(10, -5), c="r"
) # superpose the non broadened spectrum in red and show expansion.
[10]:

Zero-filling
[11]:
print("td = ", fid.size)
td = 12411
[12]:
td = 64 * 1024 # size: 64 K
fid3 = fid.zf_size(size=td)
print("new td = ", fid3.x.size)
new td = 65536
[13]:
spec3 = fid3.fft()
spec3.plot(xlim=(100, -100))
print("si = ", spec3.size)
si = 65536

Time domain baseline correction
See the dedicated Time domain baseline correction tutorial.
Magnitude calculation
[14]:
ms = spec.mc()
ms.plot(xlim=(10, -10))
spec.plot(clear=False, xlim=(10, -10), c="r")
[14]:

Power spectrum
[15]:
mp = spec.ps()
(mp / mp.max()).plot()
(spec / spec.max()).plot(
clear=False, xlim=(10, -10), c="r"
) # Here we have normalized the spectra at their max value.
[15]:

Real Fourier transform
In some case, it might be interesting to perform real Fourier transform . For instance, as a demonstration, we will independently transform real and imaginary part of the previous fid, and recombine them to obtain the same result as when performing complex fourier transform on the complex dataset.
[16]:
lim = (-20, 20)
spec3.plot(xlim=lim)
spec3.imag.plot(xlim=lim)
[16]:


[17]:
Re = fid3.real.astype("complex64")
fR = Re.fft()
fR.plot(xlim=lim, show_complex=True)
Im = fid3.imag.astype("complex64")
fI = Im.fft()
fI.plot(xlim=lim, show_complex=True)
[17]:


Recombination:
[18]:
(fR - fI.imag).plot(xlim=lim)
(fR.imag + fI).plot(xlim=lim)
[18]:

