Skip to content

KineticForces - BUGFIX! - Give the energy integration its own tolerances - #392

Open
logan-nc wants to merge 3 commits into
developfrom
bugfix/kf-split-energy-pitch-tolerances
Open

KineticForces - BUGFIX! - Give the energy integration its own tolerances#392
logan-nc wants to merge 3 commits into
developfrom
bugfix/kf-split-energy-pitch-tolerances

Conversation

@logan-nc

@logan-nc logan-nc commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Release note

  • Audience: users
  • Numerical impact: Kinetic eigenvalues shift ~1e-8 relative; energy-integration runtime rises by up to 60% on solovev_kinetic_nuzero (247s -> 395s) (harness @ ba95fd7)
  • Migration: No config change is required; set nested_tolerance_margin (or atol_x/rtol_x) if you need the previous runtime back.

The energy integration is nested inside the pitch integration, but both were handed the same tolerance pair, so the outer integrator chased its own integrand's quadrature noise instead of converging. The energy level now gets its own tolerances, derived by default as nested_tolerance_margin times the pitch tolerances and overridable per deck.


What

Give the energy (x) integration its own tolerances instead of aliasing them to the pitch (λ) integration's.

Julia analog of the second commit of Fortran GPEC PR #280.

Why

The integrations nest: ψ quadrature ⊃ λ (pitch) ⊃ x (energy). The pitch integrand is the energy integral, so handing both levels the same atol_xlmda/rtol_xlmda asks the outer integrator to resolve its integrand to exactly the tolerance to which that integrand was itself computed — it then chases the inner integrator's quadrature noise instead of converging.

The control struct already documents this same nesting rule one level further out (keep rtol_psi ≳ 10 × rtol_xlmda); this extends it to the level that was missing it.

API

[KineticForces]
atol_xlmda = 1e-8   # pitch (λ) integration
rtol_xlmda = 1e-5
atol_x     = 1e-10  # optional: energy integration; omit to derive
rtol_x     = 1e-7
nested_tolerance_margin = 1e-2   # derived energy tol = margin × pitch tol

atol_x/rtol_x default to a NaN sentinel meaning "derive as nested_tolerance_margin × the pitch tolerance". Explicit values override. Wired through both the ψ-quadrature path (tpsi!) and the kinetic-matrix path.

This changes default numerical behaviour, deliberately

energy atol energy rtol
before (aliased to pitch) 1e-8 1e-5
after (derived, 1e-2 margin) 1e-10 1e-7

Every deck that does not set the new controls integrates the energy variable more tightly than before and pays for it in runtime. That is the intent — the previous nesting was inconsistent — but it is a behaviour change, not a no-op. Reviewers should decide whether 1e-2 is the margin they want; widening nested_tolerance_margin recovers the old cost.

Note the derived values do not restore the EnergyIntegration module's own much tighter internal defaults; a deck can ask for those explicitly via atol_x/rtol_x.

Validation

  • runtests_kinetic.jl: 277/277.
  • Control-struct behaviour verified directly: defaults derive to 1e-10/1e-7, explicit atol_x/rtol_x override, and TOML-dict construction of the new keys works.
  • Regression harness: pending. Draft until the kinetic cases are run — tighter energy integration should move results slightly toward the converged answer and increase runtime, and both effects should be visible in the table.

🤖 Generated with Claude Code

…erances

The energy (x) integration was handed the same atol_xlmda/rtol_xlmda pair as the
pitch (lambda) integration that encloses it. Since the pitch integrand IS the
energy integral, the outer integrator was asked to resolve its integrand to the
same tolerance to which that integrand was itself computed, so it chases the
inner integrator's quadrature noise instead of converging.

Adds atol_x/rtol_x, defaulting (NaN sentinel) to nested_tolerance_margin = 1e-2
times the pitch tolerances, which extends the nesting rule the struct already
documents one level up for rtol_psi vs rtol_xlmda. Wired through both the
psi-quadrature path (tpsi!) and the kinetic-matrix path.

This deliberately changes default numerical behaviour: shipped decks now
integrate the energy variable to atol 1e-10 / rtol 1e-7 rather than 1e-8 / 1e-5,
and pay for it in runtime. A deck can set atol_x/rtol_x explicitly to override
the derived values, or widen nested_tolerance_margin to recover the old cost.

Julia analog of the second commit of Fortran GPEC PR #280.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@logan-nc
logan-nc marked this pull request as ready for review August 15, 2026 18:33
@logan-nc
logan-nc requested a review from krystophny August 15, 2026 18:34
@logan-nc logan-nc self-assigned this Aug 15, 2026
@logan-nc

Copy link
Copy Markdown
Collaborator Author

Ready for review; not yet ready to merge.

The physics/code argument is complete and self-contained, so review can start now. The regression-harness table is still outstanding and is a merge gate per CLAUDE.md — I'll post it here before this is merged.

Note the machine currently has other regression runs in flight, so the kinetic cases are queued behind the PR #339 re-baseline rather than run in parallel.

@logan-nc

Copy link
Copy Markdown
Collaborator Author

Regression harness — develop (b3abe074) vs this branch

regress --cases diiid_n1,solovev_kinetic_ntv,solovev_kinetic_nuzero,solovev_kinetic_calculated --refs develop,bugfix/kf-split-energy-pitch-tolerances

Case Values Runtime
diiid_n1 48 unchanged 430.0s → 419.0s
solovev_kinetic_ntv 6 unchanged 340.1s → 323.8s
solovev_kinetic_nuzero 5 changed, all ~1e-7 relative 193.3s → 251.2s (+30%)
solovev_kinetic_calculated 6 changed, all ~1e-7 relative 191.7s → 184.1s
solovev_kinetic_nuzero          develop         branch          Diff
total energy Re(et[1])          2.245439e+00    2.245440e+00    3.347e-07 (0.00%)
total energy Im(et[1])         -2.034869e+00   -2.034869e+00    7.464e-08 (0.00%)
plasma energy Re(ep[1])        -8.485145e+00   -8.485145e+00    6.104e-07 (0.00%)

How to read "unchanged" here — the guards are coarser than the effect

Two of the four cases report no change, but that is not evidence the tolerance tightening did nothing. The relevant noise_thresholds are absolute and coarse relative to the quantities they guard:

Quantity value noise_threshold effective relative sensitivity
solovev_kinetic_ntv NTV torque fgar ~1.3e-4 N·m 1e-8 ~1e-4
diiid_n1 NTV torque FGAR 5.59e-1 N·m 1e-2 ~1.8%
diiid_n1 NTV kinetic energy dW FGAR 6.9e-2 J 1e-3 ~1.4%
kinetic eigenvalues (nuzero/calculated) O(1) 1e-10 ~1e-10

So the movements we can actually resolve — the O(1) eigenvalues, guarded at 1e-10 — are ~1e-7 relative. A change of that size in the NTV torque would sit three to five orders of magnitude below its guard and be reported as "unchanged." The eigenvalue rows are the only ones in this suite sensitive enough to see this effect at all.

What the table does establish:

  1. The change is real and reaches the integrator. The +30% runtime on solovev_kinetic_nuzero is the energy integrator doing genuinely more work at rtol 1e-7 instead of 1e-5. The plumbing was verified separately: atol_x/rtol_x/nested_tolerance_margin flow from KineticForcesControl through Compute.jl into tpsi! and through compute_kinetic_matrices_at_psi!.
  2. The effect is small where it is resolvable, ~1e-7 relative on the eigenvalues — i.e. the previous aliased tolerances were already close to converged on these well-behaved cases, which is reassuring for existing published results.
  3. The runtime cost is real but not uniform+30% on one case, within noise on the other three. The nested_tolerance_margin knob exists precisely so a deck can trade this back.

The honest summary: this is a consistency fix to the tolerance nesting, and on the cases we track it moves results at the 1e-7 level for up to +30% runtime. It is not expected to change any physics conclusion; it removes the situation where the outer pitch integrator was chasing its own integrand's quadrature error.

@logan-nc logan-nc changed the title KineticForces - IMPROVEMENT - give the energy integration its own tolerances KineticForces - BUGFIX! - Give the energy integration its own tolerances Aug 18, 2026
@github-actions github-actions Bot added bugfix Something was wrong and now is not changed-results Results move or an interface breaks - read before upgrading labels Aug 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This pull request is missing a reviewer.

If you are not ready to name them, mark this pull request as a draft.
docs/development/contributors.md suggests lead developers to ask.
Merging is not blocked here, but no pull request may be merged without human review.

# Conflicts:
#	src/KineticForces/CalculatedKineticMatrices.jl
#	src/KineticForces/Compute.jl
@logan-nc

Copy link
Copy Markdown
Collaborator Author

Regression re-run after re-syncing with develop

PR #339 merged, so this branch was updated to develop@c42d558e and the harness re-run at the new head. Baseline and branch now both contain the multi-ion work.

diiid_n1	Summary: 47 unchanged
solovev_kinetic_ntv	Summary: 6 unchanged
solovev_kinetic_nuzero	Summary: 6 changed, 8 unchanged
solovev_kinetic_calculated	Summary: 7 changed, 7 unchanged

Every changed row on the two Solovev kinetic cases is at 0.00% relative (absolute diffs ~1e-6 to 1e-8 on O(1) eigenvalues, plus one-step ODE count moves) — they cross the harness's tight 1e-10 eigenvalue threshold but are not physically meaningful.

Runtime cost of the tighter energy tolerance is larger than first measured: solovev_kinetic_nuzero went 247.2s -> 394.9s (+60%, previously +30%), while solovev_kinetic_calculated was flat. The nested_tolerance_margin knob exists to trade this back.

jhalpern30 pushed a commit that referenced this pull request Aug 20, 2026
The metadata job read only requested_reviewers, which holds pending review
requests. GitHub removes a reviewer from that list as soon as they submit a
review, so the check went quiet while nobody had reviewed and fired once
somebody did -- nagging precisely the pull requests furthest along.

Treat a submitted review from anyone other than the author as evidence that a
reviewer was named. Assignment alone still suffices; the reviewer never has to
act. Verified against all open pull requests: silences #390, #391, #392, #407,
leaves every pending-request PR untouched, and still nags the nine with nobody
named.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dh1NCejnd3fYMmcRKoQRcG
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 changed-results Results move or an interface breaks - read before upgrading

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants