Skip to content

Commit 0e21828

Browse files
logan-ncclaude
andcommitted
KineticForces - REFACTOR - Restore the KineticForcesControl freeze and drop dead deck keys
A develop merge into this branch silently reverted PR #367's immutability freeze on KineticForcesControl, because the multi-species loop mutated a deepcopy of the run control. That made the branch carry an undocumented revert of an upstream refactor and depend on the mutability upstream removed ("KineticForcesControl had no writers in src/ at all" — this loop was exactly such a writer, and the only one left). Build each species' control with the @kwdef keyword constructor instead, the idiom the freeze commit documents and that check_psi_quadrature_convergence already uses, and restore @kwdef struct. Threading species identity through explicit arguments was rejected: ctrl is passed whole into integrate_psi_quadgk and onward, so that would touch several nested signatures. Also drop six keys from the multi-ion deck that develop removed when it replaced the integrator-selection axis with integrator= (mat_flag, ode_flag, force_wv_symmetry, use_parallel, parallel_threads, populate_dense_xi). They only emitted deprecation warnings, but populate_dense_xi in particular implied a guarantee it no longer provides — had integrator= been dropped on that belief, the run would have fallen back to the riccati default, which does not populate dense xi, and PE/NTV would have gone quietly wrong. Document the PerSpecies layout in the h5 schema table. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent d54cf57 commit 0e21828

4 files changed

Lines changed: 12 additions & 14 deletions

File tree

docs/development/hdf5-conventions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Top level (10 groups):
4444
| `LocalStability/` | Mercier `D_I`, resistive interchange `D_R`, `ballooning_Delta_prime` on `psi`; the ballooning α boundary on `ballooning_psi` |
4545
| `SingularSurfaces/` | Per-rational-surface data: `rational_psi`/`rational_q`/`rational_m`/`rational_n`, GGJ coefficients, `Delta_prime_matrix`/`Delta_prime_raw`/`Delta_coil`/`pest3_A`/`pest3_B`/`pest3_Gamma` (Riccati or Galerkin alike), `Kinetic/` |
4646
| `PerturbedEquilibrium/` | `ForcingModes/`, `Response/`, `ResponseMatrices/`, `SingularCoupling/`, `Energies/`, control-surface spectra |
47-
| `KineticForces/` | `<method>/` (torque/energy profiles, `EnergyIntegrals/`, `KineticMatrices/`) |
47+
| `KineticForces/` | `<method>/` (torque/energy profiles, `EnergyIntegrals/`, `KineticMatrices/`); multi-ion runs add `PerSpecies/<species>/<method>/` with the same per-method layout, summing to the top-level total |
4848
| `Tearing/` | `PerSurface/` (+ `DpMatrix/`), `Roots/`, `LayerWidths/`, `Diagnostics/{ValidRoots,Poles,FilteredRoots}`, `Scan/Surface_<k>/` |
4949
| `SurfaceGeometries/` | `{Plasma,Wall}/{x,y,z}` point clouds |
5050

examples/Solovev_kinetic_multiion_example/gpec.toml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ reg_spot = 0.05 # Regularization width for singular surf
4949
[ForceFreeStates]
5050
integrator = "forward" # Dense ξ profiles required by PerturbedEquilibrium and kinetic torque diagnostics
5151
local_stability_flag = true # Perform local stability analysis (Mercier and ballooning) across the ψ profile
52-
mat_flag = true # Construct coefficient matrices for diagnostic purposes
53-
ode_flag = true # Integrate ODEs for stability of the internal long-wavelength mode (must be true for GPEC)
5452
vac_flag = true # Compute plasma, vacuum, and total energies for free-boundary modes
5553

5654
psiedge = 0.99 # Edge dW(ψ) diagnostic scan band [psiedge, psilim]; set ≥ psilim to disable
@@ -69,13 +67,9 @@ kinetic_factor = 0.0 # Scaling of kinetic matrices (0 = ideal path; >0
6967
eulerlagrange_tolerance = 1e-7 # Relative tolerance for ODE integration of Euler-Lagrange equations
7068
singfac_min = 1e-4 # Fractional distance from rational q at which ideal jump enforced
7169
ucrit = 1e3 # Column-norm threshold that triggers solution renormalization
72-
force_wv_symmetry = true # Enforce symmetry of the vacuum energy matrix
7370
save_interval = 3 # Save every Nth ODE step (1=all). Always saves near rational surfaces.
7471

75-
# Δ' BVP + parallel integration (see ForceFreeStatesControl docstring for details)
76-
use_parallel = true # Run parallel FM-propagator BVP path (unlocks singular/delta_prime_matrix)
77-
parallel_threads = 2 # BVP thread cap (1 = serial/bit-deterministic; 2 ≈ +20% speedup; ≥3 saturates)
78-
populate_dense_xi = true # Append serial-EL pass so dense ξ is stored — REQUIRED with a [PerturbedEquilibrium] section
72+
# Integration-range truncation (see ForceFreeStatesControl docstring for details)
7973
set_psilim_via_dmlim = false # FALSE for limited/analytical equilibria — rationals sparse, dmlim would chop too much edge
8074
dmlim = 0.2 # Truncate integration at (last_rational_q + dmlim)/n (used when set_psilim_via_dmlim = true)
8175
# Solovev analytic equilibrium parameters (eq_type = "sol"); see SolovevConfig in src/Equilibrium.

src/GeneralizedPerturbedEquilibrium.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -954,11 +954,11 @@ function run_kinetic_forces(
954954
states = KineticForces.KineticForcesState[]
955955
labels = String[]
956956
for sp in species
957-
sctrl = deepcopy(kf_ctrl)
958-
sctrl.zi = sp.z
959-
sctrl.mi = sp.m
960-
sctrl.electron = sp.electron
961-
sctrl.ion_species = KineticForces.IonSpecies[]
957+
# KineticForcesControl is immutable, so each species gets a fresh control
958+
# built from the run's control with only its own identity overridden.
959+
sctrl = KineticForces.KineticForcesControl(;
960+
(f => getfield(kf_ctrl, f) for f in fieldnames(KineticForces.KineticForcesControl))...,
961+
zi=sp.z, mi=sp.m, electron=sp.electron, ion_species=KineticForces.IonSpecies[])
962962
st = KineticForces.KineticForcesState()
963963
KineticForces.compute_torque_all_methods!(st, kf_intr, sctrl, result.equil, sp.profiles)
964964
push!(states, st)

src/KineticForces/KineticForcesStructs.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,12 @@ Constructed via keyword arguments or from a TOML dict:
8080
```julia
8181
ctrl = KineticForcesControl(; (Symbol(k) => v for (k, v) in inputs["KineticForces"])...)
8282
```
83+
84+
Immutable: vary a field by building a new control rather than assigning to one (the
85+
multi-species loop does this per species, and `check_psi_quadrature_convergence`'s test
86+
builds a second control for its differing tolerance).
8387
"""
84-
@kwdef mutable struct KineticForcesControl
88+
@kwdef struct KineticForcesControl
8589
# Moment type
8690
moment::String = "pressure" # "heat" or "pressure"
8791

0 commit comments

Comments
 (0)