Resolution and convergence

An algebraically converged linear solve is not necessarily a converged drift-kinetic discretization. NTX treats three checks separately:

  1. the residual of the block equations;

  2. Fourier sampling of the retained geometry harmonics;

  3. convergence of transport coefficients under angular and Legendre refinement.

Residual semantics

The production solve eliminates the high Legendre tail by Schur complements and returns the three modes needed by the transport moments. TransportResult.schur_residual_l2 is the root-mean-square residual of that complete tail-eliminated system, evaluated from its pivoted LU factors. It includes the high-mode Schur contribution to row 2 without reconstructing another mode. TransportResult.residual_l2 remains as a compatibility alias; neither name denotes the full original DKE residual.

The test suite also factors and reconstructs every Legendre mode on a small system, applies every original block row independently, and requires both residuals to reach roundoff. Full-mode reconstruction scales as O(N_xi N_fs^2) storage, so it is an audit path rather than the default production diagnostic.

Run that audit explicitly on a prepared system:

from ntx import audit_prepared_residuals

residuals = audit_prepared_residuals(prepared, case)
print(residuals.schur_residual_l2)
print(residuals.full_system_residual_l2)
print(residuals.retained_mode_max_abs_error)

The CI oracle checks both diagnostics at N_xi=2,16,32,63. A separate small problem materializes the complete block-tridiagonal operator, solves it with a dense solver, and applies every original block row. This keeps the Schur solve, the residual action, and the dense reference as distinct numerical paths.

Geometry sampling

NTX evaluates each retained harmonic as

\[\cos(m\theta+nN_{fp}\zeta)\]

on one toroidal field period. Consequently, alias-free collocation requires

\[N_\theta \ge 2\max|m|+1, \qquad N_\zeta \ge 2\max|n|+1.\]

The odd floor avoids treating the real Nyquist mode of an even grid as a resolved derivative mode. Inspect a proposed grid before a solve:

from ntx import geometry_resolution_report

report = geometry_resolution_report(surface, grid)
report.require_resolved()
print(report.theta_oversampling, report.zeta_oversampling)

The adaptive convergence API enforces this gate. For a one-off solve, request strict pre-solve validation explicitly:

result = solve_monoenergetic(
    surface, grid, case, require_resolved_geometry=True
)

The default remains permissive so deliberately underresolved smoke diagnostics and historical reference reproductions remain runnable, but they are not research-grade convergence claims. If a surface is passed as a dynamic argument to jax.jit, call the report host-side first because traced integer harmonic arrays cannot be converted into a host report.

Nyquist adequacy is necessary, not sufficient. Products, reciprocals, and derivatives of the magnetic field can demand angular oversampling beyond the retained input bandwidth. The exact 3/2 product rule for two band-limited Fourier fields does not directly prove adequacy for this operator because its geometry coefficients include reciprocals and other non-band-limited derived fields. NTX therefore uses measured coefficient convergence rather than silently filtering the assembled operator.

The committed finite-beta QA, NCSX, and HSX audit found a worst D11/D31/D33 error of 6.889e-3 at 2.25 times the retained-mode Nyquist floor relative to the 2.5-times reference. NTX now warns below 2.25; this is a recommended starting grid, not a hard rejection or a substitute for the two-successive-refinement gate. The recommendation is exported as RECOMMENDED_ANGULAR_OVERSAMPLING.

python examples/angular_oversampling_audit.py --preset production

Angular oversampling error, runtime, and memory

Adaptive coefficient gate

Use independent angular and pitch-angle ladders:

from ntx import solve_monoenergetic_converged

audit = solve_monoenergetic_converged(
    surface,
    case,
    angular_resolutions=((13, 15), (17, 21), (21, 25), (25, 31)),
    legendre_orders=(16, 24, 32, 48, 63),
    rtol=1e-2,
    atol=(1e-10, 1e-10, 1e-10),  # D11, D31, D33
)
if audit.status != "converged":
    raise RuntimeError(audit.message)

For each coefficient c, a refinement is accepted when

\[|c_h-c_{h^-}| \le a_c + r\max(|c_h|,|c_{h^-}|).\]

The absolute term is essential for symmetry-conditioned D31 values near zero. The default research policy requires two consecutive accepted angular changes and two consecutive accepted Legendre changes. Exhausting a ladder returns unresolved; the last result is retained for diagnosis but is not silently promoted. Non-finite coefficients return model-out-of-scope.

These numerical gates follow Fourier collocation sampling theory and the angular/Legendre convergence practice used for monoenergetic neoclassical transport calculations. They do not replace independent-code validation or a separate model-discrepancy tolerance.