Repository navigation
InnerLayer.SLAYER - FEATURE! - Build P_perp from the perpendicular particle diffusivity by default and add an ion heat diffusivity profile - #501
Conversation
… kinetic HDF5 Add an optional chi_i (ion perpendicular heat diffusivity) dataset to the kinetic-profile reader and writer, alongside chi_e and chi_phi. Ship chi_i in the DIII-D-like example kinetic file, built like its chi_e: AOT CHI_I of DIII-D 147131 at 2300 ms, mapped rho_N -> psi_N with EFIT01 RHOVN, the spurious zero at psi_N = 1 dropped, Gaussian-smoothed with sigma = 12 of 201 rho_N points (the width that best reproduces the example chi_e from AOT CHI_E). Range 0.46-2.1 m^2/s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…i_perp to chi_perp_e New [SLAYER] P_perp_model selects how the perpendicular Prandtl number is built: - chi_perp_e (default, unchanged numbers): tau_R*chi_perp_e/r_s^2 - chi_perp_i: tau_R*chi_perp_i/r_s^2 (kinetic-file chi_i, scalar fallback) - P_phi: P_perp = P_tor (Fitzpatrick, Phys. Plasmas 30, 092512 (2023), Eq. 143) - D_perp: tau_perp = r_s^2/D_perp (Fitzpatrick, Phys. Plasmas 29, 032507 (2022)) - c_beta: C^2 = P_perp with C = c_beta (Park et al., Phys. Plasmas 29, 122505 (2022)) Breaking: the [SLAYER] chi_perp key is renamed chi_perp_e. chi_perp_e still sets the critical-Delta offset in every mode. Regression harness diiid_slayer_n1 develop vs local: 17 unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…= tau_R/tau_E) A single [SLAYER] tau_E [s] for the whole plasma sets P_perp = P_tor = tau_R/tau_E at every surface (Fitzpatrick, Phys. Plasmas 30, 092512 (2023), Eq. 131). validate() requires a positive tau_E when this model is selected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…. 16
P_perp_model = D_perp now builds D_perp per surface from Eq. 16 of Fitzpatrick,
Phys. Plasmas 29, 032507 (2022), with eta_perp = eta_par:
D_perp = c_beta^2 eta/mu0 + (2/3)(1 - c_beta^2)[eta_e tau_e/(1+eta_e) chi_perp_e
+ eta_i tau_i/(1+eta_i) chi_perp_i]
using the n, T_e, T_i log-gradients at the surface (Eqs. 4-6, written flat-density
safe). The scalar D_perp input is removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e DIII-D-like SLAYER example Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Effect of each
|
P_perp_model |
P_⊥ (2/1, 3/1, 4/1) | P_φ (2/1, 3/1, 4/1) | γ [Hz] (2/1, 3/1, 4/1) | Δγ vs default (2/1, 3/1, 4/1) | ω [Hz] 2/1 |
|---|---|---|---|---|---|
chi_perp_e |
50.3, 44, 35.7 | 34.7, 24.7, 19 | 192, -251, -1012 | — | -10527 |
chi_perp_i |
29.9, 26.4, 12.5 | 34.7, 24.7, 19 | 213, -274, -1217 | +11 %, -9 %, -20 % | -10523 |
P_phi |
34.7, 24.7, 19 | 34.7, 24.7, 19 | 206, -277, -1123 | +8 %, -10 %, -11 % | -10525 |
D_perp |
16.4, 17.5, 10.9 | 34.7, 24.7, 19 | 242, -296, -1249 | +26 %, -18 %, -23 % | -10516 |
c_beta |
0.0133, 0.0088, 0.00645 | 34.7, 24.7, 19 | 313, -588, -1669 | +63 %, -134 %, -65 % | -10133 |
tau_E |
34.5, 30.8, 27.6 | 34.5, 30.8, 27.6 | 206, -264, -1034 | +8 %, -5 %, -2 % | -10525 |
The Δγ column is (γ − γ_default)/|γ_default|, so + means more unstable. For scale, the run-to-run AMR noise on γ is about 0.01 %.
Takeaways
- Only
c_betachanges the answer qualitatively. Its P_⊥ = c_β² ≈ 0.01, three to four orders of magnitude below every other model. The 2/1 drive rises about 60 %, the stable 3/1 and 4/1 roots damp 65–135 % harder, and ω on the 2/1 shifts about 4 %. Every other model shifts ω by less than 0.1 %. - The χ-based models move γ by roughly 10–25 %.
chi_perp_iuses the example's χ⊥,i, which is about 0.4–0.6 of χ⊥,e at these surfaces. That halves P_⊥ and moves γ by about 10 % at the 2/1 and 3/1 and 20 % at the 4/1.D_perpgives the smallest P_⊥ of the transport-based models, so it moves γ the furthest, by 18–26 %.- Across these models, a lower P_⊥ makes the 2/1 more unstable and the 3/1 and 4/1 more stable.
P_phiandtau_Eland close together. With τ_E = 0.1 s, τ_R/τ_E happens to be close to the χ_φ-based P_φ on the 2/1.tau_Ealso overrides P_φ, so its 3/1 and 4/1 sit nearer the default. γ scales with the chosen τ_E, so these numbers only illustrate the model.chi_perp_e(the default) reproduces develop exactly; see the regression report in the PR description.
The unit tests (Pkg.test() at 27d0744 on feynman) pass.
🤖 Generated with Claude Code
Example kinetic-file χ profiles vs the DIII-D 147131 AOT sourceEach panel compares
At the SLAYER surfaces (new
Notes
🤖 Generated with Claude Code |
… and describe c_beta as the no-transport limit of D_perp The SLAYER file headers and the c_beta P_perp_model cited "Park et al., Phys. Plasmas 29, 122505 (2022)", which does not exist. The paper is J.-K. Park, Phys. Plasmas 29, 072506 (2022), doi:10.1063/5.0093079, as docs/src/citations.md already lists. Its Eq. 10 defines C² = c_β² + (1 − c_β²)K; C = c_β drops the thermal-conduction K, which makes P_perp = c_β² the χ⊥ → 0 limit of the D_perp model. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o feature/slayer-pperp-models
|
@logan-nc @d-burg This adds a variety of options for perpendicular diffusion inputs. It doesn't change the default yet, so selecting the chi_perp_e is the same as what currently exists. You can see in the comparison that the only really different one is the c_beta which is good news (and encouragement to have this increased fidelity vs the Fortran slayer implementation). I think that we should move to full D_perp as default since there is not a literature justification for chi_perp_e ~ D_perp and if people already need chi_phi and chi_perp_e inputs than its not too crazy to also have them input chi_perp_i. It's encouraging that the tau_E choice is right order of magnitude (using tau_E = chi_phi and P_perp = P_phi) so that is also a helpful, user friendly, and control friendly option. After this gets merged, then either @d-burg or I should make a PR changing the default to D_perp and removing the chi_perp_e and chi_perp_i options. Let me know if you have any questions on this. Thanks! |
|
At a high level, this looks good. I agree that if someone has chi_phi and chi_perp_e, they likely have chi_perp_i on hand. If they don't have things, or are lazy then letting them assert a "reasonable" tau_E for their device is a great way to get results quick. |
d-burg
left a comment
There was a problem hiding this comment.
Thanks -- the switch is cleanly done, the default path is the same arithmetic as before, and the rename is complete. I checked the new models against the papers: D_perp matches Fitzpatrick (2022) Eqs. 4–6 and 16, c_beta matches Park (2022) and is the exact χ⊥ → 0 limit of the coded D_perp, and P_phi matches Fitzpatrick (2023) Eq. 143. tau_E does not yet match Eq. 131 (inline at line 400). I also compared the old and new example file: only chi_i and the provenance text were added, and every pre-existing dataset is byte-identical.
A mix of top level feedback from myself and claude:
Blocking
- Docs CI. Two docstring lines parse as Markdown links (suggestion inline at
LayerParameters.jl:220). - Rebase onto develop. This now conflicts in 7 files since #298 landed. Note for the binary file: #298 raised
chi_einTkMkr_D3Dlike_Hmode_kinetic.h5, and this branch's copy predates that, sochi_ineeds to be added to develop's file rather than taking this branch's version, otherwise thechi_echange is undone. Thechi_i/chi_eratios in your table will change accordingly.
On making D_perp the default and removing chi_perp_e / chi_perp_i
I agree D_perp is the better-founded model, but I'd settle a few things before switching:
D_perpis Fitzpatrick's derived perpendicular particle diffusivity: c_β²η⊥/μ₀ plus (2/3)(1 − c_β²)(n T_e′χ⊥,e + n T_i′χ⊥,i)/p′, built from the electron and ion energy diffusivities. By comparisonchi_perp_eis an electron-only proxy, so I'd describe it in the docstring and release note as the backward-compatible choice rather than "the" P⊥. One caveat for the default: where the temperature profiles are flat, Eq. 16 reduces to thec_betafloor, which your table shows moves γ by 60–130 %, so flat-temperature regions of a real profile would change answers a lot.tau_Eneeds fixing before it is advertised as the quick option: Eq. 131's τ_E is the local r²/χ_E, not the whole-plasma confinement time, so as documented this mode is off by r̂_s²/κ_e (details inline).- χ⊥,e stays a required input in every mode because it sets the critical-Δ width, so it can't go away even if the model option does. This PR is already a breaking rename of a key every SLAYER deck sets; I'd rather agree the end state now so users migrate once.
- Please add one harness case with a non-default
P_perp_model(your table already has the reference numbers), so a later default change has a baseline.
Data question. The example file's provenance attribute now names DIII-D 147131 at 2300 ms as the source of chi_i; the previous text named no shot. Has releasing this AOT-derived profile in a public repository been cleared under the DIII-D data policy? Since the provenance is being rewritten anyway, it should also use the dataset names chi_e and chi_phi, not "chi_perp" and "chi_tor".
Small, outside the diff
docs/src/kinetic_forces.mddocuments the kinetic HDF5 schema withoutchi_iand callschi_e"the" perpendicular heat diffusivity; therun_slayerdocstring likewise mentions onlychi_e/chi_phi.- An old deck with
chi_perp = ...is rejected by the generic unknown-key error, which is the important part, but a one-line hint whenchi_perpis among the unknown keys ("renamed to chi_perp_e") would save users a search through several dozen names. - Fitzpatrick (2022) and (2023) are cited for three of the six models but aren't in
docs/resources; adding them would let later reviewers check the equations.
| - `chi_perp_i` -- ion perpendicular heat diffusivity [m²/s] (used by `P_perp_model=:chi_perp_i`) | ||
| - `dlnn`, `dlnTe`, `dlnTi` -- radial log-gradients of n, T_e, T_i in any common radial variable | ||
| (only their ratios enter; required by `P_perp_model=:D_perp`) | ||
| - `tau_E` -- whole-plasma energy confinement time [s] (required by `P_perp_model=:tau_E`) |
There was a problem hiding this comment.
These two lines are what fails the Documentation job: [m²/s] (used by ...) and [s] (required by ...) parse as Markdown links (the parser skips the space between ] and (), and this docstring is spliced into inner_layer.md. A comma after the unit bracket, as the build_slayer_inputs docstring already does, removes both. The suggestion also adds :D_perp to the models that use chi_perp_i.
| - `chi_perp_i` -- ion perpendicular heat diffusivity [m²/s] (used by `P_perp_model=:chi_perp_i`) | |
| - `dlnn`, `dlnTe`, `dlnTi` -- radial log-gradients of n, T_e, T_i in any common radial variable | |
| (only their ratios enter; required by `P_perp_model=:D_perp`) | |
| - `tau_E` -- whole-plasma energy confinement time [s] (required by `P_perp_model=:tau_E`) | |
| - `chi_perp_i` -- ion perpendicular heat diffusivity [m²/s], used by `P_perp_model=:chi_perp_i` and `:D_perp` | |
| - `dlnn`, `dlnTe`, `dlnTi` -- radial log-gradients of n, T_e, T_i in any common radial variable | |
| (only their ratios enter; required by `P_perp_model=:D_perp`) | |
| - `tau_E` -- whole-plasma energy confinement time [s], required by `P_perp_model=:tau_E` |
| # D_⊥ from Fitzpatrick, Phys. Plasmas 29, 032507 (2022), Eqs. 4-6 and 16 (η_⊥ = η_∥), with | ||
| # η_s/(1+η_s) = dlnT_s/(dlnn+dlnT_s) and τ = (T_e/T_i)(1+η_e)/(1+η_i) written flat-density safe. | ||
| frac_e = dlnTe / (dlnn + dlnTe) | ||
| frac_i = dlnTi / (dlnn + dlnTi) | ||
| tau_F = (t_e / t_i) * (dlnn + dlnTe) / (dlnn + dlnTi) | ||
| D_perp = c_beta^2 * eta / MU_0 + | ||
| (2 / 3) * (1 - c_beta^2) * (frac_e * tau_F / (1 + tau_F) * chi_perp_e + frac_i / (1 + tau_F) * chi_perp_i) |
There was a problem hiding this comment.
I checked this against Fitzpatrick (2022) Eqs. 4–6 and 16: the expression and the docstring formula match the paper. Two small things.
The intermediate ratios have a singularity the formula itself does not: with a vanishing ion (or electron) pressure gradient, frac_i and tau_F both go infinite and the product gives Inf·0 = NaN, so this throws although Eq. 16 is finite there. Writing the weights over one common denominator is the same arithmetic (η_e τ_e/(1+η_e) = T_e·dlnTe/dp and likewise for ions), leaves only p′ = 0 singular, and makes the physical content visible: the bracket is (n T_e′ χ⊥,e + n T_i′ χ⊥,i)/p′.
| # D_⊥ from Fitzpatrick, Phys. Plasmas 29, 032507 (2022), Eqs. 4-6 and 16 (η_⊥ = η_∥), with | |
| # η_s/(1+η_s) = dlnT_s/(dlnn+dlnT_s) and τ = (T_e/T_i)(1+η_e)/(1+η_i) written flat-density safe. | |
| frac_e = dlnTe / (dlnn + dlnTe) | |
| frac_i = dlnTi / (dlnn + dlnTi) | |
| tau_F = (t_e / t_i) * (dlnn + dlnTe) / (dlnn + dlnTi) | |
| D_perp = c_beta^2 * eta / MU_0 + | |
| (2 / 3) * (1 - c_beta^2) * (frac_e * tau_F / (1 + tau_F) * chi_perp_e + frac_i / (1 + tau_F) * chi_perp_i) | |
| # D_⊥ from Fitzpatrick, Phys. Plasmas 29, 032507 (2022), Eqs. 4-6 and 16 (η_⊥ = η_∥); the weights η_s τ_s/(1+η_s) equal n T_s'/p'. | |
| dp = t_e * (dlnn + dlnTe) + t_i * (dlnn + dlnTi) | |
| D_perp = c_beta^2 * eta / MU_0 + | |
| (2 / 3) * (1 - c_beta^2) * (t_e * dlnTe * chi_perp_e + t_i * dlnTi * chi_perp_i) / dp |
It would also help to say in the docstring what Eq. 16 implies at the edges, since users may be surprised: where T_e and T_i are flat, the χ terms vanish and P⊥ falls to the c_beta value (≈ 0.01 here), and where the species pressure gradients have opposite signs (hollow density) the weights can exceed 1. A test at a flat-temperature point would pin the first.
| chi_perp::Real, chi_tor::Real, | ||
| chi_perp_e::Real, chi_tor::Real, | ||
| m::Integer, n::Integer, | ||
| chi_perp_i::Real=1.0, tau_E::Real=NaN, |
There was a problem hiding this comment.
tau_E and the log-gradients default to NaN so that a model which needs them fails loudly, but chi_perp_i defaults to 1.0 m²/s, so :chi_perp_i or :D_perp called without it returns a plausible-looking number. I'd default it to NaN here and in build_slayer_inputs, and move the isfinite(...) && ... > 0 check out of the :D_perp branch to just after the if block so it applies to P_perp for every model: today a zero, negative or NaN χ⊥,i (a partly-zero profile, or spline undershoot) gives P⊥ ≤ 0 in :chi_perp_i mode with no error.
| + `:D_perp` -- τ_R/τ_⊥ with τ_⊥ = r_s²/D_⊥ and D_⊥ from Eq. 16 of Fitzpatrick, Phys. Plasmas 29, | ||
| 032507 (2022), with η_⊥ = η_∥: | ||
| D_⊥ = c_β² η/μ₀ + (2/3)(1 − c_β²)[η_e τ_e/(1+η_e) χ⊥,e + η_i τ_i/(1+η_i) χ⊥,i] | ||
| + `:c_beta` -- P_perp = C² with C = c_β (J.-K. Park, Phys. Plasmas 29, 072506 (2022), Eq. 10, with the |
There was a problem hiding this comment.
Small citation fix: in Park (2022) the C² term is in Eq. (9) and is defined in the text just below Eq. (10); Eq. (10) itself carries P, which is our P_tor. I'd cite "Eq. 9, with C² = c_β² + (1 − c_β²)K and K = 0" here and at line 423. On line 230, please also define τ_e = τ/(1+τ), τ_i = 1/(1+τ), η_s = dlnT_s/dlnn, and note that this τ is (T_e/T_i)(1+η_e)/(1+η_i), the opposite orientation to the tau = T_i/T_e field of SLAYERParameters.
| chi_perp_e = loaded.chi_perp_e === nothing ? control.chi_perp_e : loaded.chi_perp_e | ||
| chi_tor = loaded.chi_tor === nothing ? control.chi_tor : loaded.chi_tor | ||
| (loaded.chi_perp === nothing || loaded.chi_tor === nothing) && @warn( | ||
| (loaded.chi_perp_e === nothing || loaded.chi_tor === nothing) && @warn( |
There was a problem hiding this comment.
In tau_E mode P_tor is replaced by τ_R/τ_E, so the file's chi_phi and the scalar chi_tor are ignored; that is only stated in the slayer_parameters docstring. Please say it in the SLAYERControl docstring and the TOML annotation too (suggestion on the example deck), and log one @info when the mode is active. This warning should also depend on the model: as written it reports a chi_tor fallback in tau_E mode, and a chi_perp_e fallback in c_beta, P_phi and tau_E mode with dc_type = "none", where those values do not enter the result.
| chi_perp_e = 1.0 # fallback only; the kinetic file supplies χ⊥,e(ψ) | ||
| chi_perp_i = 1.0 # fallback only; the kinetic file supplies χ⊥,i(ψ) | ||
| chi_tor = 1.0 # fallback only; the kinetic file supplies χ_φ(ψ) | ||
| P_perp_model = "chi_perp_e" # Perpendicular Prandtl number model: "chi_perp_e", "chi_perp_i", "P_phi", "D_perp", "c_beta", or "tau_E" (needs tau_E [s]) |
There was a problem hiding this comment.
| P_perp_model = "chi_perp_e" # Perpendicular Prandtl number model: "chi_perp_e", "chi_perp_i", "P_phi", "D_perp", "c_beta", or "tau_E" (needs tau_E [s]) | |
| P_perp_model = "chi_perp_e" # Perpendicular Prandtl number model: "chi_perp_e", "chi_perp_i", "P_phi", "D_perp", "c_beta", or "tau_E" (needs tau_E [s]; also replaces the χ_φ-based P_tor) |
| # η_e = 2, η_i = 3, T_e = T_i ⇒ τ = 3/4, τ_e = 3/7, τ_i = 4/7 | ||
| d1 = slayer_parameters(; base..., P_perp_model=:D_perp, grads..., chi_perp_i=2.0) | ||
| D_hand = d1.c_beta^2 * d1.eta / MU_0 + (2 / 3) * (1 - d1.c_beta^2) * ((2 / 3) * (3 / 7) * 1.0 + (3 / 4) * (4 / 7) * 2.0) | ||
| @test d1.P_perp ≈ d1.tau_r * D_hand / d1.rs^2 rtol = 1e-12 |
There was a problem hiding this comment.
With T_e = T_i this check passes whichever way round the temperature ratio is written, and the struct's own tau is T_i/T_e, so I'd add a T_e ≠ T_i point (hand numbers: τ = 3/2, τ_e = 3/5, τ_i = 2/5; not run). One build_slayer_inputs test with P_perp_model=:D_perp would also cover the gradient forwarding from the profile splines, which is otherwise untested. I haven't run this test myself so this is just a suggestion, you should confirm on your end whether it checks out.
| @test d1.P_perp ≈ d1.tau_r * D_hand / d1.rs^2 rtol = 1e-12 | |
| @test d1.P_perp ≈ d1.tau_r * D_hand / d1.rs^2 rtol = 1e-12 | |
| # T_e = 2T_i ⇒ τ = 3/2, τ_e = 3/5, τ_i = 2/5: fixes the orientation of the temperature ratio | |
| d2 = slayer_parameters(; base..., t_e=2000.0, P_perp_model=:D_perp, grads..., chi_perp_i=2.0) | |
| D_hand2 = d2.c_beta^2 * d2.eta / MU_0 + (2 / 3) * (1 - d2.c_beta^2) * ((2 / 3) * (3 / 5) * 1.0 + (3 / 4) * (2 / 5) * 2.0) | |
| @test d2.P_perp ≈ d2.tau_r * D_hand2 / d2.rs^2 rtol = 1e-12 |
| ctrl.P_perp_model in _VALID_P_PERP_MODELS || | ||
| throw(ArgumentError("SLAYERControl: P_perp_model=$(ctrl.P_perp_model) " * | ||
| "not in $(_VALID_P_PERP_MODELS)")) | ||
| ctrl.P_perp_model !== :tau_E || (ctrl.tau_E !== nothing && ctrl.tau_E > 0) || |
There was a problem hiding this comment.
Missing, non-positive and NaN tau_E are caught here, but inf gets through and only fails later inside slayer_parameters, after the equilibrium and stability stages. Adding isfinite makes the two checks agree.
| ctrl.P_perp_model !== :tau_E || (ctrl.tau_E !== nothing && ctrl.tau_E > 0) || | |
| ctrl.P_perp_model !== :tau_E || (ctrl.tau_E !== nothing && isfinite(ctrl.tau_E) && ctrl.tau_E > 0) || |
| isfinite(tau_E) && tau_E > 0 || | ||
| throw(ArgumentError("slayer_parameters: P_perp_model=:tau_E needs a positive tau_E, got $tau_E")) | ||
| # P_⊥ = P_φ = τ_R/τ_E (Fitzpatrick, Phys. Plasmas 30, 092512 (2023), Eq. 131) | ||
| P_tor = tau_r / tau_E |
There was a problem hiding this comment.
I checked this against Fitzpatrick (2023), Phys. Plasmas 30, 092512. Eq. 131 is P⊥ = P_φ = τ_R/τ_E, but the τ_E there is the local energy confinement time τ_E(r) = r²/χ_E with χ_E uniform (Eqs. 81–83), not the whole-plasma confinement time. Eqs. 140–142 make this explicit: P⊥(r̂) = (τ_R0/τ_E0)·ĵ_oh(r̂) with τ_E0 = a²/χ_E, so the r² cancels and P⊥ = μ₀χ_E/η(r_s). The whole-plasma time from W_e/P_e or a scaling law is τ̄_E = κ_e a²/χ_E (Eqs. 37–38), with κ_e = (3/2)∫₀¹ T̂_e r̂ dr̂ / [−dT̂_e(1)/dr̂].
With tau_E documented as the whole-plasma confinement time, this code's P⊥ differs from the paper's by r̂_s²/κ_e. For a T_e profile like (1 − r̂²), κ_e ≈ 0.19, so the 3/1 and 4/1 surfaces come out about 2–4× larger than Eq. 131 gives, and the error grows outward.
Two ways to make it match: take the whole-plasma τ̄_E and use P⊥ = τ_R(r_s)·κ_e a²/(r_s² τ̄_E), which needs κ_e from the T_e profile and the minor radius; or take a uniform χ_E (= κ_e a²/τ̄_E) as the input, which makes this mode the same as the χ-based P⊥ with one number for every surface. Either way, the docstring should say which confinement time is meant and cite Eqs. 81–83 alongside Eq. 131.
|
Thanks for the thorough review @d-burg I'd just like to reinforce that DIII-D data cannot be included in this open repo directly. The approach for the "DIIID-like" example should be to cite a paper if available and make a roughly matching functional form of the profile needed. Ideally, note the functional form somewhere in annotations / docs so it's clear its not a direct profile fit to data (eg "y = y0 (1 -x^2) where y0=3.0 roughly matches [Name Journal 2026]" or some such). |
Brings in the post-solve TearingProblem API, the TOML renames, Z_eff(ψ) from the kinetic file and the layer-overlap cap. The P_perp_model options now reach build_slayer_inputs through TearingProblem, and the layer-overlap scan takes the same chi_perp_e, chi_perp_i, tau_E and P_perp_model as the SLAYER stage, through one helper that also warns only about a missing χ the selected model uses. Review changes folded into the resolved hunks: - D_perp writes the Eq. 16 weights over one denominator, n T_s'/p', so a flat species temperature no longer gives 0·∞. - chi_perp_i has no default (NaN), and every P_perp model now rejects a non-finite or non-positive P_perp. - tau_E is the whole-plasma confinement time: a uniform χ_E = (3/16)a²/τ̄_E sets P_perp and P_tor at each surface (Fitzpatrick 2023, Eqs. 37-38, 81-83, 131), with a taken from the boundary on the same radial label as r_s. Before, P_perp = τ_R/τ̄_E was too large by r̂_s²/κ_e. A non-finite tau_E is caught at validation. - Docstrings: units no longer parse as Markdown links, Park (2022) is cited at Eq. 9, and Eq. 16's symbols and the orientation of its temperature ratio are defined. - An old deck's chi_perp key gets a hint that it is now chi_perp_e. The kinetic file is rebuilt from develop's copy, so #298's chi_e raise is kept. chi_e and chi_phi are replaced, and chi_i added, as analytic profiles in psi_N rather than profiles derived from a measured discharge; n, T and omega_E are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and the tau_E conversion, with a D_perp harness case Adds a T_e ≠ T_i point that fixes the orientation of Eq. 16's temperature ratio, the flat-temperature c_β² floor, the missing and zero chi_perp_i errors, and the τ̄_E → χ_E conversion, both in slayer_parameters and through build_slayer_inputs (gradients from the profile splines, minor radius from the boundary). The new diiid_slayer_n1_d_perp case reruns the SLAYER deck with P_perp_model = D_perp, so the non-default models have a baseline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…inetic file and the P_perp models States the three analytic forms with their sources: Hong et al. (2023) for the magnitude of χ_e and χ_i, Yoshida et al. (2012) for χ_φ tracking χ_i, and Callen et al. (2010) for the pedestal barrier the forms do not resolve. Replaces the stability docs' note that separating the electron and ion channels was deferred with the P_perp_model options, and adds chi_i to the kinetic schema. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…x label Every P_perp model is μ₀χ/η, so r_s cancels and the only radial label left in tau_E mode is the minor radius a in χ_E = (3/16)a²/τ̄_E. The outboard midplane chord, the default rs_method, is shortened by the Shafranov shift and ignores elongation; the flux label √(2ψ_t/B₀) is the area-equivalent radius of the boundary and reduces to the minor radius on circular surfaces. tau_E mode now uses it whatever rs_method is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…erp models Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd pin the truncation deck to chi_perp_e [SLAYER] P_perp_model now defaults to D_perp, the perpendicular particle diffusivity of Fitzpatrick (2022) Eq. 16, which is the coefficient the layer equations and the Eq. 100 width call for. On the DIII-D-like profiles it is 0.22-0.32 of chi_e at the outer rational surfaces, so SLAYER growth rates move and the layers are about 30% narrower. A run that selects D_perp or chi_perp_i without a chi_i profile or a chi_perp_i value now stops with a message naming the three ways to supply one. slayer_parameters keeps chi_perp_e as its own default, since it has no profile gradients. Under D_perp the layers on the DIII-D-like profiles no longer overlap inside psi = 1, so the truncation deck pins chi_perp_e to keep exercising the layer-overlap cap; it still caps psilim at 0.99498 with q = 6 the last surface. The D_perp harness case becomes diiid_slayer_n1_chi_perp_e, a baseline for the electron-channel model. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addressed in 4ce6bfe: develop merged, the review changes folded in, D_perp made the default and the example chi profiles replaced by analytic forms. See the updated description.
|
@matt-pharr #501 is ready for your review whenever you have time. It conflicts with develop and with #463 in |
matt-pharr
left a comment
There was a problem hiding this comment.
@d-burg thanks for taking over so we can get this merged! I went through and looked at fitzpatrick 2022 eq 16 to make sure everything matches up and it looks good by my eyes. However there is one thing that claude raised that is a must fix:
Must-fix:
- Runs that never use P⊥ now crash. Any deck with a [SLAYER] profile file but no chi_i in it now throws before FFS starts, even with tearing disabled. Before #501, the overlap scan was just skipped with a warning. The fix is to move one call (GeneralizedPerturbedEquilibrium.jl:586) inside the existing try, or to require χ⊥,i only when tearing or the overlap cap is on.
I agree with claude on this matter. This should be a pretty simple fix. Some other minor things include:
Should-fix:
- Bad χ input passes silently. A negative χᵢ or χₑ passes unchecked, and p′ ≈ 0 aborts with a generic message.
- NaN means "absent". Several defaults use NaN instead of nothing (chi_perp_i, a_minor, the gradient inputs), which breaks convention with the rest of the repo. NaN should mean invalid, nothing should mean absent.
- The example deck sets chi_perp_i = 1.0. That defeats the new error for files without chi_i.
- P_perp_model has two defaults: D_perp in the deck, chi_perp_e in the library functions.
- tau_E also overwrites P_tor, which the docs don't say. (((I am not sure I understand why this happens, is this intentional?)))
- The docstrings restate the equations instead of citing them, and some comments describe history ("used before this option existed"). These should probably be trimmed down. History should never be included in a julia comment as the point is not to tell teh story of development.
…borting when the kinetic file lacks the ion diffusivity The overlap scan looked up the χ inputs outside its try, so a deck with a [SLAYER] profile_file but no chi_i, under the default P_perp_model = D_perp, threw before the force-free-states stage even with tearing disabled. The lookup now sits inside the try: the scan is skipped with a warning, as for an unreadable file, and an enabled SLAYER stage still reports the missing χ⊥,i. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Fixed the must-fix in |
Brings in the coupled SLAYER fix and the omega_E override. The overlap scan keeps its χ lookup inside the try and, as develop now does, rethrows when tearing is enabled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eck diffusivities, and give the P_perp model one default Review follow-ups: - chi_perp_i, tau_E, a_minor and the gradient inputs default to nothing rather than NaN, and a model that needs one says which it is missing. - Scalar diffusivities must be positive and finite, and a kinetic-file chi profile with a non-positive value is an error. - D_perp reports a vanishing pressure gradient explicitly instead of a generic P_perp error. - slayer_parameters, build_slayer_inputs and resistive_layer_overlap default to D_perp, as the [SLAYER] deck does; tests that exercised the electron-channel model now ask for it. - The example deck no longer sets chi_perp_i, so a file without chi_i fails as intended. - Docstrings cite the equations instead of restating them, and the docs say that tau_E also sets P_tor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
logan-nc
left a comment
There was a problem hiding this comment.
Status check at 6c1ed8b, since the open threads predate the last three pushes: all nine of d-burg's open threads are addressed in the current source (chi_perp_i defaults to NaN in Control.jl:127 and LayerParameters.jl:189/305 with the isfinite(P_perp) && P_perp > 0 guard after every model; the common-denominator D_perp form with dlnTe = 0 / dlnTi = 0 tests; the Park Eq. 9 citation; isfinite(ctrl.tau_E) in Control.jl:203; the [m²/s], comma; the T_e = 2T_i point; the TOML annotation; the chi_phi/chi_tor-ignored note in the SLAYERControl docstring), and Matt's must-fix is in 6c1ed8b: the overlap scan now skips with a warning when the profile file or χ⊥,i is missing, with a @test_logs test, while an enabled SLAYER stage still errors. Resolving the threads is the reviewers' call; I only verified the text.
Two things worth a conscious yes: (1) the tau_E model converts the whole-plasma τ̄_E to the local one with a fixed κ_e = 3/16 (LayerParameters.jl:415), which the docstring itself puts at a factor 2–3 of shape uncertainty; (2) the D_perp default now ships here rather than in a follow-up, so #508 and #509 close with this PR, and the harness keeps diiid_slayer_n1_chi_perp_e as the old-model baseline.
…d-root tests from develop Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… tau_E model's kappa_e Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@matt-pharr your should-fix list is addressed in
@logan-nc thanks for the status check. Since it was at
The description's release note and regression report will be updated when the harness at |

Release note
diiid_slayer_n1it is 0.22–0.33 of develop’s electron-χ value. The uncoupled γ changes by +30.9% (2/1), +17.7% in magnitude (3/1), and +24.1% in magnitude (4/1); the largest per-surface frequency change is 1.61%. The coupled γ rises by 31.41%, with ω changing by 0.037%. The analytic χ profiles alone change uncoupled γ by at most 1.53%. The truncation cap is unchanged (its widths move 2.05%). (harness @ 7cf5ccd)[SLAYER], renamechi_perptochi_perp_e(also thechi_perpkeyword ofbuild_slayer_inputsandslayer_parameters). The new defaultP_perp_model = "D_perp"needs the ion diffusivity χ⊥,i: add achi_iprofile to the kinetic file or setchi_perp_i, or setP_perp_model = "chi_perp_e"to keep the previous P_⊥.tau_Eis the whole-plasma confinement time.SLAYER's perpendicular Prandtl number P_⊥, which enters the inner-layer Δ(Q) with P_φ, can now be built several ways through
[SLAYER] P_perp_model, and by default comes from the perpendicular particle diffusivity of Fitzpatrick (2022), Eq. 16. The critical Δ keeps the electron χ⊥,e in every mode, so the two are no longer forced to be the same diffusivity. The kinetic HDF5 schema gains an optionalchi_iprofile.P_perp_modelD_perp(default)chi_perp_echi_perp_iP_phic_betaD_perptau_EWhat changed
slayer_parameters, selected by[SLAYER] P_perp_modeland passed throughTearingProblemand the layer-overlap scan, so the scan and the tearing stage see one plasma.D_perpdefault. A run that needs χ⊥,i and has neither achi_iprofile norchi_perp_istops with a message naming both.slayer_parametersalso defaults toD_perp; direct callers must supply its profile gradients and ion diffusivity, or choose another model explicitly.tau_Etakes the whole-plasma τ̄_E, converted to a uniform χ_E with κ_e = 3/16 (the T_e ∝ 1 − r̂² value of Eq. 38) and the boundary minor radius from the flux label √(2ψ_t/B₀).TkMkr_D3Dlike_Hmode_kinetic.h5carries analytic χ profiles (below) in place of profiles derived from a measured discharge, and gainschi_i; n, T and ω_E are byte-identical to develop's. The forms and their sources are in the file'sprovenanceattribute and in the stability docs.P_perp_model = "chi_perp_e": underD_perpthe DIII-D-like layers are about 30 % narrower and do not overlap inside ψ = 1, so the pin keeps the layer-overlap cap exercised.diiid_slayer_n1_chi_perp_ekeeps a baseline for the previous model.stability.md,chi_iin the kinetic schema, the Fitzpatrick 2022 and 2023 papers indocs/resources, and the Park citation in the SLAYER file headers corrected to Phys. Plasmas 29, 072506 (2022).chi_iorchi_phino longer aborts a run. Required profiles still reject invalid values.chi_perpis rejected with a hint that it is nowchi_perp_e.These are not fits to a discharge. Their magnitudes are of the same order as the TRANSP power-balance χ_e and χ_i of DIII-D H-modes in Hong et al., Phys. Plasmas 30, 072512 (2023), Fig. 5; χ_e keeps the factor-2 raise that makes the default deck exercise the overlap cut; χ_φ tracks χ_i (Prandtl number of order one, rising outward) as reported for DIII-D by Yoshida et al., Nucl. Fusion 52, 123005 (2012). They do not resolve the pedestal transport barrier, where power-balance χ falls to about 0.1–0.3 m²/s (Callen et al., Nucl. Fusion 50, 064004 (2010)).
Regression report
Run on ITER SDCC through the Slurm fanout launcher on
sirius, comparing develop3456e6365with this head7cf5ccdd5(2026-10-27). All 40 shared-case runscompleted successfully. The new
diiid_slayer_n1_chi_perp_ecase was run separatelyat the head and also succeeded: 41 successful runs in total, with no failed runs.
It is not run on develop because develop rejects the new
P_perp_modelkey.Julia 1.12.6, x86_64 Linux, CI manifest
4c9d54fapinned for both refs; the sameper-case CPU allocation was used on both refs (normally 2 Julia threads / 1 BLAS thread).
[290.97, −339.31, −1395.59] s⁻¹. The largest per-surface relative ω change is
1.61% (4/1); root flags and other layer inputs are unchanged.
(+0.037%); root flag, τ_k, and Doppler shifts are unchanged.
within 0.79%, 0.94%, and 1.53% of develop’s default electron-channel case.
This comparison isolates the analytic χ-profile change.
the diffusive-resistive layer widths change by 2.05% under the report’s vector metric.
gal_resistive_pehas the same eight missing/N/A quantities on both refs. Its runscompleted, but it provides no numerical comparison. Every other comparable case is unchanged.
The focused transport tests (20) and existing layer-parameter tests (92) passed after
formatting at this head. Documentation CI passes; full Julia 1.11 and 1.x test jobs
were still running when this report was updated.
diiid_n1diiid_multi_ndiiid_slayer_n1diiid_slayer_n1_coupleddiiid_truncation_n1diiid_error_fielddiiid_n1_riccatidiiid_n1_riccati_precompiledefit_fixedbdy_separatrixgal_resistive_diiidgal_resistive_diiid_galinnergal_resistive_peggj_ray_q500iggj_referencesolovev_n1solovev_multi_nsolovev_kinetic_calculatedsolovev_kinetic_multiionsolovev_kinetic_ntvsolovev_kinetic_nuzeroSDCC run directories:
/home/ITER/pharrm/Projects/GPEC_dev/regress-fanout/runs/pr501-7cf5ccdd5-sharedand
pr501-7cf5ccdd5-electron. All prep, task, and report jobs exited 0.Full reports for changed cases and the new electron-channel case
Notes for reviewers
D_perpdefault, the analytic χ and the docs are on top.tau_Ecarries a radial label, througha; it uses the flux label whateverrs_methodis, so the pending radial-label default change (InnerLayer.SLAYER - API - Choose one default radial label for the Fitzpatrick layer formalism #417) does not touch these models.chi_tordocstrings in the same files: whichever merges second needs a small conflict resolution.NO MERGE WITHOUT HUMAN REVIEW. This PR needs a third-party human reviewer's approval of the head that lands.
🤖 Generated with Claude Code