FFT filtering
Added in version 2.0.0.
Important
The brick-wall FFT filter is available only when MembraneCurvature
runs with surface_method='binning'.
Note that per-frame arrays are not FFT-filtered!
For average maps, MembraneCurvature applies the brick-wall
filter once to the averaged z_surface and computes mean and Gaussian curvature on that filtered average.
For binning, filtering defaults to fft_filter='auto'. Pass fft_filter=None to disable FFT filtering.
This module implements a brick-wall filter on a binned height field in the reciprocal space.
This implementation uses numpy.fft.fft2() with physical bin widths \(\Delta x\) and
\(\Delta y\) in Å along the x and y dimensions, respectively.
Usage
Users can control filtering in three ways via the fft_filter argument:
Automatic (default for binning):
fft_filter='auto'Low-pass with
q_high = 0.5 * q_Nyq, resolved at runtime viaresolve_fft_filter().Manual:
fft_filter={'q': (q_low, q_high)}Users explicitly specify the
(q_low, q_high)pair in rad/Å.Disabled:
fft_filter=NoneNo FFT filtering is applied to the time-averaged surface.
Warning
We recommend fft_filter='auto' when enabling the brick-wall filter,
unless there is a specific reason to choose bounds manually.
With fft_filter='auto', bounds are (0, 0.5 * q_Nyq) from the bin grid
dx and dy.
When using the manual mode, the following rules apply:
Keep
q_low=0for a normal smoothing filter. If you setq_lowabove zero, the filter drops the average height of the membrane and behaves more like a high-pass (ripples only, no overall level).With
fft_filter='auto', the upper cutoff0.5 * q_Nyqis a safe default for most grids. Note that it is not a single correct value for every system. Changeq_highonly if you know you need sharper or smoother results.
Filter cutoffs q_low and q_high are given in rad/Å. They describe how
rippled the membrane height can be along the grid: small \(|q|\) represents a broad,
gentle undulation, while large \(|q|\) represents fine, short-wavelength detail.
The finest detail the bin grid can represent is set by the Nyquist frequency
nyquist_q(), here in this module referred to as q_Nyq, and which depends on
dx and dy.
The filter is applied in two steps. First, resolve_fft_filter() picks the pass band.
Then apply_fft_filter() FFTs the height, zeros modes outside the band, and inverse-FFTs back
to the surface in real space.
Limitations
numpy.fft.fft2()treats the grid as periodic. Preferwrap=Truefor binning on trajectories with periodic boundaries, or accept edge artifacts when the patch is non-periodic.The filter is a brick-wall mask in \(|q|\), which can cause ringing (Gibbs oscillations). Curvature uses second derivatives of the height field, so ringing can be amplified.
fft_filter='auto'uses a conservative cutoff. Tune manually only if you know what you are doing.Empty bins (
NaNafter binning): before filtering, empty bins are filled with the average height of occupied bins (not zero or neighbour interpolation). Large gaps can therefore add artifacts near empty regions in the filtered surface. Finer binning or fewer empty bins usually helps. See_height_for_fft().Non-square bins (
dx != dy): the pass band uses an isotropic cutoff on \(|q| = \sqrt{q_x^2 + q_y^2}\), whilenyquist_q()uses \(\min(\pi/\Delta x, \pi/\Delta y)\). Modes that are Nyquist-resolvable along the finer-spaced axis but have \(|q|\) above the coarse-axis limit are removed by the filter even though the grid could represent them along that axis.
Functions
- membrane_curvature.fft_filtering._validate_bin_widths(dx, dy)[source]
Validate bin widths before calculating the Nyquist wavevector magnitude.
- Parameters:
dx (float) – Bin width along
x(Å).dy (float) – Bin width along
y(Å).
- Raises:
ValueError – If
dxordyis not strictly positive.
- membrane_curvature.fft_filtering.nyquist_q(dx, dy)[source]
Return a conservative Nyquist wavevector magnitude for a bin grid after validation. The Nyquist wavevector magnitude is calculated as \(q_{\mathrm{Nyq}} = \min(\pi/\Delta x, \pi/\Delta y)\) in \(\mathrm{rad}/\mathrm{\AA}\).
- Parameters:
dx (float) – Bin width along
x(Å).dy (float) – Bin width along
y(Å).
- Returns:
q_nyq – Nyquist wavevector magnitude in rad/Å.
- Return type:
- Raises:
ValueError – If
dxordyis not strictly positive.
- membrane_curvature.fft_filtering.resolve_fft_filter(fft_filter, dx, dy)[source]
Resolve
fft_filterinto(q_low, q_high)for binning analyses.- Parameters:
fft_filter (str or dict) –
'auto'applies a low-pass withq_high = 0.5 * q_Nyq. Otherwise{'q': (q_low, q_high)}in rad/Å.dx (float) – Bin width along
x(Å).dy (float) – Bin width along
y(Å).
- Returns:
q_bounds – Resolved
(q_low, q_high)in rad/Å.- Return type:
- Raises:
ValueError – If
fft_filteris not'auto'or a dict with key'q'. Invalid(q_low, q_high)values and bounds above Nyquist are raised or warned from_validate_q_pair(). Non-positivedx/dyare raised fromnyquist_q()when bounds are resolved.- Warns:
UserWarning – See
_validate_q_pair().
- membrane_curvature.fft_filtering._auto_q_bounds(dx, dy)[source]
Calculates the default tuple
(q_low, q_high)forfft_filter='auto'. Keeps modes with \(|q| \leq 0.5\, q_{\mathrm{Nyq}}\) (low-pass).
- membrane_curvature.fft_filtering._validate_q_pair(bounds, dx, dy)[source]
Parse and validate the bounds
(q_low, q_high)forfft_filter. If valid, the pass band is defined byq_low <= |q| <= q_highin rad/Å.- Parameters:
bounds (tuple or list) – Pair
(q_low, q_high)in rad/Å.dx (float) – Bin width along
x(Å).dy (float) – Bin width along
y(Å).
- Returns:
(q_low, q_high) – Validated
(q_low, q_high).- Return type:
- Raises:
ValueError – If
boundsis invalid,q_low == q_high, orq_lowexceeds Nyquist.- Warns:
UserWarning – If
q_highis above what the bin grid can represent (Nyquist), or ifq_low > 0(the filter no longer keeps the average height). Useq_low=0for a usual low-pass).
- membrane_curvature.fft_filtering.apply_fft_filter(height, dx, dy, q_bounds)[source]
Apply brick-wall FFT filtering to a binned height field.
- Parameters:
height (numpy.ndarray) – Binned height field, shape
(n_x_bins, n_y_bins).dx (float) – Bin width along
x(Å).dy (float) – Bin width along
y(Å).q_bounds (tuple of (float, float)) –
(q_low, q_high)in rad/Å fromresolve_fft_filter().
- Returns:
filtered_height – Filtered height field.
- Return type:
- membrane_curvature.fft_filtering._squared_radius_grid(n_x_bins, n_y_bins, dx, dy)[source]
Build a per-bin grid of squared angular wavevector magnitude.
Each entry matches the corresponding bin in the output of
numpy.fft.fft2()(vianumpy.fft.fftfreq()).- Parameters:
n_x_bins (int) – Number of bins along
x.n_y_bins (int) – Number of bins along
y.dx (float) – Bin width along
x(Å).dy (float) – Bin width along
y(Å).
- Returns:
grid_squared_radius – Array of shape
(n_x_bins, n_y_bins)with values \(|q|^2\) in (rad/Å)2.- Return type:
- membrane_curvature.fft_filtering._height_for_fft(height, nan_mask)[source]
Prepare a height field for
numpy.fft.fft2()when some bins are empty.- Parameters:
height (numpy.ndarray) – Binned height field, shape
(n_x_bins, n_y_bins).nan_mask (numpy.ndarray) – Boolean mask of shape
(n_x_bins, n_y_bins)withTruefor empty bins.
- Returns:
filled_height – Height field with empty bins filled to the average height of occupied bins.
- Return type:
Notes
Empty bins are stored as
NaN. Before the forward FFT, each empty bin is replaced with the mean height of occupied bins. The fill value is uniform across empty bins and it is neither zero nor obtained by spatial interpolation.This is a simple placeholder to run the filter. It can make the surface look too smooth or wavy near large gaps, especially when many bins are empty. After the inverse FFT, empty bins are set back to
NaN. If all bins are empty, the fill value is0.0.