Skip to content

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

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

d-burg wants to merge 13 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: none on existing cases; one new case lar_reversed_shear_growth_rate_n1 (harness @ c2d6aff)
  • 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.

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 7 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>
@d-burg
d-burg requested a review from logan-nc November 9, 2026 22:32

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

feature New capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant