Skip to content

Widen Optim compat; replace NLsolve; add RK4 field-line integrator - #306

Merged
jmcclena merged 8 commits into
masterfrom
compat/optim
Jun 23, 2026
Merged

Widen Optim compat; replace NLsolve; add RK4 field-line integrator#306
jmcclena merged 8 commits into
masterfrom
compat/optim

Conversation

@mgyoo86

@mgyoo86 mgyoo86 commented Jun 18, 2026

Copy link
Copy Markdown
Member

Summary

The goal was to widen the Optim compat bound. NLsolve was blocking it, so it is replaced with SimpleNonlinearSolve in the one routine that used it (theimplicit-midpoint field-line solver).

In addition, an explicit RK4 integrator was added as a faster, more accurate option for field-line tracing.

What changed

  • Dependencies: widen Optim (and Roots); drop NLsolve, add SimpleNonlinearSolve.

  • Field-line tracing:

    • The implicit-midpoint solver is now backed by SimpleNonlinearSolve
      (same algorithm, slightly more accurate but a bit slower; this path is rarely used).
    • Added an explicit RK4 method — faster and more accurate alternative
  • Tests / docs: added field-line tracing tests covering both methods and their
    accuracy trade-off; fixed a couple of stale docstrings.

mgyoo86 added 6 commits June 17, 2026 17:01
NLsolve is unmaintained and pinned NLSolversBase to v7, which blocked upgrading Optim to 2.x. Its only use was the implicit-midpoint root-find in trace_field_line, now solved with SimpleNonlinearSolve's SimpleTrustRegion (mirrors NLsolve's trust-region default).

Relax Bx_By_Bz / Br_Bphi_Bz signatures from 'where T<:Real' (which coupled all args to one type) to per-arg '::Real', so the ForwardDiff Jacobian's mixed Float64/Dual arguments flow through.

Add test/runtests_fields.jl characterization tests (constant-field straight line, rotation-field radius invariant, D3D snapshot) pinning trajectory behavior across the swap. Also widen Roots compat to '2, 3'.
trace_field_line now takes a method kwarg: :implicit_midpoint (default; the unchanged 2nd-order symplectic path solved via SimpleNonlinearSolve) or :rk4 (explicit 4th-order, no per-step nonlinear solve). Introduce an abstract FieldLineIntegrator with ImplicitMidpointState and a dedicated RK4State, sharing the angle/step bookkeeping via _advance!. RK4 is ~3.6x faster and ~3x lighter on allocations and avoids the per-step solve entirely; the implicit path is kept for exact flux-surface (symplectic) preservation.

Widen Optim compat to '1, 2' (resolves to 2.2.1 now that NLsolve is gone). Add RK4 characterization tests (straight line, radius conservation, D3D snapshot) plus an unknown-method guard.

Note: ImplicitMidpointState/RK4State carry the existing in-place mutation of start_point (struct aliases it); unchanged here.
Both integrators solve the same ODE dx/ds = B̂(x), so on a benign analytic field (unit rotation → circle) their trajectories must agree to within the lower-order method's truncation error (~1e-4 here).
Drop the RK4State / _advance! / _integrator / abstract-type scaffolding: :rk4 now dispatches to a self-contained _trace_rk4 (one loop, no state object, no per-step solve), and the implicit path moves to _trace_implicit_midpoint (still using ImplicitMidpointState for its residual/solver). Both helpers force specialization on the field/stop closures.

Tried SVector arithmetic in the loop but it was ~2x slower with no allocation benefit, so kept plain Vector. A/B benchmark shows per-step allocations (~5 KiB) are dominated by the Interpolations.gradient evaluation inside vector_field (4 calls/step), not the RK4 arithmetic — so loop-level changes can't reduce them; that would need a non-allocating field evaluation (separate change). Speed matches the prior struct version (~1 ms/trace, ~5x faster than implicit).
Extract the shared _toroidal_angle helper used by both integrators (clamping
the acos argument against round-off), and copy start_point in the implicit
path so it no longer mutates the caller's array, matching _trace_rk4.
Compare both integrators against the exact-circle solution: RK4 is far more
accurate in position (4th- vs 2nd-order), while implicit midpoint conserves the
radius invariant to ~machine precision. Includes a convergence-order check and a
non-mutation regression test.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates IMAS’s field-line tracing implementation to remove the NLsolve dependency (unblocking a wider Optim compat range), and adds an explicit RK4 tracing option with new characterization/regression tests to pin solver behavior.

Changes:

  • Replaced NLsolve usage in the implicit-midpoint field-line integrator with SimpleNonlinearSolve.
  • Added an RK4 field-line integrator and exposed it via trace_field_line(...; method=...).
  • Added a new runtests_fields.jl suite and wired it into the default test runner; updated dependency/compat bounds (Optim, Roots, solver deps).

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/physics/fields.jl Swaps nonlinear solver backend, adds RK4 tracing path, and centralizes toroidal-angle accounting.
src/physics.jl Updates imports to reflect solver dependency change.
Project.toml Removes NLsolve, adds SimpleNonlinearSolve, and widens compat bounds.
test/runtests.jl Includes the new field-line test suite in the default run.
test/runtests_fields.jl Adds characterization/regression/accuracy tests for both implicit-midpoint and RK4 tracing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/physics/fields.jl
Comment thread src/physics/fields.jl
Comment thread src/physics/fields.jl
mgyoo86 added 2 commits June 18, 2026 13:07
Pins critical_energy(...; approximate=false) on the bundled sample — the only
Roots.find_zero call site in the package, which no other test exercised. The
value is bit-identical under Roots 2.3.0 and 3.0.0, locking in the widened
Roots = "2, 3" compat.
@jmcclena
jmcclena merged commit f94e47a into master Jun 23, 2026
2 checks passed
@jmcclena
jmcclena deleted the compat/optim branch June 23, 2026 20:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants