Repository navigation
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? |
…verlap point The overlap scan skipped multi-n runs because the overlap point depends on n. The domain must stop before the first overlap of any n, so run the scan for each n in nn_low:nn_high and let the one with the innermost overlap point bind; scans that find no overlap never bind. On the DIII-D-like equilibrium at psihigh = 0.9999, n=1 overlaps at 0.99975 and n=2 at 0.99935, and nn = 1:2 binds at n=2. The recorded LayerOverlap group is the binding scan, whose n dataset says which. Also stop wrapping the whole scan in a catch-all. resistive_layer_overlap already records expected per-surface failures in its notes and rethrows anything else, and the outer catch downgraded those code errors to a warning. Guard only the kinetic-file read, which is external input to an optional diagnostic. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…erlap cap Add an integration-domain truncation section to the stability page: how qhigh, psihigh and dmlim set psilim (including that dmlim never steps past qhigh or the cap), the resistive-layer overlap criterion and how the scan locates it, why the cap is off by default, the truncation example that exercises it, and the LayerOverlap datasets. Add Fitzpatrick (2025) to the citations page, cross-linked with the new section. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The truncation deck runs with vac_flag = false, which leaves every FreeBoundaryStability dataset empty and writes no fixed-boundary energy, so extracting ep_real failed the case. Track the domain selection and the scan's layer widths, which is what the deck exists to exercise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
@d-burg I was excited to see the commits roll in! Whats the status now? Still working through any todos? |
…cati solve The scan called slayer_layer_thickness, which always runs the stiff riccati_del_s solve, and dropped any surface whose delta_s came back non-finite. The criterion uses only delta_dr, which is closed-form algebra, so a Riccati failure deleted a surface the criterion could score: _first_overlap_limit then compared its neighbours across a gap twice the physical one, hiding an overlap and reporting too wide a domain. The m_max and max_layer_solves backstops both flag an inconclusive verdict; this deletion was silent. Split the algebraic scales out as slayer_algebraic_widths and score the criterion from those. delta_s is now best-effort: a failure leaves it NaN with a note and its own channel reports no overlap, while the criterion is untouched. A surface that genuinely cannot be scored on the DR channel is skipped with an explicit "not conclusive" note. Also guards the delta_s solve against throwing, which the per-surface guard did not cover once the scan's outer catch-all was removed. Measured unchanged on the DIII-D-like equilibrium at psihigh = 0.9999: the scan returns psihigh = 0.9997535788 over 14 surfaces with no notes, in 3.9 s per n. Gives LayerOverlapScan a keyword constructor -- the positional form takes 15 arguments in a fixed order, which is how an earlier empty-scan return came to pass 13 -- and uses it for the empty return and in the tests. The JET width check now takes the no-ODE path, with a new assertion that the two agree exactly, so it no longer pays a discarded stiff solve per evaluation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
refined_psi_grid's psihigh keyword and its _truncate_density helper are left from the design that re-formed the equilibrium on the capped domain, which the qlim cap replaced. No caller passes psihigh and no test covers either, so they shipped as untested API. _truncate_density also duplicated the outer node when psihigh landed exactly on an interior knot, giving a zero-width interval. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The truncation deck ran vac_flag = false, which writes no energies, so the case that exercises the cap tracked only domain bookkeeping: a change in what the capped domain does to the physics would have passed unnoticed. Run the deck free-boundary and pin ep_real and et_real alongside the domain quantities. That also moves the case off the fixed-boundary path, which reports a non-Hermitian W-inverse on this equilibrium at any psihigh. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…n prose The scan resolves its kinetic file from the run's TOML, so the programmatic solve(prob, alg) entry point cannot run it and psilim_from_layer_overlap is inert there. Say so in the control docstring and the stability page. Trim the psilim_from_layer_overlap docstring from a nine-sentence paragraph to the contract plus a pointer to the docs section, which is the canonical home. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…s from the SLAYER inputs The scan resolved its kinetic file from either [KineticForces] kinetic_file or [SLAYER] profile_file and re-read it with its own loader, which duplicated SLAYER's _load_profiles, ignored its profile_group, and never passed any diffusivity -- so every layer width was computed with the chi_perp = chi_tor = 1.0 defaults while the SLAYER analysis of the same surfaces used the file's chi(psi). On the shipped DIII-D-like profiles chi_perp is 1.25-3.29 m^2/s, and since delta_dr goes as chi_perp^(1/4) the scan's widths were 6-35% narrower than SLAYER's own. Build the SLAYER control from [SLAYER] and call Runner._load_profiles, so the scan sees the same profiles, HDF5 group and chi(psi), with the control's scalar fallback when the file carries no chi -- the precedence run_slayer uses. A deck without a [SLAYER] profile_file no longer runs the scan. At the unscaled profiles this leaves the overlap point unchanged at psihigh 0.9995 and 0.9999 (widths grow ~27% nearly uniformly) but moves it one surface inward, 0.99969 -> 0.99857, on a psihigh = 0.995 grid where the edge surfaces are extrapolated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ead of a tuned r² floor EDGE_FIT_MIN_R2 = 0.90 sat just under the worst real reconstruction then measured (0.929), so it was fitted to that sample rather than set by a principle, and the docstring already said it was not the discriminator: r2_log > r2_linear is, and the limited Solovev edge is rejected with the floor disabled entirely. Replace it as the pathology guard with a physical check. The fitted A in q = q_bar + A*(ln(1-psi) - u_bar) is the local logarithmic shear, so it must match -(1-psi)*q'(psi) over the fitted knots; accept agreement within a factor of EDGE_FIT_SHEAR_RATIO = 2. Across 14 real DIII-D reconstructions from IDA runs the ratio is 0.884-1.160, the synthetic DIII-D-like deck 0.948, and the limited Solovev edge 0.666. Keep EDGE_FIT_MIN_R2 = 0.5 as an explicit backstop against a fit that describes nothing, documented as such. Adds a test that the shear check is live: a tolerance tighter than the DIII-D-like fit's own disagreement must reject it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… so the overlap cut binds Scale chi_e (the chi_perp profile) in the shared DIII-D-like kinetic file by 2, from 1.25-3.29 to 2.50-6.59 m^2/s; chi_phi is unchanged, and the file's provenance attribute records the scaling. The profile is an electron power-balance chi used as a proxy for the one-fluid energy diffusivity the layer width wants, and that proxy is uncertain by a factor of this order. The doubled profile centres on the 4 m^2/s TJ uses as its default. With it the truncation deck's cut moves one surface inward: the cap pulls qlim from 8.35 to 7.0, dmlim steps back to 6.2, and q=7 and q=8 are excluded. The resulting domain, psilim = 0.99498 with q=6 the last surface and ep_real = -1.3485, reproduces the hand-chosen psihigh = 0.995 of the ideal deck (ep_real -1.34833). The factor sits well clear of both thresholds found on this equilibrium (the cut first moves at chi x1.26 and next at x~30). Moves results on decks that read chi from this file: SLAYER growth rates on the SLAYER deck, and the truncation case. The ideal deck's NTV does not read chi. The truncation deck now takes its profiles from a [SLAYER] section (enabled = false) rather than [KineticForces], since the scan reads only [SLAYER]. Also corrects 6de1b8f, whose message said that running this deck with vac_flag = true moved it off the path that reports a non-Hermitian W-inverse. That warning comes from the Newcomb zero-crossing check, which runs after every forward and Riccati integration regardless of vac_flag, so it still appears here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sivity each formula needs The critical-Δ source was cited as "Connor-Hastie-Helander 2015" in eight places. It is Connor, Ham, Hastie & Liu, PPCF 57, 065001 (2015), Eqs. (59) and (61), after Lütjens, Luciani & Garbet, Phys. Plasmas 8, 4267 (2001); the W_d loop and its short/long-mean-free-path chi_parallel blend follow Fitzpatrick, Phys. Plasmas 32, 062509 (2025). Both added to the citations page. Record that the two formulas fed by chi_perp want different quantities: the layer width goes through tau_E = r^2/chi_E, the one-fluid perpendicular energy diffusivity, while W_d describes electron-temperature flattening against an electron chi_parallel and so wants the electron channel. One profile, the kinetic file's electron power-balance chi_e, feeds both for now, matching TJ's single-chi convention; separating them is deferred because it needs a chi_i in the kinetic schema, and the upstream power-balance ion chi is not yet usable. Also brings the truncation docs in line with the scaled profiles and the SLAYER-only profile path: the overlap point is now psi ~ 0.9985, outside the 0.995 decks' domain but inside the SLAYER deck's 0.9995. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…profiles
The SLAYER layer and the resistive-layer overlap scan both used a scalar
Zeff, and the scan ignored the run's [SLAYER] settings altogether (Zeff = 1,
deuterium, default resistivity model and Coulomb logarithm).
Zeff is now a profile, taken in this order:
- a `zeff` dataset in the kinetic profile file, when present;
- otherwise quasi-neutrality with one impurity of charge `zimp`
(new [SLAYER] key, default 6): Zeff = zimp - (n_i/n_e)(zimp - 1);
- otherwise the scalar [SLAYER] `zeff`.
The run logs which source it used. `build_slayer_inputs` and
`build_ggj_inputs` accept Zeff as a scalar or a callable of psi, and the
overlap scan receives the run's own bt, mu_i, Zeff, theta, resistivity
model and Coulomb-logarithm form, so the scan and the SLAYER stage see one
plasma.
Results move for any deck whose kinetic file carries n_i: the shipped
SLAYER example goes from Zeff = 1 to about 1.9, which raises its resistivity
and changes its growth rates.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nt separatrix q-law
The overlap scan placed surfaces beyond the psi grid on a fitted edge law
that assumed q diverges at psi_N = 1. A reconstruction may put its boundary
slightly inside the X-point flux (psi_N = 1.0001 on the shipped DIII-D-like
file), so those surfaces were misplaced, and one could be dropped when the
fitted law and the traced edge q disagreed across an integer.
`separatrix_q_law` replaces the fit with the X-point form taken from the
equilibrium's own 2D flux,
q(psi) = q_anchor + sum_i A_i ln[(psi_Xi - psi_anchor) / (psi_Xi - psi)],
A_i = F / (2 pi R_Xi sqrt(-det H_i)),
anchored on the traced q at the outermost grid surface, so nothing is
fitted. The saddles are found by following each poloidal-field minimum on
the last closed flux surface to the critical point it leads to, which gives
one term for a single null and two for a double null and never picks up a
null the closed surface does not pass. The law is rejected when its shear
differs from the traced one by more than a factor 1.5 over the outer knots,
and is not built for inverse equilibria or limited plasmas.
In the scan:
- the outward search starts at the first m/n above the anchor q, so no
rational can fall between the in-grid search and the law;
- one rational past psi_N = 1 is placed, to judge the outermost surface
inside; the recommendation never exceeds psi_N = 1, and when no overlap
can be established the domain keeps its requested limit;
- the flux radial label takes q from the law beyond the grid;
- messages that qualify the verdict are returned as `caveats`, separate
from per-surface `notes`, and a run logs the first as warnings and the
second as info. Neither was shown before.
`edge_q_law` remains only as the grid-refinement edge-density gate.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- [SLAYER] settings reach the overlap scan; Zeff from the file dataset,
from n_i with `zimp`, and from the scalar fallback; ion mass and field
cancel in the Eq. (100) width.
- Separatrix q-law: the law's functions on a two-term case; the X-point
flux, coefficient and shear ratio on the DIII-D-like file; a law built
on a grid stopping at psi_N = 0.995 lands on the q = 7, 8 surfaces a
longer grid traces; the scan's overlap point does not depend on where
the grid stops; no law for an analytic equilibrium.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Update at
|
| Change | Commit | Why |
|---|---|---|
| Surfaces beyond the grid sit on the X-point separatrix law, taken from the 2D flux | 1ec489c6b ! |
The fitted law assumed q diverges at ψ_N = 1. The X-point is at ψ_N = 1.0001 on the shipped file and further out on some reconstructions, which misplaced surfaces and could drop one. |
psihigh is always the upper limit |
1ec489c6b |
Only one rational past ψ_N = 1 is placed, to judge the last surface inside. If no overlap can be established the domain keeps its limit and the run warns. |
| Zeff(ψ) from the kinetic file, for SLAYER and the scan | 75457a1b3 ! |
The scan used Zeff = 1 and default closures whatever [SLAYER] said. New [SLAYER] zimp; a zeff dataset in the file overrides. Moves diiid_slayer_n1; independent of truncation. |
| Scan caveats and notes are logged | 1ec489c6b |
They were collected and never shown. |
Earlier changes (the dmlim bound, the closed-form criterion, the χ⊥ fix, the truncation deck) are unchanged and summarised in the description.
Is it firing?
Yes, on examples/DIIID-like_truncation_example (psihigh = 0.9999, flag on):
Layer-overlap scan (n = 1): q = 9/1 lies beyond the equilibrium's own boundary (ψ = 1), inside its X-point flux ψ = 1.000100; it is placed only to judge whether the outermost surface inside is clean, and needs kinetic profiles past ψ = 1 to be scored
Resistive-layer overlap caps the domain: qlim 8.352 -> 7.000 (psi 0.999900 -> 0.998508)
Setting psilim via dmlim: initial qlim = 7.000, dmlim = 0.200
Filtered 2 singular surface(s) outside integration domain: [([7], 7.0), ([8], 8.0)]
qlim = 6.200, psilim = 0.995
Where. The q = 7 and q = 8 layers are the first adjacent pair to overlap, so the cap is the q = 7 surface, and dmlim then steps inside it to qlim = 6.2. Five rational surfaces remain (q = 2–6) of seven.
How. Two adjacent layers overlap when the sum of their half-widths exceeds their separation in ψ. That ratio is 0.56 for the 6|7 pair and 1.24 for 7|8.
On real equilibria
Run outside the repository on DIII-D H-mode discharges, n = 1, χ⊥ = 4 m²/s, Zeff(ψ) from the measured profiles. Two sets of equilibria for the same discharges, which differ in where the boundary sits relative to the X-point.
Set 1: boundary within 2e-5 of the X-point flux (three discharges, two times each). The separatrix law applies on all six (shear ratio 0.987–1.001).
| case | first overlapping pair | ratio | overlap point ψ_N | qlim at psihigh 0.998 |
at 0.999 | at 0.9999 |
|---|---|---|---|---|---|---|
| A | 5|6 | 1.14 | 0.99847 | 4.2 (unchanged) | 5.2 → 4.2 | 6.2 → 4.2 |
| B | 5|6 | 1.30 | 0.99876 | 4.2 (unchanged) | 4.2 (unchanged) | 5.2 → 4.2 |
| C | 5|6 | 1.10 | 0.99862 | 4.2 (unchanged) | 4.2 (unchanged) | 5.2 → 4.2 |
| D | 5|6 | 1.25 | 0.99880 | 4.2 (unchanged) | 4.2 (unchanged) | 5.2 → 4.2 |
| E | 6|7 | 2.59 | 0.99969 | 4.2 (unchanged) | 5.2 (unchanged) | 6.2 → 5.2 |
| F | 6|7 | 2.56 | 0.99970 | 4.2 (unchanged) | 5.2 (unchanged) | 6.2 → 5.2 |
- The overlap point is the same to five digits whichever
psihighthe grid stops at. - At
psihigh = 0.998the cap never binds:dmlimalready stops inside it. It binds on one case at 0.999 and on all six at 0.9999.
Set 2: boundary at 99.9% of the X-point flux (the same three discharges and one more, eight slices). q reaches only 4.7–5.5 at ψ_N = 1, and no pair inside overlaps for any χ⊥ from 1 to 64 m²/s (closest ratio 0.48 at χ⊥ = 4). The next rational lies between the boundary and the X-point, where there are no profiles, so it cannot be scored: the scan reports that no overlap was established and the domain keeps psihigh. This is the fallback path, exercised by real data.
So the shipped example fires, and Set 2 does not, because of where each equilibrium's boundary sits, not because of the plasma.
Sensitivity to χ⊥ and Zeff (Set 1):
- Widths go as χ⊥^(1/4), so the result is quantised. The 5|6 pair starts to overlap at χ⊥ ≈ 1.4–2.7 m²/s on A–D and 4.9 on E, F; every case reaches
qlim = 4.2by χ⊥ = 16. A factor of two in χ⊥ moves the cut by at most one rational. - Zeff = 1 instead of the measured profile lowers the ratios by about 20% and changes the answer on two of six cases (A: 5|6 → 6|7; C: no overlap established). The measured Zeff matters.
- Multiplying χ_e by two as a stand-in for χ_e + χ_i is not recommended: the layer width wants the pressure-gradient-weighted mean of the two channels, not their sum.
How confident are we?
Solid
- Off by default, and safe when off: the scan then only writes diagnostics and warnings.
- Can only narrow the domain:
psihigh,qhighanddmlimstill apply inside the cap. - Matches the paper: Fitzpatrick's JET overlap points, ψ = 0.9985 (n = 1) and 0.9952 (n = 4).
- Independent of the grid: the overlap point does not move with
psihigh, in the repository's test and on the six real cases. - Fails towards "no cap", visibly: unreadable profiles, an unscorable surface, a limited edge or a law that fails its shear check each leave the domain alone and say so in the log.
Open
- Rationals beyond the reconstruction boundary cannot be scored without profiles there (Set 2).
- χ⊥ is a proxy (electron power-balance χ_e for the one-fluid χ_E), and the shipped file's is doubled so the test deck sits clear of threshold.
- Untested: disconnected double null. Not modelled: snowflake and x-point-target divertors.
- The two width channels disagree on where overlap starts; only Eq. (100) sets the domain.
- The NTV path does not yet read the new
zeffdataset.
Bottom line: ready as an opt-in diagnostic and optional cap. Not ready to be on by default.
🤖 Generated with Claude Code
|
@d-burg great figures. Good Zeff catch... whats the status in human words? close??? |
|
@logan-nc yes of course, just wanted to lay everything out for the record. The "Set 2: boundary at 99.9% of the X-point flux" point is the most concerning to me and I finally just got to the bottom of it. As you can see, in the default example TokaMaker g-file, every rational above the boundary q (8.79) lies at psi_N > 1 (these are closed surfaces inside the separatrix that the file drops)... The origin of this is the
Should I regenerate the D3D-like example without the profile padding, or just file an issue so this merges asap? With my chi_e boost + Zeff fix + small non-default pad of 1e-4 (vs the default 1e-2), the feature does fire on this Other than this issue:
|
…dary nulls and limited plasmas Saddle selection from the outermost surface usually also reaches the far secondary X-point of a single-null shape, and a limited plasma with an X-point nearby still gets a law; the source comments, docstring, the scan's no-law message and the stability docs claimed otherwise. The docs also name the usual cause of a boundary inside the X-point flux: TokaMaker's save_eqdsk with lcfs_pad and truncate_eq=True. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Wow. What a tricky nuance to find in the TokaMaker equilibrium. Good sleuthing @d-burg. I'd say that since this currently fires on the equilibrium we have, lets merge it now. This PR is getting old and a bit unwieldy, so lets avoid more creep. Similarly, I vote we do not stack this on #501 - it's better to get this foundation of truncation logic in, then naturally update the calculations as the layer models improve. If the #501 updates push the truncation too far out, then we may need to think about updating the example equilibrium to have You are the official assignee, so I'll let you do the honors. Thanks for getting this over the finish line. It's a really great feature! |
|
Wooooohooo!!! 🎉 Thanks @d-burg! |


