Source code for fluidimage.postproc.util
"""Utilities for post-processing (:mod:`fluidimage.postproc.util`)
==================================================================
.. autofunction:: get_grid_from_ivecs_final
.. autofunction:: reshape_on_grid_final
.. autofunction:: compute_rot
.. autofunction:: compute_div
.. autofunction:: compute_1dspectrum
.. autofunction:: compute_2dspectrum
"""
import numpy as np
[docs]def get_grid_from_ivecs_final(x_flat, y_flat):
"""Get a 2d grid from flat arrays"""
x = np.unique(x_flat)
y = np.unique(y_flat)
if x_flat[1] == x_flat[0]:
assert y_flat[1] != y_flat[0]
# second_index_corresponds_to_x = True
dx = x_flat[len(y)] - x_flat[0]
dy = y_flat[1] - y_flat[0]
else:
assert y_flat[1] == y_flat[0]
# second_index_corresponds_to_x = False
dx = x_flat[1] - x_flat[0]
dy = y_flat[len(x)] - y_flat[0]
if dx < 0:
x = x[::-1]
if dy < 0:
y = y[::-1]
X, Y = np.meshgrid(x, y)
return X, Y
[docs]def reshape_on_grid_final(x_flat, y_flat, deltaxs, deltays):
"""Reshape flat arrays on a 2d grid"""
X, Y = get_grid_from_ivecs_final(x_flat, y_flat)
shape = X.shape
if x_flat[1] == x_flat[0]:
assert y_flat[1] != y_flat[0]
second_index_corresponds_to_x = True
shape = shape[::-1]
else:
assert y_flat[1] == y_flat[0]
second_index_corresponds_to_x = False
U = np.reshape(deltaxs, shape)
V = np.reshape(deltays, shape)
if second_index_corresponds_to_x:
U = U.T
V = V.T
return X, Y, U, V
[docs]def compute_rot(dUdy, dVdx):
"""Compute the rotational"""
return dVdx - dUdy
[docs]def compute_div(dUdx, dVdy):
"""Compute the divergence"""
return dUdx + dVdy
[docs]def compute_1dspectrum(x, signal, axis=0):
"""
Computes the 1D Fourier Transform
Parameters
----------
x: 1D np.ndarray
signal: np.ndarray
axis: int
Direction of the Fourier transform
Returns
-------
signal_fft: np.ndarray
fourier transform
omega: np.ndarray
puslation (in rad/s if x in s)
psd: np.ndarray
power spectral density normalized such that
np.sum(signal**2) * dx / Lx = np.sum(psd) * domega
"""
n = x.size
dx = x[1] - x[0]
Lx = n * dx
dk = 2 * np.pi / Lx
signal_fft = (
dx / Lx * np.fft.fftshift(np.fft.fft(signal, axis=axis), axes=axis)
)
omega = np.fft.fftshift(np.fft.fftfreq(n, dx)) * 2 * np.pi
psd = 0.5 / dk * np.abs(signal_fft) ** 2
return signal_fft, omega, psd
[docs]def compute_2dspectrum(X, Y, signal, axes=(1, 2)):
"""
Computes the 2D Fourier Transform
INPUT:
X: 2D np.array
Y: 2D np.array
signal: np.array to Fourier transform
axis: directions of the fourier transform
OUTPUT:
signal_fft = fourier transform
kx: puslation (in rad/m if X in m)
ky: pulsation (in rad/m if Y in m)
psd: power spectral density normalized such that
np.sum(signal**2) * dx / Lx = np.sum(psd) * domega
"""
if X.ndim == 2:
nx = X.shape[1]
ny = X.shape[0]
x = X[0, :2]
y = Y[:2, 0]
else:
nx = X.size
x = X
ny = Y.size
y = Y
dx = x[1] - x[0]
dy = y[1] - y[0]
lx = nx * dx
ly = ny * dy
dkx = 2 * np.pi / lx
dky = 2 * np.pi / ly
# energy = 0.5 * np.mean(signal**2)
signal_fft = (
1 / (nx * ny) * np.fft.fftshift(np.fft.fft2(signal, axes=axes), axes=axes)
)
# if axes == (1, 2):
# nt = signal.shape[0]
# else:
# nt = 1
# energy_fft = 0.5 / nt * np.sum(np.abs(signal_fft)**2)
# assert np.allclose(energy, energy_fft), (energy, energy_fft)
# in rad/m
kx = np.fft.fftshift(np.fft.fftfreq(nx, dx)) * (2 * np.pi)
ky = np.fft.fftshift(np.fft.fftfreq(ny, dy)) * (2 * np.pi)
psd = 0.5 / (dkx * dky) * np.abs(signal_fft) ** 2
# energy_psd = dkx * dky * np.sum(psd) / nt
# assert np.allclose(energy, energy_psd)
return signal_fft, kx, ky, psd