ForceFreeStates - FEATURE! - Cap the integration domain where resistive layers overlap - #298
ForceFreeStates - FEATURE! - Cap the integration domain where resistive layers overlap#298amlakbekaluachule wants to merge 14 commits into
Conversation
|
@logan-nc this PR does two things:
Let me know if anything looks off. |
|
I synced this branch with Worth knowing why the retarget was needed: The merge hit two conflicts, both caused by Two things worth a look before review: 1.
|
|
@d-burg whats the status of this? Should it be reviewed now? Or should it still be marked as draft? |
|
Fixed both flagged issues — SetResistiveWidthPsihigh.jl is now included/exported, and δ_FKR/δ_visco are documented in the LayerWidths docstring. |
|
@claude review this |
ReviewReminder (per repo policy): this PR must not be merged without third-party human review, regardless of any automated approval. Also — please run the regression harness ( Correctness / design concerns
Physics traceability
Style / consistency
Documentation / tests
None of the above touch the pre-existing |
StatusRewritten from the original 172-line contribution, which had zero call sites and compared layer widths in metres against positions in normalized flux. That file is gone. Feature — Default is off because enabling it crosses a rational surface on a diverted plasma: on Nine defects fixed along the way, all found by trying to make the feature trustworthy:
Verification — 122 + 67 + 59 + 32 + 14 tests; docs clean; the metres→flux conversion triple-checked three independent ways (the binding pair is 1.821 mm apart with half-widths summing to 2.338 mm, argued in pure metres); the back-off gate validated against ForceFreeStates energies as ground truth, and it flips one step before they degrade. Still open
|
|
@d-burg — ready for review, but please take #399 first. #399 is the one-line That ordering also separates the regression movement. Worth your attention specifically:
Everything except the opt-in flag is a bug fix that stands on its own — the field-line ODE |
|
@d-burg, as claude says this has hit a point where it needs your expert review. Workflows are in place to decide the truncation from the resonant layer widths. The issue is that it is reporting big widths and wanting to truncate way in (~0.9892 for DIII-D). I find this suspect, since Fitzpatrick had been finding things like to be very far out past 0.9999. In the process of adding the truncation logic, claude did convince me the units of the layer widths had been wrong in the original overlap-checking-loop (comparing layer widths of m to rational spacing in psin) and a conversion from m to psin was added... so maybe double check that? Do you think we need to try and reproduce the exact case from Fitzpatricks paper on this as a sanity check? |
|
@d-burg please hold for me to find time to skim the full diff. Quick thoughts from the comments:
Great. I like this simplification a lot. Open physics question: Do we want to always cut inside this stochastic boundary (cut off some valid region) or always get at least to this boundary (include a little invalid region in the name of making sure we capture all the valid region)? The former is numerically easier (we don't have to go out as far), but perhaps the later in more physical (debatable - lets debate it here a bit)?
Please explicitly confirm non of the r-vs-rho and other normalization bugs need to be extended to delta_s. Are we tripple sure this one is being implemented correctly for getting a Jacobian independent psi width (note that this is easily testable by running with Hamada and PEST)?
Do we want this to be true? If the feature is the future and how we want users to default use the code, then shouldn't we be shipping examples that use it (i.e. psihigh 0.9995 for all DIII-D examples)? The DIII-D like example inherited the dmlim truncation behavior only to make it easy to compare to fortran, but we've moved past that I think (and the bechmark_with_fortran script can easily account for it by defaulting to a dmlim type comparison). @jhalpern30 and @matt-pharr may want to add their two cents. |
|
@d-burg and I would like to request @jhalpern30's review. Primarily focus on the top level workflow, which now does not touch the psihigh for equilibrium reconstructions but does sets a psilim that the standard truncation logics (dmlim, max dW, etc.) cannot exceed. Also, please weigh in on this being a no-op on the shipped examples (i.e. should we change psihigh to 0.995 for the DIII-D examples). Of course, calling out AI nonsense is much appreciated too. @d-burg please do post replies to my comments above. You can assume my review becomes an approval if @jhalpern30 or another human looks through the diff and approves. |
baf6746 to
29df870
Compare
…e layer width
Adds three algebraic scales to `LayerWidths` alongside the Riccati `delta_s`:
`delta_norm` (r_s S^-1/3), `delta_visco` (the P^1/6 viscous-resistive broadening) and
`delta_dr`, the diffusive-resistive width of Fitzpatrick, Nucl. Fusion 2025
(doi 10.1088/1741-4326/ae4fdd) Eq. (100), which sets the edge layer-overlap criterion.
The mapping of Eq. (100) into SLAYER variables is not literal. The paper's tau_A
(its Eq. 74) carries no shear, whereas SLAYER's tau_h divides by n*s, so
lu = tau_R/tau_h = (n|s|) * (tau_R/tau_A). Substituting that cancels Eq. (100)'s
explicit (n|s|)^(-1/2) exactly, leaving
delta_dr = r_s^(3/2) * P_perp^(1/4) / ( lu^(1/2) * d_beta^(1/2) )
with no shear factor. Carrying the (n|s|)^(-1/2) on top of SLAYER's lu double-counts
the shear and understates the width by sqrt(n|s|) -- a factor of 6.6 at q=7 on the
DIII-D-like deck, enough to report no overlap anywhere.
Validated against the paper's own Sect. 5.8 JET model, which this reproduces at
Psi = 0.9985 for n = 1 (paper: 0.9985) and 0.9936 for n = 4 (paper: 0.9952).
Marked `!` because `LayerWidths` gains fields.
…er-overlap scan Factors the diverging edge model q ~ -A*ln(1-psi) out of the grid-refinement density floor into `edge_q_law`, so one fit serves both that floor and the resistive-layer overlap scan's search for rational surfaces beyond the equilibrium grid. A cubic in psi (on q or on iota) saturates instead of diverging and is not usable for the extrapolation; measured on the DIII-D-like deck, extrapolating from a grid ending at psi=0.97 to the true q=8 surface at psi=0.99976, the log law lands within 5% while a cubic iota undershoots by 18%. `edge_q_law` returns nothing when the diverging model does not describe the equilibrium, judged by knot count, sign of A, and the log fit beating a plain linear one. That test now gates the edge density floor, which was previously applied unconditionally: a limited plasma with finite edge q is no longer packed as if q blew up. Marked `!` because that changes the auto grid, and so results, for limited decks (Solovev, LAR); diverted decks are unaffected. Also adds an optional `psihigh` to `refined_psi_grid`, which truncates the measured knot density so a pass-1 equilibrium can supply the density for a reduced domain.
Locates the q = m/n rational surfaces, computes each one's resistive layer width, and reports the outermost psi at which adjacent layers are still separated -- the outermost domain the matched-asymptotic treatment supports, per Fitzpatrick, Nucl. Fusion 2025 Sect. 5.9. Surfaces beyond the equilibrium grid are located on the shared separatrix edge q-law, so the scan can report an overlap point lying outside psihigh; that is recorded rather than clipped, since it is the bookkeeping a user needs to see whether the domain was constrained by layer physics at all. Widths are compared in normalized flux, converted from metres by the Jacobian of whichever radial label `rs_method` selects -- the same label that sets the shear, tau_R and d_beta/r, because Eq. (100) is not covariant (delta ~ J^(1/2) under a change of radial variable) and is only valid in the coordinate it was derived in. On the DIII-D-like deck the label is worth one rational surface: the cut sits at psi=0.99752 under `:midplane` and 0.99939 under `:flux`, Fitzpatrick's Eq. (30) label. Reports both the |delta_s| and Eq. (100) channels separately so the two can be compared; they disagree by about two surfaces, which is the subject of issue #411.
…ve-layer overlap `sing_lim!` gains an optional `psilim_cap`. Where the resistive layers of adjacent rational surfaces overlap, no surface retains a well-separated inner region and matched asymptotics is not defined, so that location is an upper bound on the useful domain. It is applied as a cap on `qlim` rather than by assigning `psilim` directly, which keeps it a bound rather than a setpoint: the existing `dmlim` / `qhigh` truncation still selects the final surface from inside it, and a cap lying beyond `psihigh` is inert by construction with no special case -- it simply loses the `min`. The domain can therefore only shrink, never grow, so no equilibrium ever has to be re-formed. Measured on the shipped DIII-D decks with the Eq. (100) width in the `:flux` label, the cap changes no retained surface on any of them: it is beyond `psihigh` for the ideal (0.995), riccati (0.995) and gal_resistive (0.993) decks, and on the SLAYER deck (0.9995) it binds by 1.1e-4 in psi without crossing a rational. The scan needs kinetic profiles, which two of those decks do not carry, so it is inert there regardless.
…d Fitzpatrick JET case Rebuilds the model JET equilibrium of Fitzpatrick, Nucl. Fusion 2025 Sect. 5.8 from the paper's own definitions -- the Eqs. (32)-(35) safety factor with alpha-/alpha+ solved self-consistently against Eq. (36), and mtanh edge profiles anchored to its Fig. 8 -- and checks where adjacent resistive layers first overlap against the values the paper reports. The widths are driven through `slayer_parameters` and `slayer_layer_thickness` rather than recomputed here, so the test covers the shipped tau_R / tau_A / d_beta chain and the `delta_dr` formula itself. That distinction matters: the bug this test would have caught -- carrying Eq. (100)'s explicit (n|s|)^(-1/2) on top of a `lu` that already contains it -- lives in exactly that chain, and a test that reimplemented the width would have passed while the shipped one was wrong. Measured against the paper: Psi = 0.9985111 for n = 1 (paper 0.9985, deviation 1.1e-5) and 0.9949738 for n = 4 (paper 0.9952, deviation 2.3e-4). Bounds are set at 1e-4 and 1e-3, leaving roughly 9x and 4x margin; the model is quadrature and root-finding with no BLAS, so platform spread is orders of magnitude below either. The test logs both deviations so a future failure reports how far it moved rather than only that it moved. This is the suite's second check pinned to an external published reference rather than to GPEC's own history.
Adds an InnerLayer (SLAYER) section to the per-module citation list, covering the two-fluid slab layer paper the module is built on and the Fitzpatrick 2025 paper whose Eq. (100) width and Sect. 5.9 overlap criterion the layer-overlap scan uses. Both PDFs now live in docs/resources. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…escales and widths slayer_parameters took sval_r with its sign in five places that are magnitudes: the parallel diffusivity chi_par_lmfp (twice), the island width Wd, the :lar critical-Delta, and the Alfven time tau_h. On a reverse-shear surface tau_h came out negative, hence a negative Lundquist number, and tauk = lu^(1/3)*tau_h threw a DomainError before any layer quantity was produced. Found on DIII-D 153072_3415, whose q dips to 1.855 off-axis. The layer timescales and widths depend on |dq/dr|, not its sign -- the module's own delta_dr derivation already states the convention as (n|s|). abs(s) == s wherever the shear is positive, so no currently-working surface changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…erlap scan The per-surface guard caught only ArgumentError, so a DomainError from any single surface propagated and killed the whole scan. The scan already builds surfaces one at a time precisely so a degenerate one can be recorded and skipped; this makes that hold for the error type it is most likely to hit -- kinetic profiles that stop short of an extrapolated surface, or a fractional power reached with a negative argument. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… rather than an absolute r2 EDGE_FIT_MIN_R2 = 0.99 was calibrated against the synthetic TokaMaker deck (r2_log 0.9957, 0.9989). Real EFIT/CAKE reconstructions of diverted DIII-D plasmas carry reconstruction noise in the edge q and fit the log law at r2_log = 0.929-0.977, so every one of them was rejected: no surfaces were placed past the equilibrium grid and the layer-overlap scan reported "no overlap" on 8 of 9 real shots -- a silent fail-open on the exact case the criterion exists for. No absolute threshold can do this job: the limited a10 fixed-boundary deck sits at 0.972, inside the diverted band. The r2_log > r2_linear comparison does separate the classes (diverted 0.96 vs 0.75; Solovev 0.760 vs 0.9996) and is unchanged. The floor is lowered to 0.90 rather than removed so a fit describing nothing is still rejected. Carries ! because the gate also controls the auto-grid edge density floor: an equilibrium that was previously rejected now receives it, changing its refined grid. Every shipped deck is unaffected (the DIII-D-like deck passed both thresholds; Solovev and LAR reject on the relative test; a10 rejects on knot count), so no harness case moves -- but a user running a real reconstruction on grid_type = "auto" will see a different grid. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…f a layer inside it The scan returned psi[k-1] - w[k-1]/2, which drops the outermost surface the criterion found to be clean. Fitzpatrick (2025) Sect. 5.9 retains the rational surfaces in 0 < psi < 1 - eps_c, and psi[k-1] is 1 - eps_c: the surface itself is the boundary. The branch's own external check, runtests_layer_overlap_fitzpatrick.jl, already reports Psi_of(S[k-1]) and matches the paper's 0.9985 / 0.9952 with it, so the scan and its validation were using different conventions. Measured over ten equilibria (the shipped DIII-D-like deck and nine IDA DIII-D reconstructions), this moves the limit outward by 1.2e-3 to 4.6e-3 in psi, which is consistently one rational surface. The symmetric alternative, psi[k-1] + w[k-1]/2, is not usable: it exceeds psi = 1 on four of the ten, and on those same four it also overshoots surface k, the layer half-widths there being larger than the surface separation. Also refreshes four docstrings left stale by earlier revisions: the psihigh field described a "smaller of the two" rule the code no longer uses, psihigh_dr was undocumented, the psi_cap note described a finite-difference stencil clamp removed with the analytic radial_label derivatives, and the finite-width guard cited the same defunct rule. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
@logan-nc all three taken, and you were right that the entry never landed — the branch rewrite dropped
In-source annotations already cite it ( One correction while I was in the paper: the figures are Fig. 9 for n = 1 and Fig. 10 for n = 4, not 10 and 11. The abstract states ε_c = 1.5e−3 and 4.8e−3 directly, and |
The convention question, settled with data@logan-nc you asked whether to cut inside the overlap boundary or reach it. I ran it across ten equilibria — the shipped DIII-D-like deck plus nine IDA DIII-D reconstructions — and the answer is neither of the half-width options. All values in ψ, n = 1, Eq. (100) width:
Changed in |
|
@logan-nc on your δ_s question from 2026-08-21: Confirmed clean, by measurement rather than assertion. I took your suggestion and ran it under three jacobians (
The ~1e-6 floor on the widths is the equilibrium solve's own coordinate-dependent numerical noise propagating through the Riccati layer solve, not a coordinate artifact — the inputs feeding it are invariant to 1e-14. Same result under the The check did find something adjacent, though it does not touch the truncation: Your third question — whether this should stay a no-op on the shipped decks — I'll answer separately; worth noting for @jhalpern30 / @matt-pharr that the decks are not uniform today: SLAYER and IMAS are already at |
Status: moved to draft; two bugs found against real data
Scanning ten real equilibria to settle the convention question turned up two things worth knowing. 1. The edge q-law gate rejected 8 of 9 real DIII-D reconstructions (
|
| class | case | r²_log | r²_linear |
|---|---|---|---|
| diverted (real) | five DIII-D reconstructions | 0.929–0.977 | 0.713–0.773 |
| diverted (synthetic) | TokaMaker D3D-like | 0.9957, 0.9989 | — |
| limited | a10 fixed-boundary | 0.972 | — |
| limited | Solovev | 0.760 | 0.9996 |
No absolute threshold can do this job: the limited a10 deck sits at 0.972, inside the real diverted band. The old 0.99 only appeared to work because it was tuned against synthetic data that happens to be smooth. The r²_log > r²_linear comparison does separate the classes and is unchanged.
Why 0.90 rather than removing the absolute test: it is a floor against a fit that describes nothing at all, kept deliberately weak so the relative test does the discriminating. It clears the measured diverted band (0.929–0.977) with margin while still rejecting Solovev-class fits outright. I would have dropped the absolute test entirely except that only one shipped limited deck (Solovev) actually exercises the relative rejection — a10 rejects on knot count before reaching it — which is thin evidence for removing a safety check. If someone has a limited deck with a well-fitting log edge, that is the case that would change my mind.
Carries ! because the same gate controls the auto-grid edge density floor: an equilibrium previously rejected now receives it and gets a different refined grid. Every shipped deck is unaffected (the DIII-D-like deck passed both thresholds; Solovev and LAR reject on the relative test; a10 on knot count), so no harness case moves — but a user running a real reconstruction on grid_type = "auto" will see a different grid.
2. Reverse shear crashed the layer build — split out as #431
slayer_parameters used the signed r-based shear in five expressions that are magnitudes, so a negative-shear surface produced a negative Lundquist number and tauk = lu^(1/3)·tau_h threw a DomainError before any layer quantity existed. Shot 153072_3415 has q dipping to 1.855 off-axis, giving two q = 2 surfaces with the inner one on the negative-shear branch — and _find_rational_surfaces is deliberately reverse-shear-safe, so it hands the layer build exactly those surfaces.
Pre-existing on develop, not introduced here, so it is its own PR. Cherry-picked onto this branch so the scan can run that shot; the copy resolves away once #431 merges.
Also 6781b639f: a single failing surface no longer aborts the whole scan — the per-surface guard caught only ArgumentError, so one bad surface took down the run instead of being noted and skipped.
Other changes since you last looked
- Rebased onto the rebased Tearing - BUGFIX! - Convert Δ' to the r_s reference length before slab-layer matching #403;
sing_lim!moved tosrc/ForceFreeStates/Surfaces/Finding.jlin the ForceFreeStates - REFACTOR - Reorganize ForceFreeStates into subdirectories (pure move) #400 split. - The
diiid_slayer_n1γnoise_thresholdbump (1e-1 → 2.5e-1) is pulled out of this PR so a widened acceptance criterion is reviewed as its own decision rather than riding along with a feature. It is a real effect — same-source fresh re-runs step by 0.076/0.122/0.145 Hz — but the underlying cause is the nondeterministic threaded root search (Regression - BUGFIX - Fix two silent failure modes: PATH julia resolution and single-sample result caching #418), and that is what should actually be fixed. InnerLayer.SLAYER - BUGFIX - Use the shear magnitude in the layer timescales and widths #431's harness run is a clean illustration: a provably inert change trips the current bound.
…ve-layer overlap point Wires the resistive-layer overlap scan into the stability workflow. The scan runs whenever kinetic profiles are readable and its result is always recorded under ForceFreeStates/LayerOverlap/, so a run shows where layer physics would cut the domain whether or not it did. The cap is applied only under the new opt-in ForceFreeStates control psilim_from_layer_overlap (default false). Architecture as agreed on the PR: an upper bound on qlim at the top of sing_lim!, so dmlim / qhigh still select the final surface from inside it and a bound beyond psihigh is inert by construction. Nothing re-forms the equilibrium. Profiles are taken from whichever of [KineticForces] kinetic_file or [SLAYER] profile_file exists on disk -- the shipped DIII-D SLAYER deck carries only the latter, so keying on the NTV path alone would have left the feature dead on the one deck it matters for. The scan is driven at rs_method = :flux rather than the SLAYER default. Eq. (100) is not covariant and is anchored to the toroidal-flux label of the paper's Eq. (30); running it in the midplane label would apply the formula in a coordinate it was not derived in. Verified end to end on the DIII-D-like SLAYER deck. At its shipped psihigh = 0.9995 the overlap point (0.999695) lies outside the domain, so the scan is recorded with applied = 0 and nothing moves. Raising psihigh to 0.99995 makes it bind: qlim 8.536 -> 8.000 at psi 0.999756, dmlim then selects 7.200 from inside it and the q = 8 surface is filtered out. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ational surfaces are found The no-surface early return built LayerOverlapScan with 13 arguments for a 15-field struct (width_dr and first_overlap missing), a MethodError the pipeline's catch downgraded to a spurious scan-failure warning. Also make the JET reproduction fail loudly when its alpha fixed point or Psi=1.05 bracket degenerates instead of proceeding on a silent fallback, document the rs_method and max_layer_solves kwargs, and compress the investigation narratives to the reasoning they support. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n-failure diagnostics The applied flag records that the bound entered sing_lim! as an active cap, not that it set the final psilim (dmlim/qhigh may truncate deeper) -- say so in the schema. Carry the backtrace in the scan-failure warning so a programming error is not indistinguishable from a physics refusal, and compress the edge-fit floor comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Please ping me again when this is ready for review - it seems to still be being developed. Turning off auto-notifications on this for now to avoid my inbox being blown up |
|
Same @d-burg. It says
above but I see no comment explaining how/why it got promoted from draft. The SLAYER stack is big rn so I am a bit lost - hold our hands through this please 🙏 |
|
I put together a review package for this PR — the overlap criterion and cut conventions as one diagram, the always-recorded / opt-in-applied wiring, the ten-equilibrium convention evidence, and the bugs found en route. https://claude.ai/code/artifact/71b5b47a-7b76-45e8-9714-bd43041bcde4 |
Release note
psilim_from_layer_overlap, defaultfalse) and no shipped deck enables it. The edge q-law validity gate was corrected, which can change the auto-grid edge knot density for equilibria that the old gate wrongly rejected — no shipped deck is affected, but a real reconstruction ongrid_type = "auto"will see a different grid. (harness @ 6a75d0f), re-run after the develop sync merge and the review-fix commits moved src/ past the previous stampLayerWidthsgainsdelta_norm,delta_viscoanddelta_dr; code constructing it positionally must supply them.sing_lim!andrefined_psi_gridgain optional keywords and are otherwise unchanged.GPEC can now locate the point past which adjacent rational surfaces' resistive layers overlap — the outer limit of the matched-asymptotic treatment, after Fitzpatrick, Nucl. Fusion 2025 Sect. 5.9 — and use it as an upper bound on the stability integration domain
psilim. The overlap point is recorded whether or not it binds, so a run always shows whether layer physics constrained the domain.psihighis untouched and remains user-set.What is actually wired
[ForceFreeStates] psilim_from_layer_overlap(defaultfalse)Tearing.resistive_layer_overlap, run inprepare_force_free_states!whenever kinetic profiles are readablesing_lim!(...; psilim_cap=...)— an upper bound onqlim, applied beforedmlim/qhighForceFreeStates/LayerOverlap/— always written when the scan ranThe scan runs regardless of the flag; only applying the cap is gated. That is deliberate: a run should show where layer physics would have cut the domain even when it was allowed to. Profiles are taken from whichever of
[KineticForces] kinetic_fileor[SLAYER] profile_fileexists on disk.Architecture, as agreed with @logan-nc: an upper bound on
psilim, applied as a cap onqlimat the top ofsing_lim!, sodmlim/qhighstill select the final surface from inside it and a cap beyondpsihighis inert with no special case. Nothing re-forms the equilibrium, which removes the trial/back-off loop,EdgeQuality, and the immutable-config collision of the earlier design.Verified end to end, both directions
On the shipped DIII-D-like SLAYER deck at its
psihigh = 0.9995, the overlap point is ψ = 0.999695 — outside the requested domain — so the cap is inert,applied = 0, and nothing moves. Raisingpsihighto 0.99995 makes it bind:Worth knowing: because the limit lands on a rational surface's own position,
dmlim'struncthen backs off to the rational below it (7.200 here). Withset_psilim_via_dmlim = true— the default for diverted plasmas — the dmlim floor therefore absorbs the difference between cutting at the last clean surface and cutting half a layer width inside it. That is dmlim doing its job (it exists to avoid ending the integration on a rational), but it means the convention choice matters less in practice than the raw ψ values suggest.The domain-limit convention
The scan cuts at ψ[k−1], the position of the last surface with a well-separated inner region. This is the paper's own
1 − ε_c, and it is whatruntests_layer_overlap_fitzpatrick.jlreports in order to reproduce the paper's Ψ = 0.9985 / 0.9952.Settled against ten equilibria (the shipped deck plus nine IDA DIII-D reconstructions) rather than by preference — the two half-width alternatives both fail:
Full per-case table in the thread.
Bugs found while making the feature trustworthy
1. The edge q-law gate rejected 8 of 9 real DIII-D reconstructions (
d4ce058ff, carries!).EDGE_FIT_MIN_R2 = 0.99was calibrated against the synthetic TokaMaker deck (r²_log 0.9957, 0.9989). Real EFIT/CAKE reconstructions carry noise in the edge q and fit at 0.929–0.977, so all were refused: no surfaces placed past the grid, and the scan reported "no overlap" — a silent fail-open on exactly the case the criterion exists for. No absolute threshold can separate the classes (the limited a10 deck sits at 0.972, inside the diverted band); ther²_log > r²_linearcomparison does, and is unchanged. Lowered to 0.90 as a weak floor rather than removed. The!is because the same gate controls the auto-grid edge density floor.2. Reverse shear crashed the layer build — split out as #431, being pre-existing on develop. #431 has since merged; the cherry-picked copy resolved away in the develop sync merge (
7e9e66508).3. A single failing surface aborted the whole scan (
6781b639f). The per-surface guard caught onlyArgumentError.4. The no-surfaces path threw instead of returning an empty scan (
c02a3c43d, found in adversarial review). The early return builtLayerOverlapScanwith 13 arguments for a 15-field struct — aMethodErrorthe pipeline's blanket catch downgraded to a spurious "scan failed" warning. Fixed, with a regression test forcing the path (m_max=0, extrapolation off), and the catch now logs the backtrace so a programming error is distinguishable from a physics refusal.Jacobian independence
@logan-nc asked for confirmation that
delta_sgives a Jacobian-independent ψ width. Measured underpest/hamada/equal_arc:r_sinvariant to 1e-16,da/dψto 3e-14,|δ_s|and both Δψ widths to ~7e-6 (the equilibrium solve's own noise — the inputs are invariant to 1e-14). Same under:flux. Confirmed clean.The check did find that
:fsaand:halfwidthare coordinate-dependent (up to 7% inr_s); those labels live in #403, are not defaults, and are not used here.Notes for reviewers
rs_method = :flux, not the SLAYER default. Eq. (100) is not covariant — δ ∝(q/|dq/dr|)^(1/2)— so it is only valid in the coordinate it was derived in, the toroidal-flux label of the paper's Eq. (30).delta_s_absnow appears in a third HDF5 group. The one-name-per-quantity rule says this is right, but @logan-nc asked to be told if it stayed ambiguous.psilim_from_layer_overlapis not in any shippedgpec.toml. It works as a@kwdefdefault; adding it to the SLAYER deck is bound up with the open question below.psihigh = 0.9995, ideal and riccati at 0.995, gal_resistive at 0.993.Regression report
Current head:
regress --cases diiid_slayer_n1,diiid_n1_riccati,solovev_n1 --refs 6d25c7739,6a75d0f50 --force(this branch's merge-base on #403's branch vs this head, so the comparison isolates exactly this PR's commits; raw shas, both sides fresh, julia 1.11.6, pinned manifest):The lone γ line is the threaded dispersion root search's known discrete reproducibility step (#420) — the identical 1.217e-01 value has been measured in same-commit-vs-itself forced runs — and every layer input on that case is bit-identical, so it is jitter, not this diff. An earlier run of the pre-rebase series (base
29df8700evsc9569c0ec; solovev_n1, solovev_multi_n, diiid_n1, diiid_slayer_n1) came back 99 tracked quantities, none changed.That is the expected result and the reason to trust the default-off claim: the feature is opt-in, no shipped deck enables it, and the corrected edge q-law gate does not alter any shipped deck's grid (the DIII-D-like deck passed both the old and new thresholds; Solovev and LAR reject on the relative log-vs-linear test either way; a10 rejects on knot count).
Coverage gap, stated rather than hidden: the
edge_q_lawgate change is exercised bydiiid_n1anddiiid_slayer_n1(both auto grids, both unchanged) but by no limited deck on an auto grid, because every Solovev/LAR case usesgrid_type = "ldp"with fixedmpsi. The gate's limited-plasma branch is therefore argued from measured r² values rather than covered by a harness case. A Solovev auto-grid case would close this.Validation
julia -t 4 --project=. test/runtests.jlon the develop-sync merge7e9e66508: 64 testsets, 2390 assertions, 0 failures6a75d0f50in a fresh worktree: JET reproduction 7/7, cap wiring 12/12 (includes the new empty-scan regression test)runtests_layer_overlap_fitzpatrick.jlreproduces the paper's overlap boundary at Ψ = 0.9985111 (paper 0.9985) for n = 1 and 0.9949738 (paper 0.9952) for n = 4qlim/psilimin, a cap beyondpsihighis inert,nothingis a no-op, and the control defaults to offbugfix/slayer-dprime-reference-length; it will when the base is retargeted todevelopafter Tearing - BUGFIX! - Convert Δ' to the r_s reference length before slab-layer matching #403 mergesImportant
Stacked on #403. Base will be retargeted to
developonce that merges. Do not merge without human review.