Skip to content

ForceFreeStates - FEATURE! - Add perpendicular inertia and an ELITE-style growth-rate solve - #663

Draft
d-burg wants to merge 20 commits into
developfrom
feature/growth-rate-inertia
Draft

d-burg wants to merge 20 commits into
developfrom
feature/growth-rate-inertia

Conversation

@d-burg

@d-burg d-burg commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Release note

  • Audience: users
  • Numerical impact: the solution columns are now rescaled after every Gaussian reduction, which moves results at the integration-tolerance level in 7 of 28 cases: solovev_n1 leading total energy 7.398220e-3 → 7.362675e-3 (0.48 %, a ~1400× cancellation of plasma against vacuum energy, each of which moves 6e-6), tj_n1 toroidal torque and tj_kinetic_calculated eigenfunction norms 0.03 %, NTV torques ~1e-5, all other energies ≤ 3e-6; the forward-integrator ca_left/ca_right coefficients change basis; growth rate of lar_reversed_shear_growth_rate_n1 unchanged (2.957092e3 s⁻¹) (harness @ 9b43744)
  • Migration: none

GPEC can now compute the ideal-MHD growth rate of the least-stable free-boundary mode, the way ELITE does: PerpendicularInertia(; rho) is a kinetic source carrying the perpendicular (incompressible) plasma inertia, kinetic_factor = γ² scales it, and growth_rate(prob, Forward(); inertia) finds the γ² at which the free-boundary energy eigenvalue crosses zero. A [GrowthRate] deck section runs the same solve after the force-free-states stage, with the density taken from the equilibrium's kinetic profiles unless rho is given, and reports the peak ion diamagnetic frequency ω_*pi for the EPED1 γ > ω_*pi/2 test. Existing ideal, kinetic and resistive paths are untouched.

What this adds

  • Inertia bands. For a mode ∝ e^{γt}, δW + γ²K is stationary with K = ½∫ρ|ξ_⊥|² dV. In the energy-principle variables Ξ = ξ·∇ψ and ξ_s = (ξ×B)·e_ψ (Glasser 2016 Eqs. 8–9), ξ×B = ξ_s∇ψ + Ξu with u = χ′(∇ζ − q∇θ), so |ξ_⊥|² = |ξ×B|²/B² is algebraic in the two retained variables and K adds the Hermitian bands ⟨μ₀ρ|∇ψ|²/B²⟩ → A, ⟨μ₀ρ u·∇ψ/B²⟩ → C, ⟨μ₀ρ|u|²/B²⟩ → H of Glasser 2016 Eq. A6 (src/ForceFreeStates/Inertia.jl). They are evaluated once per problem and reused at every γ².
  • Solve path. The bands ride the existing kinetic (non-Q-factorized) F, K, G reduction with kinetic_factor = γ². With inertia the rational surfaces are regular points for γ² > 0, so singfac_min defaults to 0 for the inertial solves and the integration is a single chunk with no Frobenius crossings.
  • Root solve. growth_rate runs the ideal (γ² = 0) solve and counts the modes growing faster than γ as the interior zero crossings nzero plus one when the least-stable free-boundary energy λ_min is negative; that count cannot rise with γ² since K > 0. It brackets the count by decades (down while nothing is faster, up while something still is) and, once no interior crossing is left at the lower end, closes λ_min = 0 with an Illinois (bracketed secant) iteration. Each evaluation is one force-balance solve; the root typically needs 5–6 solves after the marginal one. Returns an IdealGrowthRateResult (γ, γ², converged, the marginal and the inertial solves, and the full (γ², λ_min, nzero) history). A crossing still present at the lower bracket end is bisected away on the count (see the count section below), a root below gamma_sq_min is reported as γ = 0, and running out of maxiter as a warning with converged = false.
  • Density from the kinetic profiles. PerpendicularInertia(equil) builds ρ(ψ_N) = μ m_p n_e from the attached kinetic profiles with the SLAYER layer's mass per electron (main ions plus the impurity at the local Z_eff), i.e. the same density InnerLayer.layer_rho uses.
  • *ω_pi. ion_diamagnetic_frequency(equil, n) gives ω_*pi(ψ_N) = n (dp_i/dΨ)/(Z_i e n_i) in the layer convention (n_i ∝ n_e, p_i = n_i T_i, Ψ the poloidal flux per radian, positive for outward-decreasing profiles); IdealGrowthRateResult.omega_star_pi is its peak over the integration domain at the lowest n (NaN without kinetic T_i), so the EPED1 criterion is gamma > omega_star_pi/2.
  • Deck surface. [GrowthRate] (keys = GrowthRateConfig fields: rho in kg/m³ with NaN meaning from the kinetic profiles, gamma_sq_init/min/max, rtol, ftol, maxiter) runs the stage after the ForceFreeStates write on the very problem that stage solved, reusing its ideal solve as the marginal one; it needs vac_flag = true, the Forward integrator and no kinetic source, and cannot share a deck with [Match] (parse-time errors). The result is main's growth_rate slot and ForceFreeStates/GrowthRate/ in gpec.h5: gamma, gamma_sq, omega_star_pi, rho on psi, and History/{gamma_sq, lambda_min, nzero}, with the section recorded under Input/. docs/src/api.md and workflow.md carry the section.
  • Exact conjugate-point count. The forward integration gains a second, exact count of the fixed-boundary modes inside the ψ interval (diagnostics.nzero_conj, with nconj_flags and maslov_defect), the Maslov index of the solution frame; it is off by default and growth_rate switches it on for its solves through an internal keyword (no deck key, public solve unchanged). Details in the count section below.
  • Tests. test/runtests_inertia.jl (core suite, 86 tests): the bands are Hermitian with A and H positive definite and linear in ρ; the metric's B² matches the equilibrium's to 1e-3; inertial solves close as :regular with no crossings; λ_min(γ²) converges to the ideal value as √γ² (the inertial-layer contribution is linear in γ, checked by a factor-of-ten ratio between γ² = 1e4 and 1e2); the crossing path (singfac_min > 0) and the single chunk agree to 1e-5; growth_rate on a stable fixture returns γ = 0 after the marginal solve alone; on a q_a = 2.9 TJ external kink it converges with nzero = 0 throughout; on the q_a = 2.9, p0 = 0.8 variant with a fixed-boundary mode inside the bracket it reaches the fastest root from gamma_sq_init = 1e8 and finds γ > 0 behind a standoff-0.05 wall with nzero > 0 in the history, reports converged = false with a warning at maxiter = 1, returns γ = 0 when the root lies below gamma_sq_min, and rejects a kinetic source and a non-positive density; ρ(ψ) from the kinetic TJ fixture matches layer_rho; ω_*pi matches a finite difference of n_e T_i and scales with n; the HDF5 group passes the repo's metadata check. test/runtests_solve_api.jl gains a "deck and API parity" case: deck and API agree on the stable TJ fixture (γ = 0) and on a q_a = 2.9 variant that is free-boundary unstable (γ = 1.35e4 s⁻¹, agreement to 1e-12, identical 8-entry histories), plus the four parse-time guards, and the same kink on the two-pass auto grid (grid_type = "auto", mpsi = 0), where deck and API agree only if the deck hands growth_rate the problem the marginal solve came from.

Verification outside the test suite

Uniform-current large-aspect-ratio cylinder, external kink m = 2, n = 1, against the analytic
(γτ_A)² = 2(m − nq_a)(nq_a − m + 1)/q_a² with τ_A = R₀√(μ₀ρ)/B₀ (large-aspect-ratio equilibrium, zero pressure, no wall):

ε = a/R₀ q_a γτ_A (GPEC) γτ_A (analytic) ratio solves (incl. marginal)
0.10 1.597 0.43127 0.43442 0.9928 5
0.10 1.296 0.49128 0.49814 0.9862 6
0.10 1.797 0.31449 0.31638 0.9940 5
0.05 1.599 0.43260 0.43336 0.9982 5

The residual is O(ε²), as expected for a cylinder limit of a toroidal code. Other checks: the metric's B² reproduces the equilibrium B² to 2e-5; λ_min(γ²) is monotone over six decades of γ² on the DIII-D n = 2 example; on the TJ fixture the offset λ_min(γ²) − λ_min(0) falls 6.8e-3 → 2.1e-3 → 6.6e-4 → 2.1e-4 → 6.5e-5 for γ² = 1e6 … 1e2 (√10 per decade). On the DIII-D-like example at n = 8 with ρ = 5e19 m⁻³ deuterium: γ = 5.6e4 s⁻¹, γτ_A = 0.023, 9 solves, 21 min on a laptop.

Benchmark against ELITE 5.8 (in progress, 2026-11-13)

image

A coarse VARYPED pressure/current scan of a DIII-D pedestal equilibrium, run through ELITE 5.8 and through growth_rate on the same g-files and profile files, n = 5, 10, 15, 20, 25. Both are normalized the same way (γ over the peak ion diamagnetic frequency, the OMFIT gamws16 convention). GPEC is q-matched to ELITE's edge q per n and runs on ψ_N from 1e-4 to 0.995; ELITE runs on its usual edge window.

  • Stability boundary: the γ/(ω_*/2) = 1 contour of the two codes lies in the same place across the scan, and the most unstable n agrees over most of the map (low n on the current-driven side, n = 20-25 on the pressure-driven side).
  • Growth rates, median of GPEC/ELITE − 1 over the scan: n = 5: −3.9 %, n = 10: −2.5 %, n = 15: −2.0 %, n = 20: −0.9 %, n = 25: −0.8 %.
  • Reference point: per-n differences from −14 % to +0.2 %, largest at n ≈ 12, where ELITE's standard deck is itself unconverged at the ±5-10 % level (resolution scans of ELITE at n = 10-12).

Caveats, stated plainly:

  • This is a comparison of two perpendicular-inertia growth rates. It does not test the model against full ideal MHD.
  • A minority of GPEC points are not yet trusted: solves where the conjugate-point count flagged an ambiguous step or the root search did not converge, and one root-search crash. They are marked in the working figure, and failed root searches are excluded from the maximum over n. Reruns of these points are under way.
  • One class of failure is understood: at high n the unscaled U₂/U₁ solve for the plasma response loses all accuracy when the frame's column magnitudes span more than a hundred decades and the last Gaussian reduction falls on the final step. Scaling the columns first recovers a Hermitian matrix. The fix is commit bce191e30 on this branch; see the next section. The affected scan points have not been rerun with it yet.
  • ELITE numbers on the low-n side (n = 5) are the least reliable part of the reference.

No refinement of the scan has been done yet. The figure is temporary and diagnostic.

Column-scaled plasma response solve (bce191e30)

plasma_response_matrix(u) scales every frame column to unit U₁ norm before forming W = U₂ U₁⁻¹. This leaves W unchanged mathematically and is used by the free-boundary solve, the edge scan and the result assembly.

Verification on the two failing high-n inertial solves:

n γ² (1/s²) λ_min before λ_min after non-Hermiticity of W after
20 1e11 −1.44e21 +3.934471e-2 1.2e-13
25 1.1927e11 −1.42e31 +4.532821e-3 1.6e-12

Regression harness, all 28 cases, eb37c4503 (parent) against bce191e30: 20 cases identical, 8 with rows above their noise floor. No noise floor was changed.

  • Total energies: the leading eigenvalue is unchanged in every case; the full spectra move by at most 6e-9. The growth-rate case gives γ = 2.957092e3 s⁻¹ at both commits.
  • Plasma/vacuum split: tj_n1 leading-mode plasma energy 3.771920e-1 → 3.772271e-1 (0.01 %), vacuum energy by the opposite amount, largest shift over all modes 3.03 absolute; lar_reversed_shear_n1 1.5e-7 on the leading mode and 6e-4 over all modes; diiid_n1, diiid_multi_n, diiid_kinetic_multi_n, solovev_n1 and solovev_kinetic_nuzero at most 6e-9.
  • Perturbed equilibrium: diiid_n1 toroidal torque moves by 1.6e-12 absolute (1.7e-6 relative).

The split moves because the unscaled solve was already inaccurate on the standard ideal examples. Evaluating the same final frame both ways, the non-Hermiticity of W is 1.2e-4 unscaled against 5.6e-9 scaled on the TJ ideal example (cond(U₁) = 4e19), 9.0e-6 against 3.2e-7 on the reversed-shear example, and 8.2e-7 either way on Solovev. The difference between the two W matrices is off-diagonal in the eigenmode basis (TJ: diagonal 1.8e-7, off-diagonal 0.22), so eigenvalues are unaffected to first order and only eigenvector-dependent quantities move.

The Gaussian reductions themselves are rescaled in the next section. The full report below predates this commit (c2d6aff88 against develop); the three commits between it and eb37c4503 touch only the growth-rate path (root search, conjugate-point count, output) and its documentation; they were not compared against develop separately.

Column rescaling after each Gaussian reduction (a0354b860, 9b437442b)

After every Gaussian reduction the solution columns are scaled to unit norm over both blocks, and the scale is stored on the diagonal of that reduction's record so transform_u! undoes it together with the triangular part. W = U₂ U₁⁻¹ is unchanged mathematically. Without it the column norms grow without bound between reductions (5e238 at n = 25) and the high-n solves lose W.

Review. Two independent read-only reviews (numerics; downstream consumers) confirmed the reconstruction algebra, the growth-ratio bookkeeping, the truncation order, W, δW, the normalized eigenfunctions, the conjugate-point count and the Riccati path. Both found one defect, fixed in 9b437442b: the fixed-boundary zero-crossing check blends two stored steps before the reductions are undone, so a reduction between them left the two steps on different column scales and a real crossing could be rejected. The earlier step is now brought to the later step's scales, which restores the pre-rescaling behavior of that check; a unit test fails without the fix and passes with it. The same commit errors on a non-finite column norm and warns when the compounded scales fall below the floating-point range (total growth beyond ~1e308 would underflow the stored solutions nearest the axis on reconstruction).

Does the rescaling change the physics or only the rounding? The resonant lead column at an ideal crossing is picked by largest resonant amplitude, which is not scale-invariant, so the rescaling changes the lead column at every crossing. A test that forces the original lead columns back while keeping the rescaling shows that this is not what moves the numbers:

deck quantity no rescaling rescaling rescaling, original lead columns
Solovev ideal example Re et[1] 7.398220e-3 7.362676e-3 7.567382e-3
Re ep[1] −1.042151e1 −1.042157e1 −1.042127e1
DIII-D-like ideal example Re et[1] 7.979256869e-1 7.979256806e-1 7.979256876e-1

The solve is covariant under column scaling to integration tolerance, not to rounding: any rounding-level difference changes the adaptive step sequence. On the Solovev deck the plasma energy spreads 3e-5 across the three variants and the total energy is a ~1400× cancellation of plasma against vacuum energy, which turns that into −0.5 % / +2.3 %. The 0.48 % move of solovev_n1 is therefore the fixture's own tolerance-level sensitivity. The fixed-boundary count is 0 in all three variants on both decks.

Regression harness, all 28 cases, 8279da47a (parent) / a0354b860 / 9b437442b, exit code 0, no noise floor changed:

  • a0354b860 against the parent: solovev_n1 Re et[1] 7.398220e-3 → 7.362676e-3 (0.48 %), ep[1] and ev[1] by 6e-6 and 3e-6; tj_n1 toroidal torque 5.903723e-15 → 5.905384e-15 (0.03 %); tj_kinetic_calculated ‖ξ_α‖ and ‖dξ_ψ/dψ‖ by 0.013 %; NTV torques and energies in the kinetic cases ~1e-5; ODE step counts +3 on solovev_n1 and tj_n1; ca_left checksum changes (basis-dependent). Growth rate unchanged.
  • 9b437442b against a0354b860: 21 cases unchanged, 7 with rounding-level rows (energies ≤ 2e-8 relative, one ODE step fewer on solovev_n1 and tj_n1, the two tj_kinetic_calculated eigenfunction norms by 0.04 %). The fix commit multiplies each column by the stored inverse norm instead of dividing by the norm, so the applied and recorded scales agree exactly; that last-bit change is amplified by the adaptive stepper as above.

Left as they are on develop and noted for follow-up: the zero-crossing check still blends two steps that differ by the unit-triangular part of a reduction; plot_delta_prime builds its diagonal from forward-integrator coefficients, which are basis-dependent; the harness tracks no fixed-boundary count on the ideal cases.

Regression report

