Repository navigation
PerturbedEquilibrium - FEATURE - Solve the perturbed equilibrium for several toroidal modes in one run - #477
Conversation
…nt n cannot race The lock-free last-used-n fast path in get_pn_quad_cache could hand a thread the entry for a different n when several toroidal modes run concurrently. The kernel now looks the entry up once per call and passes it to green/Pn_minus_half_2007!. green therefore requires n >= 1; the unused n = 0 test is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h at or below 0.98 The auto/log_asymptotic grid has a fixed edge region on [0.98, psihigh], so a lower psihigh gives a non-monotonic grid and the equilibrium fails. Point the user at grid_type = "ldp" instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… several toroidal modes in one run
| @warn "grid_type = \"$(equil_params.grid_type)\" needs psihigh > 0.98 (its grid has a fixed edge region on [0.98, psihigh]); " * | ||
| "psihigh = $psihigh gives a non-monotonic ψ grid and the equilibrium will fail. Use grid_type = \"ldp\" instead." | ||
| # Distribute mpsi across the three regions by log-weights | ||
| log_core = log(0.03 / psilow) |
There was a problem hiding this comment.
Should there be a way for the user to change the core/mid/edge breakdown or a physics motivation for these psi values?
There was a problem hiding this comment.
I guess that's unrelated to this PR though
There was a problem hiding this comment.
Yeah I really only added that here because it was silently breaking some of my runs, so at least this way it dumps an output. I am not familiar with the auto/log_asymptotic parts of the code, so I'll just flag @logan-nc on this. I think its relatively moot since most humans would not set psihigh < 0.98 anyway, so this acts more as a check on an LLM setting an unphysically low psihigh.
There was a problem hiding this comment.
Agreed. This is fine for now as there is no known physical reason psihigh should ever be less than 0.98. If one comes up, we can improve this to be more robust.
NO, the user should not be exposed to this. The biggest learning hurdle of the Fortran code was the ballooned name lists. We are working on the premise that we should strive to automate things robustly instead of turning everything into a manual knob.
ebursch
left a comment
There was a problem hiding this comment.
This looks ready to go.
Release note
diiid_multi_ncase (harness @ 5e65f13)PerturbedEquilibrium/mode_mandmode_n; KineticForces now errors on multi-n runs.PerturbedEquilibrium now runs with
nn_low < nn_high: response matrices, energies, torque, field profiles and singular coupling cover every n in one run, and each n block reproduces the matching single-n run. On the DIII-D example, n > 1 is currently limited by a separate ForceFreeStates fix (see Findings).Review summary (3 min read, figures for both Solovev and DIII-D): https://claude.ai/artifact/5X3EewrkkJmo4QgQdSXnqK
Regression report
PR head 5e65f13 against develop e88b9b0:
Verification against single-n
Each case was run as n = 1, 2 together and as n = 1 and n = 2 alone, with the m range matched per n.
Solovev (
Solovev_ideal_example, q₀ = 2.2, mixed-n ASCII forcing; W healthy, anti-Hermitian at 1e-7):singfac_min/min(n).DIII-D (
DIIID-like_ideal_multi_n_example):develop. The ForceFreeStates W is 33–65% anti-Hermitian for n = 2 in every run, single-n included, so the comparison is dominated by that upstream error. Once W is healthy, multi-n and single-n agree to 4e-5 in Λ and 1e-4 in the response field.Findings (upstream, not fixed in this PR)
EulerLagrange.jlpasses onlyreltol, soabstolfalls back to 1e-6. With the fixed axis start (U₁ = 0), near-axis entries of U₁ stay far below that and are integrated with no error control. The symplectic invariant U₁†U₂ − U₂†U₁ drifts from ψ ≈ 0.01. Fortranode_stepsetsatol = max|u(:,isol,ieq)|·tolbefore every step, and the Julia Riccati propagator already does the same.abstoltogether withucrit = 1e3(the value in Fortran's shippeddcon.in). It takes W from 0.2–1.0 anti-Hermitian to ≤ 2e-6 in every DIII-D case tried: single n = 2 and multi-n n = 1–2 and 1–3, each across nudges ofsingfac_minandpsilow. It also roughly halves the ODE steps. Solovev cases are unchanged.DIIID-like_ideal_example, fixed start): the least-stable energy goes from −5426 − 3126i to 0.8014 + 8e-5i, and the non-Hermitian flags from 2243 steps to 7. The remaining ~1e-4 is introduced at the first singular-surface crossing.index(1)rule stays.Follow-ups: a ForceFreeStates BUGFIX PR for the tolerance and
ucritchanges, retitling #93 to the crossing residual, an issue for the Frobenius m = 0 start, and a re-baseline ofdiiid_multi_nonce the fix lands.Notes for reviewers
numpert_total = mpert·npert(m fastest, one block per n), labelled byPerturbedEquilibrium/mode_mandmode_n. Poloidal convolutions and DFTs act within each n block, since the axisymmetric equilibrium does not couple different n.nlow:nhigh.set_psilim_via_dmlimis ignored for multi-n; the new example sets the edge withqhigh.grid_type = "auto"or"log_asymptotic"is used withpsihigh ≤ 0.98, which gives a non-monotonic ψ grid.