Skip to content

Add post-dealiasing to nonlinear functions via fft/ifft wrapping - #96

Merged
Ceyron merged 4 commits into
mainfrom
revamped-dealising
Feb 18, 2026
Merged

Ceyron merged 4 commits into
mainfrom
revamped-dealising

Conversation

@Ceyron

@Ceyron Ceyron commented Feb 18, 2026

Copy link
Copy Markdown
Owner

Motivation

Previously, the nonlinear functions only applied dealiasing before evaluating the nonlinearity (pre-dealiasing): high Fourier modes were zeroed out before transforming to physical space. However, the nonlinear product in physical space creates new aliased modes in the upper spectral band. These aliased modes were returned as part of the nonlinear term and propagated into the ETDRK intermediate stages and the final solution.

With the 2/3 rule and quadratic nonlinearities, these aliases only land in the "expendable" modes (N/3 to N/2), so the scheme was technically stable. However:

  • The output of each nonlinear function carried spurious spectral energy in the upper modes.
  • In multi-stage ETDRK methods (ETDRK2-4), aliased modes from the nonlinear term accumulated across intermediate stages before being cleaned at the next nonlinear evaluation.
  • The solution state between timesteps was not spectrally clean (junk in upper modes).

Changes

Core change: BaseNonlinearFun.fft and ifft now handle dealiasing

Instead of requiring each subclass to manually call self.dealias(), the fft and ifft methods on BaseNonlinearFun now apply the dealiasing mask automatically:

  • ifft (pre-dealiasing): Zeros out high modes before transforming to physical space. This was already done explicitly in all subclasses; it is now handled by the base class.
  • fft (post-dealiasing): Zeros out high modes after transforming back to Fourier space. This is the new behavior that ensures the returned nonlinear term is spectrally clean.

When no dealiasing_fraction is set (i.e., dealiasing_mask is None), both methods behave as plain fft/ifft with no overhead.

Subclass cleanup

All explicit self.dealias() calls have been removed from the nonlinear function implementations:

  • PolynomialNonlinearFun
  • ConvectionNonlinearFun (all 4 variants: multi/single-channel, conservative/non-conservative)
  • GradientNormNonlinearFun
  • VorticityConvection2d
  • ProjectedConvection3d

The dealias() method still exists on the base class for standalone use, but is no longer needed in the standard __call__ flow.

Documentation updates

  • BaseNonlinearFun.__init__ docstring: Added info admonition explaining the pre- and post-dealiasing strategy.
  • BaseNonlinearFun.dealias() docstring: Added note that fft/ifft handle dealiasing automatically.
  • ETDRK1-4 docstrings: Updated the dealiasing warning to note that BaseNonlinearFun handles this via its fft/ifft methods. Removed redundant inline remarks from ETDRK2-4 parameter descriptions.
  • Tutorial notebook (creating_your_own_solvers_1d.ipynb): Updated the custom NonlinearSourceFun example to use self.ifft(u_hat) instead of self.ifft(self.dealias(u_hat)), with an updated comment explaining the automatic dealiasing.

Test additions

  • Added test_fft_ifft_apply_dealiasing() to verify that ifft pre-dealiases its input and fft post-dealiases its output.

Files changed (12)

File Change
exponax/nonlin_fun/_base.py fft/ifft now apply dealiasing mask; updated docstrings
exponax/nonlin_fun/_polynomial.py Removed explicit self.dealias()
exponax/nonlin_fun/_convection.py Removed explicit self.dealias() from all 4 eval methods
exponax/nonlin_fun/_gradient_norm.py Removed explicit self.dealias()
exponax/nonlin_fun/_vorticity_convection.py Removed explicit self.dealias()
exponax/nonlin_fun/_projected_convection.py Removed explicit self.dealias()
exponax/etdrk/_etdrk_1.py Updated dealiasing warning
exponax/etdrk/_etdrk_2.py Updated dealiasing warning, removed inline remark
exponax/etdrk/_etdrk_3.py Updated dealiasing warning, removed inline remark
exponax/etdrk/_etdrk_4.py Updated dealiasing warning, removed inline remark
docs/examples/creating_your_own_solvers_1d.ipynb Updated custom nonlinear function example
tests/test_nonlinear_funs.py Added test_fft_ifft_apply_dealiasing

Breaking changes

Users who subclass BaseNonlinearFun and call self.dealias() explicitly (as shown in the old tutorial) will now get double-dealiasing, which is harmless (idempotent) but redundant. They should update to just use self.ifft(u_hat) instead of self.ifft(self.dealias(u_hat)).

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.03%. Comparing base (b171ec2) to head (b6e77e9).
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #96   +/-   ##
=======================================
  Coverage   92.02%   92.03%           
=======================================
  Files          75       75           
  Lines        2697     2698    +1     
=======================================
+ Hits         2482     2483    +1     
  Misses        215      215           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Ceyron
Ceyron merged commit f48e30e into main Feb 18, 2026
4 checks passed
@Ceyron
Ceyron deleted the revamped-dealising branch February 18, 2026 10:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants