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

FFT of 1D NMR spectra๏ƒ

First we open read some time domain data. Here is a NMD free induction decay (FID):

Requires the official spectrochempy-nmr plugin. Install with: pip install spectrochempy[nmr].

[2]:
path = scp.preferences.datadir / "nmrdata" / "bruker" / "tests" / "nmr" / "topspin_1d"
fid = scp.nmr.read_topspin(path)
fid
 WARNING | (UserWarning) No module named 'spectrochempy_hypercomplex'

The type of the data is complex:

[3]:
fid.dtype
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[3], line 1
----> 1 fid.dtype

AttributeError: 'NoneType' object has no attribute 'dtype'

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)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[4], line 3
      1 prefs = scp.preferences
      2 prefs.figure.figsize = (6, 3)
----> 3 _ = fid.plot(show_complex=True, xlim=(0, 15000))
      4 print("td = ", fid.size)

AttributeError: 'NoneType' object has no attribute 'plot'

Now we perform a Fast Fourier Transform (FFT):

[5]:
spec = scp.fft(fid)
_ = spec.plot(xlim=(100, -100))
print("si = ", spec.size)
spec
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[5], line 1
----> 1 spec = scp.fft(fid)
      2 _ = spec.plot(xlim=(100, -100))
      3 print("si = ", spec.size)
      4 spec

File ~/work/spectrochempy/spectrochempy/.venv/lib/python3.13/site-packages/spectrochempy/processing/fft/fft.py:166, in fft(dataset, size, sizeff, inv, **kwargs)
    118 def fft(dataset, size=None, sizeff=None, inv=False, **kwargs):
    119     """
    120     Apply a complex fast fourier transform.
    121
   (...)    164
    165     """
--> 166     is_ir = dataset.meta.interferogram
    168     # On which axis do we want to apply transform (get axis from arguments)
    169     dim = kwargs.pop("dim", kwargs.pop("axis", -1))

AttributeError: 'NoneType' object has no attribute 'meta'

Alternative notation

[6]:
k = 1024
spec = fid.fft(size=32 * k)
_ = spec.plot(xlim=(100, -100))
print("si = ", spec.size)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[6], line 2
      1 k = 1024
----> 2 spec = fid.fft(size=32 * k)
      3 _ = spec.plot(xlim=(100, -100))
      4 print("si = ", spec.size)

AttributeError: 'NoneType' object has no attribute 'fft'
[7]:
newfid = spec.ifft()
# x coordinate is in second (base units) so lets transform it
_ = newfid.plot(show_complex=True, xlim=(0, 15000))
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[7], line 1
----> 1 newfid = spec.ifft()
      2 # x coordinate is in second (base units) so lets transform it
      3 _ = newfid.plot(show_complex=True, xlim=(0, 15000))

NameError: name 'spec' is not defined

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()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[8], line 1
----> 1 _ = newfid.real.plot(c="r", label="fft + ifft")
      2 ax = fid.real.plot(clear=False, xlim=(0, 5000), ls="--", label="original real part")
      3 _ = ax.legend()

NameError: name 'newfid' is not defined

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")
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[9], line 1
----> 1 _ = fid.imag.plot(ls="--", label="original imaginary part")
      2 ax = newfid.imag.plot(clear=False, xlim=(0, 5000), c="r", label="fft + ifft")
      3 _ = ax.legend(loc="lower right")

AttributeError: 'NoneType' object has no attribute 'imag'

Preprocessing๏ƒ

Line broadening๏ƒ

Often before applying FFT, some exponential multiplication emor 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.
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[10], line 1
----> 1 fid2 = fid.em(lb="50. Hz")
      2 spec2 = fid2.fft()
      3 _ = spec2.plot()
      4 _ = spec.plot(

AttributeError: 'NoneType' object has no attribute 'em'

Zero-filling๏ƒ

[11]:
print("td = ", fid.size)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[11], line 1
----> 1 print("td = ", fid.size)

AttributeError: 'NoneType' object has no attribute 'size'
[12]:
td = 64 * 1024  # size: 64 K
fid3 = fid.zf_size(size=td)
print("new td = ", fid3.x.size)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[12], line 2
      1 td = 64 * 1024  # size: 64 K
----> 2 fid3 = fid.zf_size(size=td)
      3 print("new td = ", fid3.x.size)

AttributeError: 'NoneType' object has no attribute 'zf_size'
[13]:
spec3 = fid3.fft()
_ = spec3.plot(xlim=(100, -100))
print("si = ", spec3.size)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[13], line 1
----> 1 spec3 = fid3.fft()
      2 _ = spec3.plot(xlim=(100, -100))
      3 print("si = ", spec3.size)

NameError: name 'fid3' is not defined

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")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[14], line 1
----> 1 ms = spec.mc()
      2 _ = ms.plot(xlim=(10, -10))
      3 _ = spec.plot(clear=False, xlim=(10, -10), c="r")

NameError: name 'spec' is not defined

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.
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[15], line 1
----> 1 mp = spec.ps()
      2 _ = (mp / mp.max()).plot()
      3 _ = (spec / spec.max()).plot(
      4     clear=False, xlim=(10, -10), c="r"

NameError: name 'spec' is not defined

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)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[16], line 2
      1 lim = (-20, 20)
----> 2 _ = spec3.plot(xlim=lim)
      3 _ = spec3.imag.plot(xlim=lim)

NameError: name 'spec3' is not defined
[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)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[17], line 1
----> 1 Re = fid3.real.astype("complex64")
      2 fR = Re.fft()
      3 _ = fR.plot(xlim=lim, show_complex=True)
      4 Im = fid3.imag.astype("complex64")

NameError: name 'fid3' is not defined

Recombination:

[18]:
_ = (fR - fI.imag).plot(xlim=lim)
_ = (fR.imag + fI).plot(xlim=lim)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[18], line 1
----> 1 _ = (fR - fI.imag).plot(xlim=lim)
      2 _ = (fR.imag + fI).plot(xlim=lim)

NameError: name 'fR' is not defined