You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
FFS - NEW FEATURE - Certified adaptive psi grid for the calculated kinetic matrices
The expensive bounce-average kernel ran on every equilibrium knot, which both starves
kinetic decks of resolution (the shipped calculated example uses mpsi=16, carrying
percent-level errors on the B/C totals) and wastes kernel evaluations on fine grids.
Adds certified_kinetic_grid: seed with a coarse skeleton of the ideal coefficient-
spline knots (endpoints and rational windows always retained; the seed must not scale
with the equilibrium grid), then batched certify-or-refine rounds -- spline the kinetic
increments on the current knots, propose all uncertified midpoints, evaluate the whole
batch with the existing threaded kernel, and certify or insert. The certificate is the
max-element residual against tolerance * max|total| for every consumed family including
the adjoint combination kw3 - kt3, so tolerance is held on the ideal+kinetic totals the
solver consumes, never on increments in isolation. Spacing floors: the Frobenius cap in
the core, RATIONAL_RES_SPACING outside, and RATIONAL_RES_SPACING/4 inside rational
windows so narrow layers cannot hide behind the floor where they are expected.
Control: kinetic_grid_tol (default 0 = off; knob-off verified bit-identical). Measured
on the Solovev calculated case at tol=1e-3: m64 deck 65 -> 93 knots (certification adds
knots where the coarse grid under-resolves, matching the pre-registered expectation);
m256 deck 150 kernel evaluations vs 255 (-41%) with et[1] within 9.4e-5 of the full
grid; tol sweep 3e-3..1e-4 is plateau-flat. The kernel now accepts an arbitrary psi
list (psis kwarg), threading pattern unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: src/ForceFreeStates/ForceFreeStatesStructs.jl
+3Lines changed: 3 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -233,6 +233,7 @@ gpec.toml.
233
233
- `numsteps_init::Int` - Initial array size for ODE data storage
234
234
- `numunorms_init::Int` - Initial array size for solution normalization data
235
235
- `singfac_min::Float64` - Fractional distance from rational q at which ideal jump condition is enforced
236
+
- `kinetic_grid_tol::Float64` - Relative tolerance for the certified adaptive ψ grid of the calculated kinetic matrices. When > 0, the expensive kinetic kernel is evaluated on a seed grid (the ideal coefficient-spline knots) and intervals are refined until the spline-predicted **total** (ideal + kinetic) matrices match fresh evaluations within `kinetic_grid_tol · max|T|` for every element of every consumed family, including the non-Hermitian adjoint combination. `0` (default) evaluates the kernel on every equilibrium knot, today's behaviour. Applies only to `kinetic_source = "calculated"`
236
237
- `set_psilim_via_dmlim::Bool` - Truncate the integration domain at `(last_rational_q + dmlim) / n` rather than at `qhigh` / `psihigh`. Fortran STRIDE found that truncating ~20 % above the outermost rational (`dmlim = 0.2`) avoids a numerical kink instability in δW that appears when the integration ends too close to or just below a rational surface. **For diverted equilibria where q → ∞ at the separatrix** (e.g. DIII-D geqdsks, the bulk of production use) this costs negligible physical domain because rationals get arbitrarily dense near the LCFS — `set_psilim_via_dmlim = true` is the safe and recommended default. **For limited circular / analytical equilibria with finite q at the edge** (Solovev, LAR scans), rationals are sparse and 20 % above the last rational chops off too much edge, so set `set_psilim_via_dmlim = false` and let `qhigh` / `psihigh` control the truncation. Multi-`n` runs are not supported by this truncation (the "outermost rational + dmlim / n" depends on which `n`); when `set_psilim_via_dmlim = true` with `nn_low != nn_high`, `sing_lim!` warns and falls back to `qhigh` / `psihigh`. Default `true`.
237
238
- `dmlim::Float64` - Distance beyond last rational surface (normalised ∈ [0,1) in units of 1/n). Only used when `set_psilim_via_dmlim` is true. Fortran STRIDE convention is 0.2 (truncate 20 % of one rational-surface spacing above the last surface), retained here.
238
239
- `sing_order::Int` - Order of singular layer (Frobenius) expansion at rational surfaces. Default 6 (Fortran STRIDE convention for Δ' calculations; lower values trade accuracy for speed).
@@ -270,6 +271,7 @@ gpec.toml.
270
271
numsteps_init::Int=4000
271
272
numunorms_init::Int=100
272
273
singfac_min::Float64=1e-4# Matches Fortran STRIDE; required nonzero for the Riccati path.
274
+
kinetic_grid_tol::Float64=0.0
273
275
set_psilim_via_dmlim::Bool=true# Safe default for diverted equilibria (most production use); set false for limited/analytical (LAR, Solovev). Auto-skipped for multi-n. See docstring.
0 commit comments