Repository navigation
Equilibrium/KineticForces - BUGFIX! - Port the Fortran 1.6.0 kinetic profile, bounce-integral and torque-method corrections - #625
Conversation
…ast the bounce midpoint Fortran GPEC 1.6.0 fixed the v∥ ≤ 0 fill that copied the bounce-frequency column into the precession column; Julia had kept the old copy for parity. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdRsGGg4YRG6E9fgLGbpdF
…urning it without a warning Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdRsGGg4YRG6E9fgLGbpdF
…thods RLAR used a placeholder κ average in place of the Fortran κ integral and CLAR returned zero, so neither matched Fortran GPEC. A deck setting rlar_flag or clar_flag now stops with a pointer to fgar_flag or tgar_flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdRsGGg4YRG6E9fgLGbpdF
GPEC requires q > 0, matching Fortran GPEC 1.6.0: newq0 = -1 no longer flips the sign of q, and any negative newq0 stops at configuration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdRsGGg4YRG6E9fgLGbpdF
…garithm and take monotone collision-rate slopes Match Fortran GPEC 1.6.0: the collision model floors T at 1 meV and lnΛ at 1, so a cold edge node gets a large finite ν instead of zero, negative input temperatures stop the run, and lnΛ, ν_i and ν_e are pchip interpolants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdRsGGg4YRG6E9fgLGbpdF
…tone pchip fits Match Fortran GPEC 1.6.0, which fits the kinetic table with pchip so a steep edge cannot overshoot between table points. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdRsGGg4YRG6E9fgLGbpdF
… BounceAveraging Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdRsGGg4YRG6E9fgLGbpdF
… resampling and warn when a collision floor engages Densities must be positive and temperatures non-negative on the resampled grid, as Fortran PENTRC checks them; a zero density used to give an infinite collision rate. The decks' newq0 comments now match the rejection of negative values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdRsGGg4YRG6E9fgLGbpdF
# Conflicts: # examples/Solovev_kinetic_NTV_example/gpec.toml # examples/Solovev_kinetic_calculated_example/gpec.toml # examples/Solovev_kinetic_multiion_example/gpec.toml
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdRsGGg4YRG6E9fgLGbpdF
d-burg
left a comment
There was a problem hiding this comment.
Adversarial review at b7837705f, by model reviewers working from the code and the Fortran v1.6.0 source. I checked the two pre-existing bugs and items 1–3 by hand. Items are marked ran or traced.
What held up: the A4 index map (hold, restart, backfill, first point past the midpoint); pchip slopes and extrapolation against a Fortran reimplementation (6e-16 on non-uniform grids); floor logic and constants against read_kin; RLAR/CLAR removal, with no residue; every shipped kinetic file passes the new checks. No tolerance or convergence criterion loosens. The added floors are T ≥ 1e-6 keV and lnΛ ≥ 1, and the density check tightens from < 0 to ≤ 0.
To fix here
- The new checks see unscaled profiles, but the model uses scaled ones.
KineticProfiles.jl:427-431validates before the scaling, andKineticForcesStructs.jl:117-118validates nothing. Ran:temperature_factor = -1on a 5 eV table stores T = −5 eV and gives ν = 2.7e10 with no error;density_factor = 0gives ν = NaN;temperature_factor = 1e-4is floored with no warning. Suggested fix: check after scaling, or reject non-positive factors. - The checks can stop decks that don't use NTV. Traced: a file with n_e = 0 at ψ = 1 now aborts an ideal or SLAYER deck that reads it only for two-pass grid resonance surfaces, through
CalculatedKineticMatrices.jl:180-182. This is the same class as #614. Suggested fix: run them only for a kinetic source. - The SLAYER explanation doesn't hold. The layer's n, T and ω_E are cubic splines of the raw table (
KineticView.jl:18,Utilities/KineticProfiles.jl:47-50), so they don't read the resampled profiles. The resample moves the profiles by at most 2.8e-5 on the DIII-D-like file, so γ moving 1.4e-3 is unexplained. The two-pass grid may be the cause, and a per-commit run would settle it. NTV and SLAYER also now interpolate one table differently. - A4 has no test. No test references
g_wd, so revertinge1954db00should pass (traced). Ran: the hold fired 0 times on single-well model surfaces, so the attribution to "particles reflecting past the midpoint" needs the per-commit run too.
Pre-existing bugs
Axisymmetric/Inverse.jl:288, in the block this PR edits:f0reads column 2 (μ₀p), where Fortraninverse.f:247uses column 1 (F). Withnewq0 > 0on the inverse path, F is barely rescaled, yetq0 = newq0is still assigned. The new test covers onlyDirect.jl. Traced. Fix: use column 1.BounceAveraging.jl:873,895: bounce points sort descending, while Fortran'sspline_rootssorts ascending._find_deepest_wellpairs neighbors, so with 4 roots every pair spans a barrier. Ran: on a model surface with B ∝ (1 + 0.1cos(4πθ + 1.1))/(1 + 0.05cos2πθ) at λ = 0.9488, the roots are [0.918, 0.882, 0.536, 0.314] and the window is (0.882, 1.536). The hold fires on 45 of 128 pitch points, and 0 of 128 once sorted ascending.runtests_kinetic.jl:303-304pins the wrong order. We didn't run a harness deck, so whether a shipped equilibrium reaches this is open.
Minor
maxlog = 1is per process and site, so a second file's floor warning is silent (ran).- "Cannot overshoot" holds only for the resample. A linear ramp to T = 0 passes the checks, but
Ti_splinereaches −12 to −93 eV, andUtils.jl:76andBounceAveraging.jl:325take its square root (ran to the negative T, traced after). - A NaN torque didn't return silently before this PR: QuadGK's
evalrulethrowsDomainErrorfirst (ran). The new check is reachable only by overflow. - Untested: floor warnings and the
rlar_flag/clar_flagentries, andrlar_flag = falsealso stops a run. The cold-edge test's (1 − ψ)² looks reproducible by a cubic, so0c038188bmay be unguarded.
This is not an approval. A third-party human reviewer's approval is still required before merge.
…the inverse path and require a positive CHEASE q Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-well search pairs neighbouring roots Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d keep unusable profiles out of the two-pass grid Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Release note
newq0, bounce-point order, scaling-factor and grid checks) do not move the two largest movers. RLAR/CLAR andnewq0are not used by any harness deck.rlar_flagorclar_flagstops with a pointer tofgar_flag/tgar_flag;newq0 < 0(including the old-1flip) is rejected.These are the kinetic-profile and KineticForces corrections from Fortran GPEC 1.6.0, the Equilibrium and KineticForces half of #591:
What changed, one commit per item
e1954db00BounceAveraging.jl: for v∥ ≤ 0 past the midpoint,g_wdis held fromg_wd[i-1], notg_wb[i-1]. The comment that kept the old copy "for parity" is gone. Julia's index runs one ahead of Fortran's, so Fortranfs(i-2,2)is Juliag_wd[i-1].d45567603check_psi_quadrature_convergencestops on a non-finite torque.NaN > tolis false, so a NaN torque used to return without a warning.f00d20ff8calculate_rlar(a placeholder κ average) andcalculate_clar(returned 0), with their registry entries, flags and the surface-state fields only they used._MOVED_KEYSgives the deck message. The faithful port is #622.4f2ad22aenewq0 < 0errors at configuration. The-1flip and thesign(newq0)factor are gone fromDirect.jlandInverse.jl.e812b0e6cKineticProfileSplinesgains a third type parameter. At a T = 0 node the old code returned ν = 0.0c038188b_cubic_resamplebecomes_pchip_resample, so the kinetic tables are fit with pchip and cannot overshoot between table points.5abf20f1bnewq0comment in every deck now says negative values are rejected.09e25641dFastInterpolations' pchip uses the same weighted harmonic-mean interior slopes and three-point end slope as Fortran's
spline_fit_pchip.Regression report
Harness on SDCC (
regress-fanoutrunpr625_e0bb4f7c1): develop at2d12b7962vs this branch ate0bb4f7c1.gal_resistive_peN/A by design)Attribution: a per-commit run (
pr625_attrib) oftj_kinetic_calculatedanddiiid_slayer_n1_coupledat6e2796718(develop merged, before the review fixes),e38b2c995(bounce-point order) and50a7c8efb(scaling-factor and grid checks) is bit-identical across all three, so those two cases' moves come from the kinetic corrections in the earlier commits, not from the review fixes. The other cases were not split by commit.Why each moves:
diiid_n1,diiid_truncation_n1,diiid_error_field): the SLAYER layer builds its own cubic splines of the raw table, so it does not read the resampled profiles directly; these decks place two-pass ψ-grid knots at kinetic-resonance surfaces computed from the NTV view, so the grid, and everything on it, shifts slightly.Notes for reviewers
develop, merged up to961484b92.runtests_multiion.jl(lnΛ ≥ 1; ν finite, positive and monotone between nodes; errors on a negative T and on a zero density), thenewq0rejection, and the non-finite-torque cases of the quadrature check.runtests_kinetic5187/5187,runtests_multiion92/92,runtests_solve_api175/175,runtests_kinetic_profiles7/7,runtests_equil357/358. The one failure is the TokaMaker i-file reform's exact==check, which fails the same way on develop on alfven, so it is not from this PR.5abf20f1b. One deliberate divergence from Fortran was kept: collision rates use the density- and temperature-scaled profiles, as the docstring says.🤖 Generated with Claude Code
https://claude.ai/code/session_01VdRsGGg4YRG6E9fgLGbpdF