Skip to content

ForceFreeStates - BUGFIX - Decouple the ideal-crossing jump width from singfac_min #481

Open

Description

@jhalpern30

Rewritten 2026-10-13. The original body blamed the trapezoid step and proposed re-projection, ODE sub-steps or extended precision; none of those addresses the actual source. The original text is in the edit history.

Problem

At each ideal singular surface, both integrators (forward and Riccati) stop at ψ_s − d and jump to ψ_s + d, where d = singfac_min/(n·|q′|). The jump zeroes the resonant component of the regular solutions, but that component only vanishes like x at the surface. Zeroing it at x = d instead of 0 leaves an error in W, δW and the energies that is first order in singfac_min:

Case singfac_min 1e-4 (default) 1e-6 1e-8
DIIID-like n=1 forward et₁ 0.801550 0.798864 0.798834
  • 100× smaller jump → 100× smaller change in et₁ (DIIID 2.69e-3 → 3.0e-5; Solovev 3.40e-3 → 3.4e-5), so first order, as expected.
  • Riccati et₁ drifts by 0.7% (n=1) to 10% (n=6) on DIIID-like at the default.
  • The non-Hermiticity of W after each crossing (the original symptom here) is the same truncation error.

Why lowering singfac_min isn't the fix

singfac_min sets four things at once:

  1. the jump half-width (wants to be small);
  2. the Riccati Δ′ matching distance (wants to be large: precision degrades as tol·x^(−2α); DIIID Riccati breaks at 1e-7);
  3. the chunk ends and kinetic chunking;
  4. the knot-free equilibrium grid bracket around each rational (GridRefinement).

After #520 the forward path stays accurate at small singfac_min (Solovev to 1e-8), so forward-only runs can lower it as a workaround, at the cost of moving the grid, the per-surface ca_left/ca_right stubs and kinetic chunking. Riccati cannot.

Proposed fix

Give the jump its own distance and leave singfac_min for everything else. Two candidate designs; prototype both and pick one:

A. ODE legs (prototyped). Add δ_jump (default 1e-6/(min(n)·|q′|)). Inside both crossings (cross_ideal_singular_surf!, riccati_cross_ideal_singular_surf!), integrate the same ODE from ψ_s − d to ψ_s − δ_jump, jump across ±δ_jump, and integrate back out to ψ_s + d. Every handoff point stays at d.

B. Crossing in asymptotic-coefficient space (not prototyped). Write u = V c_A (Glasser 2016 Eq. 50), set the large resonant coefficient to exactly zero, carry the non-resonant coefficients across (Glasser–Kolemen 2018 Eq. 36) and rebuild with ua on the right. This removes the O(d) error at today's 1e-4 with no legs and no new key, and also the d^(2α) residual that resonant-row elimination leaves for α < ½. Open risk: extracting coefficients through an ill-conditioned ua.

Prototype evidence (design A, δ_jump = 1e-6)

Case Today (et₁ / flagged steps) With fix
Forward DIIID n=1 0.801550 / 14 0.798864 / 0
Forward multi-n DIIID −0.575053 / 64 −0.575094 / 0
Riccati DIIID n=1 0.804133 0.798892
  • DIIID-like n=1–6: ≤ 1.1e-3 error at δ_jump = 1e-6; forward and Riccati extrapolations in δ_jump agree within 2e-5.
  • Riccati Δ′ matrix is bit-identical in every case (the STRIDE BVP reads only ua at the matching distance and the chunk propagators). Kinetic is identical.
  • Cost: +5–10% runtime at 1e-6.
  • Developer page with figures and the draft diff: https://claude.ai/artifact/MaMkwYpbRxALD99cT6GAWB

Caveat: the prototype predates #520's final trigger (cond(U₁) on the ucrit key, U₂ / lu(U₁)); re-run the table on #520's head. The earlier "DIIID n=3 forward 2.7× wrong" was mostly forward basis collapse, which #520 fixes, not this jump error (Riccati at the default is within ~1%).

Required corrections before a PR (design A)

Impact

  • Every free-boundary energy moves toward convergence by roughly singfac_min relative (DIIID et₁ −0.34%), so the PR takes !. Shipped Fortran inputs use 1e-4, so results move away from Fortran benchmarks by about that amount.
  • Re-pin the harness and add a small-jump Solovev case.

Dependencies

Related

Activity

  1. jhalpern30 commented on Oct 6, 2026

    @jhalpern30
    CollaboratorAuthor

    Findings, proposed fix and draft diff: https://claude.ai/artifact/MaMkwYpbRxALD99cT6GAWB

    The non-Hermiticity in W after ideal crossings is truncation error from the crossing's jump. It is first order in the jump half-width, so DIIID n=1 et₁ is 0.34% off at today's singfac_min = 1e-4.

    Lowering singfac_min alone doesn't work, for two reasons:

    • Forward: after a high-α surface every column grows alike, so the ucrit ratio test never fires and the basis collapses (cond up to 1e15).
    • Riccati: singfac_min is also the Δ′ matching distance, and Δ′ loses precision as it shrinks.

    Proposed fix, in three parts:

    1. ForceFreeStates - BUGFIX! - ⚠️ Remove the resonant solution at ideal crossings regardless of reduction timing #489's resonant-row crossing.
    2. A Gaussian reduction also triggered when the solution columns become nearly parallel. The ucrit test only fires when growth rates differ; here every column grows at the same rate because each is dominated by the same solution. The prototype detects this with a condition number (cond > 1e6), but that is one option among several, including cheaper ones (see the page).
    3. A separate jump half-width (default 1e-6), reached by ODE legs inside the crossings. singfac_min keeps its other roles.

    With all three, energies converge with the jump and forward and Riccati agree, while the Δ′ matrix is unchanged. Today they disagree by up to 2.7× (DIIID n=3).

    Input wanted on:

    • the parameter design;
    • crossing in asymptotic-coefficient space as an alternative;
    • which test to use for detecting the collapse.
  2. self-assigned this
    on Oct 6, 2026
  3. changed the title [-]ForceFreeStates - BUGFIX - Preserve the symplectic structure across ideal singular-surface crossings[/-] [+]ForceFreeStates - BUGFIX - Decouple the ideal-crossing jump width from singfac_min[/+] on Oct 13, 2026
  4. jhalpern30 commented on Oct 13, 2026

    @jhalpern30
    CollaboratorAuthor

    Rewrote the body above with the current diagnosis, the two candidate designs, the prototype evidence and what has to be fixed before a PR. Short version: the error is the jump being made at singfac_min, it is first order, and the fix is to give the jump its own distance (ODE legs) or to do the crossing exactly in coefficient space, stacked on #489 and #520.

  5. jhalpern30 commented on Oct 13, 2026

    @jhalpern30
    CollaboratorAuthor

    @matt-pharr + @d-burg I noticed a lot of changes/PRs for the Delta prime calculation, so for the sake of not stepping on anyone's toes I figured this was just easier to flag to you both to get thoughts versus try to implement myself.

    The human version TL/DR is that #520 and other PRs from Daniel/I fix the more numerically-focused issues with the crossing, allowing the forward integrator to get to arbitrarily close distances to the surface in the integration, which was the cause of the non-hermitian errors I was seeing since that error scales with this distance. However, reducing this error comes at the cost of increasing the matching error for calculating Delta prime or the ca coefficients, because the large and small solutions diverge more and more the closer you get to the surface. After some testing with Claude, it seems like a reasonable fix is to separate the two distances - integrate closer to the rational surface for better accuracy in the energies/eigenfunctions, but do all matching farther out with a separate jump parameter. I am not really sure what is meant by option B above, seems too good to be true.

    I think my main questions here are

    1. Is this an appropriate fix, and does it overlap/contradict anyone's current work?
    2. Is this worth trying to do before v2.0? I am not sure how important this would be to anyone's workflows
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

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