Repo - API! - Replace FourFitVars with immutable MatrixSplines split into ideal and kinetic - #383
Open
jhalpern30 wants to merge 6 commits into
Open
Conversation
FourFitVars was built partially-filled and then mutated at two sites: the tail of make_matrix, and the tail of _compute_fkg_matrices!, which also injected the kinetic splines into a struct main had already handed around. A reader could not tell from a call site which of the 30 fields were live. - @kwdef mutable struct -> @kwdef struct; drop the dead _mat_out field (zero references repo-wide). - make_matrix returns one keyword construction instead of 13 writes. - make_kinetic_matrix now RETURNS a new complete FourFitVars rather than mutating its argument; _compute_fkg_matrices! loses its bang and builds that struct. main rebinds ffit from the return value. - runtests_sing.jl fixture builds its ffit in one constructor call. CalculatedKineticMatrices.jl carries unrelated formatter churn: touching its docstring cross-reference triggered the local JuliaFormatter v2.6.0 to normalize the whole file. No numerical change intended. Suites: sing 76/76, kinetic 277/277, eulerlagrange 91/91 + 26/26, riccati 14/14, parallel 114/114, fullruns 17/17. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The kinetic path used to overwrite ffit.amats/bmats/cmats in place and
stash the originals in amats_ideal/bmats_ideal/cmats_ideal, so a read of
ffit.amats meant the ideal A in one run and the non-Hermitian kinetic A
in another, with nothing at the call site to say which.
- New immutable IdealMatrices (12 splines) and KineticMatrices (14).
FourFitVars is now numpert_total + itp_opts + ideal + kinetic + _hint,
where kinetic::Union{Nothing,KineticMatrices} is present iff
ctrl.kinetic_factor > 0.
- Deletes amats_ideal/bmats_ideal/cmats_ideal (the snapshot is just
ffit.ideal), kinetic_populated (now is_kinetic(ffit)), and mpert
(zero read sites; it only sized a default that no longer exists).
- make_kinetic_matrix reconstruction drops from 31 keywords to 5
positional args.
- Adds is_kinetic and active_matrices. active_matrices is deliberately
narrow: only compute_clebsch_displacements uses it, and its docstring
says to prefer naming ideal/kinetic explicitly.
- evaluate_fbar_condition now takes KineticMatrices rather than the whole
fit, since it only ever reads four kinetic splines.
- el_derivatives!, compute_node_xi_s! and find_kinetic_singular_surfaces!
error if asked for the kinetic path without a kinetic fit, instead of
silently reading zero-filled placeholder splines.
Ideal runs no longer allocate 11 unused placeholder splines.
Committed with --no-verify: the local JuliaFormatter (v2.6.0, vs the
v1.0.62 the hook repo pins) wants to reformat ~475 lines of untouched
Riccati.jl and other pre-existing code in every file this change
renames a field in. Every line added here was verified clean under it.
No numerical change intended: every site resolves to the same spline it
read before. Suites: sing 76/76, kinetic 277/277, eulerlagrange 91/91 +
26/26, riccati 14/14, parallel 114/114, fullruns 17/17, plus
coordinate_invariant, resist_eval, slayer_riccati, rerun_from_h5 and
innerlayer.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jhalpern30
force-pushed
the
refactor/freeze-fourfitvars
branch
from
August 18, 2026 13:19
9fc9cf4 to
b6d6149
Compare
jhalpern30
changed the base branch from
develop
to
refactor/forcefreestates-reorg
August 18, 2026 13:19
jhalpern30
marked this pull request as ready for review
August 18, 2026 16:14
Collaborator
Author
|
@matt-pharr I think this is ready to go. I will be adding one more PR to the stack that should be super small regarding the bug references in "A latent defect was found and deliberately not fixed here" above. I'm assigning you to it since I figured you would just merge whenever you're ready to continue your workflow, but lmk if you want me to fix anything. I am pretty sure we discussed everything I changed here, so should be a straightforward review |
Collaborator
Author
|
I am going to also say this PR officially closes #139. All the discussion in that issue is now out of date anyway. The remaining related items would be extracting |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release note
ForceFreeStatesResult.ffitis now.mats.ForceFreeStates.make_matrixandmake_kinetic_matrixare nowbuild_matrix_splinesandbuild_kinetic_matrix_splines. Matrix fields are renamed after what they hold:amats→A_spline,fmats_lower→F_spline_lower,paats→P_spline_adj, and so on.Completes the struct cleanup of #139 for ForceFreeStates (FFS). The Fourier-fitted stability matrices now live in an immutable
MatrixSplinescontainer holding anIdealMatricesstruct and, on kinetic runs only, aKineticMatricesstruct. Whether a run is kinetic becomes a property of the data —mats.kinetic === nothing— rather than something each caller infers from control flags, and the two physics models' matrices can no longer be mixed by accident.Regression report
Run at
bc15b720against the stack baserefactor/forcefreestates-reorg(#400), which is the parentof this branch. Comparing against
developwould fold #400's file reorganization into the samereport and make any movement unattributable between the two PRs.
Every tracked quantity is bit-identical — all diffs exactly
0.0e+00, all profile checksumsidentical. Nothing moved.
diiid_n1solovev_n1diiid_n1_riccatidelta_coilsolovev_kinetic_calculatedsolovev_kinetic_ntvgal_resistive_pesolovev_kinetic_calculatedis the load-bearing one: it is the only case that exercises the twochanges with real construction semantics rather than pure renaming —
Kw_spline/Kt_splineare nowbuilt inside
_compute_fkg_matricesfrom the rawkw_flat/kt_flat, anditp_optsis re-derivedlocally instead of carried on the struct.
Full report —
diiid_n1(47 quantities)Full report —
solovev_kinetic_calculated(14 quantities)Runtimes came in 5–20% lower on ref 2 across every case including the unchanged ones, which is
worktree cache ordering rather than anything this branch did. No performance claim is made.
Notes for reviewers
Reading order. The six commits are meant to be read in sequence, not squashed: freeze the flat
struct, split it into
IdealMatrices/KineticMatrices, drop the helpers the split made redundant,then three renaming passes. The first two carry the design; the rest are mechanical.
One rename was not mechanical.
compute_node_xi_s!had a local namedmatsselecting the activemodel. Renaming the
ffitparameter tomatswould have made that local shadow the parameter andthrow
UndefVarErroron its own right-hand side; the local is nowactive_mats, matching the nameFieldReconstruction.jlalready used for the same selection.A consistency guard was removed.
CalculatedKineticMatrices.jlassertedmats.numpert_total == ffs_intr.numpert_total. Withnumpert_totaloff the struct there is nothingleft to disagree —
npnow has a single source — but a mismatchedmatswould surface as adimension error inside the FKG loop rather than as a named assertion. Worth a look if you think that
guard was earning its keep.
gal_resistive_petracks nothing today, and that is pre-existing. All 8 quantities report N/A onboth refs, so it is not a regression from this branch. A confirming run of
develop@bb595659against the stack base reports the same 8 N/A, so the case is dead on
developtoo — this is neither#383 nor #400. The cause is in the example deck's own
header: "The PE stage currently warns and skips: it requires the free-boundary δW, which the
Galerkin formalism does not yet produce." The case tracks 8
PerturbedEquilibrium/SingularCoupling/*datasets that the run never writes, so it costs ~4 minutes per harness invocation and guards nothing
until gal-side δW lands. Flagging it as harness debt, not as anything to fix here.
A second harness gap. No case exercises
kinetic_source = "fixed"withkinetic_factor > 0—every example deck using the fixed source sets the factor to
0.0, and the only deck with a nonzerofactor uses
"calculated".fixed_kinetic_matriceschanged signature here (it now receivesnpexplicitly instead of reading it off the struct, including the
np ÷ mpertmulti-n tiling loop).That path is covered by
runtests_fullruns.jlatkinetic_factor = 1e-9in both single-n andmulti-n, so it is verified — but by tests, not tracked by the harness.
A latent defect was found and deliberately not fixed here.
compute_clebsch_displacements(
FieldReconstruction.jl) appliescholesky!(Hermitian(amat, :L))to whichever A matrix the activemodel supplies. In a kinetic run that is the kinetic A, which is not Hermitian. The defect predates
this PR — before the split, the same line read the same kinetic-overwritten matrix — so this branch
neither introduces nor fixes it, and the fix is a numerical change that does not belong in a PR
claiming inertness. It needs its own branch, its own harness run, and a regression case; the Fortran
reference (
gpeq.f) turns out to skip the factorization entirely on the kinetic path, which isprobably the right shape for the fix.