Skip to content

LocalStability - API! - Solve a LocalStabilityProblem from its own deck section - #568

Merged
matt-pharr merged 7 commits into
developfrom
feature/local-stability-problem
Oct 26, 2026
Merged

matt-pharr merged 7 commits into
developfrom
feature/local-stability-problem

Conversation

@matt-pharr

@matt-pharr matt-pharr commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Release note

  • Audience: users
  • Numerical impact: none (harness @ 342a1b2)
  • Migration: local_stability_flag is removed. In a deck's [ForceFreeStates] it is a deprecated key, reported as deprecated (and, once deprecated keys stop the run, the run stops there); passed to solve/EulerLagrangeProblem it fails as an unknown keyword. Stored gpec.h5 files whose TOML sets it no longer replay the local-stability stage. Replace local_stability_flag = true with a [LocalStability] section (copy it from any example deck) and delete local_stability_flag = false; scripts call solve(LocalStabilityProblem(eq), Ballooning()). LocalStability.compute_local_stability is gone.

LocalStability is now its own stage. A script calls solve(LocalStabilityProblem(eq), Ballooning(; ...)) and gets a LocalStabilityResult with the Mercier D_I, resistive D_R, ballooning Δ' and first α-boundary profiles as named fields; a deck gets the same result through a [LocalStability] section whose keys are the Ballooning knobs, now including the α-scan ψ window, search bound and bisection tolerance and the ballooning ODE tolerance. The HDF5 paths are unchanged. Info/Runtimes/force_free_states no longer includes the local-stability scan, which now has its own Info/Runtimes/local_stability entry. The scan logs verbosely by default (verbose = true) instead of following [ForceFreeStates] verbose; set verbose = false in [LocalStability] to quiet it.

Regression report

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

Regression Report: diiid_n1
=================================================================================================================
Ref 1: develop  @ bca7ce751 (2026-10-23)
       env: julia 1.12.6, x86_64-linux-gnu, manifest aa94b997 (pinned), 2 threads/1 BLAS, jit build
Ref 2: feature/local-stability-problem  @ 59b4b4de9 (2026-10-23)
       env: julia 1.12.6, x86_64-linux-gnu, manifest aa94b997 (pinned), 2 threads/1 BLAS, jit build
-----------------------------------------------------------------------------------------------------------------
Quantity                                      develop          feature/local-stability-problem  Diff       Status
-----------------------------------------------------------------------------------------------------------------
total energy Re(et[1])                        8.006123e-01     8.006123e-01                     0.0e+00    OK    
total energy Im(et[1])                        7.924925e-05     7.924925e-05                     0.0e+00    OK    
plasma energy Re(ep[1])                       -1.372261e+00    -1.372261e+00                    0.0e+00    OK    
vacuum energy Re(ev[1])                       2.172874e+00     2.172874e+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)                             800              800                              0.0e+00    OK    
ODE steps (total)                             795              795                              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.304340e-01     7.304340e-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)                          ea980cf7e8cf...  ea980cf7e8cf...                  identical  OK    
pressure profile (checksum)                   75be5949b7c1...  75be5949b7c1...                  identical  OK    
Mercier D_I profile (checksum)                cafa40526b39...  cafa40526b39...                  identical  OK    
resistive interchange D_R profile (checksum)  c70e46071c9d...  c70e46071c9d...                  identical  OK    
ballooning Delta' profile (checksum)          029149d57e8c...  029149d57e8c...                  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.288864e-04     5.288864e-04                     0.0e+00    OK    
dominant-coupling singular values             [3 elem]         [3 elem]                         0.0e+00    OK    
|forcing overlap with dominant mode|          1.411634e-04     1.411634e-04                     0.0e+00    OK    
|delta_nominal| of coil set 1                 7.035049e-05     7.035049e-05                     0.0e+00    OK    
||ddelta/d(shift)|| over coil sets            1.245519e-04     1.245519e-04                     0.0e+00    OK    
||ddelta/d(tilt)|| over coil sets             4.176407e-07     4.176407e-07                     0.0e+00    OK    
PE plasma energy                              3.320677e+00     3.320677e+00                     0.0e+00    OK    
PE vacuum energy                              3.174510e+00     3.174510e+00                     0.0e+00    OK    
PE surface energy                             5.509017e+00     5.509017e+00                     0.0e+00    OK    
PE toroidal torque                            1.119454e-04     1.119454e-04                     0.0e+00    OK    
NTV torque FGAR [N·m]                         5.483495e-01     5.483495e-01                     0.0e+00    OK    
NTV kinetic energy dW FGAR [J]                8.163740e-02     8.163740e-02                     0.0e+00    OK    
Runtime (s)                                   292.0s           311.7s                                      --    
||forcing b~|| (root-area-weighted)           4.683330e-04     4.683330e-04                     0.0e+00    OK    
resonant area-weighted field b^r              [5 elem]         [5 elem]                         0.0e+00    OK    
=================================================================================================================
Summary: 53 unchanged


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

Regression Report: solovev_n1
=================================================================================================
Ref 1: develop  @ bca7ce751 (2026-10-23)
       env: julia 1.12.6, x86_64-linux-gnu, manifest aa94b997 (pinned), 2 threads/1 BLAS, jit build
Ref 2: feature/local-stability-problem  @ 59b4b4de9 (2026-10-23)
       env: julia 1.12.6, x86_64-linux-gnu, manifest aa94b997 (pinned), 2 threads/1 BLAS, jit build
-------------------------------------------------------------------------------------------------
Quantity                      develop          feature/local-stability-problem  Diff       Status
-------------------------------------------------------------------------------------------------
total energy Re(et[1])        1.090560e-02     1.090560e-02                     0.0e+00    OK    
total energy Im(et[1])        2.141340e-10     2.141340e-10                     0.0e+00    OK    
plasma energy Re(ep[1])       -1.041640e+01    -1.041640e+01                    0.0e+00    OK    
vacuum energy Re(ev[1])       1.042730e+01     1.042730e+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)             287              287                              0.0e+00    OK    
ODE steps (total)             285              285                              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)          af93c6269245...  af93c6269245...                  identical  OK    
pressure profile (checksum)   e252b3b40ede...  e252b3b40ede...                  identical  OK    
ca_left (checksum)            06afdeb86cdc...  06afdeb86cdc...                  identical  OK    
Runtime (s)                   240.9s           245.2s                                      --    
=================================================================================================
Summary: 22 unchanged


================================================================
Case: solovev_kinetic_calculated — Solovev, n=1, kinetic_source=calculated, kinetic_factor=1.0, 50/50 D-T multi-ion kinetic matrices, mode-converged band (delta_m=8, mpert=32); pressure-consistent kinetic.dat (n·T tied to the Solovev P₀≈4.27e4 Pa).
================================================================

Regression Report: solovev_kinetic_calculated
=========================================================================================
Ref 1: develop  @ bca7ce751 (2026-10-23)
       env: julia 1.12.6, x86_64-linux-gnu, manifest aa94b997 (pinned), 2 threads/1 BLAS, jit build
Ref 2: feature/local-stability-problem  @ 59b4b4de9 (2026-10-23)
       env: julia 1.12.6, x86_64-linux-gnu, manifest aa94b997 (pinned), 2 threads/1 BLAS, jit build
-----------------------------------------------------------------------------------------
Quantity                  develop        feature/local-stability-problem  Diff     Status
-----------------------------------------------------------------------------------------
vacuum energy Re(ev[1])   1.054785e+01   1.054785e+01                     0.0e+00  OK    
q0                        1.900003e+00   1.900003e+00                     0.0e+00  OK    
||xi_clebsch_alpha||      7.717981e-03   7.717981e-03                     0.0e+00  OK    
total energy Re(et[1])    1.827879e+00   1.827879e+00                     0.0e+00  OK    
total energy Im(et[1])    -1.326825e+00  -1.326825e+00                    0.0e+00  OK    
plasma energy Re(ep[1])   -8.719975e+00  -8.719975e+00                    0.0e+00  OK    
ODE steps (saved)         608            608                              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)         607            607                              0.0e+00  OK    
mpert                     32             32                               0.0e+00  OK    
q95                       3.147422e+00   3.147422e+00                     0.0e+00  OK    
||Jb_theta_reg||          8.170337e-05   8.170337e-05                     0.0e+00  OK    
Runtime (s)               292.3s         292.2s                                    --    
||dxi_clebsch_psi/dpsi||  4.843513e-02   4.843513e-02                     0.0e+00  OK    
singular q values         [2 elem]       [2 elem]                         0.0e+00  OK    
npert                     1              1                                0.0e+00  OK    
=========================================================================================
Summary: 17 unchanged

Notes for reviewers

  • Same code path for deck and script. main builds the Ballooning from [LocalStability] (unknown keys raise) and calls the same solve a script calls, before ForceFreeStates, with its own banner and Info/Runtimes/local_stability entry. The section's presence runs the stage; there is no enabled key. main also returns the result as local_stability.
  • Decoupled from ForceFreeStates. local_stability_flag left ForceFreeStatesControl, and solve(::EulerLagrangeProblem) no longer runs the scan (it used to compute it and drop it when HDF5 output was off).
  • Bit-identical by construction. D_I and D_R keep the old (D·ψ)/ψ arithmetic, and SingularSurfaces/D_I still evaluates a Series spline of D_I·ψ at the singular surfaces: a plain-vector spline differs by up to 6e-14 off the knots, a one-column Series matches the old 5-column one exactly (checked at 2001 points on the DIII-D-like and Solovev decks).
  • New knobs default to the old constants: psi_window = (0.1, 0.99), max_alpha_scale = 8, alpha_tol = 1e-3, ode_tol = 1e-8. Every example and fixture deck lists all eight keys at their defaults.
  • No new HDF5 writer. write_outputs_to_HDF5 takes the LocalStabilityResult in place of the spline and α-boundary tuple and writes the same paths, with the same empty datasets when the stage is off; the save-method refactor will move this block.
  • ForceFreeStates - FEATURE - 🌱 Make the ballooning alpha-boundary scan optional #494 adds alpha_boundary_scan to ForceFreeStatesControl; here the same knob, same name, lives on Ballooning and [LocalStability].
  • Not in this PR: pruning the uncalled ballooning scan functions (second_critical_ballooning_alpha and the q′ family), held until ForceFreeStates - FEATURE - 🌱 Make the ballooning alpha-boundary scan optional #494 is settled.
  • Tests: new test/runtests_localstability.jl (kernels, α window, knobs, solve writes no file, deck/API parity on the returned arrays and the HDF5 file, deck section parsing), plus a rerun check that the section replays.

🤖 Generated with Claude Code

@logan-nc logan-nc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Read in full, plus the 25 decks. Two decisions for you, then ready to approve.

  1. local_stability_flag is not in _DEPRECATED_FFS_KEYS (nor the pre-commit regex). Every deck that exists today has it, so every one of them now fails with an unknown-keyword MethodError rather than a message saying "use a [LocalStability] section". One tuple entry fixes it; I left it to you because it is src/ (stamp) and because #569 turns that list into a hard stop, which is the message you probably want here. It also collides with #546's two-pass test override (noted there).
  2. verbose no longer follows [ForceFreeStates] verbose: Ballooning.verbose defaults to true, so the bare [LocalStability] sections the three benchmark scripts generate now log verbosely. Fine if intended; one line in the body would do.

Smaller: Info/Runtimes/force_free_states shrinks by the local-stability time, which is now its own Info/Runtimes/local_stability entry; worth a line in the release note since people diff those.

Checked and clean: section coverage is exact (the 25 = true decks gain the block, the 2 = false decks lose the key, 19 example blocks are byte-identical, every line annotated per toml-conventions); no local_stability_flag/run_local_stability/compute_local_stability leftovers; Ballooning fields, local_stability_algorithm's key check, kernels' kwargs and api.md agree; HDF5 paths unchanged; harness diiid_n1 D_I/D_R/Δ' checksums identical.

Pushed 628ddfb (docs only): stability.md listed three diagnostics and omitted the α-boundary datasets the new section writes.

Merge-forward: 25 behind develop; git merge-tree shows 4 conflict hunks, all in GeneralizedPerturbedEquilibrium.jl.

@logan-nc

Copy link
Copy Markdown
Collaborator

Heads-up: GitHub did not start the pull-request workflows on 628ddfb (my push above); no check runs exist for it after an hour although Actions itself is fine. A close/reopen or your next push will re-fire them. The change is non-src, so the harness stamp is unaffected.

@matt-pharr
matt-pharr added this pull request to stack #592 October 26, 2026 09:41
@matt-pharr
matt-pharr force-pushed the feature/local-stability-problem branch from 6d365b3 to 342a1b2 Compare October 26, 2026 10:08
@matt-pharr

Copy link
Copy Markdown
Collaborator Author

Thanks, all addressed; the branch is now rebased onto develop (578376d), which also re-fired CI.

  1. Added local_stability_flag to _DEPRECATED_FFS_KEYS and the pre-commit regex (342a1b2). Until Repo - API! - Delete the no-op KineticForces and PerturbedEquilibrium keys and stop runs that set deprecated keys #569 lands it goes through the current warn-and-ignore path, so an old deck warns and runs without the stage; with Repo - API! - Delete the no-op KineticForces and PerturbedEquilibrium keys and stop runs that set deprecated keys #569 it becomes the hard stop. I applied your Test - TEST - Add deck/API parity tests with the known drifts marked broken #546 note in the same pass: _stage_deck in runtests_solve_api.jl now does delete!(inputs, "LocalStability").
  2. Intended. The release note now says the scan logs verbosely by default and how to quiet it with verbose = false in [LocalStability].

Runtimes: added a release-note line about force_free_states shrinking by the new local_stability entry. The stage is now timed through _timed_stage like the others.

Merge-forward: done as a rebase rather than a merge. The 4 conflict hunks were resolved as develop's forcing_snapshot and run_response plus this PR's local_stability; the stale galerkin runtime entry stays deleted. Your docs commit is kept (ec0ba4b).

@matt-pharr
matt-pharr requested a review from logan-nc October 26, 2026 10:10
@matt-pharr

Copy link
Copy Markdown
Collaborator Author

@logan-nc #569 should be merged before this in order to deal with merge conflicts FYI

@logan-nc

Copy link
Copy Markdown
Collaborator

Re-read 342a1b2 after the rebase. Everything from the first review is in:

  • local_stability_flag is in _DEPRECATED_FFS_KEYS and in the pre-commit toml-no-deprecated-keys regex; no example deck or test fixture still sets it.
  • _stage_deck drops the [LocalStability] section before extra_sections is merged, so the two-pass test no longer depends on the removed flag.
  • The stage is timed through _timed_stage in its do-block form, matching develop's signature; the single return path of main carries local_stability; the docs commit ec0ba4b survived the rebase.
  • verbose = true by default: your call, and the release note now says so.

One mismatch in the body: the release note says harness @ 342a1b2, but the pasted report compares develop@bca7ce751 with 59b4b4de9 from 10-23, which predates the rebase that touched GeneralizedPerturbedEquilibrium.jl. I have diiid_n1 running at the new head against develop 578376d73; I will approve with that table once it lands. Merge order noted: #569 first, then this, then #573.

@matt-pharr matt-pharr added this to the GPEC v2.0.0 milestone Oct 26, 2026
matt-pharr and others added 7 commits October 26, 2026 22:00
… the old key fail as an unknown keyword

No backward-compatibility code: remove the EulerLagrangeProblem error for local_stability_flag, its
deprecated-key pre-commit entry, the two tests asserting it errors, and the docs sentence naming it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ity stage

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186CLfjeJeSusNTva5RMfGU
@matt-pharr
matt-pharr force-pushed the feature/local-stability-problem branch from 342a1b2 to e018bce Compare October 26, 2026 21:00
@matt-pharr
matt-pharr merged commit 93bdcd3 into develop Oct 26, 2026
3 of 4 checks passed
@matt-pharr
matt-pharr deleted the feature/local-stability-problem branch October 26, 2026 21:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api Config key, output dataset or exported name changed changed-results Results move or an interface breaks - read before upgrading

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants