Skip to content

EQUIL - BUGFIX - Detect field-line ODE failure instead of consuming a truncated surface - #442

Open
logan-nc wants to merge 1 commit into
developfrom
bugfix/fieldline-ode-retcode
Open

EQUIL - BUGFIX - Detect field-line ODE failure instead of consuming a truncated surface#442
logan-nc wants to merge 1 commit into
developfrom
bugfix/fieldline-ode-retcode

Conversation

@logan-nc

@logan-nc logan-nc commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

The bug

direct_fieldline_int solves the flux-surface field-line ODE and never checks sol.retcode:

https://github.com/OpenFUSIONToolkit/GPEC/blob/develop/src/Equilibrium/DirectEquilibrium.jl#L293-L296

The very next line consumes sol.u[end] as though the field line had closed at eta = 2*pi. But a
failed solve does not throw — it returns a solution truncated wherever the integrator gave up. So
the caller silently builds a flux surface from a partial trace, and the damage surfaces downstream as
unrelated nonsense, or not at all.

The fix

Two checks, because neither subsumes the other:

  • sol.retcode != ReturnCode.Success — the ordinary failure.
  • sol.t[end] did not reach 2*pi — a solve can stop early through a callback-driven terminate and
    still report Success. direct_fieldline_int installs a DiscreteCallback, so this is reachable.

Both errors name the psifac that failed, and the first points at psihigh sitting too close to the
separatrix, which is the usual cause. The failure becomes attributable to a surface rather than a
stack trace somewhere further down the pipeline.

Verification

  • test/runtests_equil.jl281/281 pass.
  • DIII-D-like ideal example runs end to end (equilibrium → FFS → PE → KineticForces, 476 s), neither
    guard firing.
  • Regression harness, diiid_n1, --refs develop,local against develop @ 4e430f170:
    47 quantities, all unchanged, 0.0e+00 across the board. Expected — on a healthy equilibrium this
    adds two predicates and changes nothing.

A note on the test

The added testset asserts the guards against the source text rather than triggering them. A genuinely
non-closing field line is not cheap to synthesize here, and forcing psifac toward the separatrix
would likely hit a different error first and be flaky across machines. The standing positive control
that neither guard false-fires is the EFIT testsets already in that file, which trace real surfaces
through this function. Happy to swap in a behavioral trigger if a reviewer sees a clean way to build one.

Scope

Equilibrium only; no SLAYER/Tearing code touched. Split out of the FKR-width work as an
independent fix, same shape as #399.


⚠️ NO MERGE WITHOUT THIRD-PARTY HUMAN REVIEW — NON-NEGOTIABLE ⚠️

🤖 Generated with Claude Code

https://claude.ai/code/session_01PSrf6JCViFfVzqzkQ66o6b

… truncated surface

direct_fieldline_int solved the flux-surface field-line ODE and never checked
sol.retcode, then read sol.u[end] as though the surface had closed at eta = 2*pi.
A failed solve does not throw -- it returns a solution truncated wherever the
integrator gave up -- so the caller silently built a wrong flux surface and the
damage surfaced downstream as unrelated nonsense, or not at all.

Check both the retcode and the endpoint: a solve can stop early through a
callback-driven terminate and still report Success, so neither check subsumes the
other. Both errors name the psifac that failed and point at psihigh sitting too
close to the separatrix, which is the usual cause.

No healthy deck is affected: runtests_equil.jl passes 281/281 and the DIII-D-like
ideal example runs end to end with neither guard firing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSrf6JCViFfVzqzkQ66o6b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Something was wrong and now is not

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants