Skip to content

Repo - API! - Read kinetic profiles once into the equilibrium and derive every kinetic view from them - #581

Open
matt-pharr wants to merge 9 commits into
developfrom
feature/kinetic-profiles-on-equilibrium
Open

matt-pharr wants to merge 9 commits into
developfrom
feature/kinetic-profiles-on-equilibrium

Conversation

@matt-pharr

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

Copy link
Copy Markdown
Collaborator

Release note

  • Audience: users
  • Numerical impact: SLAYER-only decks on the two-pass auto grid now refine that grid with the attached kinetic profiles too. Their tearing growth rates move by up to about 1% (diiid_slayer_n1 and diiid_slayer_n1_chi_perp_e γ 0.68%, the coupled γ 0.97%) and the truncation case's energies by about 1e-5. Every other case is unchanged (harness @ afa8e70)
  • Migration:
    • [KineticForces] kinetic_file and [SLAYER] profile_file are removed. Name the kinetic-profile file once, as kinetic_file in [Equilibrium]. [SLAYER] profile_group becomes kinetic_group in [Equilibrium]. There is no default kinetic.dat any more.
    • Equilibrium.load_kinetic_profiles(path; …) is now kinetic_profile_splines(read_kinetic_file(path); …).
    • resolve_ntv_species takes KineticProfileData instead of a path.
    • TearingProblem(ffs) takes its profiles from the equilibrium (no profile_file keyword).
    • efc_couplings drops its kinetic-profiles argument.

The kinetic-profile file is now read once into the equilibrium (equil.kinetic), set by the new [Equilibrium] kinetic_file key or PlasmaEquilibrium(…; kinetic_file=…), or attached with attach_kinetic_profiles!. NTV, the calculated kinetic matrices, SLAYER, the layer-overlap scan, error-field couplings and grid refinement each derive their own view from it, instead of up to four separate reads. solve() can now run the calculated kinetic source.

Regression report

Cases: diiid_n1, diiid_slayer_n1, diiid_slayer_n1_coupled, diiid_slayer_n1_chi_perp_e, diiid_truncation_n1,
       diiid_error_field, solovev_kinetic_calculated, solovev_kinetic_ntv, solovev_kinetic_multiion, solovev_kinetic_nuzero
Ref 1: develop  @ 6d655c74c (2026-10-27)
Ref 2: feature/kinetic-profiles-on-equilibrium  @ afa8e7063 (2026-10-27)
       env: julia 1.12.6, x86_64-linux-gnu, manifest 4c9d54fa (pinned), 8 threads/4 BLAS, jit build

diiid_n1                     Summary: 53 unchanged
diiid_error_field            Summary: 10 unchanged
solovev_kinetic_calculated   Summary: 20 unchanged
solovev_kinetic_ntv          Summary: 7 unchanged
solovev_kinetic_multiion     Summary: 6 unchanged
solovev_kinetic_nuzero       Summary: 14 unchanged

Regression Report: diiid_slayer_n1
Quantity                            Ref 1         Ref 2         Diff               Status
SLAYER surface indices              [6 elem]      [6 elem]      0.0e+00            OK
SLAYER poloidal m                   [6 elem]      [6 elem]      0.0e+00            OK
SLAYER toroidal n                   [6 elem]      [6 elem]      0.0e+00            OK
SLAYER minor radius rs              [6 elem]      [6 elem]      3.493e-09 (0.00%)  ** CHANGED **
SLAYER r-based shear                [6 elem]      [6 elem]      1.150e-02 (0.01%)  ** CHANGED **
SLAYER Lundquist S                  [6 elem]      [6 elem]      9.236e+03 (0.01%)  ** CHANGED **
SLAYER D_norm                       [6 elem]      [6 elem]      3.364e-05 (0.00%)  ** CHANGED **
SLAYER P_perp                       [6 elem]      [6 elem]      5.012e-07 (0.00%)  ** CHANGED **
SLAYER tauk                         [6 elem]      [6 elem]      6.205e-11 (0.00%)  ** CHANGED **
SLAYER iota_e                       [6 elem]      [6 elem]      1.130e-07 (0.00%)  ** CHANGED **
SLAYER Q_e                          [6 elem]      [6 elem]      1.891e-06 (0.00%)  ** CHANGED **
SLAYER Q_i                          [6 elem]      [6 elem]      4.095e-06 (0.00%)  ** CHANGED **
SLAYER Q_root [2/1,3/1,4/1]         [3 elem]      [3 elem]      2.343e-04 (0.01%)  ** CHANGED **
SLAYER ω_Hz [2/1,3/1,4/1]           [3 elem]      [3 elem]      7.9e-01            OK
SLAYER γ_Hz [2/1,3/1,4/1]           [3 elem]      [3 elem]      9.459e+00 (0.68%)  ** CHANGED **
SLAYER no_root flags [2/1,3/1,4/1]  [3 elem]      [3 elem]      0.0e+00            OK
SLAYER enabled flag                 1             1             0.0e+00            OK
Summary: 11 changed, 6 unchanged

Regression Report: diiid_slayer_n1_chi_perp_e
(same rows and pattern as diiid_slayer_n1; P_perp 2.153e-06, Q_root 1.907e-04 (0.01%), ω_Hz 4.7e-01 OK)
SLAYER γ_Hz [2/1,3/1,4/1]           [3 elem]      [3 elem]      7.710e+00 (0.68%)  ** CHANGED **
Summary: 11 changed, 6 unchanged

Regression Report: diiid_slayer_n1_coupled
SLAYER tauk                  [6 elem]   [6 elem]   6.205e-11 (0.00%)  ** CHANGED **
SLAYER q_shift               [6 elem]   [6 elem]   5.289e-07 (0.00%)  ** CHANGED **
SLAYER coupled Q_root        [1 elem]   [1 elem]   1.671e-04 (0.01%)  ** CHANGED **
SLAYER coupled ω             [1 elem]   [1 elem]   6.9e-02            OK
SLAYER coupled γ             [1 elem]   [1 elem]   2.817e+00 (0.97%)  ** CHANGED **
SLAYER coupled no_root flag  [1 elem]   [1 elem]   0.0e+00            OK
SLAYER enabled flag          1          1          0.0e+00            OK
Summary: 4 changed, 3 unchanged

Regression Report: diiid_truncation_n1
layer-overlap cap applied              1              1              0.0e+00            OK
layer-overlap psilim                   9.985081e-01   9.985081e-01   1.5e-11            OK
first overlapping surface index        7              7              0.0e+00            OK
diffusive-resistive layer widths (Δψ)  [7 elem]       [7 elem]       7.1e-09            OK
qlim                                   6.200000e+00   6.200000e+00   0.0e+00            OK
psilim                                 9.949767e-01   9.949767e-01   3.5e-11            OK
mpert                                  37             37             0.0e+00            OK
# singular surfaces                    5              5              0.0e+00            OK
singular q values                      [5 elem]       [5 elem]       0.0e+00            OK
singular psi locations                 [5 elem]       [5 elem]       8.9e-10            OK
plasma energy Re(ep[1])                -1.372379e+00  -1.372365e+00  1.388e-05 (0.00%)  ** CHANGED **
total energy Re(et[1])                 8.005436e-01   8.005487e-01   5.032e-06 (0.00%)  ** CHANGED **
Summary: 2 changed, 10 unchanged

Notes for reviewers

  • Why the SLAYER cases move.
    • Before, the two-pass grid refinement saw kinetic profiles only when a [KineticForces] section loaded them.
    • Now it sees them whenever the equilibrium carries them, on the deck and the API path alike. The deck and API share one grid path, and the API couldn't express the old gate.
    • On these DIII-D-like profiles that adds one ψ knot (217 → 218), and the tearing growth rates shift by up to about 1%.
    • The SLAYER calculation itself reads the same profiles as before. The six cases that already loaded profiles through [KineticForces] are bit-identical.
  • Data and views. PlasmaEquilibrium.kinetic holds the raw KineticProfileData on the file's own ψ grid: densities, eV temperatures, omega_E, chi_e/chi_i/chi_phi, zeff and per-species densities. Consumers build their views from it:
    • KineticForces.ntv_profiles / ntv_species: the 101-point NTV splines and species, with the [KineticForces] charges, masses and scale factors (unchanged maths);
    • the SLAYER view (Runner._slayer_profiles): the table plus the χ⊥,e/χ⊥,i/χ_φ and Z_eff splines, then _slayer_chi_inputs for the scalar fallbacks;
    • grid refinement and resonance nodes;
    • the calculated kinetic matrices;
    • efc_couplings.
  • One read, kept through the re-form. The file is read once at construction and Equilibrium.reform carries it through the two-pass re-form rather than re-reading it.
  • Absent stays absent. Missing datasets are nothing, never zero-filled, in the data. A missing omega_E stays optional for SLAYER as on develop: the view records it (has_omega_E) and TearingProblem warns that those surfaces assume Ω_E = 0 unless omega_E_kHz overrides them.
  • Fail before the solve. An enabled [SLAYER] section with no kinetic profiles errors before the force-free-states solve; a disabled one only skips the overlap scan. Profiles that lack a χ the P_perp_model needs skip the scan with a warning, as on develop.
  • One schema table. The HDF5 reader and writer iterate a single schema table, so a new profile is one table row plus its struct field (chi_i came in this way). A test keeps the table and the struct in step.
  • Scale factors stay [KineticForces] knobs applied only to the NTV view, as before. solve() has no [KineticForces] section, so it uses the defaults.
  • solve(). It now runs kinetic_source = "calculated" when the equilibrium carries profiles. A parity test checks it against the TOML-driven run. The Riccati()/Galerkin() kinetic guard from develop is kept.
  • Tests:
    • attach and re-form carry-through;
    • NTV and SLAYER views equal the data-based functions, including chi_perp_e/chi_perp_i;
    • a view without omega_E carries zero rotation and says so;
    • the transport-profile selection per P_perp_model, now on attached data;
    • calculated-source deck/API parity;
    • attached profiles change the DIII-D two-pass grid on the path main and solve() share;
    • the overlap-scan inputs come from attached data, and an enabled section without profiles errors.
  • Out of scope: the absent-χ rule (whether chi_perp_e/chi_tor keep their scalar 1.0 fallbacks), charge-constant, Z_eff and μᵢ unification, and the resistivity default (later PRs). MatchProblem defaults from the profiles are a small follow-up.

🤖 Generated with Claude Code

@matt-pharr matt-pharr self-assigned this Oct 24, 2026
@github-actions github-actions Bot added api Config key, output dataset or exported name changed changed-results Results move or an interface breaks - read before upgrading labels Oct 24, 2026
@matt-pharr
matt-pharr marked this pull request as ready for review October 24, 2026 21:58
@matt-pharr
matt-pharr marked this pull request as draft October 24, 2026 22:00
@matt-pharr matt-pharr added this to the GPEC v2.0.0 milestone Oct 26, 2026
Base automatically changed from bugfix/coupled-slayer-tauk-rescale-and-rotation to develop October 26, 2026 19:59

@logan-nc logan-nc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collision map now that #463 (its base) merged: against today's develop a three-way merge shows no conflicting hunks but 20 files changed on both sides, of which the src/ ones are Equilibrium.jl, EquilibriumTypes.jl, ForceFreeStates/CoreTypes.jl, GeneralizedPerturbedEquilibrium.jl, KineticForces/CalculatedKineticMatrices.jl, KineticForces/KineticForcesStructs.jl, plus regression-harness/src/runner.jl. Three things cross it: #551 moves the two Equilibrium files; #580 changes the efc_couplings/run_error_fields signatures that this PR also changes (species list in, profiles arg out); and the rates-v1 kinetic format from the parity plan (#591, A9) needs the [Equilibrium] kinetic_file home this creates, so I will build that on this branch once it is out of draft.

@matt-pharr

Copy link
Copy Markdown
Collaborator Author

@logan-nc this was a draft pending #501, #463, #548, #551. I will rebase once #551 is merged.

@matt-pharr
matt-pharr marked this pull request as ready for review October 27, 2026 14:54
@matt-pharr
matt-pharr requested review from d-burg and logan-nc October 27, 2026 14:54
@matt-pharr

Copy link
Copy Markdown
Collaborator Author

@logan-nc @d-burg this is now ready for your review.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api Config key, output dataset or exported name changed changed-results Results move or an interface breaks - read before upgrading

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants