Source code for torx.analysis.fourier.fourier_base_m
"""Basic Fourier analysis routines."""
import xarray as xr
import numpy as np
from scipy import fft
from scipy import interpolate
from torx.autodoc_decorators_m import autodoc_function
[docs]
@autodoc_function
def equally_sampled_interval(x_vals, n_samples):
"""Return the equally sampled interval within the given x values."""
if(type(x_vals) == xr.DataArray):
x_vals = x_vals.values
x_sampled, x_spacing = np.linspace(
np.min(x_vals), np.max(x_vals), num=n_samples, retstep=True
)
return x_sampled, x_spacing
[docs]
@autodoc_function
def equally_sampled_data(x_vals, y_vals, n_samples):
"""
Return samples of equally-spaced points within the given values.
Fast fourier transform algorithm requires equally spaced data.
Supplying n_samples as a power of 2 improves the computational efficiency
of the FFT.
"""
# Sort the data
order = np.argsort(x_vals)
x_sorted = x_vals[order]
y_sorted = y_vals[order]
# Remove repeat values
zero_positions = np.where(np.diff(x_sorted) == 0.0)
x_sorted = np.delete(x_sorted, zero_positions)
y_sorted = np.delete(y_sorted, zero_positions)
# Resample the data at evenly spaced points
x_interpolated, x_spacing = equally_sampled_interval(x_vals, n_samples)
# Interpolate the sorted data on the new interval
y_interpolated = interpolate.interp1d(x_sorted, y_sorted, kind="cubic")(
x_interpolated
)
return x_interpolated, y_interpolated, x_spacing
[docs]
@autodoc_function
def fourier_transform_1d(
y_vals: np.ndarray,
x_vals: np.ndarray,
n_samples: int,
apply_window: bool=False,
remove_mean: bool=True,
window_beta: int=14.0,
normalize: bool=True
):
"""
Perform a one dimensional fourier transform on real, unequally-spaced data.
The function will interpolate the given data onto an equally-spaced x-grid,
and then perform the fourier transform. The returned complex_amplitudes and
frequencies are given only for positive frequencies, since real input data
is assumed.
If 'apply_window" is false (e.g. for flux surfaces in the scrape-of layer),
a window function is applied to make the signal 'apply_window'.
Note
----
The order of x_vals and y_vals is switched compared to standard ordering,
in order to pass x_vals as a keyword argument to xr.apply_ufunc.
"""
# Perform interpolation (cubic by default)
_, y_interp, _ = equally_sampled_data(x_vals, y_vals, n_samples)
if remove_mean:
y_interp = y_interp - np.mean(y_interp)
if normalize:
norm = "forward"
norm_fac = 2.0 * np.pi
else:
norm = "backward"
norm_fac = 1.0
# Make signal apply_window if in open field line region
if apply_window:
window = np.kaiser(n_samples, beta=window_beta)
amplitude_full = fft.rfft(y_interp * window, norm=norm)
else:
amplitude_full = fft.rfft(y_interp, norm=norm)
# RFFT works on real data and will only return the positive part of the
# spectrum, thus we do not need to remove the negative part (as would be
# the case when using fft).
# However we crop the data to floor(n_samples / 2) because the length may
# vary depending on even and odd n_samples.
amplitude_pos = 2.0 * amplitude_full[: (n_samples // 2)]
return amplitude_pos * norm_fac
[docs]
@autodoc_function
def fourier_frequencies(x_vals, n_samples):
"""Compute the FFT-frequencies from the sampling points x_vals."""
# Get the spacing for the rfftfreq command
_, spacing = equally_sampled_interval(x_vals, n_samples)
frequencies = fft.rfftfreq(n_samples, d=spacing)
# Do the same cropping as in fourier transform to get the correct length
return frequencies[: (n_samples // 2)]