Repository navigation
Conversation
…s that have no omega_E The grid refinement and the EL kinetic stage built the NTV view of the attached profiles whenever any were attached, and that view requires omega_E, so a deck whose kinetic file has no omega_E (read for SLAYER) stopped in the equilibrium stage. The view is now built only when a calculated kinetic source needs it, where a missing field still errors, or when the profiles carry the NTV fields; otherwise the grid follows the equilibrium alone, as before the profiles moved onto the equilibrium. Closes #614 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jhalpern30
left a comment
There was a problem hiding this comment.
Fix looks right for the SLAYER-only case; the grid behavior matches the description. One accuracy concern (fail-late for [KineticForces] decks) and a few clarity nits inline.
Also: the maybe_reform_equilibrium docstring still says the grid measures "the NTV view of its attached kinetic profiles"; that is now conditional ("when they carry the NTV fields"). And the branch is behind develop.
| # kinetic source needs them, so a missing profile errors; otherwise they are built only when the profiles | ||
| # carry the NTV fields, and profiles read for another consumer (e.g. SLAYER without omega_E) are left alone. | ||
| function _ntv_views(kf_ctrl::KineticForces.KineticForcesControl, equil::Equilibrium.PlasmaEquilibrium, ctrl::ForceFreeStatesControl) | ||
| needed = ctrl.kinetic_factor > 0 && ctrl.kinetic_source == "calculated" |
There was a problem hiding this comment.
Fail-late regression for [KineticForces] decks. needed covers only the calculated source. A deck with a [KineticForces] section and profiles lacking omega_E used to error here in the equilibrium stage; now it runs the full FFS solve and writes HDF5 before erroring in write_validity! / the KineticForces stage.
Suggest adding the check to kinetic_forces_control, next to the existing "needs kinetic profiles" error: error when _needs_kinetic_profiles(inputs, ctrl) && !Equilibrium.has_ntv_fields(equil.kinetic). That keeps every NTV consumer failing up front. (needed here would then be redundant on the run_gpec path, but the solve-API path skips kinetic_forces_control, so keep it unless that path gets the same check.)
Minor: this is now the third copy of kinetic_factor > 0 && kinetic_source == "calculated" (with _needs_kinetic_profiles and resonance_grid_nodes).
| kinetic_profiles = KineticForces.ntv_profiles(kf_ctrl, equil) | ||
| kinetic_profiles, species = _ntv_views(kf_ctrl, equil, ctrl) | ||
| (equil.kinetic === nothing || kinetic_profiles !== nothing) || | ||
| @info "Two-pass psi grid: the kinetic profiles lack an NTV field (n_e, T_i, T_e, omega_E), so the grid follows the equilibrium alone" |
There was a problem hiding this comment.
Nit: the (a || b) || @info guard is a double negative; equil.kinetic !== nothing && kinetic_profiles === nothing && @info ... reads directly.
| # importing KineticForces (which would invert the load order). | ||
| kinetic_profiles = KineticForces.ntv_profiles(kf_ctrl, equil) | ||
| species = KineticForces.ntv_species(kf_ctrl, equil) | ||
| kinetic_profiles, species = _ntv_views(kf_ctrl, equil, ctrl) |
There was a problem hiding this comment.
With kinetic_source = "fixed" and profiles lacking omega_E, this now gives axis_psi_c = 0, so near-axis suppression is silently off, while the same deck with omega_E gets a nonzero boundary. Not a regression (it errored before), but worth an @info here or a line in the PR description so it isn't silent.
| # equilibrium alone, unless the run computes calculated kinetic matrices, which need it. | ||
| no_omega = GPEC.Equilibrium.KineticProfileData(; psi=kinetic.psi, n_i=kinetic.n_i, n_e=kinetic.n_e, | ||
| T_i=kinetic.T_i, T_e=kinetic.T_e, chi_e=kinetic.chi_e, chi_i=kinetic.chi_i, chi_phi=kinetic.chi_phi) | ||
| @test !GPEC.Equilibrium.has_ntv_fields(no_omega) && GPEC.Equilibrium.has_ntv_fields(kinetic) |
There was a problem hiding this comment.
Nit: split into two @tests so a failure says which half broke.
jhalpern30
left a comment
There was a problem hiding this comment.
Looks good, up to you what you want to include from the review
…ga_E and report the suppression they lose A [KineticForces] deck with omega_E-less profiles now errors in kinetic_forces_control rather than after the solve, a fixed kinetic source logs that the near-axis suppression is off, and the calculated-source test lives in one helper. Review changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Release note
omega_E. A deck whose kinetic profiles lackomega_Enow runs: its two-pass grid follows the equilibrium alone, where before this it stopped in the equilibrium stage (harness @ fc9d9bb)A run whose kinetic file has no
omega_Eno longer stops in the equilibrium stage. SLAYER growth rates can again be computed from such files, assuming Ω_E = 0 with a warning, as before kinetic profiles moved onto the equilibrium. Runs with[KineticForces]orkinetic_source = "calculated"still needomega_E, and now stop up front, before any solve, saying so.Closes #614
Regression report
regress --refs 08a6975ef,fc9d9bb74 --force(all cases) on feynman (SLURM, 8 threads, Julia 1.11.9, manifest pinned): every case is unchanged, all 24 including the kinetic, SLAYER andlar_reversed_shear_n1cases;gal_resistive_peis N/A on both refs, as on develop.runtests_solve_api.jlpasses 152/152 atfc9d9bb74.An earlier run,
regress --refs 4f33fcf60,c05b2babc --force, was also unchanged. On feynman atc05b2babc,runtests_solve_api.jl(148),runtests_grid_refinement.jl(84),runtests_kinetic_profiles.jl(7),runtests_kinetic.jl(5143) andruntests_slayer_runner.jl(164) pass.On the Mac (Julia 1.11.6) at
c05b2babc:runtests_solve_api.jlpasses 148/148, including the 4 new checks.examples/DIIID-like_SLAYER_examplewith its kinetic file copied withoutomega_E, now runs to completion with the[SLAYER.CriticalResonantField]block disabled.Notes for reviewers
Cause. The two-pass grid (
maybe_reform_equilibrium) and the EL kinetic stage (prepare_force_free_states!) built the NTV view of the attached profiles (ntv_profiles,ntv_species) whenever any profiles were attached. That view requiresn_e,T_i,T_eandomega_E. A file read for SLAYER need not carryomega_E, and the HDF5 kinetic schema treats it as optional.Change.
Equilibrium.has_ntv_fields(data)names the NTV view's required fields once._ntv_views(kf_ctrl, equil, ctrl)is the one place both call sites now get the view and species. It builds them when a calculated kinetic source needs them, where a missing field still errors fromkinetic_profile_splines. Otherwise it builds them only when the profiles carry the NTV fields.kinetic_forces_controlstops a run with[KineticForces]orkinetic_source = "calculated"up front when the profiles lack an NTV field, before any solve.omega_Eshould, and does, stop them.axis_validity_suppressionis on, the EL stage logs that the near-axis suppression is off.Unchanged. When the attached profiles carry every NTV field, which covers every shipped deck, both call sites behave exactly as before. That includes a fixed kinetic source with attached profiles, which keeps its axis-validity boundary.
Tests (extending "attached kinetic profiles refine the two-pass grid"):
has_ntv_fieldsseparates the two profile sets.omega_E-less profiles re-form without error, stay attached, and give exactly the grid of an equilibrium with no profiles attached.kinetic_forces_controlstops on those profiles with[KineticForces]or a calculated source, and returns normally for a deck with neither.🤖 Generated with Claude Code