regress --refs origin/develop,feature/growth-rate-inertia   (all 28 cases; the new case's TOML copied into the running checkout; develop fails the new case since it has no [GrowthRate] section)

================================================================
Case: tj_n1 — TJ-analytic equilibrium, n=1, no wall, ideal stability + perturbed equilibrium
================================================================

Regression Report: tj_n1
=============================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
---------------------------------------------------------------------------------------------
Quantity                      origin/develop   feature/growth-rate-inertia  Diff       Status
---------------------------------------------------------------------------------------------
total energy Re(et[1])        4.350277e-01     4.350277e-01                 0.0e+00    OK    
total energy Im(et[1])        -1.001802e-14    -1.001802e-14                0.0e+00    OK    
plasma energy Re(ep[1])       3.772270e-01     3.772270e-01                 0.0e+00    OK    
vacuum energy Re(ev[1])       5.780072e-02     5.780072e-02                 0.0e+00    OK    
vacuum matrix min eigenvalue  3.082359e-02     3.082359e-02                 0.0e+00    OK    
plasma energy (all)           [32 elem]        [32 elem]                    0.0e+00    OK    
vacuum energy (all)           [32 elem]        [32 elem]                    0.0e+00    OK    
total energy (all)            [32 elem]        [32 elem]                    0.0e+00    OK    
ODE steps (saved)             295              295                          0.0e+00    OK    
ODE steps (total)             293              293                          0.0e+00    OK    
q0                            1.599966e+00     1.599966e+00                 0.0e+00    OK    
q95                           2.980037e+00     2.980037e+00                 0.0e+00    OK    
beta_t                        6.656722e-04     6.656722e-04                 0.0e+00    OK    
beta_n                        4.156626e-01     4.156626e-01                 0.0e+00    OK    
# singular surfaces           2                2                            0.0e+00    OK    
singular psi locations        [2 elem]         [2 elem]                     0.0e+00    OK    
singular q values             [2 elem]         [2 elem]                     0.0e+00    OK    
mpert                         32               32                           0.0e+00    OK    
npert                         1                1                            0.0e+00    OK    
q profile (checksum)          c1ce6c39d2ce...  c1ce6c39d2ce...              identical  OK    
resonant area-weighted field  [2 elem]         [2 elem]                     0.0e+00    OK    
pressure profile (checksum)   2d937bf30331...  2d937bf30331...              identical  OK    
island half-width             [2 elem]         [2 elem]                     0.0e+00    OK    
Chirikov parameter            [2 elem]         [2 elem]                     0.0e+00    OK    
toroidal torque [N·m]         6.396822e-14     6.396822e-14                 0.0e+00    OK    
Runtime (s)                   84.4s            81.7s                                   --    
=============================================================================================
Summary: 25 unchanged


================================================================
Case: diiid_slayer_n1_toroidal — DIII-D-like H-mode, n=1, SLAYER with the toroidal critical-Δ offset (D_c_type = toroidal)
================================================================

Regression Report: diiid_slayer_n1_toroidal
================================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
------------------------------------------------------------------------------------------------
Quantity                            origin/develop  feature/growth-rate-inertia  Diff     Status
------------------------------------------------------------------------------------------------
SLAYER resistive interchange D_R    [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER critical-Δ offset D_c        [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER Q_root [2/1,3/1,4/1]         [3 elem]        [3 elem]                     0.0e+00  OK    
SLAYER γ in 1/s [2/1,3/1,4/1]       [3 elem]        [3 elem]                     0.0e+00  OK    
SLAYER no_root flags [2/1,3/1,4/1]  [3 elem]        [3 elem]                     0.0e+00  OK    
Runtime (s)                         133.7s          79.9s                                 --    
================================================================================================
Summary: 5 unchanged


================================================================
Case: gal_resistive_diiid_galinner — DIII-D-like, n=1, Galerkin Δ′ matched with the Galerkin inner-layer backend
================================================================

Regression Report: gal_resistive_diiid_galinner
=====================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-------------------------------------------------------------------------------------
Quantity                 origin/develop  feature/growth-rate-inertia  Diff     Status
-------------------------------------------------------------------------------------
gal # singular surfaces  4               4                            0.0e+00  OK    
gal singular q values    [4 elem]        [4 elem]                     0.0e+00  OK    
gal PEST3 Δ diagonal     [4 elem]        [4 elem]                     0.0e+00  OK    
||gal Δ′ matrix||        2.302031e+03    2.302031e+03                 0.0e+00  OK    
||gal Δ_coil block||     1.581715e+01    1.581715e+01                 0.0e+00  OK    
gal D_I per surface      [4 elem]        [4 elem]                     0.0e+00  OK    
gal α per surface        [4 elem]        [4 elem]                     0.0e+00  OK    
||gal inner-layer Δ||    1.474565e+06    1.474565e+06                 0.0e+00  OK    
||gal match cout||       3.048397e-02    3.048397e-02                 0.0e+00  OK    
||gal match bpen||       1.206218e+00    1.206218e+00                 0.0e+00  OK    
gal match residual       1.655783e-16    1.655783e-16                 0.0e+00  OK    
Runtime (s)              18.2s           19.0s                                 --    
=====================================================================================
Summary: 11 unchanged


================================================================
Case: ggj_ray_q500i — GGJ inner-layer :ray backend, q=4 surface, Q = 500i (beyond :galerkin)
================================================================

Regression Report: ggj_ray_q500i
================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
--------------------------------------------------------------------------------
Quantity            origin/develop  feature/growth-rate-inertia  Diff     Status
--------------------------------------------------------------------------------
Re(Δ₊ interchange)  2.472006e+00    2.472006e+00                 0.0e+00  OK    
Im(Δ₊ interchange)  1.335402e+01    1.335402e+01                 0.0e+00  OK    
Re(Δ₋ tearing)      1.374966e-01    1.374966e-01                 0.0e+00  OK    
Im(Δ₋ tearing)      7.427542e-01    7.427542e-01                 0.0e+00  OK    
Runtime (s)         7.5s            7.4s                                  --    
================================================================================
Summary: 4 unchanged


================================================================
Case: solovev_kinetic_nuzero — Solovev, n=1, kinetic = 'KineticForces', nutype=zero (collisionless energy integral), 50/50 D-T multi-ion kinetic matrices, mode-converged band (delta_m=8, mpert=32). Reuses the nuzero test fixture via override; pressure-consistent kinetic.dat.
================================================================

Regression Report: solovev_kinetic_nuzero
=====================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-------------------------------------------------------------------------------------
Quantity                 origin/develop  feature/growth-rate-inertia  Diff     Status
-------------------------------------------------------------------------------------
vacuum energy Re(ev[1])  1.054364e+01    1.054364e+01                 0.0e+00  OK    
q0                       1.900003e+00    1.900003e+00                 0.0e+00  OK    
total energy Re(et[1])   2.316033e+00    2.316033e+00                 0.0e+00  OK    
total energy Im(et[1])   -1.555432e+00   -1.555432e+00                0.0e+00  OK    
plasma energy Re(ep[1])  -8.227611e+00   -8.227611e+00                0.0e+00  OK    
ODE steps (saved)        619             619                          0.0e+00  OK    
# singular surfaces      2               2                            0.0e+00  OK    
singular psi locations   [2 elem]        [2 elem]                     0.0e+00  OK    
total energy (all)       [32 elem]       [32 elem]                    0.0e+00  OK    
ODE steps (total)        618             618                          0.0e+00  OK    
mpert                    32              32                           0.0e+00  OK    
q95                      3.147422e+00    3.147422e+00                 0.0e+00  OK    
Runtime (s)              24.3s           18.9s                                 --    
singular q values        [2 elem]        [2 elem]                     0.0e+00  OK    
npert                    1               1                            0.0e+00  OK    
=====================================================================================
Summary: 14 unchanged


================================================================
Case: diiid_error_field — DIII-D-like error-field assessment, n=1: ErrorFields stage with the NTV-limited correction couplings
================================================================

Regression Report: diiid_error_field
=============================================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-------------------------------------------------------------------------------------------------------------
Quantity                                         origin/develop  feature/growth-rate-inertia  Diff     Status
-------------------------------------------------------------------------------------------------------------
EFC overlap per kAt                              [1 elem]        [1 elem]                     0.0e+00  OK    
EFC whole-field NTV torque per kAt² [N·m]        [1 elem]        [1 elem]                     0.0e+00  OK    
EFC residual-field NTV torque per kAt² [N·m]     [1 elem]        [1 elem]                     0.0e+00  OK    
EFC reference rotation ω_ref [rad/s]             [1 elem]        [1 elem]                     0.0e+00  OK    
EFC rotation-scan shifts [rad/s]                 [9 elem]        [9 elem]                     0.0e+00  OK    
EFC residual torque against rotation [N·m/kAt²]  [9 elem]        [9 elem]                     0.0e+00  OK    
dominant-coupling singular values                [3 elem]        [3 elem]                     0.0e+00  OK    
|delta_nominal| of coil set 1                    1.414981e-06    1.414981e-06                 0.0e+00  OK    
|Σ δ_as_designed| of the error-field coils       3.222366e-18    3.222366e-18                 0.0e+00  OK    
Monte Carlo mean |δ| of the error-field coils    4.701440e-05    4.701440e-05                 0.0e+00  OK    
Runtime (s)                                      71.6s           68.3s                                 --    
=============================================================================================================
Summary: 10 unchanged


================================================================
Case: diiid_slayer_n1 — DIII-D-like H-mode equilibrium, n=1, SLAYER tearing-mode analysis (uncoupled per-surface, AMR, validity-gated)
================================================================

Regression Report: diiid_slayer_n1
=====================================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-----------------------------------------------------------------------------------------------------
Quantity                                 origin/develop  feature/growth-rate-inertia  Diff     Status
-----------------------------------------------------------------------------------------------------
SLAYER surface indices                   [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER poloidal m                        [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER toroidal n                        [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER minor radius rs                   [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER r-based shear                     [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER Lundquist S                       [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER D_norm                            [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER P_perp                            [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER tauk                              [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER iota_e                            [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER Q_e                               [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER Q_i                               [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER toroidal critical-Δ factor D_geo  [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER Q_root [2/1,3/1,4/1]              [3 elem]        [3 elem]                     0.0e+00  OK    
SLAYER ω in rad/s [2/1,3/1,4/1]          [3 elem]        [3 elem]                     0.0e+00  OK    
SLAYER γ in 1/s [2/1,3/1,4/1]            [3 elem]        [3 elem]                     0.0e+00  OK    
SLAYER no_root flags [2/1,3/1,4/1]       [3 elem]        [3 elem]                     0.0e+00  OK    
CRF b_r/B_φ crit [2/1,3/1,4/1]           [3 elem]        [3 elem]                     0.0e+00  OK    
CRF Q_peak [2/1,3/1,4/1]                 [3 elem]        [3 elem]                     0.0e+00  OK    
CRF Q0 [2/1,3/1,4/1]                     [3 elem]        [3 elem]                     0.0e+00  OK    
CRF b_r crit in tesla [2/1,3/1,4/1]      [3 elem]        [3 elem]                     0.0e+00  OK    
SLAYER enabled flag                      1               1                            0.0e+00  OK    
Runtime (s)                              40.8s           43.4s                                 --    
=====================================================================================================
Summary: 22 unchanged


================================================================
Case: diiid_truncation_n1 — DIII-D-like, n=1, psihigh=0.9999 with the resistive-layer overlap cap binding (truncation only)
================================================================

Regression Report: diiid_truncation_n1
===================================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
---------------------------------------------------------------------------------------------------
Quantity                               origin/develop  feature/growth-rate-inertia  Diff     Status
---------------------------------------------------------------------------------------------------
layer-overlap cap applied              1               1                            0.0e+00  OK    
layer-overlap psilim                   9.985081e-01    9.985081e-01                 0.0e+00  OK    
first overlapping surface index        7               7                            0.0e+00  OK    
diffusive-resistive layer widths (Δψ)  [7 elem]        [7 elem]                     0.0e+00  OK    
qlim                                   6.200000e+00    6.200000e+00                 0.0e+00  OK    
psilim                                 9.949767e-01    9.949767e-01                 0.0e+00  OK    
mpert                                  37              37                           0.0e+00  OK    
# singular surfaces                    5               5                            0.0e+00  OK    
singular q values                      [5 elem]        [5 elem]                     0.0e+00  OK    
singular psi locations                 [5 elem]        [5 elem]                     0.0e+00  OK    
plasma energy Re(ep[1])                -1.377409e+00   -1.377409e+00                0.0e+00  OK    
total energy Re(et[1])                 7.978611e-01    7.978611e-01                 0.0e+00  OK    
Runtime (s)                            16.4s           16.1s                                 --    
===================================================================================================
Summary: 12 unchanged


================================================================
Case: tj_kinetic_calculated — TJ-analytic, n=1, no wall, kinetic = 'KineticForces', 50/50 D-T multi-ion kinetic matrices; pressure-consistent kinetic.dat (P₀≈784 Pa).
================================================================

Regression Report: tj_kinetic_calculated
===============================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-----------------------------------------------------------------------------------------------
Quantity                           origin/develop  feature/growth-rate-inertia  Diff     Status
-----------------------------------------------------------------------------------------------
vacuum energy Re(ev[1])            5.899845e-02    5.899845e-02                 0.0e+00  OK    
q0                                 1.599966e+00    1.599966e+00                 0.0e+00  OK    
NTV fgar quadrature torque [N·m]   2.035589e-03    2.035589e-03                 0.0e+00  OK    
||xi_clebsch_alpha||               3.181999e-01    3.181999e-01                 0.0e+00  OK    
total energy Re(et[1])             4.380835e-01    4.380835e-01                 0.0e+00  OK    
total energy Im(et[1])             -1.977414e-03   -1.977414e-03                0.0e+00  OK    
plasma energy Re(ep[1])            3.790851e-01    3.790851e-01                 0.0e+00  OK    
ODE steps (saved)                  141             141                          0.0e+00  OK    
# singular surfaces                2               2                            0.0e+00  OK    
boundary-response torque [N·m]     2.046586e-03    2.046586e-03                 0.0e+00  OK    
singular psi locations             [2 elem]        [2 elem]                     0.0e+00  OK    
total energy (all)                 [32 elem]       [32 elem]                    0.0e+00  OK    
ODE steps (total)                  140             140                          0.0e+00  OK    
mpert                              32              32                           0.0e+00  OK    
q95                                2.980037e+00    2.980037e+00                 0.0e+00  OK    
||Jb_theta_reg||                   5.735159e-02    5.735159e-02                 0.0e+00  OK    
Runtime (s)                        10.9s           7.9s                                  --    
T_xe applied-forcing torque [N·m]  2.046586e-03    2.046586e-03                 0.0e+00  OK    
||dxi_clebsch_psi/dpsi||           1.989670e+00    1.989670e+00                 0.0e+00  OK    
singular q values                  [2 elem]        [2 elem]                     0.0e+00  OK    
npert                              1               1                            0.0e+00  OK    
===============================================================================================
Summary: 20 unchanged


================================================================
Case: diiid_slayer_n1_coupled — DIII-D-like H-mode equilibrium, n=1, SLAYER coupled multi-surface determinant with kinetic-file E×B rotation (reuses the n=1 deck via overrides)
================================================================

Regression Report: diiid_slayer_n1_coupled
=========================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-----------------------------------------------------------------------------------------
Quantity                     origin/develop  feature/growth-rate-inertia  Diff     Status
-----------------------------------------------------------------------------------------
SLAYER tauk                  [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER q_shift               [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER coupled Q_root        [1 elem]        [1 elem]                     0.0e+00  OK    
SLAYER coupled ω             [1 elem]        [1 elem]                     0.0e+00  OK    
SLAYER coupled γ             [1 elem]        [1 elem]                     0.0e+00  OK    
SLAYER coupled no_root flag  [1 elem]        [1 elem]                     0.0e+00  OK    
SLAYER enabled flag          1               1                            0.0e+00  OK    
Runtime (s)                  39.2s           41.8s                                 --    
=========================================================================================
Summary: 7 unchanged


================================================================
Case: gal_resistive_diiid — DIII-D-like, n=1, RDCON outer-region Galerkin Δ′ with rpec coil columns (delta_coil)
================================================================

Regression Report: gal_resistive_diiid
=====================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-------------------------------------------------------------------------------------
Quantity                 origin/develop  feature/growth-rate-inertia  Diff     Status
-------------------------------------------------------------------------------------
gal # singular surfaces  4               4                            0.0e+00  OK    
gal singular q values    [4 elem]        [4 elem]                     0.0e+00  OK    
gal PEST3 Δ diagonal     [4 elem]        [4 elem]                     0.0e+00  OK    
||gal Δ′ matrix||        2.302031e+03    2.302031e+03                 0.0e+00  OK    
||gal Δ_coil block||     1.581715e+01    1.581715e+01                 0.0e+00  OK    
gal D_I per surface      [4 elem]        [4 elem]                     0.0e+00  OK    
gal α per surface        [4 elem]        [4 elem]                     0.0e+00  OK    
||gal inner-layer Δ||    1.474577e+06    1.474577e+06                 0.0e+00  OK    
||gal match cout||       3.048397e-02    3.048397e-02                 0.0e+00  OK    
||gal match bpen||       1.206218e+00    1.206218e+00                 0.0e+00  OK    
gal match residual       1.434417e-16    1.434417e-16                 0.0e+00  OK    
Runtime (s)              5.6s            5.8s                                  --    
=====================================================================================
Summary: 11 unchanged


================================================================
Case: solovev_n1 — Solovev analytical equilibrium, n=1, ideal stability
================================================================

Regression Report: solovev_n1
=============================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
---------------------------------------------------------------------------------------------
Quantity                      origin/develop   feature/growth-rate-inertia  Diff       Status
---------------------------------------------------------------------------------------------
total energy Re(et[1])        7.361643e-03     7.361643e-03                 0.0e+00    OK    
total energy Im(et[1])        2.474573e-09     2.474573e-09                 0.0e+00    OK    
plasma energy Re(ep[1])       -1.042158e+01    -1.042158e+01                0.0e+00    OK    
vacuum energy Re(ev[1])       1.042894e+01     1.042894e+01                 0.0e+00    OK    
vacuum matrix min eigenvalue  2.170703e+00     2.170703e+00                 0.0e+00    OK    
plasma energy (all)           [32 elem]        [32 elem]                    0.0e+00    OK    
vacuum energy (all)           [32 elem]        [32 elem]                    0.0e+00    OK    
total energy (all)            [32 elem]        [32 elem]                    0.0e+00    OK    
ODE steps (saved)             381              381                          0.0e+00    OK    
ODE steps (total)             379              379                          0.0e+00    OK    
q0                            1.900006e+00     1.900006e+00                 0.0e+00    OK    
q95                           3.147422e+00     3.147422e+00                 0.0e+00    OK    
beta_t                        4.621027e-02     4.621027e-02                 0.0e+00    OK    
beta_n                        3.217775e+00     3.217775e+00                 0.0e+00    OK    
# singular surfaces           2                2                            0.0e+00    OK    
singular psi locations        [2 elem]         [2 elem]                     0.0e+00    OK    
singular q values             [2 elem]         [2 elem]                     0.0e+00    OK    
mpert                         32               32                           0.0e+00    OK    
npert                         1                1                            0.0e+00    OK    
q profile (checksum)          1f70d9bad4ed...  1f70d9bad4ed...              identical  OK    
pressure profile (checksum)   af7dbf7be9e7...  af7dbf7be9e7...              identical  OK    
ca_left (checksum)            23c43e08a244...  23c43e08a244...              identical  OK    
Runtime (s)                   6.3s             6.7s                                    --    
=============================================================================================
Summary: 22 unchanged


================================================================
Case: ggj_reference — GGJ inner-layer Galerkin solver, Glasser & Wang 2020 Eq. 55 with physical τ_A, τ_R, 1 kHz rotation (Q ≈ 0.22i)
================================================================

Regression Report: ggj_reference
================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
--------------------------------------------------------------------------------
Quantity            origin/develop  feature/growth-rate-inertia  Diff     Status
--------------------------------------------------------------------------------
Re(Δ₊ interchange)  1.225728e+03    1.225728e+03                 0.0e+00  OK    
Im(Δ₊ interchange)  2.064823e+04    2.064823e+04                 0.0e+00  OK    
Re(Δ₋ tearing)      -5.345663e+00   -5.345663e+00                0.0e+00  OK    
Im(Δ₋ tearing)      2.592091e+01    2.592091e+01                 0.0e+00  OK    
Runtime (s)         0.0s            0.0s                                  --    
================================================================================
Summary: 4 unchanged


================================================================
Case: tj_kinetic_ntv — TJ-analytic equilibrium, n=1, no wall, ideal + PE + NTV torque quadrature
================================================================

Regression Report: tj_kinetic_ntv
=======================================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-------------------------------------------------------------------------------------------------------
Quantity                                   origin/develop  feature/growth-rate-inertia  Diff     Status
-------------------------------------------------------------------------------------------------------
NTV torque fgar [Re, Im]                   [1 elem]        [1 elem]                     0.0e+00  OK    
NTV ψ quadrature evaluations               75              75                           0.0e+00  OK    
fluid CGL energy fcgl [J]                  5.446427e-04    5.446427e-04                 0.0e+00  OK    
root-area-weighted total energy Re(et[1])  4.350277e-01    4.350277e-01                 0.0e+00  OK    
# singular surfaces                        2               2                            0.0e+00  OK    
singular psi locations                     [2 elem]        [2 elem]                     0.0e+00  OK    
q0                                         1.599966e+00    1.599966e+00                 0.0e+00  OK    
Runtime (s)                                2.4s            2.5s                                  --    
=======================================================================================================
Summary: 7 unchanged


================================================================
Case: solovev_multi_n — Solovev analytical equilibrium, multi-n, ideal stability
================================================================

Regression Report: solovev_multi_n
=============================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
---------------------------------------------------------------------------------------------
Quantity                      origin/develop   feature/growth-rate-inertia  Diff       Status
---------------------------------------------------------------------------------------------
total energy Re(et[1])        -2.134718e+00    -2.134718e+00                0.0e+00    OK    
total energy Im(et[1])        1.050131e-10     1.050131e-10                 0.0e+00    OK    
vacuum matrix min eigenvalue  1.730832e+00     1.730832e+00                 0.0e+00    OK    
plasma energy (all)           [70 elem]        [70 elem]                    0.0e+00    OK    
vacuum energy (all)           [70 elem]        [70 elem]                    0.0e+00    OK    
total energy (all)            [70 elem]        [70 elem]                    0.0e+00    OK    
ODE steps (saved)             56               56                           0.0e+00    OK    
ODE steps (total)             1038             1038                         0.0e+00    OK    
q0                            1.900006e+00     1.900006e+00                 0.0e+00    OK    
q95                           3.147422e+00     3.147422e+00                 0.0e+00    OK    
# singular surfaces           3                3                            0.0e+00    OK    
singular psi locations        [3 elem]         [3 elem]                     0.0e+00    OK    
mpert                         35               35                           0.0e+00    OK    
npert                         2                2                            0.0e+00    OK    
q profile (checksum)          1f70d9bad4ed...  1f70d9bad4ed...              identical  OK    
Runtime (s)                   14.5s            15.4s                                   --    
=============================================================================================
Summary: 15 unchanged


================================================================
Case: diiid_n1 — DIII-D-like equilibrium, n=1, ideal + perturbed equilibrium
================================================================

Regression Report: diiid_n1
=============================================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-------------------------------------------------------------------------------------------------------------
Quantity                                      origin/develop   feature/growth-rate-inertia  Diff       Status
-------------------------------------------------------------------------------------------------------------
total energy Re(et[1])                        7.979262e-01     7.979262e-01                 0.0e+00    OK    
total energy Im(et[1])                        1.122437e-06     1.122437e-06                 0.0e+00    OK    
plasma energy Re(ep[1])                       -1.377302e+00    -1.377302e+00                0.0e+00    OK    
vacuum energy Re(ev[1])                       2.175228e+00     2.175228e+00                 0.0e+00    OK    
vacuum matrix min eigenvalue                  1.873975e-01     1.873975e-01                 0.0e+00    OK    
plasma energy (all)                           [35 elem]        [35 elem]                    0.0e+00    OK    
vacuum energy (all)                           [35 elem]        [35 elem]                    0.0e+00    OK    
total energy (all)                            [35 elem]        [35 elem]                    0.0e+00    OK    
ODE steps (saved)                             1211             1211                         0.0e+00    OK    
ODE steps (total)                             1206             1206                         0.0e+00    OK    
q0                                            1.204212e+00     1.204212e+00                 0.0e+00    OK    
q95                                           4.781723e+00     4.781723e+00                 0.0e+00    OK    
beta_t                                        1.327024e-02     1.327024e-02                 0.0e+00    OK    
beta_n                                        1.372511e+00     1.372511e+00                 0.0e+00    OK    
internal inductance li1                       8.842269e-01     8.842269e-01                 0.0e+00    OK    
internal inductance li2                       7.080757e-01     7.080757e-01                 0.0e+00    OK    
internal inductance li3                       7.304341e-01     7.304341e-01                 0.0e+00    OK    
poloidal beta betap1                          6.680739e-01     6.680739e-01                 0.0e+00    OK    
poloidal beta betap2                          5.349836e-01     5.349836e-01                 0.0e+00    OK    
poloidal beta betap3                          5.518764e-01     5.518764e-01                 0.0e+00    OK    
# singular surfaces                           5                5                            0.0e+00    OK    
singular psi locations                        [5 elem]         [5 elem]                     0.0e+00    OK    
singular q values                             [5 elem]         [5 elem]                     0.0e+00    OK    
current beta betaj                            4.236480e-01     4.236480e-01                 0.0e+00    OK    
plasma volume                                 1.829472e+01     1.829472e+01                 0.0e+00    OK    
plasma current                                1.152130e+00     1.152130e+00                 0.0e+00    OK    
mpert                                         35               35                           0.0e+00    OK    
npert                                         1                1                            0.0e+00    OK    
toroidal field bt0                            2.006573e+00     2.006573e+00                 0.0e+00    OK    
wall field bwall                              3.880145e-01     3.880145e-01                 0.0e+00    OK    
aspect ratio                                  2.845746e+00     2.845746e+00                 0.0e+00    OK    
elongation kappa                              1.708322e+00     1.708322e+00                 0.0e+00    OK    
q profile (checksum)                          ce38cd35fd0e...  ce38cd35fd0e...              identical  OK    
pressure profile (checksum)                   6ab81268d66d...  6ab81268d66d...              identical  OK    
Mercier D_I profile (checksum)                c3e460e14c0d...  c3e460e14c0d...              identical  OK    
resistive interchange D_R profile (checksum)  a70d25e1ff98...  a70d25e1ff98...              identical  OK    
ballooning Delta' profile (checksum)          cdebb3000867...  cdebb3000867...              identical  OK    
island half-widths                            [5 elem]         [5 elem]                     0.0e+00    OK    
Chirikov parameter                            [5 elem]         [5 elem]                     0.0e+00    OK    
||resonant area-weighted field||              5.286404e-04     5.286404e-04                 0.0e+00    OK    
dominant-coupling singular values             [3 elem]         [3 elem]                     0.0e+00    OK    
|forcing overlap with dominant mode|          1.419632e-04     1.419632e-04                 0.0e+00    OK    
|delta_nominal| of coil set 1                 7.074907e-05     7.074907e-05                 0.0e+00    OK    
||ddelta/d(shift)|| over coil sets            1.246839e-04     1.246839e-04                 0.0e+00    OK    
||ddelta/d(tilt)|| over coil sets             4.175435e-07     4.175435e-07                 0.0e+00    OK    
PE plasma energy                              3.321081e+00     3.321081e+00                 0.0e+00    OK    
PE vacuum energy                              3.174319e+00     3.174319e+00                 0.0e+00    OK    
PE surface energy                             5.521988e+00     5.521988e+00                 0.0e+00    OK    
PE toroidal torque                            9.083768e-07     9.083768e-07                 0.0e+00    OK    
NTV torque FGAR [N·m]                         5.539324e-01     5.539324e-01                 0.0e+00    OK    
NTV kinetic energy dW FGAR [J]                8.224183e-02     8.224183e-02                 0.0e+00    OK    
Runtime (s)                                   15.5s            15.5s                                   --    
||forcing b~|| (root-area-weighted)           4.683171e-04     4.683171e-04                 0.0e+00    OK    
resonant area-weighted field b^r              [5 elem]         [5 elem]                     0.0e+00    OK    
=============================================================================================================
Summary: 53 unchanged


================================================================
Case: tj_kinetic_ntv_rates — TJ-analytic equilibrium, n=1, no wall, NTV torque with supplied rates-v1 collision rates (reuses the TJ NTV deck via overrides)
================================================================

Regression Report: tj_kinetic_ntv_rates
==========================================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
----------------------------------------------------------------------------------------------------------
Quantity                                      origin/develop  feature/growth-rate-inertia  Diff     Status
----------------------------------------------------------------------------------------------------------
NTV torque fgar with supplied rates [Re, Im]  [1 elem]        [1 elem]                     0.0e+00  OK    
NTV ψ quadrature evaluations                  75              75                           0.0e+00  OK    
Runtime (s)                                   2.5s            2.6s                                  --    
==========================================================================================================
Summary: 2 unchanged


================================================================
Case: diiid_slayer_n1_chi_perp_e — DIII-D-like H-mode equilibrium, n=1, SLAYER tearing-mode analysis with P_perp_model = chi_perp_e
================================================================

Regression Report: diiid_slayer_n1_chi_perp_e
=====================================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-----------------------------------------------------------------------------------------------------
Quantity                                 origin/develop  feature/growth-rate-inertia  Diff     Status
-----------------------------------------------------------------------------------------------------
SLAYER surface indices                   [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER poloidal m                        [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER toroidal n                        [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER minor radius rs                   [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER r-based shear                     [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER Lundquist S                       [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER D_norm                            [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER P_perp                            [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER tauk                              [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER iota_e                            [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER Q_e                               [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER Q_i                               [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER toroidal critical-Δ factor D_geo  [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER Q_root [2/1,3/1,4/1]              [3 elem]        [3 elem]                     0.0e+00  OK    
SLAYER ω in rad/s [2/1,3/1,4/1]          [3 elem]        [3 elem]                     0.0e+00  OK    
SLAYER γ in 1/s [2/1,3/1,4/1]            [3 elem]        [3 elem]                     0.0e+00  OK    
SLAYER no_root flags [2/1,3/1,4/1]       [3 elem]        [3 elem]                     0.0e+00  OK    
CRF b_r/B_φ crit [2/1,3/1,4/1]           [3 elem]        [3 elem]                     0.0e+00  OK    
CRF Q_peak [2/1,3/1,4/1]                 [3 elem]        [3 elem]                     0.0e+00  OK    
CRF Q0 [2/1,3/1,4/1]                     [3 elem]        [3 elem]                     0.0e+00  OK    
CRF b_r crit in tesla [2/1,3/1,4/1]      [3 elem]        [3 elem]                     0.0e+00  OK    
SLAYER enabled flag                      1               1                            0.0e+00  OK    
Runtime (s)                              44.9s           45.7s                                 --    
=====================================================================================================
Summary: 22 unchanged


================================================================
Case: diiid_n1_riccati — DIII-D-like equilibrium, n=1, Riccati integrator Δ' matrix
================================================================

Regression Report: diiid_n1_riccati
===========================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-------------------------------------------------------------------------------------------
Quantity                       origin/develop  feature/growth-rate-inertia  Diff     Status
-------------------------------------------------------------------------------------------
delta prime (BVP diagonal)     [5 elem]        [5 elem]                     0.0e+00  OK    
delta prime (raw side-major)   [10 elem]       [10 elem]                    0.0e+00  OK    
edge coil response delta_coil  [10 elem]       [10 elem]                    0.0e+00  OK    
total energy Re(et[1])         8.031978e-01    8.031978e-01                 0.0e+00  OK    
plasma energy Re(ep[1])        -1.368533e+00   -1.368533e+00                0.0e+00  OK    
vacuum energy Re(ev[1])        2.171731e+00    2.171731e+00                 0.0e+00  OK    
total energy (all)             [35 elem]       [35 elem]                    0.0e+00  OK    
# singular surfaces            5               5                            0.0e+00  OK    
singular psi locations         [5 elem]        [5 elem]                     0.0e+00  OK    
singular q values              [5 elem]        [5 elem]                     0.0e+00  OK    
ODE steps (saved)              91              91                           0.0e+00  OK    
ODE steps (total)              2657            2657                         0.0e+00  OK    
mpert                          35              35                           0.0e+00  OK    
npert                          1               1                            0.0e+00  OK    
q0                             1.204212e+00    1.204212e+00                 0.0e+00  OK    
q95                            4.781723e+00    4.781723e+00                 0.0e+00  OK    
beta_n                         1.372511e+00    1.372511e+00                 0.0e+00  OK    
Runtime (s)                    16.6s           17.1s                                 --    
===========================================================================================
Summary: 17 unchanged


================================================================
Case: lar_reversed_shear_growth_rate_n1 — Large-aspect-ratio reversed-shear equilibrium, n=1, ELITE-style ideal growth rate with uniform deuterium density
================================================================

Regression Report: lar_reversed_shear_growth_rate_n1
===================================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07) (FAILED)
       env: environment unknown (cached before fingerprinting)
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
  Ref 1 error: in expression starting at /var/folders/wz/x_qd5n8d62s0qj0_kcyyqf780000gn/T/jl_mvGzyfyfe7.jl:5
---------------------------------------------------------------------------------------------------
Quantity                                  origin/develop  feature/growth-rate-inertia  Diff  Status
---------------------------------------------------------------------------------------------------
growth rate gamma (1/s)                   FAILED          2.957092e+03                       FAILED
growth rate squared gamma^2 (1/s^2)       FAILED          8.744392e+06                       FAILED
root-find gamma^2 history                 FAILED          [9 elem]                           FAILED
root-find lambda_min history              FAILED          [9 elem]                           FAILED
root-find nzero history (integer counts)  FAILED          [9 elem]                           FAILED
total energy Re(et[1])                    FAILED          -2.263762e-02                      FAILED
# singular surfaces                       FAILED          2                                  FAILED
Runtime (s)                               FAILED          13.4s                              --    
===================================================================================================
Summary: 7 missing/N/A


================================================================
Case: lar_ggj_tearing_n1 — LAR TJ equilibrium, n=1, GGJ tearing growth rates per surface from the Galerkin Δ′ (reuses the LAR ideal-match deck via overrides)
================================================================

Regression Report: lar_ggj_tearing_n1
===============================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-------------------------------------------------------------------------------
Quantity           origin/develop  feature/growth-rate-inertia  Diff     Status
-------------------------------------------------------------------------------
GGJ γ in 1/s       [2 elem]        [2 elem]                     0.0e+00  OK    
GGJ ω in rad/s     [2 elem]        [2 elem]                     0.0e+00  OK    
GGJ no_root flags  [2 elem]        [2 elem]                     0.0e+00  OK    
===============================================================================
Summary: 3 unchanged


================================================================
Case: diiid_multi_n — DIII-D-like equilibrium, n = 1, 2, ideal + perturbed equilibrium
================================================================

Regression Report: diiid_multi_n
=============================================================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-----------------------------------------------------------------------------------------------------------------------------
Quantity                                                      origin/develop   feature/growth-rate-inertia  Diff       Status
-----------------------------------------------------------------------------------------------------------------------------
total energy Re(et[1])                                        -5.750944e-01    -5.750944e-01                0.0e+00    OK    
total energy (all)                                            [58 elem]        [58 elem]                    0.0e+00    OK    
ODE steps (saved)                                             897              897                          0.0e+00    OK    
mpert                                                         29               29                           0.0e+00    OK    
npert                                                         2                2                            0.0e+00    OK    
toroidal mode label of each mode index (checksum)             8d8d42b8832d...  8d8d42b8832d...              identical  OK    
permeability diagonal P~_ii (all n blocks)                    [58 elem]        [58 elem]                    0.0e+00    OK    
forcing b~ (root-area-weighted), n = 1, 2                     [58 elem]        [58 elem]                    0.0e+00    OK    
response b~ (root-area-weighted), n = 1, 2                    [58 elem]        [58 elem]                    0.0e+00    OK    
PE plasma energy (sum over n)                                 2.936508e+00     2.936508e+00                 0.0e+00    OK    
PE vacuum energy (sum over n)                                 3.113946e+00     3.113946e+00                 0.0e+00    OK    
PE surface energy (sum over n)                                3.523287e+00     3.523287e+00                 0.0e+00    OK    
PE toroidal torque sum_n -2n Im(Phi_n' Lambda_nn^-1 Phi_n)/4  2.148574e-08     2.148574e-08                 0.0e+00    OK    
resonant n per row (rows sorted by psi across n)              [7 elem]         [7 elem]                     0.0e+00    OK    
resonant m per row                                            [7 elem]         [7 elem]                     0.0e+00    OK    
resonant area-weighted field b^r per row                      [7 elem]         [7 elem]                     0.0e+00    OK    
island half-widths per row                                    [7 elem]         [7 elem]                     0.0e+00    OK    
Chirikov parameter per row                                    [7 elem]         [7 elem]                     0.0e+00    OK    
forcing-driven Delta' per row                                 [7 elem]         [7 elem]                     0.0e+00    OK    
Runtime (s)                                                   7.1s             7.9s                                    --    
=============================================================================================================================
Summary: 19 unchanged


================================================================
Case: diiid_slayer_n1_winding — DIII-D-like H-mode equilibrium, n=1, SLAYER tearing-mode analysis with root_classification = winding
================================================================

Regression Report: diiid_slayer_n1_winding
=====================================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-----------------------------------------------------------------------------------------------------
Quantity                                 origin/develop  feature/growth-rate-inertia  Diff     Status
-----------------------------------------------------------------------------------------------------
SLAYER surface indices                   [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER poloidal m                        [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER toroidal n                        [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER minor radius rs                   [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER r-based shear                     [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER Lundquist S                       [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER D_norm                            [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER P_perp                            [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER tauk                              [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER iota_e                            [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER Q_e                               [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER Q_i                               [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER toroidal critical-Δ factor D_geo  [6 elem]        [6 elem]                     0.0e+00  OK    
SLAYER Q_root [2/1,3/1,4/1]              [3 elem]        [3 elem]                     0.0e+00  OK    
SLAYER ω in rad/s [2/1,3/1,4/1]          [3 elem]        [3 elem]                     0.0e+00  OK    
SLAYER γ in 1/s [2/1,3/1,4/1]            [3 elem]        [3 elem]                     0.0e+00  OK    
SLAYER no_root flags [2/1,3/1,4/1]       [3 elem]        [3 elem]                     0.0e+00  OK    
CRF b_r/B_φ crit [2/1,3/1,4/1]           [3 elem]        [3 elem]                     0.0e+00  OK    
CRF Q_peak [2/1,3/1,4/1]                 [3 elem]        [3 elem]                     0.0e+00  OK    
CRF Q0 [2/1,3/1,4/1]                     [3 elem]        [3 elem]                     0.0e+00  OK    
CRF b_r crit in tesla [2/1,3/1,4/1]      [3 elem]        [3 elem]                     0.0e+00  OK    
SLAYER enabled flag                      1               1                            0.0e+00  OK    
Runtime (s)                              41.4s           43.7s                                 --    
=====================================================================================================
Summary: 22 unchanged


================================================================
Case: efit_fixedbdy_separatrix — Fixed-boundary EFIT (box hugs LCFS), Newton separatrix-find regression
================================================================

Regression Report: efit_fixedbdy_separatrix
========================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
----------------------------------------------------------------------------------------
Quantity                    origin/develop  feature/growth-rate-inertia  Diff     Status
----------------------------------------------------------------------------------------
psio (total poloidal flux)  3.344390e-01    3.344390e-01                 0.0e+00  OK    
q0                          1.155202e+00    1.155202e+00                 0.0e+00  OK    
q95                         5.741022e+00    5.741022e+00                 0.0e+00  OK    
beta_t                      3.945177e-05    3.945177e-05                 0.0e+00  OK    
beta_n                      1.009506e-01    1.009506e-01                 0.0e+00  OK    
Runtime (s)                 15.1s           14.9s                                 --    
========================================================================================
Summary: 5 unchanged


================================================================
Case: tj_kinetic_multiion — TJ-analytic n=1, no wall, ideal + PE + multi-ion NTV (D + T + electron summed)
================================================================

Regression Report: tj_kinetic_multiion
=========================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-----------------------------------------------------------------------------------------
Quantity                     origin/develop  feature/growth-rate-inertia  Diff     Status
-----------------------------------------------------------------------------------------
NTV total (D+T+imp+e) [N·m]  8.956318e-04    8.956318e-04                 0.0e+00  OK    
NTV Deuterium [N·m]          4.759144e-04    4.759144e-04                 0.0e+00  OK    
NTV Tritium [N·m]            4.133811e-04    4.133811e-04                 0.0e+00  OK    
NTV electron [N·m]           6.336228e-06    6.336228e-06                 0.0e+00  OK    
total energy Re(et[1])       4.350277e-01    4.350277e-01                 0.0e+00  OK    
# singular surfaces          2               2                            0.0e+00  OK    
Runtime (s)                  2.7s            3.0s                                  --    
=========================================================================================
Summary: 6 unchanged


================================================================
Case: diiid_kinetic_multi_n — DIII-D-like equilibrium, n = 1, 2, KineticForces kinetic matrices, kinetic_factor=1.0, stability only, truncation at psihigh.
================================================================

Regression Report: diiid_kinetic_multi_n
============================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
--------------------------------------------------------------------------------------------
Quantity                        origin/develop  feature/growth-rate-inertia  Diff     Status
--------------------------------------------------------------------------------------------
total energy Re(et[1])          5.185833e-01    5.185833e-01                 0.0e+00  OK    
total energy Im(et[1])          -1.542940e-01   -1.542940e-01                0.0e+00  OK    
total energy (all)              [82 elem]       [82 elem]                    0.0e+00  OK    
# kinetic singular surfaces     0               0                            0.0e+00  OK    
kinetic singular psi locations  [0 elem]        [0 elem]                     0.0e+00  OK    
ODE steps (saved)               818             818                          0.0e+00  OK    
mpert                           41              41                           0.0e+00  OK    
npert                           2               2                            0.0e+00  OK    
Runtime (s)                     462.0s          424.8s                                --    
============================================================================================
Summary: 8 unchanged


================================================================
Case: diiid_n1_riccati_precompiled — DIII-D-like equilibrium, n=1, Riccati integrator Δ' matrix, precompiled build
================================================================

Regression Report: diiid_n1_riccati_precompiled
===========================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, aot build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, aot build, warm
-------------------------------------------------------------------------------------------
Quantity                       origin/develop  feature/growth-rate-inertia  Diff     Status
-------------------------------------------------------------------------------------------
delta prime (BVP diagonal)     [5 elem]        [5 elem]                     0.0e+00  OK    
delta prime (raw side-major)   [10 elem]       [10 elem]                    0.0e+00  OK    
edge coil response delta_coil  [10 elem]       [10 elem]                    0.0e+00  OK    
total energy Re(et[1])         8.031978e-01    8.031978e-01                 0.0e+00  OK    
plasma energy Re(ep[1])        -1.368533e+00   -1.368533e+00                0.0e+00  OK    
vacuum energy Re(ev[1])        2.171731e+00    2.171731e+00                 0.0e+00  OK    
total energy (all)             [35 elem]       [35 elem]                    0.0e+00  OK    
# singular surfaces            5               5                            0.0e+00  OK    
singular psi locations         [5 elem]        [5 elem]                     0.0e+00  OK    
singular q values              [5 elem]        [5 elem]                     0.0e+00  OK    
ODE steps (saved)              98              98                           0.0e+00  OK    
ODE steps (total)              2662            2662                         0.0e+00  OK    
mpert                          35              35                           0.0e+00  OK    
npert                          1               1                            0.0e+00  OK    
q0                             1.204212e+00    1.204212e+00                 0.0e+00  OK    
q95                            4.781723e+00    4.781723e+00                 0.0e+00  OK    
beta_n                         1.372511e+00    1.372511e+00                 0.0e+00  OK    
Runtime (s)                    23.4s           24.8s                                 --    
===========================================================================================
Summary: 17 unchanged


================================================================
Case: lar_reversed_shear_n1 — Large-aspect-ratio reversed-shear equilibrium (hollow current), n=1, ideal stability
================================================================

Regression Report: lar_reversed_shear_n1
=============================================================================================================================
Ref 1: origin/develop  @ a1149c5c2 (2026-11-07)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
Ref 2: feature/growth-rate-inertia  @ c2d6aff88 (2026-11-08)
       env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest f1e0eb38 (pinned), 8 threads/8 BLAS, jit build, warm
-----------------------------------------------------------------------------------------------------------------------------
Quantity                                                      origin/develop   feature/growth-rate-inertia  Diff       Status
-----------------------------------------------------------------------------------------------------------------------------
total energy Re(et[1])                                        -2.263762e-02    -2.263762e-02                0.0e+00    OK    
total energy Im(et[1])                                        2.135928e-15     2.135928e-15                 0.0e+00    OK    
plasma energy Re(ep[1])                                       -5.912341e-02    -5.912341e-02                0.0e+00    OK    
vacuum energy Re(ev[1])                                       3.648579e-02     3.648579e-02                 0.0e+00    OK    
vacuum matrix min eigenvalue                                  3.488391e-02     3.488391e-02                 0.0e+00    OK    
plasma energy (all)                                           [31 elem]        [31 elem]                    0.0e+00    OK    
vacuum energy (all)                                           [31 elem]        [31 elem]                    0.0e+00    OK    
total energy (all)                                            [31 elem]        [31 elem]                    0.0e+00    OK    
ODE steps (saved)                                             229              229                          0.0e+00    OK    
ODE steps (total)                                             227              227                          0.0e+00    OK    
q0                                                            2.397229e+00     2.397229e+00                 0.0e+00    OK    
q95                                                           2.687221e+00     2.687221e+00                 0.0e+00    OK    
beta_t                                                        3.331679e-04     3.331679e-04                 0.0e+00    OK    
beta_n                                                        1.898177e-01     1.898177e-01                 0.0e+00    OK    
# singular surfaces                                           2                2                            0.0e+00    OK    
singular psi locations                                        [2 elem]         [2 elem]                     0.0e+00    OK    
singular q values                                             [2 elem]         [2 elem]                     0.0e+00    OK    
singular dq/dpsi (opposite signs on the two q = 2 crossings)  [2 elem]         [2 elem]                     0.0e+00    OK    
mpert                                                         31               31                           0.0e+00    OK    
npert                                                         1                1                            0.0e+00    OK    
q profile (checksum)                                          39d1db7ce55d...  39d1db7ce55d...              identical  OK    
pressure profile (checksum)                                   0996d4ffdc7a...  0996d4ffdc7a...              identical  OK    
Runtime (s)                                                   2.8s             1.4s                                    --    
=============================================================================================================================
Summary: 22 unchanged

Adversarial review (two Opus agents, read-only, after the first push)

The physics reviewer re-derived the inertia bands and found them correct: the metric cofactors, B², the J weighting, the Fourier normalization and band layout, the (ξ_s, ξ_s)/(ξ_s, Ξ)/(Ξ, Ξ) placement of A/C/H, the sign of u (hence of C), the kinetic Schur reduction, and that the pencil's normalization is γ²-independent; the cylinder benchmark excludes any constant factor. Defects found and fixed in the last commit:

  • A fixed-boundary-unstable marginal (nzero > 0, λ_min ≥ 0) was reported as γ = 0, and decade bracketing on λ_min alone could step over the fastest root (λ_min has a pole at each fixed-boundary eigenvalue) and converge on a slower branch. The bracket now runs on the mode count; a fixed-boundary fastest mode errors out instead of being mislabeled.
  • On the default two-pass auto grid, main rebuilt the problem from the re-formed equilibrium, which re-forms again on a third grid: deck and API disagreed there. main now passes ffs.problem; a parity case on the auto grid covers it.
  • The inertial solves overrode singfac_min to 0, which changes the two-pass grid relative to the marginal solve. They now take every control of the marginal problem (grid identical); crossings from the kinetic surface finder can still appear far below any physical root, where only the sign of λ_min matters, and are documented.
  • maxiter exhaustion was silent (now a warning and converged = false, also in HDF5); the gamma_sq_min exit returned a never-evaluated nonzero γ with the wrong solve (now γ = 0, marginal); a kinetic source on the problem was silently replaced (now an error, API and deck); ω_*pi used the problem's lowest n (now the least-stable mode's n); missing T_e crashed the ω_*pi evaluation (now NaN, with clear errors from the constructors); the API form wrote an empty deck into Input/gpec_toml_raw (skipped without a config); the first γ² prepared the problem twice; ρ ≤ 0 or non-finite is now rejected.

Not addressed, carried as limitations: per-n ω_*pi for multi-n runs, and ω_*pi's n_i ∝ n_e assumption with Z_eff-aware μ in ρ. The fixed-boundary branch and the count are handled below.

The exact conjugate-point count

Why. The bracket of growth_rate is provably on the fastest mode only if the number of modes growing faster than the trial γ, nconj + (λ_min < 0), is exact. nconj is the number of conjugate points (fixed-boundary modes fitting inside the ψ interval). DCON's detector nzero (sign change of the smallest-magnitude eigenvalue of W⁻¹ = U₁U₂⁻¹ between stored steps) misses one whenever a stiff harmonic pins a tiny eigenvalue near zero. On the TJ fixture at qa = 2.9, p0 = 0.8 a fixed-boundary mode at γ² ≈ 1.2e8 was invisible to it, so a search from gamma_sq_init = 1e8 converged to the slower free-boundary root (6.5e7 instead of 4.9e9) and the plasma behind a standoff-0.05 wall was reported stable. nzero is kept as a diagnostic; the new count sits beside it.

What. The forward integration counts conjugate points as the Maslov index of the solution frame (Arnold 1967; Robbin & Salamon 1993). With U = [U₁; U₂] the frame of regular solutions, Ψ = (U₁ − i s U₂)(U₁ + i s U₂)⁻¹ is unitary whenever U is Lagrangian (U₁†U₂ Hermitian), has an eigenvalue −1 exactly where det U₁ = 0 (a conjugate point) and +1 where U₂ is singular (a pole of W⁻¹, harmless), and is invariant under U → U·T, so the Gaussian reductions and a column normalization leave it unchanged. At every accepted step the eigenphases of Ψ are computed, each is followed by its eigenvector (the eigenvectors are orthonormal), and a phase whose shortest arc crosses π counts one conjugate point. Result fields: diagnostics.nzero_conj, nconj_flags, maslov_defect.

How it stays exact, each item the fix for a failure seen on the fixtures:

  • Seed one accepted step into each chunk. At the axis start every column of U₁ vanishes, and after an ideal crossing the resonant column does, so those phases sit exactly at π; seeding at the chunk start counted them all (20, the number of harmonics). Counting from the first step inside the interval is Newcomb's open-interval convention.
  • Follow phases by eigenvector, not by sorted order. Matching sorted phases through a cyclic shift misattributes phases that overtake each other and produced spurious wraps.
  • Bisect a step whose phase rotates by more than π/4. Near a conjugate point the W⁻¹ eigenvalue can pass zero within one accepted step (rotations of 1.8–2.7 rad were seen), which leaves the direction of travel ambiguous; the integrator's dense output supplies the frame at intermediate points. Rescaling s instead was tried and rejected: minimizing the rotation drives s up without bound (0.26 → 1.7e4), squashing every phase toward π until a pole of W⁻¹ hops across the branch cut like a zero crossing.
  • Flag a frame that is not Lagrangian. A unitarity defect max||λ|−1| above 1e-3 means the eigenphases cannot be trusted; a flagged solve makes growth_rate warn and report converged = false. A rotation still above π/2 after bisection flags as well.
  • Column-normalize before the Cayley inverse. Between reductions the column norms span up to twenty decades, which made the inverse meaningless after the q = 2 crossing on Solovev; Ψ is invariant under the scaling, so this costs nothing.

Cost: 10–15 % of an inertial solve. Off by default; growth_rate switches it on for its solves through the internal _solve_force_balance(…; count_conjugate_points=true) (no deck key; public solve unchanged), and main turns it on for the marginal solve when a [GrowthRate] section follows.

Validation (TJ qa = 2.9, p0 = 0.8, ρ = 1e-7, n = 1; neg = negative free-boundary eigenvalues; the Morse index nconj + neg must be non-increasing and continuous across the pole):

γ² 0 1e7–5.6e7 7.5e7–1e8 1.33e8–3.16e9 4.2e9 ≥ 5.6e9
nzero (DCON) 0 0 0 0 0 0
nzero_conj 1 1 1 0 0 0
neg 1 1 0 1 1 0
Morse index 2 2 1 1 1 0

Same table with the standoff-0.05 wall (its neg differs, the index is again monotone). Flags 0 and defect ≤ 5e-10 at every γ². growth_rate from gamma_sq_init = 1e8: γ² = 4.9155e9 without the wall and 7.17449e8 with it, both converged in 11 solves; the three cases pinned as @test_broken at the previous commit now pass as @test.

A fixture no W⁻¹-based count can handle, and what it says about nzero. On the Solovev ideal fixture the frame loses the Lagrangian property after the q = 2 crossing (defect 0.2–4; the raw frame's relative non-Hermiticity stays at 1e-8 because its large columns dominate), with non-monotone phase motion even in the first chunk at tolerances 1e-7 and 1e-9. The new count flags it on 132 steps, as designed. DCON's nzero reports 6 conjugate points there (7 at 1e-9) on a fixture whose free-boundary energy has eight positive eigenvalues and none negative, hence Morse index 0: a tolerance-dependent false positive of the existing detector, not tracked by the harness. An issue is being drafted separately.

Relation to ELITE (Snyder et al. user guide, Wilson 2002, Lunniss 2016). ELITE shoots too: a finite-difference sweep in ψ, an edge matching matrix T(γ²), and a secant iteration on one of its eigenvalues from the user's gamsq guess, with no documented mode counting, γ² scan or treatment of poles, so it converges to whichever root the guess leads to. The count-based bracket here is therefore a step beyond ELITE. ELITE's inertia (Wilson 2002 Eq. 9) is the perpendicular incompressible form used here. The method and its references are written up in docs/src/stability.md and docs/development/references.md.

Notes for reviewers

  • The result type is IdealGrowthRateResult; the bare GrowthRateResult is Tearing's dispersion-root type, and the first export clashed with it (precompile warning), hence the name.
  • Not in this PR, planned as follow-ups: a Newton step from the eigenvector to cut the solve count, and an issue on the Solovev frame losing the Lagrangian property (the DCON nzero false positive above).
  • New regression case lar_reversed_shear_growth_rate_n1 (reversed-shear LAR example, uniform deuterium density 3.3e-7 kg/m³): γ = 2957 s⁻¹ after 9 solves, no nzero > 0 in the history. Its noise floors are absolute (the harness has no relative option): 1e-5 on γ and 1e-1 on γ² and the γ² history, which is the shift a 1e-10 change in λ_min near the root produces (dλ/dγ² ≈ 1.8e-9 per s⁻² there); 1e-10 on the λ_min history as elsewhere. These are new floors on a new case, not relaxed existing ones; tighten if you prefer.

🤖 Generated with Claude Code

d-burg and others added 6 commits November 7, 2026 11:18
…te solve

ELITE's inertia model in the force-balance equations: the perpendicular kinetic energy
K = ½∫ρ|ξ_⊥|² dV, with parallel inertia dropped, is algebraic in the normal displacement
Ξ = ξ·∇ψ and the in-surface variable ξ_s = (ξ×B)·e_ψ because ξ×B = ξ_s∇ψ + Ξ χ′(∇ζ − q∇θ),
so it adds Hermitian bands ⟨μ₀ρ|∇ψ|²/B²⟩, ⟨μ₀ρ u·∇ψ/B²⟩ and ⟨μ₀ρ|u|²/B²⟩ to A, C and H
(Glasser 2016 Eqs. 8-9, A6). PerpendicularInertia is a `kinetic` source with
kinetic_factor = γ² in s⁻², so the reduction runs on the kinetic (non-Q-factorized) path,
where the rational surfaces are regular for γ² > 0 and the integration is one chunk.

growth_rate brackets and Brent-solves the γ² at which the least-stable free-boundary
energy vanishes, each evaluation one solve. Prototype: constant or user-supplied ρ(ψ),
no export yet, no tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s and growth rate

Bands Hermitian and A, H positive definite, linear in ρ; the metric B² identity the bands
rest on; the inertial solve regular with no crossings, λ_min approaching the ideal value as
√γ² (the inertial-layer term is linear in γ) and rising with γ²; the kinetic crossing
machinery and the single chunk agreeing; growth_rate returning γ = 0 on the stable fixture.
Memoize growth_rate's evaluations so Brent's endpoint re-evaluations are free.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e docs

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…secant

Brent kept bisecting to its x-tolerance after |λ_min| was already 1e-11, ten extra solves per
root on the kink benchmark; an Illinois iteration stopping on |λ_min| < ftol·|λ_min(0)| or a
narrow bracket finds the same γ in 5–6 solves in total.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…wthRateResult

The bare GrowthRateResult is Tearing's dispersion-root type; exporting a second one from
ForceFreeStates made the top-level import ambiguous (precompile warning, import ignored).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@d-burg d-burg self-assigned this Nov 7, 2026
@github-actions github-actions Bot added the feature New capability label Nov 7, 2026
d-burg and others added 8 commits November 7, 2026 18:36
… the kinetic profiles

PerpendicularInertia(equil) builds ρ(ψ_N) = μ m_p n_e from the attached kinetic profiles with the
layer's mass per electron; ion_diamagnetic_frequency gives ω_*pi(ψ_N) for a toroidal n and
growth_rate reports its peak over the domain for the EPED1 γ > ω_*pi/2 criterion. GrowthRateConfig
carries the settings of the coming [GrowthRate] deck section, and growth_rate reuses a marginal solve.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… HDF5 output

A [GrowthRate] section (keys = GrowthRateConfig fields; rho NaN derives the density from the kinetic
profiles) runs growth_rate after the force-free-states stage, reusing that stage's ideal solve as the
marginal one; it needs vac_flag and the Forward integrator and cannot share a deck with [Match]. The
result is the growth_rate slot of main's tuple and ForceFreeStates/GrowthRate/ in gpec.h5 (gamma,
gamma_sq, omega_star_pi, rho on psi, and the root-search History), with the inputs recorded under Input/.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…oup and a harness case

Deck-and-API parity for [GrowthRate] on the stable TJ fixture and on a q_a = 2.9 variant that is
free-boundary unstable (γ agrees to 1e-12), the three parse-time guards, the writer's metadata, and
a regression case pinning γ on the reversed-shear example with uniform deuterium density.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…and keep the inertial solves on the marginal grid

From an adversarial review of the branch. The count of modes faster than γ (interior zero crossings
plus a negative free-boundary energy) is what the decade bracket now follows, so a fixed-boundary-
unstable marginal is no longer reported as γ = 0 and the bracket cannot step over the fastest root;
a fixed-boundary fastest mode errors out. main hands growth_rate the problem its marginal solve came
from and the inertial solves keep every control of that problem, so the two-pass grid is the same.
Also: a converged flag with a warning when maxiter runs out, γ = 0 below gamma_sq_min, errors for a
kinetic source and a non-positive density, ω_*pi at the least-stable mode's n and NaN without the
profiles, no empty deck recorded from the API form, the inertia bands evaluated once.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…sing count and pin the missed-count cases

A crossing still present at the bracket's lower end is a fixed-boundary eigenvalue inside the bracket,
where the free-boundary energy has a pole. The fastest mode is free-boundary and at least as fast as any
fixed-boundary one, so growth_rate now bisects on the count until the lower end clears that eigenvalue
instead of erroring; a crossing still present at rtol means the fastest mode has no edge displacement and
the upper end is reported.

The count comes from the conjugate-point detector, which misses a crossing when a stiff harmonic holds the
smallest-magnitude eigenvalue of the inverse energy matrix near zero; on the TJ fixture at p0 = 0.8 that
hides a fixed-boundary mode at γ² ≈ 1.2e8, so a search started below it lands on a slower root or reports
stability behind a close wall. Both outcomes are pinned as test_broken and the docstring says to start
gamma_sq_init above the fastest expected growth rate.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…yley eigenphases of the solution frame

The DCON detector behind nzero follows the smallest-magnitude eigenvalue of W⁻¹ between stored steps and misses a
conjugate point whenever a stiff harmonic pins a tiny eigenvalue near zero, so growth_rate could bracket a slower mode
or report stability. The forward integration now also counts conjugate points exactly, at every accepted step, as the
eigenphases of the Cayley transform Ψ = (U₁ − i s U₂)(U₁ + i s U₂)⁻¹ that cross π (its Maslov index): Ψ is invariant
under the Gaussian reductions, has no poles, and its eigenvectors identify each phase across a step. Phases are seeded
one step into each chunk so the vanishing columns at the axis and after an ideal crossing are not counted, a step with
a fast-moving phase is bisected on the integrator's dense output, and a unitarity defect above 1e-3 (a frame that is
not Lagrangian to working precision) flags the count. growth_rate switches the count on for its solves through an
internal keyword, uses it in place of nzero, and marks a flagged result not converged; the deck path turns it on for
the marginal solve when a [GrowthRate] section follows. The three pinned cases on the TJ fixture now pass.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… its references

Explain in the stability page how nzero (DCON's smallest-eigenvalue test) and nzero_conj (the Maslov index of the
solution frame through its Cayley eigenphases) differ, what keeps the latter exact (seeding one step into each chunk,
eigenvector tracking, bisection of fast steps on the dense output, the unitarity-defect flag), its cost and its
validation; add Arnold 1967 and Robbin & Salamon 1993 for the Maslov index and Wilson 2002, Snyder 2002 and Lunniss
2016 for ELITE's formulation and solve procedure to the references list.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…owth-rate search

growth_rate stored the full solve of every γ² it evaluated, so memory grew by one
solve per iterate (3 GB for one n = 5 solve, 21 GB after nine; 75 GB at n = 15).
Only a bracket end can be returned, so keep the latest solve on each side of the
root and drop the rest. The iterates and the returned result are unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
…holds and correct the EPED1 factor

growth_rate applies no threshold to the ideal growth rate. State the EPED1 and EPED1.6 conventions in terms of gamma and omega_star_pi, their sources and caveats, and replace the docstring statement gamma > omega_star_pi/2 with omega_star_pi/4 (the effective frequency is half the peak).

Co-Authored-By: Claude <noreply@anthropic.com>
@logan-nc

Copy link
Copy Markdown
Collaborator

@d-burg this needs plots or an artifact containing plots please

…ith bisection

When the free-boundary energy at one bracket end dwarfs the other, the interpolated point equals the opposite end in floating point and the search stalled there, then stopped on the bracket width. The iteration now lives in _sign_change_root and takes a geometric bisection step when the interpolated point is not strictly inside the bracket or the same end has been kept three times running.

Co-Authored-By: Claude <noreply@anthropic.com>

@logan-nc logan-nc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@d-burg this is a good start. The algebra holds up, and the count-based bracket is the right design. I went through the diff at 98fa97b and re-derived the pieces by hand against Glasser, Phys. Plasmas 23, 072505 (2016), Wilson et al., Phys. Plasmas 9, 1277 (2002) and Snyder et al., Phys. Plasmas 16, 056118 (2009). Nothing below was run. These pieces check out:

  • the identity ξ×B = ξs∇ψ + Ξu and its sign;
  • the metric cofactors and B²;
  • the normalization, with no stray 2π;
  • the A/C/H bands acting on the true ξs through the kinetic change of variable;
  • the ξs elimination redone with A + γ²AK;
  • the cylinder formula.

The physics comes first below, because it decides how γ can be described and used. These are also the points Glasser would most likely raise against a growth rate from DCON.

Physics

  1. The number this computes is the Γ = 0 growth rate, and that is an upper bound on the ideal-MHD rate, not the rate itself.

    • DCON has already minimized δW over ξ∥. Dropping ξ∥ inertia on top of that is exactly ideal MHD with Γ = 0: δW then contains no ξ∥, and the Rayleigh quotient is maximized at ξ∥ = 0. So γ is an exact eigenvalue of a well-posed self-adjoint problem, which is good.
    • For Γ = 5/3 it is an upper bound, because δWfull ≥ δW⊥ and Kfull ≥ K⊥. The two share only the marginal point.
    • For γ ≫ cs/qR the gap is O(β). For γ ≪ cs/qR, incompressibility needs parallel return flow, and its inertia raises the effective mass by the Pfirsch–Schlüter factor. Then γ⊥/γideal → √(1+2q²) at large aspect ratio, which is √3 for a q = 1 internal kink.
    • Near marginal stability every mode is slow, so this is where the gap is largest. Wilson 2002 says this just before Eq. 9: the model is "valid at marginal stability".
    • The zero-β cylinder benchmark cannot see any of this.
    • Requested: (a) docstrings, the release note and the HDF5 long_name should say something like "growth rate with perpendicular inertia only (Γ = 0); an upper bound on the ideal-MHD growth rate". (b) Add a toroidal internal-kink known-answer test on a TJ case with q0 < 1; the code should return √3 × the Bussac rate.
    • Caveat on the factor: I have √(1+2q²) from our own derivation and from memory of Glasser–Greene–Johnson and Bussac. It should be pinned to a source before it goes in a docstring.
  2. The count in faster(h) = h.nzero + (h.lambda < 0) is the right verdict.

    • It is the Morse index of δW + γ²K with a free end point: the number of modes faster than γ equals the number of conjugate points plus the number of negative eigenvalues of WP(γ²) + WV.
    • Your own qa = 2.9, p0 = 0.8 TJ table shows why the sign of λmin alone is not enough. At γ² = 7.5e7–1e8, λmin > 0 while a mode at γ² = 4.9155e9 exists.
    • In a pedestal this is the normal case. High-n ballooning modes are nearly fixed-boundary, so their free-boundary root sits just above a pole.
    • Requested:
      • Count the negative eigenvalues instead of using (λ_min < 0), so the integer is the full index. This does not change which root the solver finds, but it matters when the count itself is the verdict. That is what an ELITE/EPED replacement would use: "is anything growing faster than ω*/2?" with no root solve.
      • State the theorem and its condition (K > 0 on (ξs, Ξ)) in docs/src/stability.md, with a citation. This is Morse / σ-stability (Goedbloed & Sakanaka, Phys. Fluids 17, 908 (1974)), not Glasser's γ = 0 theorem.
      • Add a regression test that the fixture above returns "unstable" at γ² = 9e7.
  3. The small-γ limit is non-analytic, and the √γ² test only holds at zero pressure.

    • The inertial layer at a rational surface sets cL/cS ∝ γ2μ, so WP(γ²) − WP(0) ∝ γ2μ with μ = √(−DI).
    • μ = ½ at zero β gives your linear-in-γ result. In a pedestal the exponent moves. It goes to zero as a surface approaches Mercier marginality, and for DI > 0 roots pile up at γ = 0.
    • The 8 < ratio < 12 window runs on a low-β fixture, so it confirms the exponent only where it is trivially 1.
    • Requested: fit d ln[λmin(γ²) − λmin(0)]/d ln γ on a finite-β case and compare it with 2√(−DI) from resist_eval_all! at the dominant surface.
    • The same physics means that near marginality γ ∝ (−δW)1/2μ. The layer half-width, about γτAq in |m − nq|, quickly drops below ρi.
    • So the small-γτA numbers are the most model-dependent output of the feature, including the 0.023 in the n = 8 example. It would help to write the layer widths, in metres, next to γ.
  4. ω*pi and the EPED1 threshold are off by a factor of two.

    • Snyder et al. 2009, Sec. II, gives the threshold as γ > ω*pi/2, with ω*pi taken at its half-maximum value across the edge barrier.
    • _peak_omega_star_pi returns the full maximum over [psilow, psilim], and the docstring then states gamma > omega_star_pi/2. The threshold as written is therefore twice EPED1's.
    • Because the maximum is over the whole domain, a core gradient can set it, and a psilim inside the pedestal foot can cut it off.
    • Requested: either return the profile and let the caller choose, or take the maximum over a stated pedestal window and apply the half-maximum rule explicitly. Add a test on a tanh pedestal with a steeper core gradient.
    • Separately, γ ∝ ρ−½, so uniform versus profile density can move γ/γ0 by up to about 2 in a pedestal. That deserves a switch and a note on which convention ELITE uses.
  5. For the record, though not a problem in this PR (the guard against combining with a kinetic source is right): any later kinetic growth rate needs the KineticForces resonances evaluated at ω = iγ inside the root search.

    • At γ ~ 1e4–1e5 s⁻¹, the static ω = 0 matrices are the wrong functional.
    • Once the matrices are non-Hermitian, the monotone count and the bracket no longer hold.

Code

  1. growth_rate does not set singfac_min, so the inertial solves inherit the default 1e-4.

    • The kinetic surface finder then makes a single trapezoid crossing wherever cond(F̄) > 1e8. That happens once γτA falls below roughly 1e-3 to 1e-4, near marginality.
    • The "Solve path" bullet in the description says the opposite; the adversarial-review section agrees with the code.
    • The only evidence that the crossing is accurate is one point at γ² = 1e4.
    • Requested: run the inertial solves as a single chunk, as described; or record kmsing per solve and mark the result not converged when the root used a crossing. Also compare singfac_min = 0 against the default at γτA ≈ 1e-4.
  2. nzero_conj = abs(odet.nconj) can cancel silently.

    • With a Lagrangian frame every crossing goes the same way, so a +1 and a −1 can only come from a miscounted step. Taking the absolute value hides that.
    • Requested: accumulate the up and down wraps separately and flag any solve where both are non-zero.
    • The ideal frame is the one that loses the Lagrangian property on Solovev. Consider deciding stability from an inertial single-chunk solve at gamma_sq_min and keeping the ideal count as a diagnostic.
    • It would also help to report the flag counts and maslov_defect for the DIII-D-like marginal solve.
  3. The ftol * scale stop can return converged = true with a wide bracket.

    • When the marginal λ is positive, scale is |λmin(lo)|. λmin → −∞ just above a pole, so if the bisection leaves lo near one, the first secant iterate can pass the test.
    • Requested: require the bracket to meet rtol as well, and store the final [lo, hi] on the result.
  4. CK and the non-orthogonal part of HK are untested.

    • In the cylinder they enter γ² only at O(ε²). For up-down symmetric equilibria CK has no diagonal and is purely imaginary, so a sign or conjugation error would pass every current fixture. My derivation agrees with your signs, but no test would notice if they were wrong.
    • Cheap checks:
      • the pointwise identity |∇ψ×u|² = B², i.e. integrands[1]*integrands[3] - integrands[2]^2 ≈ (jac*mu0rho)^2/b2;
      • joint positive-definiteness of [AK CK; CK† HK];
      • the same γ across jac_type = hamada / pest / boozer / equal_arc on the DIII-D-like case.
    • Truncation in m is a Ritz restriction, so γ converges from below. An under-resolved run looks more stable, which is the wrong direction for a stability tool. Please show m-convergence on that case too.
  5. Smaller items:

    • A root below gamma_sq_min comes back as γ = 0 with converged = true, so it cannot be told apart from stable. An "unstable below floor" flag would fix that.
    • Reject kinetic_factor <= 0 for PerpendicularInertia; the LU path has no definiteness check.
    • The γ² controls (1e4 / 1e9 / 1e16 s⁻²) are dimensional. In units of 1/τA² they would mean the same thing on every device.
    • The HDF5 annotation still says "at the lowest n", but the code uses the least-stable mode's n.
    • docs/src/stability.md does not yet describe the model or the solver.

Review drafted with Claude from a read of the diff and the cited papers; nothing here was run.


Generated by Claude Code

…response solve

The Euler-Lagrange frame's column magnitudes can differ by well over a hundred
decades at the edge of a high-n integration. The unscaled U2/U1 solve then loses
all accuracy when taken directly after a Gaussian reduction, giving a complex,
non-Hermitian plasma energy. Scaling each column to unit norm leaves W unchanged
and restores the solve.

Co-Authored-By: Claude <noreply@anthropic.com>
d-burg and others added 3 commits November 13, 2026 16:40
…uple

The stage result tuple gained a growth_rate entry with the [GrowthRate] deck section; the forward-run test still listed the old fields.

Co-Authored-By: Claude <noreply@anthropic.com>
…ussian reduction

The reductions only combine columns, so the column norms grew without bound (1e238 at n=25).
Each reduction now ends by scaling every column to unit norm, with the scale on the diagonal of
fixfac so transform_u! undoes it. W = U2/U1 is unchanged; ca_left/ca_right on the forward path
change with the column basis.

Co-Authored-By: Claude <noreply@anthropic.com>
…tion records consistent with the column rescaling

The fixed-boundary zero-crossing check blends two stored steps before the reductions are undone. A reduction between them now rescales the columns, so the earlier step is brought to the same scales first; without this a genuine crossing could be rejected as noise.

The rescaling errors on a non-finite solution column, skips a column whose inverse norm is not finite, and transform_u! warns when the compounded scales fall below the floating-point range.

Co-Authored-By: Claude <noreply@anthropic.com>
@logan-nc

Copy link
Copy Markdown
Collaborator

This is the missing piece for RMP studies of the pedestal. Ideal δW alone does not settle pedestal stability: the EPED test compares a growth rate with an ad hoc damping (γ > ω_*pi/2), which is why ELITE has been needed. With this PR, GPEC can do that step itself.

The chain it completes:

  1. The saturated width of an RMP-driven island at the top of the pedestal, from the modified Rutherford equation with applied-field forcing (#665: MRE with saturated-island roots and applied-field forcing).
  2. The pressure flattened across that island, and the pedestal-top pressure it removes (#677: island flattening).
  3. The bootstrap current recomputed self-consistently from the flattened profiles (#667: Redl bootstrap current), and the equilibrium re-solved with TokaMaker.
  4. This growth-rate solve for n = 1–30 on the re-solved equilibrium, which says whether the modified pedestal is peeling–ballooning stable.

Together this is a full prediction of the RMP's impact on pedestal stability, from the coil currents to the stability verdict. As far as we know, no one has done that so far.

🤖 Generated with Claude Code

@d-burg d-burg changed the title ForceFreeStates - FEATURE - Add perpendicular inertia and an ELITE-style growth-rate solve ForceFreeStates - FEATURE! - Add perpendicular inertia and an ELITE-style growth-rate solve Nov 14, 2026
@github-actions github-actions Bot added the changed-results Results move or an interface breaks - read before upgrading label Nov 14, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changed-results Results move or an interface breaks - read before upgrading feature New capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants