Skip to content

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

Open

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions