Repository navigation
Differentiable DG simulation: solver keywords and eigh upwind matrices - #45
Open
rogeriojorge wants to merge 8 commits into
Open
rogeriojorge wants to merge 8 commits into
rogeriojorge wants to merge 8 commits into
Conversation
Expose Diffrax's max_steps, adjoint and progress_meter as static keywords of simulation, with defaults that reproduce the previous behaviour (RecursiveCheckpointAdjoint(), TqdmProgressMeter(1), max_steps=1000000). This enables jax.jacfwd with ForwardMode() and a fixed checkpoint budget for reverse mode. Add tests on a tiny two-species 2D DG Orszag-Tang case comparing reverse mode, forward mode and centred finite differences for an initial-condition amplitude and the collision frequency, checking gradient invariance to the checkpoint budget, and checking that the reverse-pass workspace does not grow with max_steps at fixed checkpoints.
…rentiable The Hermite advection matrices alpha*J + u*I are real symmetric (Jacobi). eig returned complex eigenvectors whose derivatives JAX does not support, which blocked gradients with respect to thermal speeds and drifts. eigh gives the same upwind split to round-off and is differentiable. Tests compare the split with an independent host eigendecomposition and check gradients with respect to a thermal-speed scale and a drift against forward mode and finite differences.
At zero drift the field energy is stationary in the drift by symmetry, so that derivative was zero and only the absolute floor was exercised.
The workflow only triggered for main, so pull requests against this branch, including this one, were never tested in CI.
Codecov Report✅ All modified and coverable lines are covered by tests.
🚀 New features to boost your workflow:
|
This was referenced Sep 23, 2026
A = alpha*J + u*I shares the eigenvectors of the constant Jacobi matrix J, so the split needs one host eigendecomposition per size instead of a differentiable eigh per species and direction. Matches the eigh split to 2e-15; gradients stay finite at alpha = 0 (eigh gives NaN) and, at an exact zero eigenvalue (u = 0 for odd N), are the mean of the one-sided derivatives instead of a roundoff-signed one-sided derivative. Tests cover both.
rogeriojorge
changed the base branch from
Discontinuous-Galerkin
to
sync/dg-with-main
October 3, 2026 14:33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Companion to #44, which makes the same change on
main. The Hermite–DGsimulationgains three keywords passed todiffrax.diffeqsolve. Their defaults reproduce the current behaviour exactly:max_steps=1000000,adjoint=RecursiveCheckpointAdjoint(),progress_meter=TqdmProgressMeter(1). With them:jax.jacfwdruns through the solver withadjoint=ForwardMode(). Before, it failed with "can't apply forward-mode autodiff (jvp) to a custom_vjp function".RecursiveCheckpointAdjoint(checkpoints=K).The library change is 7 lines plus one docstring line in
spectrax/_simulation.py, with no numerical change.Tests (
tests/test_autodiff.py, 5 tests, about 75 s on CPU)The case is a two-species 2D Orszag–Tang setup projected with
legT, exactly as inExamples/2D_Orszag_Tang.py: 4×4 cells, N_DG=2, 4³ Hermite modes, adaptive Dopri5 at tolerance 1e-8. The objective is the final field energy plus the Hermite free energy. Four Hermite modes are needed because the collision matrix is identically zero with three.checkpoints=2andcheckpoints=8match the default adjoint to 1.3e-15.checkpoints=4, the compiled reverse-pass workspace stays at 4.1 MB asmax_stepsgrows from 256 to 65536. With the default adjoint it grows from 6.0 MB to 56.1 MB.Verification
simulation(input_parameters, **solver_parameters), run with a reduced copy ofinput_2D_orszag_tang.toml, returnsCk,Fkandtimebitwise identical to the unchanged branch.Discontinuous-Galerkinto thebuild_test.ymltriggers, so CI now runs here and passes.Merge notes
Differentiable thermal speeds and drifts (
20605a8,e75052d)compute_A_pm_matricessplits the Hermite advection matrices αJ + uI into upwind parts withjnp.linalg.eig. JAX cannot differentiate the complex eigenvectorseigreturns, sojax.gradwith respect toalpha_soru_sraisedNotImplementedError. These matrices are real symmetric (Jacobi), sojnp.linalg.eighgives the same split and is differentiable. The change is three calls plus a comment. The constant Maxwell flux matrix keepseig.eigresult to 8.9e-16. A short DG simulation matches to 7.6e-15 relative. A new test compares the split with an independent host LAPACK eigendecomposition.The drift is tested at a nonzero value because at zero drift the field energy is stationary in it by symmetry.
Found while testing, not changed here
diagnostics(output)is commented out in the DGsimulation, and it expectsmi_meandOmega_cs. Energies must therefore be computed fromCkandFkdirectly, as the tests do.Update 2026-10-03 (head c715971)
Base changed to
sync/dg-with-main(#54), which is merged in (no history rewrite), so the diff is only this PR's 4 files.The upwind split no longer calls a differentiable
eighper species and direction.A = alpha*J + u*Ishares the eigenvectors of the constant Hermite Jacobi matrixJ, socompute_A_pm_matricesdiagonalizesJonce on the host (per size) and formsV max(alpha*x_k+u, 0) V^TandV min(., 0) V^T.Evidence (x64 CPU, standalone comparison of the two forms):
(lam +- |lam|)/2, and roundoff in the computed root decided which one-sided derivative autodiff returned (N = 40: Jacobians differ by 0.19). The new code symmetrizes the roots (middle root exactly 0) and usesmaximum/minimum, so an exact zero eigenvalue gets the mean of the one-sided derivatives. Near (not exact) ties are still one-sided; that is inherent to the nonsmooth flux;New test
test_upwind_split_derivatives_away_from_and_at_zero_speed[N=3,4,7]: finite AD Jacobian, forward = reverse, kink detected exactly where expected, AD = mean of one-sided finite differences, finite at alpha = 0.Tests:
pytest tests17 passed (Python 3.12, JAX 0.11.2, CPU, x64 set by the package). Local results, not GitHub CI.