Skip to content

Kinetic-aware equilibrium grid packing: place knots on rational and kinetic-resonance surfaces #309

Description

@logan-nc

Sub-issue of #302.

Motivation

#302 showed the auto grid (log_asymptotic, mpsi=0) places knots by a curvature/separatrix heuristic that is blind to where the solution is actually sharp — it under-resolves the edge and, to converge, over-resolves the mid-region (~4× the knots ldp needs). The right fix is to place knots where the physics has sharp features rather than globally tightening a tolerance.

With #112 (KineticForces/NTV) merged, the grid must also serve kinetic calculations, which add new sharp radial features the current heuristic ignores:

  1. Rational surfaces (q = m/n). Orbit-pitch / bounce-harmonic resonances near rationals produce large, sharp kinetic features. The grid should land points on the rationals. (This also directly addresses the auto mpsi under-resolves the edge, giving spurious dW and Delta-prime #302 near-separatrix-rational symptom, independent of kinetics.)
  2. Kinetic resonance surfaces. The superbanana-plateau resonance near ω_E ≈ 0 and other bounce/precession resonances (Logan 2013, §IV–V) are sharp and localized. Their ψ-locations are cheaply estimable from cylindrical approximations of the resonance operator evaluated at thermal energy x = 1.

Proposal: a two-pass "form → refine grid → reform" workflow

The equilibrium↔kinetic ordering is physical: resonance frequencies (ω_b, ω_d) need the formed geometry, and rational surfaces need the q-extrema that only exist after the first build. The solver also builds psi_nodes before the field-line integration that produces the geometry, so a true single pass is impossible. Equilibrium formation is fast and the existing rerun path re-forms from the same in-memory input (no file re-read), so a second pass is cheap.

Driver flow:

  1. Form the equilibrium once on the default/coarse grid.
  2. refined_psi_nodes(equil, base_nodes; kinetic_profiles, n, m-range, …) → augmented ψ vector: union of (a) rationals q=m/n (q forced positive, via the existing sing_find! bisection on the q-spline + q-extrema) and (b) kinetic-resonance ψ where Ω(x=1; ψ) = ℓ_eff·ω_b·√x + n·(ω_E + ω_d·x) changes sign (cylindrical ω_b/ω_d from Logan 2013), each locally packed with a few knots.
  3. Re-form on the augmented grid via the existing in-memory rerun path; discard the first pass.

Merge must guard against near-duplicate nodes. A mandatory node landing a hair from an existing one (existing […0.5, 0.6…], rational at 0.5+1e-15) creates a near-zero interval that pollutes the bicubic spline reconstruction of those surfaces with ringing. A plain unique! does not catch this. Enforce a minimum spacing δ_min (a fraction of local base spacing, not float epsilon): either snap — drop the redundant existing node, keep the mandatory one ([…0.5+1e-15, 0.6…], default) — or displace — nudge neighbors outward to open room while preserving local psi_accuracy ([…0.45, 0.5+1e-15, 0.6…]). Post-merge grid must be strictly monotone with every interval ≥ δ_min.

Minimal code surface

  • Equilibrium: add optional override_psi_nodes kwarg to equilibrium_solver / setup_equilibrium; _build_psi_grid branches untouched. Only a Vector{Float64} crosses the module boundary — no kinetic physics enters the Equilibrium foundation module.
  • ForceFreeStates: rational_psi_nodes(equil; n, m-range) reusing the sing_find! root-find.
  • KineticForces: refined_psi_nodes(...) (resonance locator + merge/pack), reusing BounceAveraging.jl / EnergyIntegration.find_resonance_energies.

Activation

  • Rational-surface packing: always-on (benefits ideal runs too).
  • Kinetic-resonance packing: auto-enabled whenever kinetic profiles are loaded.

Acceptance criteria

  • Auto grid lands knots on (or symmetrically straddling) every q=m/n rational in range, including near-separatrix ones, with a few packing knots each.
  • When kinetic profiles are present, knots are packed near the ω_E≈0 surface and the dominant bounce/precession resonance surfaces predicted by the x=1 cylindrical estimate.
  • The auto mpsi under-resolves the edge, giving spurious dW and Delta-prime #302 case (et[1] ≈ 0.80) converges at a total knot count comparable to ldp, mpsi=256, not the ~1012 the current auto heuristic needs.
  • A regression case (kinetic NTV torque on an example deck) shows the refined grid reaches the converged torque at far fewer knots than uniform tightening of psi_accuracy.
  • No near-duplicate nodes survive the merge: every post-merge interval ≥ δ_min, and a mandatory node within δ_min of an existing node is resolved by snap-or-displace (no 0.5 / 0.5+1e-15 pairs that ring the reconstructed surfaces).
  • No dependency from Equilibrium onto KineticForces.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions