Skip to content

Tearing - BUGFIX - Check the inner model and profile file only when tearing is enabled, and fail before the solve - #567

Merged
matt-pharr merged 4 commits into
developfrom
bugfix/tearing-enabled-checks
Oct 26, 2026
Merged

matt-pharr merged 4 commits into
developfrom
bugfix/tearing-enabled-checks

Conversation

@matt-pharr

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

Copy link
Copy Markdown
Collaborator

Release note

  • Audience: users
  • Numerical impact: none (harness @ 2f98eb9)
  • Migration: none.

A [SLAYER] section with enabled = false may again name a GGJ inner_model, for example when the section is kept only for the layer-overlap scan's profiles; GGJ tearing models are rejected only when tearing is enabled. With tearing enabled, an empty or unreadable profile_file now stops the run before the force-free-states solve, instead of warning there and failing in the tearing stage after every other stage has run. The same holds when no singular surface is in range, where the run used to finish with an empty tearing result, and for content errors in an existing file (a missing n_e, T_e or T_i, or zeff < 1).

Regression report

Run on ITER SDCC (sirius, 8 CPUs per task) with regress_fanout.sh --refs develop,bugfix/tearing-enabled-checks --cases diiid_slayer_n1,diiid_slayer_n1_coupled,diiid_n1_riccati, after merging develop.

================================================================
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: develop  @ 4c4126738 (2026-10-26)
       env: julia 1.12.6, x86_64-linux-gnu, manifest 4c9d54fa (pinned), 8 threads/4 BLAS, jit build
Ref 2: bugfix/tearing-enabled-checks  @ 2f98eb9fa (2026-10-26)
       env: julia 1.12.6, x86_64-linux-gnu, manifest 4c9d54fa (pinned), 8 threads/4 BLAS, jit build
--------------------------------------------------------------------------------------------
Quantity                            develop   bugfix/tearing-enabled-checks  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 Q_root [2/1,3/1,4/1]         [3 elem]  [3 elem]                       0.0e+00  OK    
SLAYER ω_Hz [2/1,3/1,4/1]           [3 elem]  [3 elem]                       0.0e+00  OK    
SLAYER γ_Hz [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    
SLAYER enabled flag                 1         1                              0.0e+00  OK    
Runtime (s)                         201.4s    206.9s                                  --    
============================================================================================
Summary: 17 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: develop  @ 4c4126738 (2026-10-26)
       env: julia 1.12.6, x86_64-linux-gnu, manifest 4c9d54fa (pinned), 8 threads/4 BLAS, jit build
Ref 2: bugfix/tearing-enabled-checks  @ 2f98eb9fa (2026-10-26)
       env: julia 1.12.6, x86_64-linux-gnu, manifest 4c9d54fa (pinned), 8 threads/4 BLAS, jit build
-------------------------------------------------------------------------------------
Quantity                     develop   bugfix/tearing-enabled-checks  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)                  199.0s    197.9s                                  --    
=====================================================================================
Summary: 7 unchanged


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

Regression Report: diiid_n1_riccati
============================================================================================
Ref 1: develop  @ 4c4126738 (2026-10-26)
       env: julia 1.12.6, x86_64-linux-gnu, manifest 4c9d54fa (pinned), 8 threads/4 BLAS, jit build
Ref 2: bugfix/tearing-enabled-checks  @ 2f98eb9fa (2026-10-26)
       env: julia 1.12.6, x86_64-linux-gnu, manifest 4c9d54fa (pinned), 8 threads/4 BLAS, jit build
--------------------------------------------------------------------------------------------
Quantity                       develop        bugfix/tearing-enabled-checks  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.031971e-01   8.031971e-01                   0.0e+00  OK    
plasma energy Re(ep[1])        -1.368534e+00  -1.368534e+00                  0.0e+00  OK    
vacuum energy Re(ev[1])        2.171732e+00   2.171732e+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)              94             94                             0.0e+00  OK    
ODE steps (total)              2632           2632                           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)                    173.8s         183.1s                                  --    
============================================================================================
Summary: 17 unchanged

Notes for reviewers

Follow-up to @d-burg's post-merge notes on #547.

  • validate keeps rejecting an unknown inner_model whether or not tearing is enabled, so typos are still caught; the three GGJ names count as known. Only the "GGJ tearing is not supported" rejection waits for enabled = true.
  • _overlap_slayer_inputs is where the deck first parses [SLAYER], before the force-free-states stage. With tearing enabled it now lets the profile-loading error through (an empty path or an unreadable file); with tearing disabled it still skips the scan with a warning. Checking earlier, right after the TOML is read, would mean restructuring main, which Repo - API! - Time stages in main and drop the runtimes keyword #552 and §7D are changing.
  • Tearing - BUGFIX! - Reject GGJ tearing models at parse time and stop swallowing stage errors #547's own release note was already corrected to say the rejection fires at the start of the force-free-states stage.

🤖 Generated with Claude Code

…earing is enabled, and fail before the solve

The layer-overlap scan parses every [SLAYER] section before the
force-free-states stage, so the GGJ rejection aborted decks that keep a
disabled [SLAYER] section only for its profiles. validate now rejects a GGJ
inner model only when tearing is enabled; an unknown inner_model is still an
error either way. With tearing enabled, an empty or unreadable profile_file
now fails at the start of the force-free-states stage instead of only
warning there and failing after every later stage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@matt-pharr matt-pharr self-assigned this Oct 23, 2026
@matt-pharr
matt-pharr requested a review from d-burg October 23, 2026 10:39
@github-actions github-actions Bot added the bugfix Something was wrong and now is not label Oct 23, 2026

@d-burg d-burg 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.

Thanks! I couldn't break either fix: a disabled [SLAYER] section can keep a GGJ inner_model, typos are still rejected, and an enabled section with an empty or missing profile_file now fails before the force-free-states solve instead of at the end. A few small claude things for your consideration, none blocking:

  1. Where the early check runs. It depends on _overlap_slayer_inputs staying an eager argument to prepare_force_free_states!. With main being restructured, it would be easy to make that call lazy (or gate it on psilim_from_layer_overlap) and quietly lose the check. Suggestion 1 adds a comment; a pipeline-level test, or the post-parse check you mentioned, would be sturdier.
  2. One behavior change for the release note. TearingProblem returns an empty result before loading profiles when there are no surfaces, so a deck with tearing enabled, a bad profile_file and no singular surfaces in range used to finish and now aborts. Content errors in an existing file (missing n_e/T_e/T_i, zeff < 1) also fire early now. I think that's right, but it's worth a clause.
  3. Wording. run_local_stability runs before the check, so "before the force-free-states solve" is more exact than "at the start of the force-free-states stage", and matches the title.

Suggestions 2 and 3 fix the inner_model docstring's "only option" and pin the disabled, empty-file branch in a test.

ffs_start = time()

locstab, ballooning_boundary = run_local_stability(ctrl, equil)
metric, mats, layer_overlap = prepare_force_free_states!(intr, ctrl, equil, kf_ctrl, kinetic_profiles;

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.

The early failure only works because _overlap_slayer_inputs is evaluated eagerly ahead of the solve. A note here protects it when main is restructured.

Suggested change
# _overlap_slayer_inputs also rejects an enabled [SLAYER] section's unusable profile_file, so keep it ahead of the solve.
metric, mats, layer_overlap = prepare_force_free_states!(intr, ctrl, equil, kf_ctrl, kinetic_profiles;

Comment thread src/Tearing/Runner/Control.jl Outdated
Comment on lines +19 to +20
- `inner_model` -- `:slayer_fitzpatrick` (default and only option; when `enabled`,
the `:ggj_*` models are rejected because GGJ tearing growth rates are not implemented)

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.

"Only option" now contradicts the disabled section exception.

Suggested change
- `inner_model` -- `:slayer_fitzpatrick` (default and only option; when `enabled`,
the `:ggj_*` models are rejected because GGJ tearing growth rates are not implemented)
- `inner_model` -- `:slayer_fitzpatrick` (default and only supported option; the `:ggj_*` names
are accepted only when `enabled = false`, because GGJ tearing growth rates are not implemented)

Comment on lines +237 to +242
# A disabled section feeds the scan whatever its inner model, and an unreadable file only skips the scan;
# with tearing enabled the same file is an error, raised here rather than after every other stage.
@test overlap_inputs(Dict("inner_model" => "ggj_ray")) !== nothing
@test (@test_logs (:warn, r"Layer-overlap scan skipped") match_mode = :any overlap_inputs(Dict("profile_file" => "missing.h5"))) === nothing
@test_throws ErrorException overlap_inputs(Dict("enabled" => true, "profile_file" => "missing.h5"))
@test_throws ErrorException overlap_inputs(Dict("enabled" => true, "profile_file" => ""))

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.

Claude: this pins the disabled side of the new empty-file short-circuit, and the comment now matches what's exercised (a missing file, not an unreadable one).

Suggested change
# A disabled section feeds the scan whatever its inner model, and an unreadable file only skips the scan;
# with tearing enabled the same file is an error, raised here rather than after every other stage.
@test overlap_inputs(Dict("inner_model" => "ggj_ray")) !== nothing
@test (@test_logs (:warn, r"Layer-overlap scan skipped") match_mode = :any overlap_inputs(Dict("profile_file" => "missing.h5"))) === nothing
@test_throws ErrorException overlap_inputs(Dict("enabled" => true, "profile_file" => "missing.h5"))
@test_throws ErrorException overlap_inputs(Dict("enabled" => true, "profile_file" => ""))
# A disabled section feeds the scan whatever its inner model, and a missing or empty file only skips the scan;
# with tearing enabled the same file is an error, raised here rather than after every other stage.
@test overlap_inputs(Dict("inner_model" => "ggj_ray")) !== nothing
@test (@test_logs (:warn, r"Layer-overlap scan skipped") match_mode = :any overlap_inputs(Dict("profile_file" => "missing.h5"))) === nothing
@test (@test_logs overlap_inputs(Dict("profile_file" => ""))) === nothing
@test_throws ErrorException overlap_inputs(Dict("enabled" => true, "profile_file" => "missing.h5"))
@test_throws ErrorException overlap_inputs(Dict("enabled" => true, "profile_file" => ""))

@matt-pharr matt-pharr added this to the GPEC v2.0.0 milestone Oct 26, 2026
@matt-pharr

Copy link
Copy Markdown
Collaborator Author

Thanks, Daniel. All three suggestions are applied in dc8356a.

  1. The comment now guards the call. A check right after parsing is the sturdier fix, but it means restructuring main, which Repo - API! - Time stages in main and drop the runtimes keyword #552 and §7D are already doing, so I've left it for there.
  2. The release note now mentions both: the no-surfaces case and content errors in an existing file also fail early.
  3. The release note now says "before the force-free-states solve".

…bled-checks

# Conflicts:
#	src/GeneralizedPerturbedEquilibrium.jl
@matt-pharr
matt-pharr merged commit 75395cb into develop Oct 26, 2026
2 of 3 checks passed
@matt-pharr
matt-pharr deleted the bugfix/tearing-enabled-checks branch October 26, 2026 21:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Something was wrong and now is not

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants