Skip to content

ForceFreeStates - FEATURE! - Cross ideal singular surfaces with a Frobenius-basis jump #669

Open

Description

@jhalpern30

Summary

The ideal crossing in both integrators is first order in singfac_min. Mapping the solution across the surface
through the Frobenius bases that are already computed removes that dependence: in a scratch prototype the edge
Riccati matrix and the free-boundary energy are flat in singfac_min to ~1e-9 and ~1e-7, and the existing
crossings converge linearly to the same values.

Current behavior

riccati_cross_ideal_singular_surf! (Riccati/Crossings.jl) and cross_ideal_singular_surf!
(EulerLagrange.jl) zero the resonant column, take a two-point trapezoidal step across the surface, and zero
the resonant row before inserting the small solution. The step and the row zeroing each leave an
O(singfac_min) error.

DIII-D-like n = 1 example, eulerlagrange_tolerance = 1e-10, free-boundary et[1]:

singfac_min Riccati (current) Forward (current) Riccati (prototype jump)
1e-3 0.84554 + 3.3e-3i 0.82231 0.79789523 + 8e-11i
1e-4 0.80320 + 4.1e-4i 0.80061 0.79789529 + 8e-11i
1e-5 0.79847 + 4.6e-5i 0.79818 0.79789529 − 1e-10i
1e-6 — 0.79793 —

Edge S (Riccati, relative to the jump at 1e-6): current 1.3e-2 / 1.6e-3 / 1.9e-4 at 1e-3 / 1e-4 / 1e-5 with an
anti-Hermitian part of the same order; jump ≤ 6e-8 with anti-Hermitian part ~1e-10. The Solovev example behaves
the same way (6.1e-3 → 6.7e-5 against ≤ 1e-8).

Proposed change

At a crossing, with ua_L, ua_R from sing_get_ua:

  • ca = ua_L \ u, the coefficients in the left Frobenius basis (what sing_get_ca returns).
  • For each resonance, eliminate the big-solution coefficient from every column using the column where it is
    largest, and move the surviving column out of the slot the small solution will take.
  • Zero the big and small coefficient rows.
  • u_R = ua_R · ca, then place the right small solution in the freed column. No resonant-row zeroing.

Both the elimination and the basis transfer are needed: elimination alone halves the error, and the transfer
without elimination is worse than the trapezoid.

Scope

  • Riccati path (_predict_across_singular_surface!, _capture_right_crossing_data!).
  • Forward path, recording the elimination and permutation for transform_u!.
  • Multi-n shared surfaces, reversed shear (double crossing), Mercier-unstable (complex α).
  • Regression cases: a singfac_min scan of et[1] on diiid_n1 and solovev.
  • Revisit the singfac_min defaults (1e-4 for Riccati Δ′, 1e-6 for Forward decks feeding
    PerturbedEquilibrium) once the crossing no longer sets them.

Not covered

  • The Δ′ matrix is unchanged by the crossing (it comes from the propagator BVP) and still drifts with
    singfac_min: first diagonal entry 9.089 at 1e-3, 9.241 at 1e-4. Separate issue.
  • Kinetic crossings (cross_kinetic_singular_surf!) are untouched.

Impact

Results change at every current singfac_min by the size of the present defect (about 3e-5 in et[1] for
Forward at 1e-6, about 5e-3 for Riccati at 1e-4), so regression baselines move. Run time per solve is unchanged;
a larger singfac_min becomes usable (2.6 s at 1e-3 against 3.9 s at 1e-6 on the case above).

Evidence and caveats

Prototype: method overrides in a scratch script on v2.0.1, two equilibria, n = 1, Riccati path only for the
jump. The Forward numbers above are the unmodified code. Not yet tested: the Forward implementation, multi-n,
complex α, and PerturbedEquilibrium outputs.

Background: Schiff & Shnider, SIAM J. Numer. Anal. 36, 1392 (1999). Their Möbius-scheme result needs continuous
coefficients, so it does not remove the ideal crossing, but it motivates treating the crossing as a map on the
solution subspace.

Activity

  1. jhalpern30 commented on Nov 10, 2026

    @jhalpern30
    CollaboratorAuthor

    Related open work

    Supersedes / fixes

    • ForceFreeStates - BUGFIX - Decouple the ideal-crossing jump width from singfac_min #481 (decouple the jump width from singfac_min): this is its coefficient-space option. With it the jump no longer needs its own distance key. The first-order drift measured there (DIIID-like forward et₁ changing by 2.69e-3 → 3.0e-5 per 100× in singfac_min) is the same drift as in the table above; absolute et₁ values differ slightly between the two tables because the runs used different tolerances and vacuum resolution.

    Should land first

    Should land after

    Unblocked by this

    Partly addressed

    Not addressed

    Must keep passing

    singfac_min = 1e-6 in the Forward decks (#555) can be revisited once this lands.

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

    featureNew capability

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions