Repository navigation
Repo - API! - Read kinetic profiles once into the equilibrium and derive every kinetic view from them - #581
Conversation
…netic profiles and delete [KineticForces] kinetic_file
…iter from one dataset table
…quilibrium's kinetic profiles
… on an equilibrium with kinetic profiles
… into feature/kinetic-profiles-on-layout
logan-nc
left a comment
There was a problem hiding this comment.
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.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0186CLfjeJeSusNTva5RMfGU
…tored equilibrium The replay constructor still passed the pre-kinetic positional arguments, so every post-hoc entry point built on equilibrium_from_h5 failed with a MethodError. The stored deck's kinetic_file is re-read against the h5's directory; a file that has since moved warns and leaves the profiles off. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0186CLfjeJeSusNTva5RMfGU
…nder KineticForces or SLAYER and fix the docs cross-reference A deck carrying the removed KineticForces kinetic_file or SLAYER profile_file/profile_group keys now stops with the new home named, in main and on replay, and the example-deck hook rejects the SLAYER keys. The scripting-API page qualifies the attach_kinetic_profiles! reference, which only the Equilibrium module exports. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0186CLfjeJeSusNTva5RMfGU
There was a problem hiding this comment.
Thorough read of the ready version at a54a78889, checked against the source rather than the diff summary. The design is right and I would like this in for 2.0.0. Three things were red in CI, all mechanical, so rather than write them up as review comments I pushed the fixes to the branch on top of a fresh develop merge (post-#593): 7bee5a62c (merge), 559d96d20 (replay constructor) and 255586db5 (retired keys, docs cross-reference, tests). CI is green at that head and the body carries the new harness stamp. Numbers from my end are below.
Blocking today (fixed in the pushed commits)
- Both test matrices failed on the replay constructor.
src/Rerun.jl:367still called the positionalPlasmaEquilibriumwith the seventeen pre-kinetic arguments; the struct now has eighteen, soequilibrium_from_h5and everything built on it (ResonantDriveContext, the error-field post-hoc path) threw theMethodErrorthatruntests_error_fields.jl:56reported on 1.11 and 1.x alike. Fix: the restored equilibrium re-reads the stored deck'skinetic_file, resolved against the h5's directory likeeq_filename; a file that has since moved warns and leaveskinetic = nothingso a post-hoc analysis that never needed profiles still works. Round-trip test added toruntests_frozen_equilibrium.jl. - Documentation failed on one cross-reference.
docs/src/api.md:55writes[attach_kinetic_profiles!](@ref); the page has noCurrentModuleand the top module re-exportsEquilibriumbut not that function, so Documenter cannot resolve the binding. Qualified asEquilibrium.attach_kinetic_profiles!. - Old decks got an unhelpful stop.
[KineticForces] kinetic_file,[SLAYER] profile_fileandprofile_groupare removed, but a deck that still set them failed on an unknown-keyword error from the control constructor rather than through the retired-key stop #569 built. Added them to the retired-key lists with a remedy that names the new home (name the kinetic-profile file once, as kinetic_file (and kinetic_group) in [Equilibrium]), somainand the gpec.h5 replay both stop early with the move spelled out, and extended the example-deck pre-commit hook for the SLAYER keys. Tests inruntests_equil.jl.
The harness stamp afa8e7063 was also stale by the conventions check (develop moved under it); the body is re-stamped at 255586db5 with the run below, and I added the one-sentence rule I ask for next to the release note while I was in there. Adjust the wording if you disagree.
The design
The equilibrium owning its profiles, and the two-pass grid resolving them whenever they are attached, is the right coupling, and the one-schema-table reader/writer is exactly how a new profile should cost one row. I checked the result movement claim: log_asymptotic is the two-pass alias (GridRefinement.jl:192), so the SLAYER decks really do gain the extra knot, and the six cases that already loaded profiles through [KineticForces] being bit-identical is the right control. One point for the release note: the new behaviour reads better as a rule than as two cases. Any two-pass deck that names kinetic_file now refines its grid with the profiles, ideal δW included, so a user who adds the key to an ideal deck in preparation for SLAYER will see their energies move at the 1e-5 level. That is what people will hit, so the note now says it in one sentence.
Deliberately left for later, not requests on this PR
solve()cannot take aKineticForcesControl, so the calculated source on the API path is pinned to the default species, charges and scale factors. Akinetic=keyword is a small follow-up once the API settles in #602.- The replay re-reads the kinetic file from disk. Storing the raw profiles under
Input/in gpec.h5 would make a moved h5 self-contained, and the schema table makes that a few lines; it belongs with the #388 schema work.
Harness, feynman, 4 threads, Julia 1.11.7, pinned manifest
Run at 255586db5 against origin/develop (5b2258e5a), eleven cases: the body's ten plus diiid_slayer_n1_winding, which develop added since the branch last merged. No stale-baseline warning. The full report is in the body.
| case | rows | changed | what moved |
|---|---|---|---|
| diiid_n1 | 53 | 0 | |
| diiid_error_field | 10 | 0 | |
| solovev_kinetic_calculated | 20 | 0 | |
| solovev_kinetic_ntv | 7 | 0 | |
| solovev_kinetic_multiion | 6 | 0 | |
| solovev_kinetic_nuzero | 14 | 0 | |
| diiid_slayer_n1 | 21 | 14 | γ 0.57 %, the rest at or below 0.02 % |
| diiid_slayer_n1_winding | 21 | 14 | row for row identical to diiid_slayer_n1 |
| diiid_slayer_n1_chi_perp_e | 21 | 15 | γ 0.57 %, CRF Q_peak 0.11 %, the rest at or below 0.02 % |
| diiid_slayer_n1_coupled | 7 | 4 | coupled γ 0.98 % |
| diiid_truncation_n1 | 12 | 3 | plasma energy 1.5e-5, total energy 5.3e-6, psilim 1.6e-10 |
Same shape as the body's run, with two differences worth naming. The growth rates move a little less than in the 10-27 run (0.57 % against 0.68 %), most likely because develop itself moved under the branch since then (the singfac_min change among others; I have not isolated it), and the critical-resonant-field rows that develop added since the stamp move with everything else, at or below 0.11 %. The six cases that already loaded profiles through [KineticForces] stay bit-identical, which is the control for the claim that only the grid changed.
Tests for the files the fixes touch, at 255586db5: runtests_frozen_equilibrium.jl 32/32, runtests_rerun_from_h5.jl 85/85, runtests_error_fields.jl 291/291 (the file CI failed on before the fix), and runtests_equil.jl 334/335. The one failure there is the i-file re-form equality check at runtests_equil.jl:265, which fails identically on develop 5b2258e5a in the same environment (feynman, native CPU target, 2 or 4 threads) while passing in CI; it is independent of this PR and I will raise it separately. A local docs/make.jl build passes with the cross-reference fix.
Brings #581 as merged, with its two final fixes. The deck stops for the kinetic file named under [KineticForces] (kinetic_file) or [SLAYER] (profile_file, profile_group) move to src/Deck.jl's moved-key list with develop's remedy, and the hook rejects the [SLAYER] keys. Rerun takes develop's _replay_kinetic_profiles. Also fixes tests stale since the tearing rework: the dispersion tests use Tearing.Dispersion and GrowthRateResult's omega/gamma, and the multi-ion species test reads ion_species from [Equilibrium].
Release note
kinetic_filenow refines its grid with the attached profiles, ideal δW included. SLAYER-only decks on the two-pass auto grid, which previously refined without the profiles, gain one ψ knot on the DIII-D-like cases: their tearing growth rates move by up to about 1% (diiid_slayer_n1, diiid_slayer_n1_chi_perp_e and diiid_slayer_n1_winding γ 0.57%, the coupled γ 0.98%) and the truncation case's energies by about 1e-5. Every other case is unchanged (harness @ 255586d)[KineticForces] kinetic_fileand[SLAYER] profile_fileare removed. Name the kinetic-profile file once, askinetic_filein[Equilibrium].[SLAYER] profile_groupbecomeskinetic_groupin[Equilibrium]. There is no defaultkinetic.datany more. A deck that still sets one of the old keys stops with a message naming the new home.Equilibrium.load_kinetic_profiles(path; …)is nowkinetic_profile_splines(read_kinetic_file(path); …).resolve_ntv_speciestakesKineticProfileDatainstead of a path.TearingProblem(ffs)takes its profiles from the equilibrium (noprofile_filekeyword).efc_couplingsdrops its kinetic-profiles argument.equilibrium_from_h5re-reads the stored deck'skinetic_filefrom disk; if the file has moved, the restored equilibrium carries no profiles and a warning says so.The kinetic-profile file is now read once into the equilibrium (
equil.kinetic), set by the new[Equilibrium] kinetic_filekey orPlasmaEquilibrium(…; kinetic_file=…), or attached withattach_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
Run on feynman from the pushed head; the winding case is included because develop added it after the branch last merged.
Notes for reviewers
[KineticForces]section loaded them.[KineticForces]are bit-identical.PlasmaEquilibrium.kineticholds the rawKineticProfileDataon the file's own ψ grid: densities, eV temperatures,omega_E,chi_e/chi_i/chi_phi,zeffand 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);Runner._slayer_profiles): the table plus the χ⊥,e/χ⊥,i/χ_φ and Z_eff splines, then_slayer_chi_inputsfor the scalar fallbacks;efc_couplings.Equilibrium.reformcarries it through the two-pass re-form rather than re-reading it.nothing, never zero-filled, in the data. A missingomega_Estays optional for SLAYER as on develop: the view records it (has_omega_E) andTearingProblemwarns that those surfaces assume Ω_E = 0 unlessomega_E_kHzoverrides them.[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 χ theP_perp_modelneeds skip the scan with a warning, as on develop.chi_icame in this way). A test keeps the table and the struct in step.[KineticForces]knobs applied only to the NTV view, as before.solve()has no[KineticForces]section, so it uses the defaults.solve(). It now runskinetic_source = "calculated"when the equilibrium carries profiles. A parity test checks it against the TOML-driven run. TheRiccati()/Galerkin()kinetic guard from develop is kept.chi_perp_e/chi_perp_i;omega_Ecarries zero rotation and says so;P_perp_model, now on attached data;mainandsolve()share;chi_perp_e/chi_torkeep their scalar 1.0 fallbacks), charge-constant, Z_eff and μᵢ unification, and the resistivity default (later PRs).MatchProblemdefaults from the profiles are a small follow-up.🤖 Generated with Claude Code