The claim
Mixture.SigT is derived twice, from two spellings of one identity:
orpheus/data/macro_xs/mixture.py compute_macro_xs builds it as literal arithmetic BEFORE the Mixture exists — SigT = SigC + SigL + SigF + rowsum(SigS[0]) + rowsum(Sig2[0]);
Mixture.balance_residual re-derives the same identity through the named properties (total_scattering_xs, n2n_out_xs) and its own docstring says it is "VERBATIM the line that derives SigT in compute_macro_xs" — a Pattern-2 duplicate the code knows about and keeps.
[M] 2026-09-03 (#426 step 1): the Sig2 → list[csr_matrix] retype had to edit BOTH spellings (compute_macro_xs and n2n_out_xs), and the ingest carries a THIRD copy for the microscopic total (gendf.py _build_isotope: sigT = sigC + sigF + sigL + rowsum(sigS[0]) + n2n_reaction_xs). A future channel (MT=17/37 with multiplicity 3/4, or an ℓ-weighting change) lands on all three.
The remedy
One home for the total: build the Mixture (and the Isotope) with the total derived by the identity through the named channel verbs — e.g. a total_xs_from_channels(...) used by compute_macro_xs, _build_isotope and balance_residual, or construct with a provisional SigT and let __post_init__ derive it when a physical builder asks. assert_balanced then pins the single source against the stored field for synthetic mixtures, instead of comparing two hand-written copies of one line.
Surfaced by the elegance review of #426 step 1 (architectural opportunity 2). Labels: module:data, type:improvement. Related: #426, #60.
The claim
Mixture.SigTis derived twice, from two spellings of one identity:orpheus/data/macro_xs/mixture.pycompute_macro_xsbuilds it as literal arithmetic BEFORE theMixtureexists —SigT = SigC + SigL + SigF + rowsum(SigS[0]) + rowsum(Sig2[0]);Mixture.balance_residualre-derives the same identity through the named properties (total_scattering_xs,n2n_out_xs) and its own docstring says it is "VERBATIM the line that derives SigT in compute_macro_xs" — a Pattern-2 duplicate the code knows about and keeps.[M]2026-09-03 (#426 step 1): theSig2 → list[csr_matrix]retype had to edit BOTH spellings (compute_macro_xsandn2n_out_xs), and the ingest carries a THIRD copy for the microscopic total (gendf.py_build_isotope:sigT = sigC + sigF + sigL + rowsum(sigS[0]) + n2n_reaction_xs). A future channel (MT=17/37 with multiplicity 3/4, or an ℓ-weighting change) lands on all three.The remedy
One home for the total: build the
Mixture(and theIsotope) with the total derived by the identity through the named channel verbs — e.g. atotal_xs_from_channels(...)used bycompute_macro_xs,_build_isotopeandbalance_residual, or construct with a provisionalSigTand let__post_init__derive it when a physical builder asks.assert_balancedthen pins the single source against the stored field for synthetic mixtures, instead of comparing two hand-written copies of one line.Surfaced by the elegance review of #426 step 1 (architectural opportunity 2). Labels: module:data, type:improvement. Related: #426, #60.