Release note
diiid_slayer_n1, from two input changes: Zeff(ψ) is now derived from the kinetic file'sn_i(Zeff 1 → about 1.9 at its surfaces), and the shipped DIII-D-like kinetic file's χ⊥ is doubled. Growth rates go 2/1 194.1 → 224.8 s⁻¹ (+16%), 3/1 −247.5 → −283.1 s⁻¹ (+14%), 4/1 −1000.8 → −1116.7 s⁻¹ (+12%); LundquistSfalls 22–31%,P_perprises 38–57%,D_norm,tauk,Q_e,Q_iandQ_rootmove 8–12%, ω ≤ 0.01%. The other 227 tracked quantities across the 16 cases shared with develop are unchanged. Results also change, on no shipped deck, for runs withset_psilim_via_dmlim = trueand a finiteqhigh, wheredmlimcould previously step pastqhigh(qhigh = 4.0gaveqlim = 4.2, now3.2) (harness @ 484a95a)n_i: Zeff is now derived from it (new[SLAYER] zimp, default 6) instead of the scalar[SLAYER] zeff, which remains only as the fallback when the file has neithern_inor azeffdataset. To prescribe Zeff, add azeffdataset to the kinetic file. A deck withset_psilim_via_dmlim = trueand a finiteqhightruncates one rational lower wheneverqhighsits on a rational or less thandmlim/nabove one; raiseqhighabove(rational + dmlim)/nto keep the old domain.LayerWidthsgainsdelta_norm,delta_viscoanddelta_dr, so code constructing it positionally must supply them.build_slayer_inputsandbuild_ggj_inputsacceptzeffas a scalar or a callable of ψ. The shipped DIII-D-like kinetic file'schi_eis doubled.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, when
psilim_from_layer_overlap = true, use it as an upper bound on the stability integration domain.psihighstays the upper limit: the cap can only narrow the domain. The SLAYER layer and the overlap scan now take Zeff(ψ) from the kinetic profiles.What is wired
[ForceFreeStates] psilim_from_layer_overlap(defaultfalse, off on every shipped deck except the truncation example)Tearing.resistive_layer_overlap, run whenever[SLAYER]names a readableprofile_file, once pern; the innermost overlap point bindsψ_k − w_k/2 < ψ_{k−1} + w_{k−1}/2, withwthe closed-form Eq. (100) width (no Riccati solve); the cut is at ψ_{k−1}sing_lim!(...; psilim_cap=...): an upper bound onqlim, applied beforedmlim, which cannot step past itForceFreeStates/LayerOverlap/, written whenever the scan ranpsilimlands past the overlap point (flag on or off), and the scan's own caveats and notesThe scan uses the run's own
[SLAYER]plasma: profiles, χ⊥(ψ), Zeff(ψ),bt,mu_i, resistivity model and Coulomb-logarithm form, through SLAYER's own loader.New since the last review
1. Surfaces beyond the grid are placed on the X-point separatrix law, not a fit (
1ec489c6b,!).The fitted edge law assumed q diverges at ψ_N = 1. A reconstruction may put its boundary slightly inside the X-point flux — ψ_N = 1.0001 on the shipped DIII-D-like file — so extrapolated surfaces were misplaced, and one could be dropped when the fit and the traced edge q disagreed across an integer.
Equilibrium.separatrix_q_lawtakes the law from the equilibrium's own 2D flux:edge_q_lawremains only as the grid-refinement edge-density gate.2.
psihighis the upper limit, with a graceful fallback. At most one rational past ψ_N = 1 is placed, only to judge whether the outermost surface inside is clean. The recommendation never exceeds ψ_N = 1, and when no overlap can be established the domain keeps its requested limit and the run says so.3. Zeff(ψ) from the kinetic profiles (
75457a1b3,!). The scan had used Zeff = 1, deuterium and default closures whatever[SLAYER]said; SLAYER itself used a scalar Zeff. Both now use one profile, in this order: azeffdataset in the kinetic file; elsezimp − (n_i/n_e)(zimp − 1)with the new[SLAYER] zimp; else the scalar[SLAYER] zeff. The SLAYER stage logs which. This is what movesdiiid_slayer_n1(Zeff 1 → about 1.9 on the shipped profiles). It is independent of truncation and wants a SLAYER reviewer's eye.4. The scan's messages are now shown. They were collected but never logged. Messages that qualify the verdict (
caveats: an overlap that could not be established, a deciding surface placed on the law, a backstop reached) are logged as warnings; per-surface detail (notes) as info.Verified end to end
examples/DIIID-like_truncation_example(DIII-D-like equilibrium,psihigh = 0.9999, cap on; about two minutes):The q = 7 and q = 8 layers are the first adjacent pair to overlap (sum of half-widths over separation = 1.24; the 6|7 pair is at 0.56), so the cap is the q = 7 surface and
dmlimthen stops 0.2 past q = 6. The resultingpsilim = 0.99498reproduces the ideal deck's hand-chosenpsihigh = 0.995:Re(ep[1])−1.37193 against −1.37187,Re(et[1])0.80082 against 0.80084. The harness casediiid_truncation_n1pins this run, including theLayerOverlapoutputs.Re-run at this head with lower limits: at
psihigh = 0.999the cap still binds (qlim7.247 → 7.000, thendmlimto 6.2); at 0.998 and 0.995 it is inert andqlimis 6.2 fromdmlimalone.Tests (
runtests_layer_overlap_fitzpatrick.jl, 9 + 42 + 29 passing):[SLAYER]settings reaching the scan.qlim.Limits
psihighand the run warns. This arises only for g-files written with boundary padding, which puts ψ_N = 1 inside the X-point flux: TokaMaker'ssave_eqdskwithlcfs_padand the defaulttruncate_eq=Truecuts the equilibrium at 1 − pad and renormalises, so the X-point lands at ψ_N = 1/(1 − pad) (the shipped example is padded by 1e-4). On real DIII-D cases the first overlapping pair sits 2e-5 to 2e-4 inside the separatrix, so a truncating pad of 1e-3 hides it. EFIT-written files are not affected: 34 of 34 diverted ones checked have the X-point at ψ_N = 1 to within 6e-8. Writing withtruncate_eq=Falseavoids it: the flux map, F and p then reach the separatrix, and only the last entry of the file's q column is a stand-in (q at 1 − pad), which GPEC does not read because it integrates q from the flux map.chi_eis the electron power-balance value. The shipped file's χ⊥ is doubled so the truncation deck sits clear of threshold (a test-enabling choice, recorded in the file's provenance attribute).zeffdataset, and the X-point flux and per-surface Zeff are not written togpec.h5. Both are left for follow-up issues.solve(prob, alg)path.delta_sand Eq. 100) disagree on where overlap starts. Both are written; only Eq. (100) sets the domain, following the paper.Regression report
regress --refs 44e0afcc4,484a95ae2 --force— develop as merged into the branch against this head (the merge commit), all 18 cases; julia 1.11.6, CI-pinned manifest, 8 threads:236 quantities compared, 227 unchanged. The nine that move are all
diiid_slayer_n1. Unchanged there: the surfaces,r_s, the shear andiota_e, which do not depend on Zeff or χ⊥. The two causes pull the growth rates in opposite directions: measured separately on this branch before the develop sync, the doubled χ⊥ alone lowered the 2/1 rate by 11% and the Zeff profile alone raised it by 30%.Every other case is unchanged, including
diiid_n1, whose NTV reads neither.gal_resistive_peextracts nothing on either ref, develop included, so that predates this PR. Growth rates are e-folding rates in s⁻¹; the harness labelγ_Hzis a misnomer. The harness exits non-zero only becausediiid_truncation_n1's example directory does not exist on develop.diiid_truncation_n1is new, so it has values at this head only:The width array has 7 elements (q = 2–8) where the previous head had 15: surfaces past the first rational beyond ψ_N = 1 are no longer placed. Runtimes are not quoted; the run was not made under controlled load.
Notes for reviewers
rs_method = :flux, not the SLAYER default. Eq. (100) is not covariant, so it is valid only in the toroidal-flux label of the paper's Eq. (30).delta_s_absappears in a third HDF5 group, by the one-name-per-quantity rule.Earlier history of this PR (bugs found, review responses) — unchanged since the last review
dmlimcould step pastqhighand the cap (14d60b6b9,!):qhigh = 4.0gaveqlim = 4.2; it now steps back againstmin(qmax, qhigh, cap).f5c60e779,!): every width was computed with χ = 1 while SLAYER used the file's χ(ψ).cd6dce306,!): a faileddelta_ssolve used to drop a surface and hide an overlap.6781b639f,c02a3c43d,8cfbbee7e).nruns cap at the innermost overlap point acrossnn_low:nn_high.1 − ε_c; ψ[k−1] + w/2 exceeded ψ = 1 on 4 of 10 equilibria checked.cf85db8c5,!): shear consistency within ×2 replaces a tuned r² floor.9c1937bda): Connor, Ham, Hastie & Liu, PPCF 57, 065001 (2015).r_sinvariant to 1e-16 and the Δψ widths to ~7e-6 underpest/hamada/equal_arc.psihigh = 0.9995was declined: it adds the q = 7 surface and moves their results substantially (pe_toroidal_torquechanges sign), and with the flag on the cap would pull them back to the domain they already have.Validation
484a95ae2, no conflictsImportant
Do not merge without a fresh third-party human review. Both earlier approvals predate the changes listed under "New since the last review".
🤖 Generated with Claude Code