Using xrft#

xrft is a package to do Fourier transforms of xarray data easily. One can use the numpy-like functions, which are not exact (phase and amplitude shifts), but deliver the same results as a numpy-FT would do. There is also the possibility to correct these shifts with xrft. Another handy advantage of xrft is that the visualization of the results can be done with HoloViews in a very simple manner.

(Note the generation of each plot takes about 5 secs., you can add the dynamic=True parameter, if you want to load the plots faster. This is thoroughly explained in the holoviews tutorial)

[1]:
import xrft

%config InlineBackend.figure_formats = ['svg']


import holoviews as hv

from postopus.octopus_run import Run

hv.extension("bokeh", "matplotlib")  # Allow for interactive plots

input file is already defined in the folder (s. GitLab repo), otherwise we recommend defining it in the notebook

[2]:
cd ../octopus_data/interference/
/builds/octopus-code/postopus/docs/octopus_data/interference

Assuming you have octopus in your PATH:

[3]:
!octopus > out_gs.log 2>&1
[4]:
run = Run(".")

Original Data#

[5]:
xa = run.Maxwell.td.b_field(source="z=0").vx
[6]:
xa
[6]:
<xarray.DataArray 'vx' (t: 17, x: 41, y: 41)> Size: 229kB
[28577 values with dtype=float64]
Coordinates:
    step     (t) int64 136B 0 10 20 30 40 50 60 ... 100 110 120 130 140 150 160
  * t        (t) float64 136B 0.0 0.02107 0.04213 0.0632 ... 0.2949 0.316 0.337
  * x        (x) float64 328B -10.0 -9.5 -9.0 -8.5 -8.0 ... 8.0 8.5 9.0 9.5 10.0
  * y        (y) float64 328B -10.0 -9.5 -9.0 -8.5 -8.0 ... 8.0 8.5 9.0 9.5 10.0
Attributes:
    units:    au

Visualization with HoloViews#

[7]:
hv_dst_orig = hv.Dataset(xa)
hv_imt_orig = hv_dst_orig.to(hv.Image, ["x", "y"])
hv.output(max_frames=3000)
hv_imt_orig.opts(
    colorbar=True, width=500, height=400, cmap="seismic", clim=(-(10**-3), 10**-3)
)
[7]:

Numpy-like fft#

[8]:
# `xa.drop_vars("step")` is needed here as "step" and "t" represent the same dimension.
# Fxa = xrft.fft(xa.drop_vars("step"), dim="t") # numpy.fft-like behaviour
Fxa = xrft.fft(xa.drop_vars("step"), dim="t", real_dim="t")  # numpy.rfft-like behaviour
[9]:
Fxa
[9]:
<xarray.DataArray (freq_t: 9, x: 41, y: 41)> Size: 242kB
array([[[-1.19793340e-05+0.00000000e+00j,
         -1.51782559e-05+0.00000000e+00j,
         -1.99394843e-05+0.00000000e+00j, ...,
         -1.86336875e-05+0.00000000e+00j,
         -1.37752569e-05+0.00000000e+00j,
         -1.14321021e-05+0.00000000e+00j],
        [-1.06707295e-05+0.00000000e+00j,
         -9.83375078e-06+0.00000000e+00j,
         -1.21946988e-05+0.00000000e+00j, ...,
         -2.41516278e-05+0.00000000e+00j,
         -1.72796354e-05+0.00000000e+00j,
         -1.13740378e-05+0.00000000e+00j],
        [-1.14535267e-05+0.00000000e+00j,
         -8.13124846e-06+0.00000000e+00j,
         -8.11934843e-06+0.00000000e+00j, ...,
         -2.33994956e-05+0.00000000e+00j,
         -2.10661716e-05+0.00000000e+00j,
         -1.49552563e-05+0.00000000e+00j],
        ...,
        [-2.46157413e-05+0.00000000e+00j,
...
         -7.41306307e-05-3.31533773e-05j],
        ...,
        [ 2.66486388e-05-2.92516002e-05j,
          2.96726524e-06+9.66818951e-06j,
         -1.81344937e-05+4.73515077e-05j, ...,
          2.98016204e-05+1.18512323e-04j,
          2.31442888e-05+1.00987975e-04j,
          1.23292186e-05+7.05357745e-05j],
        [ 5.04332157e-05-6.16614708e-05j,
          2.73593815e-05-2.77036632e-05j,
          2.62716076e-06+1.28677947e-05j, ...,
          2.59060830e-05+1.20600298e-04j,
          2.41800082e-05+1.18674163e-04j,
          1.71752975e-05+9.94970001e-05j],
        [ 6.78150243e-05-8.26997336e-05j,
          4.92095458e-05-5.69909680e-05j,
          2.52443767e-05-2.23129418e-05j, ...,
          1.71446115e-05+1.03976907e-04j,
          1.95656689e-05+1.17210937e-04j,
          1.77978986e-05+1.13644828e-04j]]], shape=(9, 41, 41))
Coordinates:
  * x        (x) float64 328B -10.0 -9.5 -9.0 -8.5 -8.0 ... 8.0 8.5 9.0 9.5 10.0
  * y        (y) float64 328B -10.0 -9.5 -9.0 -8.5 -8.0 ... 8.0 8.5 9.0 9.5 10.0
  * freq_t   (freq_t) float64 72B 0.0 2.792 5.585 8.377 ... 16.75 19.55 22.34

Visualization with HoloViews#

[10]:
Fxa.name = "testFT"  # needed to use holoviews
hv_dst = hv.Dataset(Fxa.real)
hv_imt = hv_dst.to(hv.Image, ["x", "y"])
[11]:
hv_imt.opts(
    colorbar=True, width=500, height=400, cmap="seismic", clim=(-(10**-3), 10**-3)
)
[11]:

Inverse transform#

There is a shift when inverse transforming.

[12]:
# iFxa = xrft.ifft(Fxa, dim="freq_t") # Inverse of np.fft
iFxa = xrft.ifft(Fxa, dim="freq_t", real_dim="freq_t")  # Inverse of np.rfft

Visualization with HoloViews#

[13]:
iFxa.name = "iFtest"
hv_dst_ift = hv.Dataset(iFxa.real)
hv_imt_ift = hv_dst_ift.to(hv.Image, ["x", "y"])
hv.output(max_frames=3000)
hv_imt_ift.opts(
    colorbar=True, width=500, height=400, cmap="seismic", clim=(-(10**-3), 10**-3)
)
[13]:

FFT with true amplitude and true phase#

This is good for the cases in which the data is not centered at 0, see https://xrft.readthedocs.io/en/latest/DFT-iDFT_example.html. As one can see we go back to the original data after the inverse transform. There is no shift.

[14]:
Fxa2 = xrft.fft(xa.drop_vars("step"), dim="t", true_amplitude=True, true_phase=True)

Visualization with HoloViews#

[15]:
Fxa2.name = "testFT2"
hv_dst2 = hv.Dataset(Fxa2.real)
hv_imt_2 = hv_dst2.to(hv.Image, ["x", "y"])
hv_imt_2.opts(
    colorbar=True, width=500, height=400, cmap="seismic", clim=(-(10**-3), 10**-3)
)
[15]:

Inverse Transform#

[16]:
iFxa2 = xrft.ifft(Fxa2, dim="freq_t", true_amplitude=True, true_phase=True)

Visualization with HoloViews#

[17]:
iFxa2.name = "iFtest2"
hv_dst_ift2 = hv.Dataset(iFxa2.real)
hv_imt_ift2 = hv_dst_ift2.to(hv.Image, ["x", "y"])
hv_imt_ift2.opts(
    colorbar=True, width=500, height=400, cmap="seismic", clim=(-(10**-3), 10**-3)
)
[17]:

Spectral density#

[18]:
sd = xrft.power_spectrum(xa.drop_vars("step"), dim="t", real_dim="t", scaling="density")

Visualization with HoloViews#

[19]:
sd.name = "Spectral_density"
hv_dst_sd = hv.Dataset(sd)
hv_imt_sd = hv_dst_sd.to(hv.Image, ["x", "y"])
hv_imt_sd.opts(colorbar=True, width=500, height=400, cmap="seismic")
[19]: