KineticForces - PERFORMANCE - Panel NTV psi quadrature at rational and kinetic-resonance surfaces with rtol-primary tolerances#313
Open
logan-nc wants to merge 22 commits into
Conversation
…rfaces, rtol-primary tolerances with maxevals guard Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rium solver paths Adds an override_psi_nodes keyword to setup_equilibrium and threads it through the direct, arclength, inverse, and by-inversion solvers, bypassing the config-driven grid with a validated externally supplied node vector. This is the injection point for the two-pass auto-grid refinement. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…on, and mandatory-node merge GridRefinement.jl derives the pass-2 knot density from the formed pass-1 equilibrium using the cubic h^4 error model on nodal fourth divided differences (1D profiles, rzphi geometry channels at sampled theta lines, and kinetic profiles when present), with a-priori edge/core geometric floors, then equidistributes and pins mandatory knots with a delta_min snap guard. The log_asymptotic auto path now forms a coarse fixed-128 pass-1 layout; the one-pass a-priori heuristics (make_optimal_mpsi, probe log-slope, 300-point mid-spacing sampling) are removed. All regions now scale as psi_accuracy^(-1/4), so tightening the tolerance refines edge and mid proportionally. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… add rational_psi_nodes The qextrema-interval Brent walk moves from sing_find! into _find_rational_surfaces, which returns (m, n, psifac) tuples; sing_find! rebuilds its SingType multiplicity bookkeeping on top. rational_psi_nodes exposes the unique surface locations for the two-pass grid refinement. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ession case covering the psi torque quadrature Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…onal-surface packing Validation on the DIIID-like example drove four corrections to the knot density model: - h^3 derivative error model (err(f') ~ h^3|f''''|/24) replaces the h^4 value model: the stability physics consumes spline derivatives (q' at rational surfaces, p' and V' in the EL and ballooning coefficients), and the value model under-resolved delta-prime by 2x at q=2. - Curvature is measured against rho = sqrt(psi), where the equilibrium is regular at the axis; the psi-space geometry channels diverge as psi^(k/2-4) there and made the implied knot count grow without bound under refinement. - Local packing around mandatory (rational) surfaces: spacing 0.06*tau^(1/3) at the surface with geometric growth, within radius 0.05 — converges the delta-prime BVP, which samples the psi-splined coefficient matrices around each singular surface. - The core below psi=0.03 uses the a-priori geometric density exclusively: nodal data on the smallest flux surfaces is dominated by integration and axis-extrapolation error. Near-duplicate mandatory nodes (same surface via different m,n) collapse onto one knot. Noise floor scales as eps/h^4 on tightly packed sample grids. Adds test/runtests_grid_refinement.jl covering the merge invariants, equidistribution, quartic-exact divided differences, layer concentration, tau^(-1/3) scaling, and a Solovev two-pass round-trip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ts in main driver When grid_type=log_asymptotic with mpsi=0, the driver forms a coarse pass-1 equilibrium, pins knots on all rational surfaces in the requested n range, derives the refined grid from the measured curvature (including kinetic profiles when loaded), and re-forms from the in-memory input. The nn range validation and the kinetic-profile load are hoisted above equilibrium formation to feed the refinement; a consistency check warns when the refined equilibrium implies substantially more knots than used. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ha-boundary scan drivers The alpha-boundary drivers now scan only psi_N in [0.1, min(0.99, psi_edge)], reusing the existing NaN sentinel for skipped surfaces. Ballooning boundaries are physically relevant in the mid-radius and pedestal; the packed axis and far-edge surfaces dominated the scan cost. The locstab path (compute_ballooning_stability!) is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Quantifies knots-vs-accuracy for cubic splines of q versus iota = 1/q on the DIII-D-like example: iota gives a modest constant-factor improvement (~1.2-2x at coarse N) but the same convergence order, confirming that wholesale iota replacement is not warranted for grids ending inside the separatrix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…md, example annotations) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…psi=256 grid These testsets target the bidirectional FM integration and STRIDE BVP machinery, so they now pin the radial grid instead of inheriting the example default (mpsi=0), which previously baked the defective one-pass auto grid into the pinned values and would otherwise move whenever the auto grid evolves. Re-pins et_par and the delta-prime diagonals to the ldp-256 values: q=2,3,4 real parts are grid-converged and pinned tightly; the near-separatrix q=5,6 entries are not grid-converged (value and sign vary O(1) between grids) and are now asserted finite and non-zero only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… equidistribution, IMAS rerun note Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The two-pass grid is a measured spline-derivative-error density with geometric floors, not a log-asymptotics model — q stays finite everywhere on the grid — so the old name misdescribed it. grid_type="auto" is the new default; "log_asymptotic" remains a working alias (two-pass when mpsi=0, the three-region log layout when mpsi>0), so no existing TOML breaks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…roduction auto grid Per review: the auto grid is the production default, so the machinery testsets now build it exactly as the main driver does (rational_psi_nodes + refined_psi_grid + ingest re-form) and pin its values. The auto-vs-ldp512 convergence evidence is recorded on the PR; tightening psi_accuracy converges the pinned delta-prime entries toward the dense-reference values. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…) and generator script Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…performance/ntv-psi-quadrature-paneling
…-crossing quadrature panels, shared sign-change root scan, psilims floor above axis Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…scription and future settings docs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nances for quadrature panels, revert psilims floor, persist panel data to HDF5 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ance surfaces, annotate epsilon clamp Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolved three conflicts, all downstream of the DIIID example grid choice (develop retargeted the equilibrium to Ip=1.15 and switched to a fixed ldp/mpsi=256 grid; this branch uses the two-pass auto grid): - examples/DIIID-like_ideal_example/gpec.toml: hybrid — keep develop's psihigh=0.995 and jac_type "custom" naming, keep this branch's grid_type="auto"/mpsi=0. Auto grid still needs validation on the new Ip=1.15 equilibrium (tracked TODO). - src/Equilibrium/EquilibriumTypes.jl: keep develop's new jac_custom_power_* fields (required by the constructor) and this branch's grid_type="auto" default. - test/runtests_parallel_integration.jl: took develop's tight et_par and delta_prime pins provisionally; will re-assess against the auto grid via the regression harness and propose an update. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
FYI @d-burg I had claude go another round with this now that a few other things merged. I had claude working through a smarter auto grid. It tried denser packing near the rationals, but that failed. It was looking into at least just consistent packing around the rationals, but I ran out of tokens. That seems like a hack to force "robustness" anyways - why trust any given fixed packing? Do you have any better ideas for how to make a smart grid that gets the "right" answer for dpm? |
…ar-layer knot ladder
The two-pass "auto" grid gave a delta-prime matrix whose diagonal swung ~30%
with the psi_accuracy (τ) knob — not grid-converged — because Δ' extracts the
subdominant small-solution coefficient through a dpsi^{-2α}-conditioned Frobenius
projection, so it tracks the equilibrium-spline error in the layer around each
rational. The old packing was τ-scaled and its merge snapped out the pinned
rational's own neighbors, so the local stencil changed topology with τ.
Add `rational_psi_ladder`: a geometric knot ladder straddling each rational with
a fixed absolute innermost step h0=1e-3 (independent of τ, matched to the
equilibrium's native radial resolution), emitted as mandatory knots. Anchoring h0
to the equilibrium resolution — not the far finer perturbed matching offset
singfac_min/|n·q1| (~5e-5), which would ring the re-spline and blow Δ' up — is what
makes Δ' grid-robust. A min-spacing thinning (h0, rationals prioritized) prevents
overlapping ladders from nearby rationals (low shear / multi-n) creating sub-h0
intervals. `merge_mandatory_nodes` collapse now uses an absolute tolerance so fine
ladder knots survive.
Result on the DIIID example: dpm auto-τ spread 30% → ~4% (q2), <1% (q3,q4); the
converged value (dpm[1,1]≈8.4) matches the finer ldp mpsi=512 grid, while ldp
mpsi=256 undershoots (7.28) by under-resolving the rationals. Equilibrium, energies,
NTV torque and resonant flux are unchanged; runtime drops 2.2× vs ldp mpsi=256.
Re-pin the parallel-integration dpm diagonals at rtol=0.1 on the ladder values
(real parts only; et_par kept tight); add a grid-refinement test pinning the
ladder min-spacing invariant in the multi-n regime.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Closes #303. Built on #312 (
feature/two-pass-psi-gridis merged into this branch, so its commits appear in this diff until #312 lands — merge #312 first, after which this PR reduces to the KineticForces changes).Update (2026-07) — merged
develop, and made Δ′ grid-robustorigin/developis now merged into this branch.develophad retargeted the DIII-D example to Ip=1.15 MA and switched that example to a fixedldp/mpsi=256grid; the merge conflicts (DIIIDgpec.toml,EquilibriumTypes.jl, and the parallel-integration tests) were resolved to keep this branch's two-passautogrid running on the new equilibrium.That surfaced a real problem the merge exposed: on the two-pass
autogrid the delta-prime matrix diagonal was not grid-converged — apsi_accuracy(τ) scan swungdpm[1,1](q=2) by ~30%. Root cause: Δ′ extracts the subdominant small-solution coefficient through adpsi^{-2α}-conditioned Frobenius projection, so it tracks the equilibrium-spline error in the layer around each rational; the old packing was τ-scaled and its merge snapped out the pinned rational's own neighbors, so the local stencil changed topology with τ. (et[1], a whole-plasma integral, stayed robust to <1% — confirming this is a local singular-layer matching problem, not a global-response one.)Fix —
rational_psi_ladder: a geometric knot ladder straddling each rational with a fixed absolute innermost steph0 = 1e-3, independent of τ and matched to the equilibrium's native radial resolution, emitted as mandatory grid knots. Anchoringh0to the equilibrium resolution — not the far finer perturbed matching offsetsingfac_min/|n·q1|(~5e-5), which rings the re-splined equilibrium and blew Δ′ up to ~149 — is what makes Δ′ grid-robust. A min-spacing thinning prevents overlapping ladders from nearby rationals (low shear / multi-n) creating sub-h0intervals;merge_mandatory_nodescollapse now uses an absolute tolerance so fine ladder knots survive.dpm[1,1]spread across τ ∈ {2e-3…2.5e-4}dpm[1,1]valuempsi=512(8.48)mpsi=256The converged value matches the finer ldp
mpsi=512grid; develop's ldpmpsi=256(7.28) undershoots by under-resolving the rationals. In thediiid_n1harness, equilibrium, energies, NTV torque, and ‖resonant flux‖ are unchanged (<0.5%); the delta-prime and its downstream island/Chirikov diagnostics move intentionally (the ladder converges Δ′). Afortran-physics-revieweraudit passed: the fixed ladder is a faithful analog of DCON (equilibrium splined at native resolution, singular layer handled analytically by the Frobenius matching atdpsi = singfac_min/|n·q1|), and it flagged the overlapping-ladder edge case that the min-spacing thinning now guards.Re-pinned the parallel-integration
dpmdiagonals atrtol=0.1on the ladder values (real parts only;et_parkept tight); added a grid-refinement test pinning the ladder min-spacing invariant in the multi-n regime. The NTV-quadrature description below is unchanged by this update.Problem
The outer ψ-integration of the NTV torque
tpsi(ψ)used a single whole-domain adaptive quadrature with no interior breakpoints, nomaxevalsbound, and a discarded error estimate. The resonant torque-density peaks — at the rational surfaces and at the kinetic resonances (dominantly the ExB/superbanana-plateau resonance near ω_E ≈ 0) — forced deep adaptive bisection (1375+ evaluations, ~30 min reported in #303 on the DIII-D-like case). Separately, theatol_psi = 1e-2N·m default was an amplitude-sensitive trap: NTV ∝ δB², so a 10× weaker applied field gives a 100× smaller torque and a fixed absolute tolerance can silently dominate termination with O(1) relative error.Changes
ψ-quadrature paneling at resonant surfaces
sing+kinsing) are threaded intoKineticForcesInternal.sing_psisand passed as quadrature panel boundaries — Gauss–Kronrod handles peaks at interval endpoints natively instead of hunting them by bisection.kinetic_resonance_psi_nodes(kinetic_profiles, equil; n, nl, ...)scans for zeros of the trapped-branch resonance denominator at thermal energy, Ω_ℓ(x=1; ψ) = ℓ·ω_b(ψ) + n·(ω_E(ψ) + ω_d(ψ)) for every ℓ ∈ −nl:nl, using the pitch-averaged RLAR closed-form frequencies already intpsi!(single spline evaluations, no bounce averaging). The ℓ=0 node is the ω_d-shifted ExB resonance; ℓ≠0 nodes capture the bounce resonances. On the DIII-D case this finds 8 in-range surfaces that line up with the observed pedestal dT/dψ structure (see figure) — including the previously unexplained bump at ψ_N ≈ 0.955.kinetic_forces/<method>/panel_psi,resonance_psi) for diagnostics/plotting; the per-method log line reports torque, error estimate, evaluation count, and panel composition.fortran-physics-revieweraudit of the locator passed all checks: frequency conventions matchtpsi!/torque.F90exactly (thewdhat = q·T/(2εR₀²ZeB₀)closed form verified algebraically identical), the denominator correctly uses ω_E alone (diamagnetic terms are numerator-only), and leff = ℓ matches the trapped branch. Passing-branch (ℓ+nq transit) resonances are a documented non-goal (broad/Landau-like; quadrature resolves them without dedicated panels).Unified helpers (one source of truth per concept, no drift)
src/KineticForces/Utils.jl:find_sign_change_roots(f, grid)(grid sign-scan + Brent) — also replaces the two pre-existing inline copies of the same idiom inTorque.jl(dB/dθ extrema).kinetic_resonance_psi_nodesis the single kinetic-resonance locator, shared-signature-ready for the Kinetic-aware equilibrium grid packing: place knots on rational and kinetic-resonance surfaces #309 grid-knot packing (refined_psi_nodes=rational_psi_nodes∪ this). Rational-surface identification stays unified under_find_rational_surfaces(shared bysing_find!and EQUIL + GPEC - Two-pass measured-curvature auto psi grid with rational-surface knots #312'srational_psi_nodes).psi_panel_pointsowns sort/dedupe/bound-filtering of the merged panel list.Tolerance redesign (rtol-primary)
atol_psi: 1e-2 → 0.0. Convergence is controlled byrtol_psi = 1e-2(~2 significant figures — the limit of the NTV model's validity; tighter tolerances buy digits the physics can't back).maxevals_psi = 2000runaway guard; the error estimate is checked and a clear warning fires when the quadrature terminates without meeting tolerance, or when a user-set nonzeroatol_psidominates termination (the weak-field silent-garbage scenario).psilimsstays[0.0, 1.0]. (An interim commit floored it at 0.1 to dodge a near-axis dT/dψ spike; measurement showed that was over-optimization — at rtol=1e-2 only 7 of 345 develop evaluations land below ψ=0.1 and the core carries a real 0.3% of the torque — so the floor was reverted. The near-axis feature itself turns out to be two located ℓ≠0 resonance surfaces, now paneled like the rest.)Coverage
examples/Solovev_kinetic_NTV_example(ideal FFS → PE → NTV torque) — previously no shipped config exercised this code path — plus asolovev_kinetic_ntvregression case pinning the fgar torque and ψ evaluation count.atol_psi/rtol_psikeys from the a10 example (it stops after kinetic stability and never reaches the torque quadrature).Benchmarks (DIII-D-like case from #295, n=1, C-coil 1 kA, nl=6, 5 rationals)
The defaults run agrees with the tightly-converged answer to 0.024% in Re(T) at 1.6× fewer evaluations than develop at equal tolerance — and 210 = 14 panels × 15 nodes exactly: the initial pass alone converges, i.e. the panels sit precisely where the quadrature work is. Whole n=1 pipeline: ~3 minutes. Regularization note: the PE default
reg_spot = 0.05must stay on for this post-PE diagnostic — with raw (unregularized) ξ the "torque" becomes the grid-capped ideal-MHD 1/(m−nq)² divergence (~66,000× larger on a Solovev test) rather than a physical answer.Torque density with the identified resonance locations (rationals dashed gray; located kinetic resonances Ω_ℓ(x=1)=0 dash-dot violet — note the match to the pedestal structure and the small near-axis feature):
Quadrature evaluation clustering — develop (no panels) goes near-vertical at the edge resonances; the paneled defaults run spreads effort almost uniformly:
Cumulative torque integral — all runs overlay; ~95% of the torque accumulates in the pedestal, with the sharp rise starting at the ℓ=0 kinetic resonance:
Regression
solovev_n1: 21/21 unchanged;diiid_n1: 46/46 unchanged vsfeature/two-pass-psi-grid(this PR's changes are isolated to[KineticForces]-configured runs).solovev_kinetic_ntv: NTV torque and evaluation count move intentionally with the quadrature improvements (final baseline pinned at this head).Notes for reviewers
solovev_kinetic_calculated/solovev_kinetic_nuzeroregression cases are broken on develop and this branch alike ([SOL_INPUT]in a separatesol.tomlthatbuild_inputs_from_tomldoesn't resolve) — separate fix needed; not touched here.atol_psi/rtol_psi = 1e-2lines can simply be dropped after this merges — the new defaults are equivalent or better.docs/src/assets/(same pattern as EQUIL + GPEC - Two-pass measured-curvature auto psi grid with rational-surface knots #312's grid figure) and can back a future docs page on NTV tolerance settings.🤖 Generated with Claude Code