Changelog
All notable changes to Nucleide are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Releases are cut with scripts/bump-version.sh X.Y.Z, which stamps the
[Unreleased] section below and updates [workspace.package] version and
workspace crate dependency versions in the root Cargo.toml. Git tags
(vX.Y.Z) are the release source of truth; CI publishes Python wheels and
workspace crates from tags.
[Unreleased]
[0.16.0] - 2026-09-18
Added
-
Per-species ion temperatures on single-fuel parametric configs in
nucleide-plasma-source(exposed through thespecies_temperaturesdict without afueldict): single-fuel D-T reacts at the landed mass-weightedT_DT = T_D + (2/5)·(T_T − T_D), single-fuel D-D atT_D, with the Ballabio lines following per branch (D-T atT_DT, D-D atT_D). Equal pair temperatures(T, T)reproduce the shared-temperature single-fuel kernel bit-for-bit (strengths, sampled stream, cards). Anchors:(T_D, T_T) = (20, 30)keV givesT_DT = 24keV with⟨σv⟩_DT = 5.414667327922193e-22m³/s and the D-T line at mean 14.077934372230146 MeV, sigma 0.3700390508551786 MeV. Mixture paths are untouched; theNone-pair path keeps the landed expression trees verbatim (no new RNG draws); the deuterium tail still requires a fuel mixture. -
Sublet S4 ingestion and S5 inhalation hazard kernels in
nucleide-alara-io(one sharedHAZARDSshape): each folds per-nuclide activities with the caller-supplied 50-year committed dose coefficients (e_ing/e_inh, Sv/Bq, never vendored) into theTOTAL ... HAZARD FOR ALL MATERIALSdose (Sv) plus the excluding-tritium companion, with no split key. Pure arithmetic over caller inventories; negative / non-finite inputs and overflow are loud errors. Exposed asnucleide.alara.alara_ingestion_hazard/alara_inhalation_hazard(entry keysnuclide,activity_bq,e_ing_sv_per_bq/e_inh_sv_per_bq), with hand-vector gates intests/test_alara.pyand C9 rows invalidation/clearance_vs_pypact.py. -
Sublet S3 gamma dose-rate kernel in
nucleide-alara-io(newdosemodule): the slab doseC·B/2·Σ μa/μm·Sγ(B = 2) and the point doseC·Σ μa/(4πr²)·e^(−μr)·mₛ·Sγ(Sv/h,C = 3.6e9·|e|,Sγ = I·A) over caller gamma groups — specific activity, group yields, and air/mixture attenuation all caller-supplied, never vendored (mixture_mufolds elemental values with mass fractions). Point distances below 0.3 m clamp to 0.3 m and report it loudly via theclampedflag, never silently. Exposed asnucleide.alara.alara_dose_slab/alara_dose_point/alara_dose_mixture_mu, with hand-vector gates intests/test_alara.pyand C8 rows invalidation/clearance_vs_pypact.py. -
Sublet S6 transport-ratio and S7 IAEA clearance-index kernels in
nucleide-alara-io: S6 folds per-nuclide activities with the caller-suppliedA2limits (TBq, never vendored) into the dimensionlessTotal Bq/A2ratio plus the effective A2 it defines (nucleide.alara.alara_transport_ratio, entry keysnuclide,activity_bq,a2_tbq); S7 folds activities with the caller-supplied IAEA levels (Bq/kg, never vendored) and the total mass into the dimensionless clearance index with its<= 1screening class (boundary included) and dominant contributor (nucleide.alara.alara_iaea_clearance_index, entry keysnuclide,activity_bq,limit_bq_per_kg). Pure arithmetic; bad inputs and overflow are loud errors. Hand-vector gates intests/test_alara.pyand C10 rows invalidation/clearance_vs_pypact.py. -
He/dpa ratio uncertainty propagation in
nucleide-damage(he_dpa_ratio_uq, exposed asnucleide.damage.he_dpa_ratio_uq): per-draw ratios over the seeded joint[flux, he, dpa]MVN block (the landedlinalgengine, no new sampling machinery), reporting the draw mean ± draw standard deviation withk-SE gates against the second-order bias-corrected expectation and the first-order delta-propagated standard deviation, plus the distribution-free 68% interval (draw 16th/50th/84th percentiles) gated against the Fieller-construction quantiles — the all-regime verdict that holds at honestly-sized blocks where the symmetric-spread gate cannot. Any draw at non-positive dpa fails loudly with the landedZeroDpavocabulary, neverinf/NaNwith a spread. The single-responsefold_uq("he_dpa_ratio")spelling stays a loud error naming the two-response function. Gates intests/test_damage.pyand G6–G7 rows invalidation/damage_vs_specter.py. -
Runnable workflow notebooks in
notebooks/(first-wall damage + UQ, activation screening + radiological totals, tokamak source sampling + card emission): synthetic inputs and committed fixtures only, fixed seeds, no outputs vendored in the files, executed end to end in CI (tests/test_notebooks.py, needsnbclient/nbformat/ipykernel), with Colab badges for one-click runs once the PyPI release is live. -
Tutorial code-execution gate (
tests/test_tutorial_code.py): every runnable ```python block indocs/tutorials/python/*.mdexecutes in CI in document order, so the tutorials double as verified examples. Placeholder-path snippets now point at real fixtures (or carry an explicit skip marker for user-side/h5py/scratch-file demos), and thefetch_compendiumtutorial snippet reads the committed compendium file instead of downloading. -
Interactive demos for the new surfaces (new tabbed radiological-totals demo covering S3–S7, He/dpa ratio-UQ block in the damage demo, single-fuel species-pair inputs in the fusion-source demo, all backed by new thin WASM endpoints), plus theory-page coverage of the new equation sets, tutorial prose, and citations — including verified references for the previously uncited equilibrium (VMEC, DESC, simsopt) and blanket (Stellaris concept paper) theory pages.
-
Tutorial sidebar reorganization: one workflow-grouped reading order shared by the sidebar, the index pages, and the docs index (duplicate and missing position values fixed).
Fixed
- Target-less
<decay>chain entries now parse as decay out of the modeled chain (pure diagonal loss, no gain term) — the decay analogue of the long-standing target-less<reaction>convention, includingtarget="nothing". Previously the committedfixtures/depletion/chain_ni.xmlwas rejected outright even though its target-less decays (Fe55→Mn55 and friends) are physically just truncated chains. An explicittargetnaming a nuclide absent from the chain stays a loudUnknownNuclideerror; CRAM and Bateman agree exactly on the pure-loss exponential.
[0.15.0] - 2026-09-18
Added
-
OpenMC statepoint tally bridge (
nucleide.openmc, pure Python — no Rust changes). Statepoint files are HDF5 and are never read in Rust; the facade drives the caller-side OpenMC Python API (call sequence pinned against OpenMC 0.16.0, the validation-container build:StatePoint(path, autolink=False)with an explicitclose(), thentally.get_reshaped_data(value=..., expand_dims=True)for"mean"and"std_dev") and returns plain nested lists the landed folds already accept:tally_arrays/read_statepointyield[voxel][group]fluxplus matchingrel_err(std_dev / mean, 0.0 where the mean is 0) and theG+1MeV energy edges ([]unbinned — a single total group), withcell_flux/voxel_fluxselecting the group vector thedamagefolds take alongside the edges asbounds.tally_to_csv/tally_from_csvmove the same dict through a commented CSV with stock stdlib only, so an OpenMC machine exports and any machine reads back bit-identically. Mesh tallies areRegularMesh-only (C-order voxels, centimetre bounds); a missing file, missing OpenMC, missing tally/score, multi-nuclide tallies, multi-bin non-energy filters, a second mesh/energy filter, and non-regular meshes are all loud errors naming the remedy — never silent empties. OpenMC stays a caller-side lazy import (the base package never depends on it). Gates are synthetic-statepoint unit tests intests/test_openmc_bridge.py(pinned fetch sequence, mesh C-order and unit conversion, singleton cell-filter transparency, CSV round-trips, loud-error vectors, and thecell_flux→nrt_dpafeed check). -
Wall-load mapping in flux coordinates in
nucleide-equilib-io(exposed throughnucleide.equilibaswall_load): closed-form per-cell accumulationq[j][k] = Σ_i S·J·Δsof a caller birth-rate density field onto the caller wall surface sharing the same flux coordinates (radial-major voxels, one-field-period toroidal convention, Jacobian voxels from the landedfourier_jacobiankernel) plus the angle-weighted one-field-period total, which conserves the discrete births to roundoff. Pure glue — no transport, no shadowing, no FEM/thermal, no CAD, no HDF5; shape mismatches and non-finite or negative densities/Jacobians are loud named errors reusing the crate vocabulary. Gates are hand-computed toy geometries at 1e-12 relative (axisymmetric uniform field vs the analyticS·J0wall flux with the12π²total, a cosine-Jacobian vector, radial weighting, and discrete conservation) inwall.rsunit tests andtests/test_equilib.py, with always-run E9–E10 rows in the sameequilibvalidation report. -
Closed-form ECRH accessibility kernel in
nucleide-plasma-source(exposed throughnucleide.plasma_sourceasecrh_scalars/ecrh_accessibility): cold electron-cyclotron resonance (B_res = 2π·m_e·f/(n·e)), the weakly-relativistic Maxwell–Jüttner shift (B_res(T_e) = γ·B_res,γ = 1 + T_e/511 keV), and the O1/X1 cut-off densities (n_c,O1 = ε₀·m_e·(2π·f)²/e²,n_c,X1 = n_c,O1·(f − f_ce)/f, evanescentNonewheref ≤ f_ce) from the published ECRH/dispersion facts (Bornatici et al., Nucl. Fusion 23 (1983) 1153; Stix, Waves in Plasmas (1992) Ch. 1), located by linear interpolation along a caller beamline (s[m] strictly increasing,B[T],n_e[m⁻³]) into plain-float resonance/cut-off positions — the per-port penalty inputs for blanket bookkeeping. Pure functions with named loud errors (empty/ragged/non-monotonic beamlines, negative densities, bad frequency/harmonic/temperature); no ray tracing, no launcher design, no HDF5. Gates are hand-computed vectors at 1e-9 relative (170 GHz ITER-class resonance/shift/cut-offs plus beamline crossings) inecrh.rsunit tests andtests/test_ecrh_access.py. -
Sublet S1+S2 radiological totals in
nucleide-alara-io(exposed throughnucleide.alaraasalara_total_activity/alara_decay_heat). S1 sums per-nuclide activitiesAi = Ni λi(Bq) with the FISPACT-II IRT α/β/γ split (IRT 4 → alpha; IRT 1, 2, 11, 14, 16, 17, 19, 20 → beta; IRT 3 → gamma; IRT 12, 13 split α/β and IRT 15 splits α/γ by caller-supplied branch fractions); S2 folds caller-supplied average decay energies into per-class decay heatAi·E·C1(kW, C1 = eV→kJ). Both carry the excluding-tritium companion and conserve parts-to-total exactly; unmapped IRTs and bad values are loud named errors. Pinned to the open CCFE-PR(16)53 preprint (Table X, Tables VI–VII) and the open FISPACT-IIoutput_interpretationreference — never the paywalled journal pages; decay energies stay caller-supplied, never vendored. S3–S7, ICRP/IAEA coefficients, bremsstrahlung, DPA/KERMA/gas, and fission counts stay out.validation/clearance_vs_pypact.pygains always-run C7 gates (hand-computed vectors at exact equality) in the sameclearancereport. -
TBR and blanket power bookkeeping (
nucleide-blanket,nucleide.blanket): raw TBR from caller(bred, source)tallies, multiplicative per-port coverage haircuts, blanket energy multiplication, the tritium burn rate from pinned17.6MeV /3.0160492u constants, and the breeding-margin / net-surplus fuel-cycle metrics. Pure arithmetic over caller transport tallies — no transport solving, no geometry optimization, and no coupling into thetritiumpermeation kernel (which keeps its no-breeding-coupling scope line). Stellaris Point-A values gate the kernel as regression vectors: raw TBR1.1070,1.074after the 3% ECRH-port haircut, multiplication1.20, burn416.6g/day. -
Coil fast-fluence / lifetime analytics in
nucleide-damage(exposed throughnucleide.damageascoil_fast_flux/coil_fast_fluence/coil_accumulate/coil_lifetime/coil_remainingplusFPY_SECONDS): magnet lifetime bookkeeping over caller spectra — the fast-flux sum above a caller threshold (groups count iff their upper edge clears the threshold; a threshold cutting through a group includes the whole group), fluence as flux × time, piecewise-constant history accumulation, and weakest-link life (minof limit/rate, or limit-minus-accumulated over rate clamped at zero). dpa spectral weighting stays in the landed folds (the NRT/arc fold at one second is the rate the life kernel consumes). Zero rates never breach and are skipped (all zero is infinite life with aNonelimiting channel); all shapes, signs, and orderings are loud named errors reusing the fold vocabulary. Limit tables are always caller-supplied — published design numbers (3e22 / 1.5e23 m⁻² fast-fluence limits at a 99th-percentile coil fast flux of 9.5e13 m⁻²s⁻¹ → ~10 FPY, limited by the first channel) are gates at pinned tolerance, never defaults. Magnetics, quench, structural analysis, and transport solving stay out. -
Equilibrium data readers in
nucleide-equilib-io(exposed throughnucleide.equilibasprobe_variant/read_wout/jacobian/jacobian_grid/parse_indataplus typedindata_*helpers). A pure classic-netCDFwoutreader accepts CDF-1 (43 44 46 01) and CDF-2 (43 44 46 02) per magic probe (ncdump -kmust reportclassic/64-bit offset) and rejects HDF5-backed netCDF-4 and CDF-5 loudly for facade-side conversion (no HDF5 in Rust); it reads the documented variable lists (nfp,ns,xm,xn,rmnc,zmns,lmns,gmncminimum with dimsradius/mn_mode/mn_mode_nyq, later-usebmnc/bsubumnc/bsubvmnc/bsubsmns/currumnc/currvmnc, optionalmpol/ntor/phiedge/volume_p). The&INDATAnamelist grammar covers scalar switches, power-series profiles, and boundary Fourier tables (sliced indices likeRBC(0:4,2)are a loud error;NCURR = 1reads as theI'(s)profile; absentLFREEBreads fixed boundary). Flux-surface Jacobian helpers evaluate thegmncFourier sum at points and over one-field-period grids for volume weighting. Reads data, never solves equilibria; synthetic fixtures are built from the published variable lists only (never solver output). Gates are magic-byte accept/reject vectors, Jacobian hand vectors, and INDATA grammar vectors in-crate plustests/test_equilib.pyand thevalidation/equilib_vs_desc.pyoracle (DESC/simsopt legs SKIP loudly outside the container). -
Arbitrary-3D / stellarator birth-rate lattice source in
nucleide-plasma-source(exposed throughnucleide.plasma_sourcewithkind="lattice"): the caller supplies the full 3D birth distribution as a point list —pointsof{"position": [x, y, z] [cm], "rate": w >= 0, "ion_temperature_kev": Ti}(relative birth-rate weights, arbitrary global scale;Ti = 0is the monoenergetic nominal line) — with the landed machinery applied per node (D/T mixture rates, Ballabio spectra, seeded sampling, card emission with drift reports; MCPL projection stays caller-side). The optionalfield_periods/base_anglepair declares field-period symmetry (both keys or neither): the points are the base-sector cloud, replicated uniformly around the machine axis, with the symmetric total scaled by the period count. A two-point hand vector pins the contract (total 3, mean birth position(100, 0, 25), mono 14.021 MeV line); a single node reproduces the landed point source bit-for-bit; a uniform ring cloud converges to the analytic ring moments; the folded base-sector stream reproduces the expanded full-cloud stream bit-for-bit (including the single-node case: one base node fans out over all copies); cards carry the cloud’s cylindrical-R/vertical/energy marginals with the same drift rows plus alattice discretizationrow and still round-trip byte-identically through the typed SDEF reader. Non-finite positions, negative rates, empty or zero-total clouds, and non-symmetric fold inputs are loud named errors (InvalidLattice/InvalidSymmetry). The Stellaris-class precedent (30 radial × 50 poloidal × 100 toroidal point sources, 14.06 MeV monoenergetic) is the named design point; equilibrium solving, transport solving, CAD/DAGMC, tabulated plasma data, and HDF5 stay out.validation/ plasma_source_vs_openmc.pyadds always-run P12 gates (hand vectors, single-point recovery, ring-limit closure, fold replication and totals, card round trip, mixture branch fire) and a container-only O12 probe on a dense monoenergetic ring cloud vs the upstream ring geometry and Ballabio helpers. -
Deuterium hot-tail fraction for the D/T fuel mixture in
nucleide-plasma-source(exposed throughnucleide.plasma_sourceas thedeuterium_tail={"fraction": eta, "temperature_kev": T_tail}dict [keV] on a parametricfuelblend): the one pinned single-tail-temperature non-Maxwellian shape within the Eriksson et al., Comput. Phys. Commun. 199 (2016) 40 arbitrary-distribution framework — the bulk(1 − eta)deuterium atT_Dplus a hot tailetaatT_tail, each sub-pair reacting at its own mass-weighted relative temperature (D-T atT_DT/T_DTt, D-D atT_D/T_mix/T_tail) with the Ballabio lines following per sub-branch through the strength rule, the sampler’s branch roulette, the card energy marginals, and adeuterium taildrift row carrying the tail neutron share. The pinned 70/30 hand vector (T_D = 20,T_T = 30keV,eta = 0.05atT_tail = 60keV) gates the rate at 1e-12 relative;eta = 0reproduces the no-tail mixture kernel bit-for-bit (strengths, sampled stream, cards — regression gates); the tail requires afuelmixture and composes with species temperatures and toroidal sectors. Non-finite, out-of-range, or negative tail parameters are loud named errors (NonFinite/InvalidTail/NegativeIonTemperature). The effective-temperature line convention carries the same error stance as the species pair (the residual against the full Eriksson numerical integration is out of scope); the rest of the full Eriksson generalization stays a loud boundary.validation/ plasma_source_vs_openmc.pyadds always-run P13 gates (sub-rate-rule moments and branch share, the drift tail-neutron share, zero-fraction recovery, loud errors) and a container-only O13 probe against the upstream quadrature with the NeSST reactivities evaluated at the five sub-pair temperatures. -
Interactive demos for the new surfaces: the fusion-source demo gains a parametric + hot-tail tab, the activation demo a Sublet S1+S2 tab, plus new ECRH, blanket, equilibrium, coil (damage), and wall-load demos. Fixed the WASM
sampleFusionSourceparametric path, which rejected thekind/n/seedsampling envelope its own dispatcher requires (FusionParametricSpecJsondenies unknown fields to keep strayfuel_mixture/species_temperatures/sectorkeys loud — only the envelope keys are stripped before parsing).
[0.14.0] - 2026-09-17
Added
-
Vented-sink recombination internal interfaces for multi-layer tritium stacks in
nucleide-tritium(exposed throughnucleide.tritium):LayerStack::newnow acceptsInterface::recombination(rate)alongside the linear Sieverts/Henry laws. A vented gap carries a single face concentrationx ≥ 0with half-cell fluxesJ_L = g_L·(c_L − x),J_R = g_R·(x − c_R)(g = 2D/dxper side, no solubility involved) and the desorption sinkK_r·x²venting from the face, soJ_L − J_R = K_r·x². The bulk matrix is CUT at the gap (block-diagonal); a lone gap closes in closed formx = 2P/(√(Q²+4K_rP)+Q), coupled faces (several gaps, or recombination outer ends alongside) by Newton iteration with the analytic Jacobian — same FV θ-stepper andlinalg::tridiagsolve, with the per-step close fused into the trap Picard loop exactly like the G6 outer ends. TheK_r → 0limit is a continuous-concentration joint (not a Sieverts law); a non-positive permeation drive is a loud error in the steady state (the transient follows the G6 clamping stance per step). The flux-continuous product law has no spelling by construction (spurious insulated root, symmetry breaking — permanently rejected). Gates G10a–G10g/G11a–G11d (in-crate unit tests, synthetic stacks with hand-derived closed forms, nofixtures/tritium/changes): 2-layer closed form at1e-12with gap residual and desorption-carrying outer balance,K_rlimit pins, a 4-layer mixed-law stack (one interface of each law) with flux continuity across both linear gaps, coupled-Newton residuals at the Newton contract, traps on the owning layer’s isotherm, transient θ-balance with the desorption sink to roundoff,dt-halving order bands, late-time asymptote, and the trap-coupled Picard-fusion balance. The Pythonsteady_layers/transient_layersfacades accept a{"kind": "recombination", "rate": Kr}dict per gap (bare"recombination"strings fail loudly — the rate has nowhere to go) and reportinterface_faces(face value per gap,Noneat linear gaps;[time][gap]in transients) for closing the discrete balance with the desorption term; single-slab and default-stack signatures are unchanged. -
IRDFF-II dosimetry-response registry expansion (
nucleide-nuclei,nucleide._internal.parse_irdff_g725): theV2_REACTIONSextension adds the 26 further namedMF=3dosimetry sections of the same pinnedIRDFF-II_g725.zip(same URL + SHA-256 pins, same cache, same loud offline/mismatch errors) — the remaining threshold (n,p)/(n,a)/(n,2n) monitors, capture foils, fission chambers, and high-threshold bismuth monitors, for 34 reactions total.MF=10isomer sections stay out, the default pack stays v1, and theunfolditerators are unchanged. Parser review: the pinned file carriesL2=99(not 0) in threeMF=3head cards (notably v1’s56Fe(n,p)), now accepted as 0-or-99 and loudly pinned. Gates: synthetic mocked-fetch unit tests plus a container live-fetch gate (loud SKIP when offline) with hand-checked first-response groups andunfoldround-trips at pinned tolerance. Nothing IAEA-copyright is vendored. -
MAXED maximum-entropy unfolding (
nucleide-unfold,nucleide.unfold.maxed): the fourth and last adjustment family in the crate — Shannon relative entropy against the guess-as-prior over the exponential Lagrange family, driven by an equilibrated Levenberg-Marquardt dual step with a chi-square target (target_chi2, default: the detector count) and a per-iteration trust-region cap. Caller sigmas are the1/σ²weights, zero measurements carry zero weight (the GRAVEL house rule), and a relatively converged run whose chi-square still exceeds the target is a hardNotConverged(never a partial spectrum). Validation adds always-run U19-U23 gates (determined recovery, counting-statistics underdetermined round-trip, bracketed IRDFF-II analytical shape probes, SAND-II cross fixed points, contract errors) to the sameunfoldreport. -
D(d,p)T proton bookkeeping and the pinned T-T branch normalization for the parametric tokamak plasma source (
nucleide-plasma-source,nucleide.plasma_source):ParametricPlasmaConfig::proton_strength_density/total_proton_strength(plusFuelMixture::tt_neutron_coefficient) report the D(d,p)T proton production rate alongside the neutron source under the pinned 50/50 convention — the proton density IS the D-D neutron-branch density bit-for-bit (pure D-D gives exactly one proton per neutron; D-T-only gives exactly none) — while the sampler draws no proton energies, the cards carry no proton distributions, and proton transport stays out of scope. The T-T neutron term is pinned as the third mixture termS = n²·[f_D·f_T·⟨σv⟩_DT + (f_D²/2)·⟨σv⟩_DD + f_T²·⟨σv⟩_TT](neutron multiplicity 2 folded in, reacting atT_T, matching the upstreamopenmc-plasma-sourcerule) but is NOT transported: no Bosch–Hale T-T fit exists (Bosch & Hale 1992 covers only D(d,n), D(d,p), D-T, D-³He), no Ballabio-class T-T line exists (three-body continuum), and the oracle’s vendored tables stay out of scope — a pure-tritium mixture stays a loud error. A mixture withf_T = 0reproduces the landed two-branch kernel bit-for-bit (regression gates). Newproton_accounting(spec)facade (per-neutron ratio plus sector-aware totals for parametric specs;Nonetotals for ring/point specs, which carry no density model).validation/plasma_source_vs_openmc.pyadds always-run P9 gates (exact pure-fuel ratios, the 70/30 share vs quadrature, the no-T recovery anchor, the loud pure-T error) and a container-only O9 probe on a tritium-rich 10/90 blend showing the sampled stream sits on the two-branch upstream quadrature while the three-branch reference pulls away by exactly the T-T-predicted scale (protons have no oracle — upstream models neutrons only). -
Toroidal sectors for the parametric tokamak plasma source (
nucleide-plasma-source,nucleide.plasma_source): the additivestart_angle/rotation_anglepair [rad] (ToroidalSector) restricts births to[start_angle, start_angle + rotation_angle)with uniform birth angles and emission totals scaled byrotation_angle/2π(half torus ×0.5 exactly, quarter torus ×0.25 exactly); the exact full-rotation spelling reproduces the landed full-torus kernel bit-for-bit (same seeded stream, same cards — regression gates). A partial sector adds a uniform angle-bin marginal to the cards (PHI=D4on SDEF,phi d4on Serpent) plus atoroidal sectordrift row; the SDEF card still round-trips byte-identically through the typed reader (themcnp-ioSDEF subset gains thePHIkeyword). Non-finite or out-of-range angles are loud errors (NonFinite/InvalidSector; both keys or neither in the Python spec).validation/plasma_source_vs_openmc.pyadds always-run P10 gates (in-sector uniform births, full-rotation stream/card recovery, the PHI marginal round-trip with its drift row) and a container-only O10 probe showing a partial sector leaves the upstream-quadrature (r, z, E) moments untouched. -
Per-species ion temperatures for the parametric tokamak plasma source (
nucleide-plasma-source,nucleide.plasma_source): the additivespecies_temperatures={"D": T_D, "T": T_T}dict [keV] (SpeciesIonTemperatures) reacts a D/T fuel mixture at distinct Maxwellian species temperatures — the D-T branch at the mass-weighted relative temperatureT_DT = T_D + (2/5)·(T_T − T_D), the D-D branch atT_D(Eriksson et al. 2016, the full distinct-Maxwellian generalization) — through the emission strength, the sampler’s branch roulette, the card energy marginals, and the axis spectrum summary. The D-T Ballabio line atT_DTis an effective-temperature convention (Table III was fitted to single-temperature Maxwellians; the residual against the full Eriksson numerical integration is out of scope, and non-Maxwellian reactants stay a loud boundary). A uniformT_D = T_Tpair reproduces the shared-temperature mixture kernel bit-for-bit (strengths, sampled stream, cards — regression gates). The pair requires afuelmixture (a single-fuel config with the pair set is a loud error); the profile ion temperature is then unused for rate and spectrum (still validated), while the density profile keeps shaping the emission. Non-finite or negative temperatures are loud errors (NonFinite/NegativeIonTemperature).validation/ plasma_source_vs_openmc.pyadds always-run P11 gates (pair-temperature sampled moments and branch share vs quadrature, equal-T stream/card recovery) and a container-only O11 probe against the upstream quadrature with the NeSST reactivities evaluated at the pair temperatures.
[0.13.0] - 2026-09-16
Added
-
D/T fuel mixtures for the parametric tokamak plasma source (
nucleide-plasma-source,nucleide.plasma_source): afueldict{"D": f_D, "T": f_T}on the parametric source spec (the openmc-plasma-source spelling; thenreactionis optional and unused) switches the emission model to the Eriksson et al., Comput. Phys. Commun. 199 (2016) 40 mixture rate at the shared profile ion temperature,S = n²·[f_D·f_T·⟨σv⟩_DT + (f_D²/2)·⟨σv⟩_DD]— the standardn_a·n_b·⟨σv⟩rate with the1/(1+δ_ab)same-species guard, the DRESS rate construction upstreamopenmc-plasma-sourceimplements. Birth energies run a per-particle branch roulette between the two Ballabio lines; emitted cards carry the same product-form marginals, and the magnetic-axis spectrum summary reports the two-branch Gaussian-mixture moments. The equimolar (f_D = f_T = 1/2) and pure-D-D (f_D = 1) limits reproduce the landed single-fuel kernels bit-for-bit (regression gates), and non-finite, negative, or non-summing fractions are loud named errors. Toroidal sectors stay out of scope.validation/plasma_source_vs_openmc.pyadds always-run analytic gates P8 (pinned 70/30 blend: sampled moments vs in-script mixture quadrature, D-D branch share) and a container-only O8 cross-check against the upstream map/profiles with the NeSST D-T and D-D reactivities. -
Spanish CSN conditional-clearance tables for NORM landfill disposal in
nucleide-alara-io(exposed throughnucleide.alaraasalara_clearance_es_table). The Consejo de Seguridad Nuclear draft technical opinion CSN/PDT/AICD/TGE/2503/02 (TGE/VAR/2025/1, hosted on csn.es, accessed 2026-09-16 — RD 1029/2022 and RD 1217/2024 (RINR), both transposing Directive 2013/59/Euratom) publishes Tables 1-3, per-landfill type (inert / non-hazardous / hazardous) clearance levels in Bq/g for each NORM material nature (rocks / ashes / sands / slags / oil & gas), keyed by the Table 4 natural decay-chain keys. All three tables are vendored as attributed embedded TSVs in the EU-table pattern (try_+ cache split, row-count-pinned tests); the Table 4 chain keys expand to per-member entries at the parent value through thenucleidialect machinery (EU Part-2 precedent), with the last claiming key supplying a shared member’s level, as the source lists each secular-equilibrium chain before its subchains. The caller selects the table and material column explicitly — no cross-table logic, no “most permissive wins”; the EU Annex VII table stays the vendored default. Screening arithmetic only, never a compliance decision.validation/clearance_vs_pypact.pygains always-run C6 gates (transcription spots, chain-expansion members, hand-computed screening vector at exact equality). -
Henry internal interfaces for multi-layer tritium stacks in
nucleide-tritium(exposed throughnucleide.tritium):LayerStack::newnow acceptsInterface::HenryalongsideInterface::Sieverts. The linear Henry law (c/K_Hcontinuous, flux continuous) shares the Sieverts resistance form — face fluxJ = (c_i/K_i − c_{i+1}/K_{i+1})/R_fwithR_fthe two half-cell resistancesdx/(2·D·K)and the layersolubilityplayingK_H— so it folds into the same tridiagonal θ-step matrix with no new solver machinery; recombination internal interfaces stay loudUnsupportedInterfaceerrors (recorded limitation, deferred to its own spike cycle). Gates G9a/G9b (in-crate unit tests, synthetic stacks with hand-derived series-resistance closed forms, nofixtures/tritium/changes): a 2-layer all-Henry stack against the series-resistance closed form at1e-12relative on the fluxes (1e-18absolute floor) with interface flux continuity and half-cell-corrected interface potentials to roundoff, and a 3-layer mixed Sieverts/Henry stack holding flux continuity across both interface laws against the same closed form; the G7c one-layer recovery anchor is unchanged. The Pythonsteady_layers/transient_layersfacades gain an additiveinterfaceskeyword ("sieverts"per gap by default,"henry"supported,"recombination"fails with a clear error); single-slab and default-stack signatures are unchanged. -
Vendored SPECTER Table VII fallback in
nucleide-damage(exposed throughnucleide.damageasspecter_table/specter_damage_energy/specter_ed/specter_spectra): spectrum-averaged damage-energy cross sections in keV-b for 24 elements (Be through Pb) across the report’s 7 spectra (thermal / fission / 14 MeV / HFIR / EBR-II / FFTF / fusion), transcribed from Greenwood & Smither, ANL/FPP/TM-197 (January 1985, US-government public domain — OSTIpurl/6022143, the same hash-pinned PDF the validation oracle downloads) by reading the scan pages directly, plus the Table IIE_dcolumn (eV) per element. Displacement cross sections in barns follow the report’s own0.8/2E_drule with the vendoredE_d(or a caller-chosen value). Scope is displacement XS only (no gas columns); the group-wise Appendix A printouts are image-only pages in the scan and stay untranscribed, so the table covers the 24 Table VII elements rather than the 41 Table I entries. The table is an opt-in caller input only — the fold kernels are unchanged and never consult it implicitly; thetry_+ cache constructor split, row-count pin (24), and spot tests follow the EU-table pattern. The HFIR column reproduces the oracle’s Table VI Fe/Ti/Cu spots at Table VII print precision (3 significant figures, 5e-3 relative); the oracle script itself is untouched and keeps passing. -
STAYSL-class damped least-squares spectral adjustment in
nucleide-unfold(exposed asnucleide.unfold.staysl, second method in the one-method-per-cycle order after SAND-II): the Perey ORNL/TM-6062 (1977) anchored-damped objectiveφ† = argmin_φ Σ_i (c_i(φ) − N_i)²/σ_i² + λ·Σ_j (φ_j − φ_guess,j)², re-solved each cycle as one weighted least-squares system through the sharedlinalg::lstsqkernel (no second least-squares implementation) on the current active set, with non-positive groups clipped to zero and pinned NNLS-style. Sigmas are caller-supplied, one finite positive value per detector (w_i = 1/σ_i²); zero, non-finite, or missing sigmas are loud named errors. Dampingλis absolute Tikhonov damping on the distance to the guess; convergence is the shared contract (per-group relative-change tolerance with an explicit iteration cap; exhausting the cap is a hardNotConverged, reusing the landed variant). Gates are synthetic forward-fold-then-recover round-trips at pinned tolerances (determined recovery, sigma-weighted precise-reading dominance, underdetermined rate reproduction, IRDFF-II analytical benchmark-field shapes) plus the cross-method fixed-point check (a SAND-II fixed point is a least-squares fixed point) and the named-contract errors; the always-runvalidation/unfold_vs_analytic.pyoracle gains gates U9-U13 in the sameunfoldreport (noSECTION_ORDERchange). -
GRAVEL chi-square-weighted spectral adjustment in
nucleide-unfold(exposed asnucleide.unfold.gravel, third method in the one-method-per-cycle order after SAND-II and STAYSL-class): the Matzke PTB-N-19 (1994) multiplicative iterationφ_j ← φ_j·exp(Σ_i W_ij·ln(N_i/c_i) / Σ_i W_ij)withW_ij = (R_ij·φ_j / c_i)·(N_i²/σ_i²)— the SAND-II base rate share times the caller-sigma measurement-weight factor — as an iterator with the same diagnostics shape (spectrum/rates/rate_factors/iterations/tolerance/max_rel_change). Sigmas are caller-supplied, one finite positive value per detector; zero, non-finite, or missing sigmas are loud named errors, never silent uniform weighting. Convergence is the shared contract (per-group relative-change tolerance with an explicit iteration cap; exhausting the cap is a hardNotConverged, reusing the landed variant). Pinned divergences from SAND-II: zero measurements carry zero weight and are simply not fitted (they pin nothing to zero); at a fixed point every rate ratio is 1, so the weights are irrelevant and SAND-II, STAYSL-class, and GRAVEL fixed points coincide where the formulations agree (cross-checked both directions). Gates are synthetic forward-fold-then-recover round-trips at pinned tolerances (determined recovery, counting-statistics-sigma underdetermined rate reproduction plus error reduction) plus the IRDFF-II analytical benchmark-field shapes (Trkov et al., Nucl. Data Sheets 163 (2020) 1 — published facts used with citation, not vendored data); the always-runvalidation/unfold_vs_analytic.pyoracle gains gates U14-U18 in the sameunfoldreport (noSECTION_ORDERchange). MAXED stays recorded for a later cycle. -
IRDFF-II runtime-download response pack in
nucleide-nuclei(nucleide.data.fetch_irdff+nucleide._internal.parse_irdff_g725): the official IAEAIRDFF-II_g725.zipdistribution is fetched at runtime and hash-pinned (SHA-256 recorded innucleide.data, FGR-15 precedent — nothing IAEA-copyright is vendored), and theIRDFF-II.g725member text is parsed into caller-ready response rows over the SAND-II 725-group structure (groups726 eV bounds plus one cross-section vector per named reaction) for the v1 foil-activation subset (au197_ng,in115_ng,u235_nf,u238_nf,fe56_np,ni58_np,al27_na,na23_n2n; MF=3 sections only — the pointwise MF=10 isomer reactions stay out). The rows feedunfold.sandii/staysl/gravelunchanged; structural problems, missing/duplicate sections, off-structure energies, and row-shape violations are loud errors. Offline or hash-mismatch fetches are loudRuntimeErrors naming the URL or both digests.
[0.12.0] - 2026-09-16
Added
-
Clearance / waste-classification analytics in
nucleide-alara-ioandnucleide-fispact-io(exposed throughnucleide.alaraandnucleide.fispact). The clearance index CI = Σ Aᵢ/CLᵢ and the sum-of-fractions screening rule (RS-G-1.7 §5, referenced by designation) run as pure arithmetic over parsed(nuclide, activity)inventories — Sublet et al., NDS 139 (2017) 77 is the methodology reference; further indices from that paper stay recorded, not implemented. Limit tables are caller-supplied, with the EU 2013/59/Euratom Annex VII Table A vendored as the attributed default (official legal text transcribed from EUR-Lex CELEX:32013L0059, OJ L 13, 17.1.2014, accessed 2026-09-15; reusable per Decision (EU) 2011/833; Bq/g == kBq/kg; IAEA tables never vendored). Nuclides key through thenucleidialect machinery; missing limits and bad values are loud named errors; the boundary CI == 1 classifies as satisfied (“does not exceed”). Screening arithmetic only — never a compliance decision.fispact-iogains the clearance-block reader for the real FISPACT-II wide inventory table printed with theHAZARDS+CLEARkeywords (grammar cross-checked against the Apache-2.0fispact/workshopsreference outputs): per-step(interval, time_s, cooling)rows with activity, per-nuclide clearance index, flags, and half-life (Stable→-1). Validation addsvalidation/clearance_vs_pypact.py: always-run analytic gates C1-C5 (exact CI vectors, boundary probes both sides, EU table spots, fixture parse, end-to-end screening) plus a pypact (Apache-2.0) cross-check of the overlapping inventory columns with loud SKIP outside the oracle environment. -
GDML (Geant4) emission direction for
nucleide-csg-xlate: scoped MCNP CSG decks now translate to GDML documents (deck_csg_to_gdmlin Rust,nucleide.mcnp.parse_csg_to_gdml/read_csg_to_gdmlon the Python side) alongside the OpenMC/Serpent/PHITS directions, over the same v3 scope (surfaces, cells, nested universes, rectangularLAT=1lattices, material stub). Cells become named boolean solids inside aworldvolume, universes become assemblies, lattices expand to one placement per element, and infinite half-spaces are bounded by a per-deck cutoff recorded in the drift report (new actionshalfspace-bounded,lattice-expanded,material-stub). Emitted documents pin the GDML schema version 3.1.7 (Geant4v11.4.2tag); the validation harness validates them against the runtime-fetched, SHA-256-pinned XSD whereverlxmlis available. Reflecting and periodic boundaries stay loud errors in this direction (Geant4 expresses boundaries through wrapper code, not geometry markup). Newfixtures/mcnp/inp/deck_csg_cylinders.txtextends thedeck_csg_*family. -
Parametric tokamak plasma source in
nucleide-plasma-source(exposed throughnucleide.plasma_sourcewithkind="parametric"): Miller-geometry flux surfaces (Fausser et al., Fus. Eng. Des. 87 (2012) 787 —major_radius/minor_radius/elongation/triangularity/shafranov_factor) carrying caller-supplied L/H/A-mode density and temperature profiles (mode,pedestal_radius, and theion_density_*/ion_temperature_*parameter sets; profiles are inputs, never computed), with reactivity-weighted emission (Bosch & Hale, Nucl. Fusion 32 (1992) 611, Atzeni–Meyer-ter-Vehn parametrization; newnucleide.plasma_source.reactivityexposes ⟨σv⟩ in m³/s). Birth positions are drawn ∝S·R·|J|— closed-form Miller Jacobian with the toroidalRfactor — and energies from the local-ion-temperature Ballabio Gaussian, all through the seeded sampler. Source cards for parametric sources carry the radial/vertical/energy marginals as discrete histograms (RAD/EXT/ERGdistributions onSDEF,rad/ext/ergon Serpentsrc) and the drift report quantifies the tabulation truncation plus the joint-correlation distance a product-form card cannot carry; SDEF cards still round-trip byte-identically through the typed reader. Analytic gates:J == rtorus limit,J == κ·r·(1 − 2·esh·r·cosθ/a²), areaπ·κ·a², volume2π²·κ·a²·R₀(any Shafranov shift), finite-difference Jacobian agreement at general triangularity, profile pedestal continuity, reactivity transcription goldens, and sampled-moment gates vs fine quadrature. The validation oracle adds always-run P5–P7 and container-only O4–O7 legs against openmc-plasma-source’s Miller map, Fausser profile functions, and NeSST reactivities (loud SKIP outside the container). Loud boundary: fuel mixtures (Eriksson-weighted reactant distributions), toroidal sectors, and the D(d,p)T proton branch stay namedNotYetSupportederrors. -
Tokamak fusion neutron sources in the new
nucleide-plasma-sourcecrate (exposed asnucleide.plasma_source): ring and point sources over the D-D (2.45 MeV) and D-T (14.1 MeV) reactions with ion-temperature- broadened Gaussian spectra (Brysk, Plasma Phys. 15 (1973) 611; Ballabio et al., Nucl. Fusion 38 (1998) 1723 Table III coefficients), a seeded sampler to particle vectors (position/direction/energy/weight arrays, deterministic under a pinned seed), and MCNPSDEF+ Serpentsrccard emission with a drift report (rel_driftof the tabulated spectrum,reparsedflags). Emitted SDEF cards round-trip byte-identically through the typednucleide-mcnp-ioreader, whose accepted subset now also carries the ring keywordsAXS/RAD/EXT; Serpent rows are analytic by design (no Serpent source reader). MCPL projection stays caller-side (vr-toolsKDE layering rule): the crate has nomcpl-iodependency. Parametric Miller-geometry plasma profiles, mixed-fuel spectra, toroidal sectors, and the D(d,p)T proton branch are loud namedNotYetSupportederrors — the second landing’s scope. New-crate checklist applied (workspace/release wiring,gen-referenceentries, crate-responsibilities section, README feature table); the two-partvalidation/plasma_source_vs_openmc.pyoracle adds always-run analytic gates (ring/spectrum closed-form moments, determinism, card round trips) plus container-only moment cross-checks against the upstream MITopenmc-plasma-sourcepackage (loud SKIP outside the container; theContainerfilegains the pinned oracle layer). -
OpenMC/Serpent weight-window emission in
nucleide-vr-tools(exposed asnucleide.vr.emit_openmc_weight_windows/emit_serpent_wwin, taking the sameMeshTally+MagicOutputpair asnucleide.vr.magic): MAGIC lower bounds are formatted into an OpenMCsettings.xmlfragment (<mesh>+<weight_windows>— spelling pinned against the public OpenMC docs §3.29/§3.66 with the sub-element layout the C++ reader parses; energies converted MeV → eV; flat bounds emitted x-fastest per energy group) or into the MCNP WWINP text spelling that Serpent reads viawwin <name> wf "<file>" 2(user guide §2.2.8.3 — the onlywwinFMT whose layout is publicly pinned; the Serpent-native FMT=1 layout is never emitted). OpenMC upper bounds are synthesized as5 × lowerand drift notes report synthesized or null-cell structure; ill-formed inputs (negative/non-finite windows, unsorted energy or mesh bounds, OpenMC tuning parameters outside the reader’s ranges) are loud named errors. Golden-text fixtures and re-parse assertions cover both emitters — the Serpent file round-trips through the existingnucleide-mcnp-ioWWINP reader — and always-run structural probes extend the existingmagicvalidation report (container OpenMC load cross-check; Serpent load is a loud SKIP, proprietary and not installed). -
Damage and gas-production metrics in the new
nucleide-damagecrate (exposed asnucleide.damage): NRT-dpa (Norgett, Robinson & Torrens, Nucl. Eng. Des. 33 (1975) 50–54 — modified Kinchin–Pease with the Lindhard damage-energy partition via the Robinson fit) and arc-dpa (Nordlund et al., Nat. Commun. 9 (2018) 1084, CC BY 4.0 — piecewise efficiency ξ(T_d)) as closed forms over caller-supplied material constants (E_d,b_arc,c_arc; no fitted table is vendored); He/H production in appm and He/dpa ratios by piecewise-constant-per-group spectral folding of caller(flux, response, bounds)slices (nrt_dpa/arc_dpa/gas_appm/he_dpa_ratio). Zero-flux groups contribute exactly 0; negative inputs, non-increasing bounds, and the He/dpa ratio at zero dpa are loud named errors — neverinf(theZeroMaxFluxprecedent).fold_uqpropagates caller-block uncertainty through the landedlinalgMVN machinery (pinned seed,k-standard- error gates against the exact bilinear expectation and the first-order propagated standard deviation; UQ on the ratio is a named-open). SPECTER (Greenwood & Smither, ANL/FPP/TM-197, US-gov PD) is the validation oracle only —validation/damage_vs_specter.pyfolds report-transcribed HFIR-CTR32 spots (Fe/Ti/Cu dpa; C12/Li7/B10/N14 gas) against the runtime hash-pinned PDF at report print precision, loud SKIP outside the container; no SPECTER table is vendored and ASTM E693/E521 stay designation-only. PKA-spectra solving stays out (the fispact-org PKA evaluator is GPL-3.0, never read). New-crate checklist applied (workspace/release wiring,gen-referenceentries, crate-responsibilities section, README feature table); theContainerfilegainspoppler-utilsfor the oracle’s PDF text extraction. -
MCPL particle-list utilities in
nucleide-mcpl-io(exposed asnucleide.mcpl.merge_mcpl/extract_mcpl/mcpl_stats/repair_mcpl, all file-based and.gz-transparent):merge_mcplconcatenates compatible files with the first-file header plus a provenance comment (stat:sumcomments ride along verbatim; sums are never synthesized or updated; mixed single/double input promotes to double, the lossless direction; any other header-option disagreement is a loud error);extract_mcplwrites an index-range (start/stop) or predicate subset with the source header preserved verbatim;mcpl_statsreturns record counts, energy moments, total weight, and a per-PDG-code histogram;repair_mcplimplements the paper-pinnedmcpl_repairsemantics, recomputing the particle count of a file whose writer never patched it and ignoring a partially written trailing record. Synthetic golden fixtures infixtures/mcpl/utils/pin merge concat order, precision promotion, and extract subsets; new always-run U1-U9 gates invalidation/mcpl_vs_refs.pycover the same contracts. -
Neutron spectrum unfolding in the new
nucleide-unfoldcrate (exposed asnucleide.unfold): the SAND-II iterative spectral adjustment (McElroy et al., AFWL-TR-67-41, 1967 — US government work, clean-room from the report) as an iterator over a caller-supplied response matrix, measured activation rates, and guess spectrum, with per-group relative-change convergence diagnostics (iteration count, final max change, per-detector measured/folded rate factors). The guess is adjusted by a positivity- preserving weighted geometric mean of per-detector correction factors until the fold reproduces the rates; non-convergence past the explicit iteration cap is a named hardNotConvergederror — never a silent partial spectrum (thetritiumface-Newton precedent) — and unreachable measurements (zero response rows or rates wiped by pinned-to-zero groups) are a namedRatesUnreachable. Every response value and energy-group bound is caller-supplied: IRDFF and other IAEA-copyright libraries are never vendored (a runtime-download pack stays a later decision under the FGR-15 precedent). STAYSL-class least-squares adjustment (on the sharedlinalg::lstsqkernel), GRAVEL, and MAXED are recorded for later one-method-per-cycle landings. Gates are synthetic forward-fold-then-recover round-trips at pinned tolerances plus the IRDFF-II analytical benchmark-field shapes (Trkov et al., Nucl. Data Sheets 163 (2020) 1 — published facts used with citation, not vendored data); the new always-runvalidation/unfold_vs_analytic.pyoracle replays them (no external unfolding oracle exists, so no container dependency is added). New-crate checklist applied (workspace/release wiring,gen-referenceentries, crate-responsibilities section, README feature table). -
Multi-layer tritium permeation in
nucleide-tritium(exposed throughnucleide.tritiumassteady_layers/transient_layers): series stacks of caller-specified layers (thickness, cells, ArrheniusD, solubilityK_S, per-layer traps/temperature/source — e.g. W/Cu/CuCrZr first walls) with Sieverts internal interface conditions (c/K_Scontinuous, flux continuous). The interface fluxes are linear in the cell values and fold directly into the tridiagonal θ-step matrix (same finite-volume stepper andlinalg::tridiagThomas solve), so no interface iteration is needed; outer ends reuse the landed surface taxonomy including recombination ends through the same face-response construction. Henry/recombination internal interface laws are loudUnsupportedInterfaceerrors in v1 (recorded limitation). A one-layer stack dispatches to the landed single-slab kernel and reproduces it exactly. Gates are analytic and synthetic, in unit tests with recorded provenance (nofixtures/tritium/changes): 2/3-layer series-resistance closed forms at1e-12with interface flux continuity to roundoff, property-continuous 2-layer ≡ landed kernel, layered recombination-outlet closed form, transientdt-halving order bands and late-time asymptote (Dirichlet and recombination outlets), and the discrete mass balance to roundoff.
Fixed
- Robustness hardening across the 0.12.0 surface (all loud errors, never
silent partials or panics): GDML
#ncomplement of an empty region isComplementTooComplexinstead of a panic; the FISPACT-II clearance reader rejects non-finite/negative inventory values and step times andtotal_clearance_indexreports overflow as an error (now returningResult<f64>);alara-iofraction sums report overflow instead ofOk(inf)and the EU Annex VII loader gains a fallibletry_eu_annex_viiconstructor with a row-count-pinned test;mcpl-iodecoding rejects non-finite particle fields and universal weights on read (mirroring the write path) so corrupt files fail loudly instead of poisoningmcpl_stats; parametric sampling uses a panic-free total-order CDF search plus a fallibletry_sample; SAND-II replaces its lastexpectwith a loud error; layered-tritiumunreachable!sites become named errors. - Efficiency:
merge_mcplpre-sizes from declared counts; layered-tritium per-cell layer lookup is a cached-offset binary search; Criterion coverage added for every 0.12.0 kernel and reworked emission path (newunfold,tritium,mcpl,plasmabenches;windows_emitandwwinp_to_textcases in the existingvr-tools/mcnp-iobenches).
[0.11.0] - 2026-09-14
Added
- EPA FGR 15 external-dosimetry coefficients (EPA 402-R-25-001, July 2025)
as a runtime-download feature — nothing from the EPA file is vendored.
nucleide.data.fetch_fgr15downloads the official coefficient zip into the per-user platform cache (%LOCALAPPDATA%\nucleide\Cacheon Windows,~/Library/Caches/nucleideon macOS,$XDG_CACHE_HOME/nucleideor~/.cache/nucleide/elsewhere), hash-pinned to SHA-25671314b3f1d73c197da8b589e74c450f61befce48c66ace6ac1f6e0597560bd91(mismatch fails loudly — EPA may have revised the file). The newnucleide-nucleifgr15module parses the sevenTable_4_*.DATscenario tables (1,252 nuclides × 6 age columns; units string recorded from the header), andnucleide.nucleigainsfgr15_dose_rate/load_fgr15_table/parse_fgr15_table/fgr15_age_index(FGR 15 name spellingsH-3,Ba-137m,Sb-124n). FGR 15 is external exposure only and complements — never silently overrides —dose_per_g(HNF-5636/PyNE).validation/nuclear_data_vs_refs.pygains an FGR 15 section (row-count/column gates plus ten hand-transcribed spots at exact equality). Screening-level only — not for safety decisions. - Fission-yield perturbation consumer:
nucleide-linalgperturb_fission_yieldsnow perturbs caller-supplied yield blocks (raw = base * (1 + rel), negatives clamped to zero, rescaled to the incoming block sum — independent blocks sum to 2, cumulative blocks higher; zero-base rows stay zero), closing the last named-open hook in the UQ kernel (theFissionYieldsOpenerror variant is removed). The thin Pythonnucleide.uq.perturb_fission_yieldswrapper starts working (signature unchanged); the caller supplies the block and buildsrel(tapedY/Ysigmas or correlated draws forwarded from the sampling kernel). New U7 validation gate invalidation/uq_lite_vs_sandy.py(theory U8) over a caller-selected U235-thermal subset: perturbed sums exact at the pinned seed, draw moments within the IID k-SE bound. - Scoped MCNP→OpenMC CSG translation v1 (new
nucleide-csg-xlatecrate):deck_csg_to_openmc_xmlmaps deck surfaces, cells, and a material stub (material="void"or the MCNP number) to OpenMCgeometry.xmlwith a per-cell/per-surface drift report (macrobody expansions, complement inlines, reflecting/periodic links, dropped params/cards, each with reasons). Axis planes, spheres, on-axis cylinders,SPH,RPP, and axis-alignedRCCmap; cones, quadrics, tori, general planes, other macrobodies, non-flat complements, transforms, lattices, matrix or transformed fills, tallies, and sources are loud named errors, while simple nested universes (U=k, single-universeFILL n) map touniverse=/fill=. Thin Pythonnucleide.mcnp.parse_csg_to_openmc/read_csg_to_openmcfacades plus syntheticfixtures/mcnp/inp/deck_csg_*.txtdecks and a CSG section invalidation/parsers_vs_refs.py(structural probes always run; the OpenMCRegion.from_expressioncross-check runs in the container). - Scoped MCNP→Serpent CSG translation (same
nucleide-csg-xlatecrate):deck_csg_to_serpent_inputemitssurf/cellcards over the same v2 scope with Serpent-native simplifications (RPPtocuboid, axis-alignedRCCto truncated cylinders,#npassed through as the native cell complement, empty regions via a synthesizedinfsurface; materials asm<n>/voidnames for caller-suppliedmatcards). Reflecting/periodic boundaries are a loudSerpentBoundaryOutOfScope(Serpentset bcis global; per-surface mapping unverified). Thin Pythonnucleide.mcnp.parse_csg_to_serpent/read_csg_to_serpentfacades plus structuralserpent structureprobe rows invalidation/parsers_vs_refs.py. - Scoped MCNP→PHITS CSG translation (same
nucleide-csg-xlatecrate):deck_csg_to_phits_inputemits[Surface]/[Cell]sections over the same v2 scope with manual-verified identical symbols (planes, spheres, on-axis cylinders,SPH/RPP/RCC-any-orientation/axis-alignedBOX, coefficients verbatim), native#complements,U=/FILL=params,*reflective surfaces, verbatim densities, and an outer-void-1heuristic for void union/complement cells (drift-noted). Periodic pointers are a loudPhitsBoundaryOutOfScope. Thin Pythonnucleide.mcnp.parse_csg_to_phits/read_csg_to_phitsfacades plus structuralphits structureprobe rows invalidation/parsers_vs_refs.py. - Rectangular-lattice (
LAT=1) CSG translation in all three directions (samenucleide-csg-xlatecrate): aLAT=1cell with a full matrixFILL(every element filled, no transform) now emits a rectangular lattice — OpenMC<lattice type="rectangular">(dimension,lower_left,pitch,universes; MCNPk, j, iorder maps toz-ascending /y-descending /x-ascending row-major), a Serpent cuboidallatcard (type 11) filled viafill <lattice id>, and PHITSLAT=1with a matrixFILL(ranges plus the universe list in MCNP order verbatim) — with pitch and lower-left derived only from the lattice cell’sRPPor axis-plane-box bounds and lattice ids allocated outside both cell and universe id spaces (each noted aslattice-emitteddrift). HexagonalLAT=2lattices,0-holes, non-RPP/axis-plane-bounded lattice cells, single-universe fills of lattice type, and fill transforms stay loud named errors. New syntheticfixtures/mcnp/inp/deck_csg_lattice_rect.txtdeck, per-direction universe-order tests, and lattice cross-reference probe rows invalidation/parsers_vs_refs.py. - Tritium-transport analytic-gate spec (no kernel yet):
docs/theory/tritium.mdxpins the T1–T2 equation set and the G1–G5 gate contract (steady linear, permeation time-lag, single-trap limits, Sieverts steady, recombination steady; recombination transient stays named-open) plusfixtures/tritium/oracles for the analytic-gate replay at kernel landing. - 1D tritium-transport kernel v1 (new
nucleide-tritiumcrate): T1 mobile diffusion with N extrinsic McNabb–Foster trap species (T2), caller supplied Arrhenius data and steady temperature profile (no tables, no heat solve), Dirichlet/Sieverts/Henry/zero-flux/recombination surface taxonomy (recombination steady state closed by the exact face-response construction with G5a/G5b gates; recombination transient stays a named-open G5 error), and a cell-centred finite-volume theta-stepper (Crank–Nicolson default, backward Euler on request) solving through the new sharednucleide-linalgtridiagThomas-solver module. Analytic-gate replay only (Rust fixture tests plustests/test_tritium.pyon G1–G5 steady at the pinned tolerances; novalidation/*_vs_*.py, no FESTIM oracle). Thin Pythonnucleide.tritiumfacade, WASMtritiumBreakthroughfacade with a breakthrough-curve interactive tutorial. - Gaussian KDE source sampling (KDSource-class, clean-room) in
nucleide-vr-tools:KdeSamplerfits an axis-aligned Gaussian KDE over caller particle vectors (Silverman or fixed bandwidths; zero-variance dims are loud) with deterministicdraw/pdf(caller randoms, no RNG inside), gated by Gaussian recovery, normalization, and determinism tests. Thin Pythonnucleide.vr.KdeSamplerfacade. - WATTS-class sweep expansion in
nucleide-r2s:SweepAxisvalidation, deterministic cartesianexpand_sweepto named case bundles, andassemble_resultsrequiring exact per-case coverage, plus thin Pythonnucleide.r2s.r2s_expand_sweepfacade. - Facility-flow accounting in
nucleide-r2s:snapshot_inventorytotals atoms per ARMI bare name (N × V × 1e-24over snapshot zones) for differencing facility snapshots, plus thin Pythonnucleide.r2s.r2s_snapshot_inventoryfacade. - JADE-class library assessment oracle
(
validation/library_vs_refs.py, auto-discovered): data hygiene, independent-block sums to 2.0, and cumulative coverage over the committed fission-yield pack (synthetic thresholds, no tapes). - Legacy
SDEFfixed-source reader innucleide-mcnp-io(newsdefmodule): typedSdefCard/SdefDist/SdefProblemmodel over thePOS,CELL,SURF,VEC,DIR,ERG,NRM,PAR,WGT,TMEkeywords (inline literals orDnreferences) plus the discreteSIn L/SPn D/SBn Ddistribution forms; every other keyword or option letter is a loud drift note or a cleanSdefError, never silently misread. Validation covers duplicate cards/keywords, danglingDnreferences, orphanSPn/SBncards, entry-count mismatches, and empty tables; canonical re-emission round-trips the spectroscopysdef_decay_sourcecard dialect byte-identically (80-column wrapping, C++ defaultfloat precision-6).DeckProblem.sdeftyped view (enforced invalidate) and thin Pythonnucleide.mcnp.parse_sdeffacade; E9 reader round-trip rows invalidation/spectroscopy_vs_pyne.pyplus synthetictests/test_sdef.pyround-trip and error-case coverage. - Tritium recombination transient closure in
nucleide-tritium(the last named-open gate in the kernel):solvenow accepts recombination ends (J = K_r c_m²) in the transient, closing each implicit θ-step by the G5 affine face-response construction reused per step — one base Thomas solve plus one sensitivity column per recombination end perdt, closed-form face for one end, analytic-Jacobian Newton for two, fused into the trap-Picard loop and sharingrtol/atol. TheRecombinationOpenerror variant is removed. Since no closed form exists for the recombination transient, asymptotic + self-convergence gates replace the algebraic style: G6a late-time asymptote to the G5a steady flux (1e-6 relative at 60 permeation lags), G6b K_r→∞/K_r→0 recovery of the Dirichlet/zero-flux transients, G6c discrete mass balance with the recombination leak to roundoff, G6d dt-halving θ-method order (Crank–Nicolson ≈ 2, backward Euler ≈ 1), G6e independent method-of-lines cross-check (node-centred central FD + explicit RK4, ghost-node recombination face) of the mid-transient trajectory at 0.5/1/2 permeation lags against the production transient, trap-free and trap-coupled (1.5e-4 relative on the mobile-profile max-norm, ≈5x measured headroom), plus positivity under the face clamp. Pythonnucleide.tritium.transientfacade unchanged (dict shape and signatures stay); the theory page gains the G6 section.
[0.10.0] - 2026-09-14
Added
- Fission product yields from the ENDF/B-VIII.0 neutron-induced and
spontaneous fission-yield sublibraries: committed
crates/nuclei/src/data/fission_yields.tsv(151,490 rows over 36 parents and 122 incident-energy sets; MF8/MT454 independent + MF8/MT459 cumulative, values verbatim — both sublibraries are unchanged carries of ENDF/B-VII.1 evaluations per the tapes’ README.txt, with the Mattera–Sonzogni cumulative-yield correction not applied), generated by the new--endf-nfpy/--endf-sfpyflags ofscripts/gen-nuclear-data.py.nucleide-nucleigainsFissionYieldOrigin/FissionYieldKindenums plusfission_yield_table/fission_yields/default_fission_yields/fission_yieldlookups (lowest-energy independent set = the OpenMCget_default_fission_yieldsdepletion convention); thin Pythonnucleide.nuclei.fission_yields/fission_yieldfacades. Thedepletionchain XML parser falls back to this library (lowest-energy independent set, products filtered to chain members) when a fissionable parent has no<neutron_fission_yields>, raising the existingBadStructureerror when the library lacks the parent. Spot gates invalidation/nuclear_data_vs_refs.pypin the table to tape values, with a loud-skip OpenMC cross-check when the nfy tapes are absent. - Seeded Latin-hypercube sampling (
nucleide-linalgsample_lhsover caller-supplied blocks: per-dimension one jittered draw per stratum via Fisher–Yates permutation, hand-rolled inverse-normal CDF, shared Cholesky/eigen-clip factor path withx = μ + Bz; thin Pythonnucleide.uq.sample_lhs+ WASMsampleLhsfacades). Ships with its separate validation gate U6 (theory U7) (validation/uq_lite_vs_sandy.py: G1 stratification-exact plus G2 moments within the IID bound as an upper bound only) over syntheticfixtures/uq/lhs_2x2.json. A draw mode, not a perturbation convention (perturb_energies(..., "lhs")stays an error). - Interactive UQ demo: MVN/LHS draw-mode toggle calling the WASM
sampleLhsfacade with the same (mean, cov, n, seed) inputs and result shape, with E2E coverage. - Interactive deterministic demo: the RTFLUX section now renders a chart-only
per-point flux profile (one grouped-bar trace per energy group over the
capped
parseRtfluxvalues, following the ISOTXS grouped-bar precedent) with E2E coverage. - Interactive deterministic demo: PARTISN writer section rendering and
validating structured deck dicts in the browser via new WASM
partisnRender/partisnValidatefacades (exacttitle/dim/zones(id/material/isotxs_labels/density)/sourcekeys mirroring the Pythonpartisn_render/partisn_validateshape, no camelCase aliases; zone labels validated against a pasted ISOTXS library viaparseIsotxs) with render preview, inline validation-error display, and E2E coverage. - Interactive activation demo: the ORIGEN TAPE6 per-step comparison now takes
a dynamic list of N pasted snapshots (add/remove, each re-parsed with the
same
parseOrigenTape6reader) into a per-nuclide series chart over all steps in the existing grouped log-bar bundle, with E2E coverage. Multi-snapshot file grammar stays out — pasted snapshots only. - Interactive activation demo: the R2S snapshot tab hosts an R2S voxels
section over a drift-accepted
VoxelTagscopy-port innucleide-wasm(copy/split zone-total tagging plus.photonSrcgroup select-and-sum overnucleide-alara-iophoton types only — nonucleide-r2sdependency, so the Rayon chain stays untouched). Zone totals and voxel maps tag in the browser (200-voxel demo cap) with a scatter/bar/heatmap/histogram chart bundle, with E2E coverage. - Crate-root error re-exports and
Resultaliases (API-stability pass, additive half):nucleide-linalgre-exportsDecayError/LstsqError/SampleError,nucleide-nucleire-exportsDialectError/ParticlesError/RxnameError,nucleide-depletionre-exportsCramError,nucleide-mcpl-iore-exportsSswError, andnucleide-fluka-iore-exportsMaterialError/UsrbinError(nucleide-mcnp-iostays module-path-only by design); the nine crates lacking one gainpub type Result<T>(crate-root innucleide-depletion,nucleide-kinetics,nucleide-linalg,nucleide-mcpl-io,nucleide-nuclei,nucleide-spectroscopy,nucleide-vr-tools, per-module innucleide-fluka-ioand all ninenucleide-mcnp-ioerror modules).#![warn(missing_docs)]now covers 17 of 18 crates —nucleide-mcnp-io(~132 missing docs) is the recorded deferral, owned by a future doc-volume cycle.
Changed
#[non_exhaustive]on all 38 public error enums across the 18 workspace crates (nucleide-mcnp-io’s nine per-module errors included): the API-stability sweep breaks exhaustive downstreammatches on these enums — a wildcard arm is now required — and new variants become non-breaking additions. No data struct or non-error enum was touched, and 154 internalResult<T, Error>respellings to the new aliases change no public signature.
Fixed
- Sampling kernel draw-count cap (
nucleide-linalgsample::MAX_SAMPLES= 10M enforced invalidate_blockasSampleError::TooManySamples→ValueError, coveringsample_mvn/sample_lhs/sample_lognormal). - Corrected
inv_normal_cdfdocs: inputs at/beyond[0, 1]yieldNaN, not∓inf(unreachable —sample_lhsclamps into(0, 1)). - WASM UQ facades (
uqSample/sampleLhs) rejectn < 2with a moments-need-n >= 2message instead of the confusingsample_covDimensionMismatch, and rejectseed >= 2^53(f64 integer precision — the saturatingas u64cast would collide streams). - WASM
voxelTagsFromTotalsrejects emptyzone_of_voxel(previously a vacuous success);voxelPhotonSumsrejects non-finitetimeSand emptynuclides;partisnValidaterejects emptyzones(previously a vacuous pass) whilepartisnRenderkeeps rendering zoneless decks by design (callers validate separately). - UQ demo clears stale results when switching MVN/LHS and rejects
seed >= 2^53client-side; activation demo treats a blank voxel cooling-time field as an error instead of silently querying0.0. - U6 validation gate reports FAIL instead of crashing with
ZeroDivisionErroron a hypotheticaln < 2fixture.
[0.9.0] - 2026-09-13
Added
-
MCTAL mesh tallies (
nucleide-mcnp-iomctalheadline): parses mesh-tally bodies (detector_type <= -1) — the 4-int meshfline (unknown ni nj nk, bare-forf<tally>spellings),(ni+1)+(nj+1)+(nk+1)cora/corb/corcbounds, the sharedd/u/s/m/c/e/tcards, andvalspairs over theni*nj*nkmesh cells (writer-loop order,ifastest). Syntheticfixtures/mcnp/mctal/synthetic_mesh.mctaloracle with closed-form val/err pairing, Rust tests, and PythonMctal.mesh_tallies/mesh_tally_vals_arrayviews. Radiograph (detector_type >= 3), point-detector (tally names ending in 5), and perturbation bodies stay named-open errors. -
MCTAL S-tails: optional
tfcblocks after standard tallies (jtfline plus 3–4-float data rows, exposed astallies[].tfc), total/cumulative card variants (ut/uc/…, stored verbatim intallies[]cardvariant), and third-token flags onc/e/tcards (stored verbatim as cardflag), with syntheticfixtures/mcnp/mctal/synthetic_tfc_variants.mctalcoverage. WWINP cylindrical (nr=16), SerpentUnsupportedExpr, SDEF versions beyond 5/6, and SPE gating stay as-is (loud errors or unchanged surfaces — no second field-order source on disk). -
validation/parsers_vs_refs.py: MCTAL section comparing header scalars plus per-cycle keff/lifetime series against PyNEMctalon the kcode-only fixtures; body-bearing files (standard, mesh, tfc/variant) are loud SKIPs — PyNE skips tally bodies without advancing past them, so no oracle exists there. -
Interactive completion bundle (WASM thin facades only — no new math): MCPL
readMcpl/writeMcpl/ssw2mcpl/mcpl2ssw(bytes-based, gzip magic-sniffed, 200-row cap) with a file-uploadmcpl-iotutorial staging the goldenssw2mcpl_expected.mcpl+reference.wpair; materialmaterialSeparate/materialBlend/cusumDetect; spectroscopyparseLinesTsv/energyBins/detectorEfficiency/parseDollarSpe/parsePlainSpe; kineticsinhourRho/stablePeriod/promptJump; deterministicparseRtflux(kind-switch mirrors Python); ORIGEN per-step comparison overparseOrigenTape6plus chart-only ISOTXS totals, TAPE5 flux-vs-step, and TAPE6 per-nuclide views. Named-open and recorded in AD-13: R2SVoxelTagsport (rayon feature unification), PARTISN writer, RTFLUX profile / ORIGEN per-step full views, multi-snapshot TAPE6 grammar. -
Shared dense-real weighted least-squares kernel (
nucleide-linalglstsqmodule, zero dependency change — faer 0.20 column-pivoted QRsolve_lstsqroute):weighted_lstsq(X, y, w)over callerVec<Vec<f64>>inputs (non-negative caller weights,sqrt(w)row scaling, named errors for shape/finiteness/weight/underdetermined inputs) with closed-form synthetic gates. One module, two consumers — the UQ-lite sampler keeps its Cholesky/eigen factor path. -
Efficiency-coefficient fit (
nucleide-spectroscopy, E7-fit extension, not a new equation number):fit_efficiencysolves the log-space linear least squares (y = ln eff,X[i][j] = (ln E_i)^jforfit == 1or(1/E_i)^jforfit == 2, caller-supplied weights) through the shared kernel, returningorder + 1coefficients fordetector_efficiency. Peak search/fit and FWHM stay out; every other coefficient vector stays a caller input. Syntheticfixtures/spectroscopy/efficiency_fit.jsonoracle (recovery + round-trip at 1e-9). -
Python API:
nucleide.spectroscopy.fit_efficiencythin wrapper over the new fit. -
validation/spectroscopy_vs_pyne.py: E7-fit synthetic recovery + round-trip gates (always run) plus a loud oracle-check SKIP — the upstream module ships no efficiency-fitting routine, so there is no container cross-check. -
Python facade bundle over existing Rust (thin wrappers only — no new math or data):
nucleide.nucleigainsrxname_label/rxname_doc/rxname_reaction/rxname_id_from_nucdelta/rxname_child/rxname_parent,particle_is_valid/particle_is_valid_pdc/particle_is_hydrogen/particle_is_heavy_ion, anddose_f1/dose_lung_model;nucleide.materialgainsmix_by_mass/mix_by_volume/specific_activity/materials_doc_to_xml/expand_elements/collapse_elements(bare element symbols map to natural-element placeholders);nucleide.flukagainsfluka_material_str/fluka_compound_str/fluka_builtin_set;nucleide.alaragainsalara_validate_deck/alara_check_block/alara_flux_total/alara_flux_len/alara_output_totals/alara_output_total_activity/alara_photon_total_strength/alara_schedule_total_time;nucleide.origengainsorigen_tape6_find/origen_tape6_total_activity/origen_tape9_find;nucleide.ccccgainscccc_rtflux_npoints/cccc_rtflux_point/cccc_rtflux_total/cccc_isotxs_find/cccc_isotxs_len;nucleide.fispactgainsfispact_is_output;nucleide.enrichmentgains the six assay mass ratios (prod_per_feed,tail_per_feed,tail_per_prod,feed_per_prod,feed_per_tail,prod_per_tail) plusalphastar_i;nucleide.kineticsgainsfrom_ifp;nucleide.vrgainsmagic_withplusMeshSourceSamplermode/num_voxels/table_lenaccessors;nucleide.mcnpis unchanged (dict surface complete);nucleide.mcplgainsmcpl_statsum_validate/mcpl_statsum_comment.depletion/serpentstay internals by design (no wrappers). Covered bytests/test_facade_bundle.pyon synthetic inputs and committed fixtures. -
Python tutorials: new
docs/tutorials/python/ vr-magic.md(MAGIC + alias-table sampling) anddocs/tutorials/python/rxname.md(registry, graph ops, particle helpers), a standalone-SWU section inenrichment-cascade.md, and worked mixing/specific-activity/MaterialsDoc/element-expansion sections inbuild-materials.md(replacing the “not yet exposed” note); all registered indocs/tutorials/python/index.mdanddocs/README.md. -
UQ-lite log-normal sampling (log-normal scope only; LHS stays out):
nucleide-linalgsamplegains aLogNormalperturbation convention (parse("lognormal"/"log_normal"/"log-normal"),applyasnominal * exp(delta)) plussample_lognormal(shared Cholesky/eigen-clip factor path and ChaCha RNG assample_mvn, then ay = exp(x)post-transform with log-space mean/covariance semantics) and the closed-form (U6)lognormal_mean/lognormal_covhelpers. Syntheticfixtures/uq/lognormal_2x2.jsonoracle with pinned seed and separate U5 validation gate (validation/uq_lite_vs_sandy.py:ln(y)MVN recovery plus closed-form mean; existing k-SE gates untouched). Python API:nucleide.uq.sample_lognormal/lognormal_mean/lognormal_covthin wrappers (perturb_energies(..., "lognormal")now parses). -
SSW↔MCPL fidelity tail (
nucleide-mcpl-iossw; bitarray words stay out — noisurf/rawtypedecode, no type-word reverse-engineering without a licensed type-table source; synthetic fixtures only, no transport semantics):mcpl2sswgainsforce_cs_to_one(defaultfalsekeeps the true cosine; opt-in forces the storedcsslot to1.0withu/vverbatim, removing the stored-cosine delta vs the 2.2.8 binary),niss_override(None= reference passthrough, the upstream-2.2.8 spelling verified over the synthetic pair;Some(v >= 0)stamps the tally-convention value, negative isNissOutOfRange), andallow_polarisation(default rejects non-zero input vectors withPolarisationPresent; opt-in drops them since SSW has no slot);ssw2mcplgains opt-inpolarisation(uniform vector +has_polarisation, non-finite isInvalidPolarisation),universal_pdg(single-kind inputs collapse file-wide, mixed isMixedPdgForUniversal), anduniversal_weight(equal weights collapse file-wide, mixed isMixedWeightForUniversal); the SSW-PDG table widens beyond neutron/gamma to electron 11 / positron −11 / proton 2212 with identical verbatim geometry (anything else stays loudUnsupportedPdg, no particle framework beyond the table). Each leg has named errors, synthetic both-direction Rust tests, Python facade options (ssw2mcplpolarisation/universal_pdg/universal_weight;mcpl2sswforce_cs_to_one/niss/allow_polarisation; five accepted kind spellings), committedfixtures/mcpl/ssw_conversion/goldens (extended, polarised, universal), andvalidation/mcpl_vs_refs.pygates S5–S8 in the samemcplreport (noSECTION_ORDERchange). WASM/demo untouched (no cheap surface). -
Python API:
nucleide.mcpl.mcpl2sswkeyword argsforce_cs_to_one/niss/allow_polarisationandssw2mcploptionspolarisation/universal_pdg/universal_weightover the widened kind table.
[0.8.0] - 2026-09-13
Added
- SSW↔MCPL conversion, neutron/gamma-only v1 (
nucleide-mcpl-iosswmodule, newnucleide-mcnp-iodependency for the SSW header/track types):ssw2mcplmaps kinetic energy verbatim (MeV), time shakes→ms (×1e-5), surface id→userflags(opt-out), and kind→PDG 2112/22, with single-precision + gzip-compressed-bytes output, an optional ≤100 MiB deck-embed blob, and named errors for other particle kinds;mcpl2sswclones a reference SSW header (code/version/deck passthrough,nrss/np1/orignp1patched to the converted tally with the reference’s table-2 sign) with surface id fromuserflagsor an explicit[1, 999999]override, time ms→shakes (×1e5), and direction cosines propagated verbatim. Surface id and particle kind ride in as explicit caller parameters — the local SSW reader has noisurf/rawtypedecode, so v1 never guesses them frombitarray. Deferred, all named-open: non-neutron/gamma particles (UnsupportedPdg), polarisation and universal codes, upstreambitarraytype-word round-trips, and any transport semantics. - Python API:
nucleide.mcpl.ssw2mcpl/mcpl2sswthin file-based wrappers over the new module (per-track surf+kind pairing, options dict,.gztransparent), plus a syntheticfixtures/mcpl/ssw_conversion/oracle pair (hand-framed two-track SSW reference with provenance notes + goldenssw2mcploutput). validation/mcpl_vs_refs.py: SSW round-trip gates S1–S4 on the synthetic pair plus a live upstreamssw2mcpl/mcpl2ssw(mcpl-extra2.2.8) converter cross-check T3–T5 — count and closed-form energies agree both directions; the scripts SKIP loudly when absent, never fail without the oracle.- UQ-lite sampling kernel, decay-only sub-scope (
nucleide-linalgsample+decaymodules, newrand0.9 dependency with OS-entropy features off so the crate still builds for wasm): seeded multivariate-normal sampling over caller-supplied covariance blocks (sample_mvn— Cholesky primary with namedcholesky/eigen_clipreporting, eigen-clipping fallback at 1e-12 relative floor, relative / absolute perturbation conventions, sample mean/unbiased-covariance convergence diagnostics with caller-supplied tolerances), plus a decay consumer (perturb_branchespreserving the1-BR(SF)deficit by renormalisation,perturb_energies, finiteness-checkedpassthrough). Fission-yield perturbation stays a named-open hook (FissionYieldsOpen, waits on ENDF fission-yield tapes); ERRORR/NJOY, MF32/40, transport coupling, and vendored covariance stores stay out. Closed-form covariance-recovery gates on synthetic blocks only (pinned seeds, k-SE statistical tolerances,fixtures/uq/). - Python API:
nucleide.uqthin wrappers (sample_mvn/sample_mean/sample_cov/check_convergence/perturb_branches/perturb_energies/passthrough/perturb_fission_yields) over the new kernel. validation/uq_lite_vs_sandy.py: synthetic covariance-recovery gates U1–U4 (always run) plus a live SANDYSamples.get_mean/get_covmoment cross-check over the nucleide draws (tape-free, no NJOY) with a loud SKIP when SANDY/pandas are absent; tape-drivensandy.sampling/ERRORR comparisons stay NJOY-gated skips.- UQ-lite tutorial + interactive demo:
docs/tutorials/python/uq-sampling.md(seededsample_mvnover the syntheticfixtures/uq/blocks, convergence diagnostics, decay perturbation) and a thinuqSampleWASM facade with an interactivetutorials/interactive/uqsampling demo.
[0.7.0] - 2026-09-12
Added
- Fuel-cycle micro-adds (
nucleide-material,nucleide-enrichment):Material::separatesplits a composition into product/tails streams by per-nuclide efficiency (finite values in[0, 1], unlisted nuclides go wholly to tails, per-nuclide mass conserved),Material::blendmixes streams at fixed ratios with explicit normalization (errors on empty, all-zero, or negative recipes — never a silent uniform split), and a dependency-free one-sided Page CUSUM change detector (Cusumwith Welford running mean/variance,new(ref_shift_k=0.5, alarm_h=4.0, startup=10)tuning plusupdate/status/statistic/reset). - Python API:
nucleide.material.separate_material/blend_material/Cusumandnucleide.enrichment.value_func/swu_per_feed/swu_per_prod/swu_per_tailthin wrappers over the new core. validation/enrichment_swu_vs_cyclus.py: two-tier SWU oracle — hand-recomputed closed-form gates on the cyclus assay ladder (feed 0.0072, product 0.05, tails 0.002, 10 units of product) plus a livecyclus.toolkit.enrichmentcross-check with a loud SKIP when cyclus is absent.- Full MCTAL tally bodies (
nucleide-mcnp-io): the standard (non-mesh, non-radiograph) per-tally layout —f/d/u/s/m/c/e/tbin cards plusvals(value, rel_error)pairs — with a syntheticfixtures/mcnp/mctal/synthetic_tally_bodies.mctaloracle (closed-form val/err pairing). Mesh tallies, radiograph/point-detector specials,tfcblocks, total/cumulative variants, and perturbation bodies stay named-open errors, never silent skips. - Python API:
Mctal.tallies/tally_nums/npertviews plusMctal.tally_vals_arrayNumPy bridge ((n_pairs, 2), float64 C-order) over the new bodies. - MCPL particle-interchange reader/writer (new
nucleide-mcpl-iocrate): format versions 2 (read-only octahedral directions) and 3 (read/write, adaptive-projection directions), single/double precision, polarisation, user flags, universal PDG/weight, header comments/blobs with upstreamstat:sumsyntax validation, gzip-transparent paths, and byte-exact synthetic round-trips. Record-level interop is cross-checked both directions against the upstream 2.2.8 implementation. SSW conversion is deferred. - Python API:
nucleide.mcpl.read_mcpl/write_mcpl/McplFilethin wrappers over the new crate, plus adocs/tutorials/python/ mcpl-interchange.mdtutorial. validation/mcpl_vs_refs.py: synthetic round-trip gates plus an upstreammcpl-package record-level cross-check (count, energies, PDG codes) with a loud SKIP when absent.- R2S per-voxel photon-source tags (
nucleide-r2s, no MOAB/HDF5):VoxelTagsmapping zone totals onto native-mesh voxels (tag_zone_totalscopies,split_zone_totalsconserves), plus.photonSrcgroup selection (photon_groups_at) and element-wise sums (sum_group_strengths) feeding real group spectra where the uniform-split placeholder stood. - Python API:
nucleide.r2s.tag_zone_strength/photon_group_sumsthin wrappers over the new tags. - SDEF runtime line tables (
nucleide-spectroscopy, E9 input path): the documented decay-lines TSV interchange (parse_lines_tsv/read_decay_linesoverenergy_MeV intensityrows) feeding the existing normalizer — no vendored lines, no legacy reader, synthetic fixtures only. - Python API:
nucleide.spectroscopy.parse_lines_tsv/read_decay_linesthin wrappers over the new reader. validation/spectroscopy_vs_pyne.py: runtime TSV interchange oracle — Cs-137 ENSDF lines throughpyne.dataround-trip the reader (energy column within 1e-9, E9 normalization to 1.0), loud SKIP when PyNE is absent or the energy/intensity pairing stays open.- Interactive MCTAL demo:
parseMctalWASM function over the new tally bodies (header plus per-tally pair counts and totals) with anmctalmode in the MCNP parser demo (inline sample plus the stagedsynthetic_tally_bodies.mctalfixture) and E2E coverage.
Changed
- Release workflow (
release.yml): the crates.io publish list now covers every publishable workspace member (nucleide-kineticsandnucleide-spectroscopywere never listed, which failed the 0.6.0 release atnucleide-bindingswithno matching package named nucleide-kinetics), plus the newnucleide-mcpl-io, and a fail-fast guard rejects future omissions instead of retrying lag-waits against a deterministic failure.
Fixed
- Hostile-input hardening in the MCTAL reader (
nucleide-mcnp-io): allocations now follow the bytes actually present instead of reserving from declared bin/cycle counts (same class as the 0.5.0FILL-matrix cap), the eight-card bin product saturates instead of wrapping, and fractional/negative bin counts are malformed-input errors rather than silent truncations. Valid files parse identically.
[0.6.0] - 2026-09-12
Added
- Plotly charts in the interactive Serpent and activation tutorials: the
Serpent
_res.mdemo plots theIMP_KEFFmean ± uncertainty across burnup blocks (newkeff_historyfield onparseSerpentRes), the_det.mdemo plots per-detector tally spectra against energy-bin midpoints (newspectrafield onparseSerpentDet, with Serpent 1 vs 2 column layouts), and the ALARA/FISPACT output tabs plot the reportedtotalspecific activity against cooling time. The activation demo stages the ALARAoutput/sample2.outand FISPACTinventory.fisfixtures behind new Load sample buttons. Website E2E smoke tests assert each chart renders. - Serpent and FLUKA output-parsing Python tutorials
(
docs/tutorials/python/parse-serpent-output.md,docs/tutorials/python/parse-fluka-output.md): read_res.m/_dep.m/_det.mfiles vianucleide.serpent.read_serpentand USRBIN.listallies vianucleide.fluka.read_usrbin, mirroring the MCNP parsing tutorial. Both are listed in the Python tutorial index and the docs index, and cross-linked from the tutorials “Finding more examples” section. - SVG figures on all six theory pages (
docs/theory/figures/): burnup-matrix column anatomy (depletion), MARC cascade schematic (enrichment), Walker alias-table construction (variance reduction), nucid digit layout (nuclear data), step-reactivity prompt-jump transient (kinetics), and peak/background anatomy (spectroscopy). Figures are staged into the website bysync-data.mjsand embedded with the kit’s theme-aware<SvgFigure>shortcode. - Theme-adaptive inline-SVG capability map on the documentation home page
(
docs/README.md):currentColorfollows the site theme andvar(--primary)follows the accent picker. The home intro was also rewritten as a reader-facing welcome with a “Where to start” section. - Decay-line SDEF source emission (
nucleide-spectroscopy, equation E9):nucleide.spectroscopy.sdef_decay_sourcetakes caller-supplied decay lines (energy, intensity), merges duplicates, sorts, and normalizes them to probability bins rendered as an MCNP point-source card — PyNE field order (POS=always;VEC=/DIR=1only when directed;WGT=;PAR=via thenucleiparticle dialects, MCNP versions 5/6), inlineERG=<E>for a single line, and discrete-distributionERG=D1+SI1 L/SP1 Dcards wrapped at 80 columns for several. Distribution-card syntax is parser-verified surface only (MCNP sampling semantics stay caller-side); no decay data is vendored. Validation gains E9 normalization/card gates plus container-gated byte-exact diffs against PyNEPointSource.mcnp. - Interactive legacy-reader tutorials: Serpent output (
_res/_dep/_det) and FLUKA USRBIN browser demos, and ORIGEN TAPE5/6/9 tabs in the activation demo —serpent-io,fluka-io, andorigen-ioare now WASM-exposed with live demos and E2E coverage. - “Common workflows” section in the Python API reference (five task-oriented
snippets in the submodule idiom), an “Optional runtime dependencies” note in
getting started (when
numpy/h5pyare needed), and “See also” links from the depletion, enrichment, kinetics, and spectroscopy tutorials to the committed cross-code validation results.
Changed
- Binding error messages for unsupported CRAM orders and unknown dose pathways/sources now name the accepted values (Python and WASM wordings aligned).
Fixed
- Theory-page SVG figures no longer render as white boxes in the site’s dark
theme: the six figures in
docs/theory/figures/are theme-aware (no opaque background, near-black strokes/text followcurrentColor, muted grays followvar(--muted-foreground), panel fills followvar(--muted)), and the pages inline them with the kit’s<SvgFigure>shortcode instead of<ImageFigure>. Labels that sit on hardcoded pastel fills keep hardcoded dark text (the nucid Z/A digits no longer turn near-white in dark theme), and the peak-anatomy sideband/background labels gained pastel pill backdrops for contrast in both themes. nucleide.serpent.read_serpentno longer wraps matrix variables in a spurious one-element outer list: matrices now map directly to 2-D lists of row lists (r["ABS_KEFF"][cycle]instead ofr["ABS_KEFF"][0][cycle], same for_dep.m/_det.mmatrices), matching the scalar/vector mapping. A matrix holding non-numeric values now raisesValueErrorinstead of silently reading as an empty list. The Serpent tutorial and the parser cross-validation script were updated to the faithful shapes.- Interactive demos: the Serpent parser demo no longer crashes on a
one-column
IMP_KEFFrow, and the FLUKA/MCNP mesh views no longer overflow the browser argument limit on meshes above ~65k bins (min/max now computed with loops instead of spread).
[0.5.0] - 2026-09-11
Added
- Legacy I/O completion (
nucleide-mcnp-io+nucleide.mcnp): ENDL EEDL/EPDL table reader (endlmodule:END_OF_TABLEframing, header slices,NFIELDS_RPROPbody widths,get_rxselector lookup, and anendftodmatching the PyNE C++ semantics) exposed asnucleide.mcnp.read_endl/EndlLibrary.get_rx/endl_endftod, with a syntheticfixtures/endl/oracle; shared Fortran unformatted-record framework (fortranmodule: typedBadRecordMarkerinstead of the upstreamAttributeErrorpath, little-endian only) now backing the SSW reader/writer; SSW combining (combine_filesport ofscripts/ssw_combine.py: signed-orignp1sum, plain-nrsssum, sign-preservingnpsshift, typed incompatibility errors) exposed asnucleide.mcnp.combine_ssw_files(+ssw_combine_mainCLI); Python-side PTRAC event-row stream (ptrac_event_rowsoverread_ptrac, pinned against the 19-columnPtracEventschema) with anh5pytable writer (write_ptrac_hdf5,ptrac_to_hdf5_mainCLI; HDF5 bytes never asserted) and a MOAB-free meshtal mesh-data extractor (meshtal_mesh_data, MOAB tagging stays caller-side). Validation gains an ENDL-vs-pyne.endloracle section (loud skips preserved). Known parity quirks stay documented, not fixed: SSWSF_00001split and negative-np1⇒table-2,ncrdsign⇒abs width, PTRAC echo-repack to 8-byte mode, meshtal single-group total mirror andRel_column map, WWINP{0:13.5E}fields. Big-endian real-bytes input stays unsupported (no fixture oracle). - Prescribed-reactivity point kinetics (new
nucleide-kineticscrate): PKE solver for 1+ delayed-neutron groups under constant/step/impulse/ ramp/polyline reactivity schedules in Δk, equilibrium initials, the inhour relation with stable-period solve, and the prompt-jump approximation. The integrator is an adaptive implicit θ-method (trapezoidal default, backward Euler on request) with exact stepping to schedule knots — no explicit-solver parity. All delayed data are caller-supplied (from_ifpdocuments the OpenMC provenance note); thermal feedback and flux coupling are out of scope (the WASMkineticsTransienttutorial facade is covered in its own entry below). - Python API:
nucleide.kinetics.solve/equilibrium/initial_rate/inhour_rho/stable_period/prompt_jumpthin wrappers over the new core, plustests/test_kinetics.pyand thevalidation/kinetics_vs_pyrk.pytwo-tier oracle (synthetic-fixture analytic gates O1–O4 plus a container PyRK ramp cross-check at 1e-3). - Gamma-ray spectroscopy and measurement (new
nucleide-spectroscopycrate): E1 rectangular / E2 five-point smoothing (edges copied), E3m == 1background / E4 half-open gross / E5 net counts with positional indexing, E6 quadratic energy bins, E7 log-polynomial efficiency evaluation in MeV (caller coefficients only, no fitting), E8 X-ray line algebra over caller-supplied atomic constants (no vendored tables), and the dollar/plain.spereaders with the upstream quirks pinned (duplicate-tag first-wins, missing-tag errors, live-real$MEAS_TIM:, last-plus-one$DATA:,keV-suffixed triplets, ignored$ROI:/$PRESETS:/$ENER_FIT:, positional 0-based dollar labels). FWHM fits are parsed, never evaluated; peak search/fit, activities, decay spectra, and plotting stay out of scope. - Python API:
nucleide.spectroscopy.rect_smooth/five_point_smooth/calc_bg/gross_count/net_counts/energy_bins/detector_efficiency/xray_lines/parse_dollar_spe/parse_spe/read_dollar_spe/read_spethin wrappers over the new core, plustests/test_spectroscopy.pyand thevalidation/spectroscopy_vs_pyne.pytwo-tier oracle (synthetic E1–E8 gates plus a container PyNE cross-check at 1e-12). - Browser-interactive tutorials for point kinetics and spectroscopy:
kineticsTransient(step-reactivity PKE solve returning then(t)series plus the E4 prompt-jump value) andspectroscopySmooth(E1 rectangular / E2 five-point smoothing plus E3–E5 gross/background/net counting) thin WASM facades over the verified crate APIs, withKineticsTransient/SpectroscopyDemodemos andtutorials/interactive/kinetics.mdx/spectroscopy.mdxpages. Demo presets stay synthetic (fixtures/kinetics/+fixtures/spectroscopy/values, hand-picked small numbers otherwise). - NumPy bridges for MCTAL/WWINP/PTRAC (bindings-only, same
result_arraypattern as the meshtal bridge: flatten →Vec::into_pyarray→ reshape view, float64 C-order,ValueErroron ragged/out-of-range):Wwinp.ww_row_array/ww_column_array/ww_particle_array((nft,),(n_groups,),(n_groups, nft)withnft = nf[0] * nf[1] * nf[2]),Mctal.k_arrays(five(n_cycles,)series) /averages_array((n_cycles, 14),(0, 14)when empty),PtracFile.events_array((n_events, 19)inptrac_event_columnsorder, absent variables as 0.0) /event_field_array((n_events,)). All arrays are owned, writable, and decoupled from the file data; the wheel stays NumPy-free (NumPy required only at call time,pytest.importorskipintests/test_mcnp_io.py). Plain-copy helpers (ww_row,ww_column, thek_col/averagesgetters,events) are unchanged.
Fixed
- Website build: the
phillips-1978entry indocs/theory/spectroscopy.mdxcarried no URL, which the docs content schema requires (references.0.url), sonpm run buildfailed. The entry is now plain prose in the References section (citation text only, no journal-verification claim, no invented link). - Stable-as-zero analytics (
nucleide-material): a known nuclide with mass data but no decay constant is now stable (λ = 0) and contributes exactly 0.0 toactivity/decay_heat/dose_per_ginstead of raisingMissingDecay. Stable nuclides skip the dose-factor lookup entirely, so fully stable compositions such as H2O return 0 on every pathway/source. Nuclides with no mass data still raiseMissingMass, and radioactive nuclides without a dose row (including-1air sentinels) still raiseMissingDose. Python/WASM signatures are unchanged.
[0.4.0] - 2026-09-10
Added
-
Python API:
MeshTally.result_array()/totals_array()zero-copy NumPy bridge over the meshtalresult/rel_errortables (supersedes the 0.3.0 deferral):result_array()returns owned writable C-order float64(ve, group)arrays withve = (i * ny + j) * nz + k, moved out of one row-major flatten viaVec::into_pyarray+ a reshape view (no second copy);totals_array()returns(num_ves,)totals pairs directly. Requires NumPy installed at runtime (numpy>=1.26in thetestextra; base wheel stays dependency-free).to_list()/totals_list()stay as the NumPy-free plain-copy path. -
License-free decay-data pack (
nucleide-nuclei, AD-7): per-branch daughters from ENDF/B-VIII.0 decay tapes (crates/nuclei/src/data/decay_branches.tsv, 5 068 rows over 3 541 parents; MF8/MT457 NDK RTYP/RFS/BR with SF/fission branches dropped and zero-half-life tapes absent), exposed asdecay_branches/branching_fractionlookups plus theDecayDatafacade and theDecayBranch/DecayBranchModetypes. Isomer masses extendame2020.tsv(738 rows:m_ground + ELIS/931.49410242 ufrom each isomer tape’s File-1 MT451 record; tapeless isomers fall back to the ground-state mass, Q-values stay ground-state-only). New free-formnormalize_nuclide_nameincrates/nuclei/src/dialects.rs(symbol-first, then mass-first soN15stays nitrogen and92235stays a ZAID; bare symbols rejected;Ir-192nresolves to state 2).scripts/gen-nuclear-data.py --endf-decay8regenerates both tables with hard ENDF spot-checks (K-40 beta-/EC pair summing to 1, Es-254 members);decay_energy.tsvstays on ENDF/B-VII.1 with its basis in its header. -
Python API:
nucleide.nuclei.decay_branches/nucleide.nuclei.decay_branch_fractionthin wrappers over the new tables. WASMdecay_branches/decay_branch_fractiontable-level mirrors. -
validation/nuclear_data_vs_refs.py:DECAY_BRANCH_SPOTSoracle (K-40 pins 3.93839e16 s plus its beta-/EC pair; Es-254 members per the repo tables) following the decay-energy spot pattern; committed results untouched, container rerun deferred. -
nucleide-depletionanalytic Bateman decay fast path (crates/depletion/src/bateman.rs): cachedC/C⁻¹eigendecomposition closed form (Bateman 1910; Amaku–Pascholati–Vanin CPC 181 (2010)) for decay-only lower-triangular chains, generated at runtime from the crate’s own chain data, plus an f64-carefulBatemanHpvariant (magnitude-sorted terms, Neumaier compensation,exp_m1for smallλt; no new dependencies). NewMethod::{Cram(Order), Bateman, BatemanHp}selector ("cram16"/"cram48"/"bateman"/"bateman_hp") threadingdeplete_with_method/integrate_with_method/DecayInventory::decay_with_method(existingOrder-based entry points stay as CRAM shims;dt == 0.0returns the input exactly). Non-decay systems fall back to CRAM-48 inside the dispatcher: near-degenerate half-lives (1e-12relative gap with a live coupling path), cyclic or out-of-order topology, and reactions/fission on; stable nuclides use limit forms inline. -
Python API:
method=onnucleide.depletion.deplete/deplete_series/DepletionSystem.solve/solve_vec/Inventory.decay(default"cram48"; an explicitly non-defaultmethodoverridesorder). WASMdeplete/depleteSeriesgain an optional trailingmethod. -
validation/depletion_vs_openmc.py: in-memory Bateman-vs-CRAM-48 cross-check column (single-step vs CRAM-48 within1e-6, vs analytic within1e-8, series band within1e-6); committed results untouched, container rerun deferred. -
nucleide-mcnp-ioL3 semantic objects (crates/mcnp-io/src/semantic.rs): typedMODE(37 particle shorthands, default{N}),TRncards (degrees flag, 3-entry displacement, 5–9 entry rotation, main-to-aux flag, hidden inlineFILLtransforms), auto-created universes (U/-U, data-block lists withJ/nJ/nR/nMexpansion),LAT(1|2 only), cellFILL(single universe or 3-D matrix with transform reference or hidden transform; data-block lists are simple per-cell only), per-cellIMP/VOL, periodic surface pointers, and a minimal typed tally model (Fnnumber + particle classifier + entries with groupedFMn/En).DeckProblem::validate()centralizes duplicate-number conflicts, dangling material/surface/complement/transform/periodic/fill links, redundant cell+data definitions, write-time state checks, and nucleide-defined lattice/fill cross-checks (LAT-without-FILLandFILL-matrix-without-LATare errors); particle/mode mismatches are notes viavalidation_notes(). Synthetic fixturefixtures/mcnp/inp/deck_l3.txt(no license needed). -
Python API:
DeckProblemmode/transforms/universes/lattices/fills/importances/volumes/talliesgetters,validate()/validation_notes(), andset_mode/set_cell_universe/set_cell_lattice/set_cell_fillsetters. -
nucleide-emitsingle-material emission (crates/emit/): oneMaterialrenders through five code dialects — MCNPmcards (mass fractions, configurable xs suffix, 128-column packing), Serpentmatcards ({zaid}.{lib}ids), FLUKACOMPOUNDcards, ALARAmixtureblocks, single-zone PARTISN decks — plus a mass-drift report (emit_drift) with per-nuclide drop reasons and re-parse verification where readers exist (MCNP, ALARA). -
Python API:
nucleide.emit.emit_cards/emit_drift_tablethin wrappers over the new crate. -
nucleide-emitARMI blueprint bridge (crates/emit/src/armi.rs):from_armi_mass_fracsbuilds aMaterialfrom ARMI-side post-expansion mass-fraction keys (database names, bare names, ZAIDs-as-strings, AAAZZZS ids, unambiguous MC2-3 labels via the nuclei bridge) for unchanged MCNP/Serpent/FLUKA/ALARA/PARTISN emission. One-way, dict-in only: elemental keys (caller passes expandedmassFrac), number fractions, enrichment shorthands,balance, and temperatures stay caller-resolved; bareAM242is rejected (passAM242M/AM242Gexplicitly). -
Python API:
nucleide.emit.emit_armi_cards/emit_armi_drift_tablethin wrappers over the bridge. -
nucleide-nucleidose factors (crates/nuclei/src/data/dose_factors.tsv, 1,116 rows: 93 folded nuclides × 4 pathways × 3 sources from the BSD-3 PyNEdbgen/dosefactors*.csvtables, HNF-SD-WM-TI-707 Rev.1 / HNF-5636 App. O;+Dfolds into the parent, GENII/DOE air are-1sentinels):DosePathway/DoseSource/DoseEntrylookups plus aDoseDatafacade, generated by the committed stdlib-onlyscripts/gen-nuclear-data.py(--dose-*.csvflags over local copies). -
nucleide-materialdose per gram:DoseProvidertrait (DoseFactors,NoDoses, plusimplfornucleide_nuclei::data::DoseData),Material::dose_per_g/total_dose_per_gfollowing PyNE’sMaterial::dose_per_gequations (per-gram map semantics; screening-level only, not for safety decisions). -
Python API:
nucleide.nuclei.dose_factorandnucleide.material.dose_per_gthin wrappers over the vendored tables + material analytics. -
WASM
WasmDeckProblem(bindings/wasm/src/lib.rs) +deck-editordemo (website/src/components/interactive/DeckEditor.tsx,docs/tutorials/interactive/deck-editor.mdx):fromTextdeck parsing (text only, no filesystem in the browser), typedcells/surfs/mode/transforms/universes/lattices/fills/importances/volumes/talliesviews,materialNumbers/dataNames,cellInventory(cell→material map),validate/validationNotes, andsetCellDensity/setCellMaterial/setMode/setCellUniverse/setCellLattice/setCellFillsetters over the existing Tier 1DeckProblemAPI.WasmInventoryover the existingDecayInventoryAPI (unitsdefault"atoms",decayhonoring rates + solvermethodvia the predictor single-step,activities/masses/moles, fractions,halfLivesReadable,add/sub/mul/div,toCsv/fromCsv) pluscumulativeDecays/progeny/branchingFraction/decayMode/chainEdgesfree functions. The demo parses the inline syntheticdeck_minimal.txt, fetches the syntheticdeck_l3.txtsample staged bysync-data.mjs, edits cells through the setters, and shows validation plus byte-identicaldumps. -
nucleide-r2sARMI DB-snapshot adapter (crates/r2s/src/snapshot.rs):deck_from_snapshot/R2sWorkflow::from_snapshot/snapshot_workflowbuild a validated volumes-method ALARA template deck from versionless caller-dumped dicts (opaque zone ids 1:1 from block names, 1:1 zone→mixture with atoms/barn-cm number densities as elementvol_fraction, caller-supplied fluxes/cooling/schedule; empty compositions map tovoidand are skipped). Dict-in only: no HDF5 dependency, no ARMI layout versioning mirrored, synthetic test data only. Composition keys follow the emit ARMI-input rule (post-expansion nuclide keys; elemental keys, bareAM242, and unknown names are errors). -
Python API:
nucleide.r2s.r2s_from_snapshotthin wrapper returning{workflow, deck, decks}(workflow summary, canonical template deck, one deck per step). -
Interactive
mcnp-iodemo (website/src/components/interactive/McnpParser.tsx,docs/tutorials/interactive/mcnp-io.mdx): the xsdir histogram now pairs the existing per-element table-count bar with a table-count bar by library suffix and an AWR-distribution histogram over the stagedxsdir_sample.txt. -
Interactive
variance-reductiondemo (website/src/components/interactive/VrDemo.tsx,docs/tutorials/interactive/variance-reduction.mdx): the MAGIC energy upper-bound step line (hvshape, log energy axis) gains a group-count title. -
Interactive tutorial E2E coverage (
website/e2e/smoke.spec.ts): follow-up clicks with per-click output assertions on every demo page — deck-editorValidate+ a setter flow +Load L3 sample, variance-reductionSample index/Sample voxel, materialsMix+To XML, enrichmentOptimize M*, activation ALARA-output / FISPACT / R2S mode tabs, mcnp-iowwinpparse, depletionLoad sample chain. -
WASM mirrors for the new Rust APIs (
bindings/wasm/src/lib.rs, typed inwebsite/src/types/nucleide-wasm.d.ts):emitCards/emitDriftTableovernucleide-emit(all five dialects,EmitOptsoverrides, drift rows with per-nuclide drop reasons),emitArmiCards/emitArmiDriftTableover the ARMI bridge (elemental keys, bareAM242, and unknown names throw),doseFactor/dosePerGramover the vendored HNF-5636 tables (pathwayair/soil/ingest/inhale, sourceEPA/DOE/GENII, screening-level only), andr2sFromSnapshotover the snapshot adapter (typedSnapshot*Jsoninputs returning a{workflow, deck, decks}bundle). -
Interactive
emitterdemo (website/src/components/interactive/ EmitterDemo.tsx,docs/tutorials/interactive/emitter.mdx): GNDS/ARMI key toggle with inline presets, material name/density/per-dialect options, all five dialect cards plus the drift table, with inline errors for elemental/AM242/bad keys. -
Interactive
materialsdemo dose-per-gram section (website/src/ components/interactive/MaterialBuilder.tsx): pathway/source selects scoring the current formula viadosePerGram, with a screening-only note. -
Interactive
activationdemor2s-snapshotmode (website/src/ components/interactive/ActivationDemo.tsx): an inline two-zone JSON snapshot parsed viar2sFromSnapshotinto workflow steps plus the template and per-step deck count. -
Interactive tutorial E2E coverage (
website/e2e/smoke.spec.ts): emitter GNDS emit plus ARMI re-emit with drift assertion, materialsCompute dosevalue, activationR2S snapshottab with steps and deck-count outputs.
Fixed
- Interactive tutorial input guards (inline
WASM error:before any WASM call instead of NaN passthrough): deck-editor density / material / universe / lattice / fill / mode setters reject non-finite input (website/src/components/interactive/DeckEditor.tsx,docs/tutorials/interactive/deck-editor.mdx); variance-reduction tally / tolerance / null value plus alias-table PDF tokens reject non-finite input instead of silently dropping bad tokens (website/src/components/interactive/VrDemo.tsx,docs/tutorials/interactive/variance-reduction.mdx); depletion atom counts / time step / burnup steps require finite (and positive where applicable) values (website/src/components/interactive/DepletionStep.tsx,docs/tutorials/interactive/depletion.mdx). - Invalid nuclide-id fallback labels instead of panics (
crates/nuclei/src/ lib.rs,crates/nuclei/src/armi.rs):NuclideId::from_nucidon a raw integer outside the validated(Z, A, state)domain previously panicked downstream — out-of-boundsELEMENTS[z]indexing into_name, or an.expect()on the element-symbol lookup innucid_to_armi_label. Both are now total: such ids render the diagnostic fallbackZ{z}A{a}[m{s}](with the samen-prefix/capitalization treatment on the ARMI side), whichfrom_namedoes not parse. Newtry_from_nucid/is_validvalidate untrusted integers (1 <= Z <= 118,Z <= A <= 999, canonical tail) with the matchingErrorinstead of producing a fallback id. - Poisoned-lock getters now return errors instead of panicking
(
bindings/python/src/lib.rs): everyPyCascadegetter andPyDeckProblemgetter/setter previously called.lock().unwrap(), so a poisonedMutexaborted the interpreter. Each now maps the poison to aValueError("cascade lock poisoned"/"deck lock poisoned"). - Cell-
FILLmatrix cap at 1 000 000 cells (crates/mcnp-io/src/semantic.rs): a hostileFILLrange such as-2147483648:2147483647previously overflowed thei32width computation (debug panic / release wrap) and could drive a gigabyte allocation. Widths are now computed ini64with checked multiplication, and any matrix above the cap fails with a cap error before allocation; larger lattices must be built programmatically, not spelled per cell. - Non-finite
n0validation at the Bateman solve entry (crates/depletion/src/bateman.rs):BatemanCache::solvepreviously propagated NaN/infinite/negative initial counts into the triangular solves. It now rejects them with aLinalgerror (n0must hold finite atom counts>= 0), matching the existingdt/length-mismatch errors. - Snapshot-mixture emission returns an error instead of panicking
(
crates/r2s/src/snapshot.rs,crates/r2s/src/error.rs): thedeck_from_snapshotmixture writer previously ended its entry match withunreachable!, so a future non-element variant would panic. It now returnsError::Invalid(theerror.rs:16arm) with the offending entry attached.
[0.3.0] - 2026-09-08
Added
nucleide-depletiontime-series integrators (crates/depletion/src/integrate.rs):Integrator::{Predictor, Cecm, Cf4}withintegrate(sys, n0, steps, integrator, order)returning per-node atoms, activity (λ·N), and decay heat (λ·N·E, chaindecay_energy→ ENDF/B-VII.1 table → zero). Synthetic A→B→C fixturefixtures/depletion/chain_abc.xmlplustests/test_depletion_timeseries.pycovering multi-step Bateman accuracy.nucleide-nucleidata breadth (no HDF5, no hand values):simple_xs.tsv(241 rows; thermal totals from NIST NCNR bound XS as free-atomxs·(A/(A+1))² + xs_a, fast totals from ENDF/B-VII.1 MF3/MT1 at 14 MeV),scattering_lengths.tsv(267 rows from NIST NCNR Sears-1992, complex lengths by real part, monoisotopic element attributions),decay_energy.tsv(3,557 rows of mean prompt recoverable MeV/decay from ENDF/B-VII.1 MF8/MT457; daughter gammas live on daughter rows, so Cs137 reports prompt 0.179 MeV and Ba137_m1 0.661 MeV — chain codes sum members). All three generated by the committed stdlib-onlyscripts/gen-nuclear-data.py(run--helpfor upstream URLs); resonance absorbers without NIST rows and isomers stay absent by construction.nucleide-materialdecay heat:DecayEnergyProvidertrait (DecayEnergies,NoDecayEnergies, plusimplfornucleide_nuclei::data::DecayDataso depletion gets λ and MeV from one provider with no circular deps),Material::decay_heat/total_decay_heatin watts, andMEV_TO_JOULES. Dose coefficients stay a documented deferral (PyNE’sdbgen/dosefactors*.csvare the future source).nucleide-depletion: single decay-energy source of truth — the hand-kept fallback table is removed anddecay_energy_mevresolves chain → ENDF/B-VII.1 table →0.0; new shareddecay_energies_by_namehelper.- Python API:
nucleide.depletion.deplete_series— multi-step depletion driver over the coreintegrateseries (predictor/cecm/cf4; per-steprates/rates_list), returningtimes/atoms/activity/ per-nuclidedecay_heatmaps through the shared resolver (identical to coreintegrateheats; the coret = 0row is omitted). - Python API:
nucleide.nuclei(simple_xs,scattering_length,decay_energy) andnucleide.material(decay_heat) thin wrappers over the vendored tables + material analytics. - Python API:
MeshTally.to_list()/totals_list()plain-copy helpers; the zero-copy NumPy bridge (result_array()) stays deferred — nonumpydependency introduced. nucleide-mcnp-iofull-deck round-trip (crates/mcnp-io/src/cell.rs,surf.rs,problem.rs): typed cell/surface cards (CSG expressions with:union, juxtaposition intersection, parentheses,#complement,*reflecting markers; 40 surface kinds incl. macrobodies),DeckProblemparse/edit/write with format-preserving write-back (byte-identical when unedited; only touched cards re-render canonically) plusset_cell_density/set_cell_material. Synthetic fixturesfixtures/mcnp/inp/deck_{minimal,macro,params}.txt(no license needed).nucleide-depletionunit-aware inventories (crates/depletion/src/ inventory.rs):DecayInventoryover atom counts with activity/mass/mole units (Bq…Ci,g/kg,mol, time ins/m/h/d/y), fractions, readable half-lives, arithmetic, CSV round-trip, pluscumulative_decaysand chain observables (progeny,branching_fraction,decay_mode,chain_edges).nucleide-nucleiARMI dialect bridge (crates/nuclei/src/armi.rs):armi_name_to_nucid(nU235, ZAIDs, MCC3, AAAZZZS),nucid_to_armi_label,mcc3_to_nucid.nucleide-materialcomposition checks (crates/material/src/check.rs):check_labels(truncated-label collisions at DIF3D/MC2 6/8-char widths across GNDS/ZAID/Serpent/ALARA/ARMI labels) and conservationaudit(non-positive totals, negative masses, duplicates, unknown masses).- Python API:
nucleide.mcnp.DeckProblem(parse_deck/read_deck,dumps, setters, cell/surf/data accessors),nucleide.depletion. Inventory+cumulative_decays/progeny/branching_fraction/decay_mode/chain_edges,nucleide.nuclei.armi_to_nucid/nucid_to_armi/mcc3_to_nucid,nucleide.material.check_labels/audit_material.
[0.2.0] - 2026-09-06
Added
cccc-iocrate: CCCC text-subset parsers (ISOTXS multigroup libraries, RTFLUX/ATFLUX/RZFLUX flux files) + PARTISN deck writer with ISOTXS nuclide mapping and validation (no solver). Synthetic fixtures underfixtures/cccc/(no license needed).fispact-iocrate: FISPACT-II inventory-output parser producing ALARA-compatible response frames (output-only, reusingResponseFrame; no solver). Synthetic fixturefixtures/fispact/inventory.fis(no license needed).origen-iocrate: scoped ORIGEN 2.2 TAPE readers —TAPE5input echo,TAPE6output inventory,TAPE9-style decay constants. Synthetic fixtures underfixtures/origen/(no license needed).r2scrate: scoped R2S workflow builder — zone-to-flux linking from ALARA decks, schedule expansion, per-zone deck emission, and photon-source assembly with a documented uniform-split approximation (total strength preserved; group-wise emission lines stay in ALARA.photonSrcspectra). Transport and activation solving stay out of scope.- Python API:
nucleide.cccc(isotxs_parse,rtflux_parse,partisn_render,partisn_validate),nucleide.fispact(fispact_parse_output),nucleide.origen(origen_parse_tape5/6/9), andnucleide.r2s(r2s_from_deck,r2s_validate,r2s_expand,r2s_assemble) exposing the four new crates as plain dicts/lists. alara-iocrate: ALARA input-deck, group-flux, material/element/WDR library, activation-output, photon-source, and schedule-expansion parsers (glue only; solver out of scope). Fixtures vendored verbatim from UW ALARA BSD samples underfixtures/alara/(terms infixtures/alara/LICENSE.ALARA).- Python API:
nucleide.alara(alara_parse_deck,alara_parse_flux,alara_parse_output,alara_expand_schedule) exposing thealara-iodeck/flux/output/schedule glue as plain dicts/lists.
Changed
- Release workflow: removed the non-functional
cargo publish --dry-runstep for workspace crates and addedset -euo pipefailto the publish loop so a failure in one crate stops the job.
Fixed
- Workspace crate dependencies now specify a
versionrequirement socargo publishaccepts them for crates.io. scripts/bump-version.shnow updates workspace crate dependency versions alongside[workspace.package]version.
[0.1.0] - 2026-08-30
Added
depletion: reactionbranching_ratiosupport in chain XML (default 1.0), with branched gains in the burnup matrix.depletion: light-particle production in the burnup matrix — He-4/H-1 from alpha/proton decays and from reaction secondaries such as (n,α) and (n,p), when the product nuclide is in the chain.depletion: decay branching ratios are renormalized to sum to 1 (largest branch adjusted) for programmatically built chains, matching OpenMC’s chain-generation behavior.depletion: CASL-style fission-yield borrowing in chain XML (<neutron_fission_yields parent="X"/>resolves X’s yields, transitively), matching OpenMC.nuclei:NuclideId::from_namenow accepts PyNE-normalized forms such as"U-235","u235", and uppercase-Mmetastable markers.- Python API:
Cascade.solve_multicomponent()(M*-optimizing solve),DepletionSystembuilder andsolve_vec()for repeated solves without per-call overhead, andMeshTally.total_rel_error. - Python API:
nucleide.datamodule (fetch,fetch_compendium,default_ref) for downloading repo data files — the Materials Compendium, sample depletion chains — pinned to the installed version’s tag, since the wheel bundles no data files. - Criterion benchmarks in
crates/*/benches/(cargo bench). validation/: runnable cross-code validation harness against PyNE 0.7.5 and OpenMC 0.16.0 (containerized viavalidation/Containerfile+validation/run_container.sh) with committed results (validation/results.md).validation/: full-chain depletion validation on the CASL/VERA simplified chain (downloaded to the git-ignoredvalidation/.cache/), parser cross-validation against PyNE and serpentTools oracles (parsers_vs_refs.py), and generated paper figures (validation/figures/, viamake_figures.py).- JOSS submission materials:
paper.md,paper.bib,CITATION.cff,CONTRIBUTING.md, and a draft-PDF workflow. bindings/wasm:wasm-bindgencrate exposing a subset of the workspace to the browser, including aWasmMaterialsCompendiumAPI that parses the DOE/PNNL Materials Compendium from its JSON text.- Documentation website (
website/, Astro +@nukehub/docs-kit) with content synced fromdocs/, theory pages, and interactive WASM tutorials (including a compendium browser that fetches the stagedMaterialsCompendium.jsonand charts compositions with Plotly), deployed to GitHub Pages. - Interactive tutorials: MCNP meshtal z-slice heatmap with log colorscale and relative-error toggle, xsdir table-count histogram by element, depletion burnup-curve plot, material atom-vs-weight-fraction bar chart, enrichment cascade stage-profile line chart, and MAGIC energy upper-bound step line.
enrichment:Cascade::stage_profile()returns per-stage assays of the enriching key via the ideal-cascade recurrence, exposed through WASM asWasmCascade.stageProfile().- Release workflow:
vX.Y.Ztags build and publish Python wheels for Linux, macOS, and Windows to PyPI, publish workspace crates to crates.io, and draft a GitHub release from the matching changelog section.
Changed
-
Documentation website moved from
https://nukehub-dev.github.io/nucleideto a custom domain,https://nucleide.nukehub.org, hosted on GitHub Pages with Cloudflare DNS; the Astrobasepath changed from/nucleideto/. -
Documentation theory pages (
depletion,enrichment,nuclear-data,variance-reduction) now use the citation support from@nukehub/docs-kit: references are declared in frontmatter, cited inline with<Citation />, and rendered as a linked bibliography with copy-to-clipboard export. -
Python API reorganized into domain submodules (
nucleide.nuclei,nucleide.material,nucleide.mcnp,nucleide.serpent,nucleide.fluka,nucleide.vr,nucleide.enrichment,nucleide.depletion) mirroring the workspace crates; the top level re-exports the domain submodules alongsideversion()and__version__. -
vr-tools:MeshSourceSamplernow rejects negative or non-finite tally / user-density values with an error instead of silently absolutizing them. -
depletion: reaction loss is subtracted once per reaction type per nuclide; duplicate entries only add branched gains. -
depletion: CRAM pole solves reuse scratch buffers across poles. -
nuclei: FLUKA name lookups use lazily-built maps instead of linear scans.
Fixed
-
Documentation website “Edit this page on GitHub” links now point at real repo files (docs-kit sync injects the source path as
editPathfrontmatter); previously every link 404’d — all were missing the file extension, and the changelog link pointed atdocs/changeloginstead of the repo-rootCHANGELOG.md. -
Documentation website deployment now builds the
bindings/wasmpackage in CI, so the interactive tutorials can load/wasm/nucleide_wasm.jsinstead of receiving the SPA fallback HTML response. -
Interactive tutorials: file/text inputs now use the docs-kit
Textareaauto-resize (content-fitted height, no manual resize grip); kit bumped for aSelectdropdown fix so short lists flipped above the trigger no longer float with a gap. -
depletion:Chain::from_xmlno longer renormalizes decay branching ratios — file values are used verbatim, matching OpenMC’sChain.from_xml(renormalization only happens at chain generation). Found by the CASL full-chain validation (e.g. I-128 β⁻ branching ratio 0.931). -
depletion: fission production now uses the yield set at the lowest incident neutron energy, matching OpenMC’sget_default_fission_yields. -
nuclei:NuclideId::newrejects mass numbers above 999, fixing a debug overflow panic from inputs like"U999999". -
depletion: zero/negative/non-finite half-lives and non-positivedtnow return errors instead of producing inf/NaN. -
linalg: LU solve validates matrix/RHS dimensions and symbolic-pattern identity instead of panicking inside faer. -
enrichment: validatesalpha > 1, assay ordering, andM*bounds; guards secant divide-by-zero; golden-section M* polish no longer trusts unconverged probe solves;recompute_nmreset path no longer uses stale right-hand sides. -
vr-tools:magicno longer panics on tallies with empty energy bounds and validates array lengths and finiteness. -
bindings/wasm: fraction and result maps are now serialized as plain JS objects instead of JSMaps, fixing empty tables in all interactive tutorials.