Fix ACE law 4/44/61 sampling for discrete lines and for missing continuum - #4102
Open
GuySten wants to merge 3 commits into
Open
Fix ACE law 4/44/61 sampling for discrete lines and for missing continuum#4102GuySten wants to merge 3 commits into
GuySten wants to merge 3 commits into
Conversation
GuySten
marked this pull request as ready for review
September 1, 2026 22:59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix ACE law 4/44/61 sampling of discrete lines and of missing continuum
Two defects in the discrete-plus-continuum sampling shared by
ContinuousTabular,CorrelatedAngleEnergyandKalbachMann. Both sit in thesame block of each function, so they are fixed together.
Reference for the format and for MCNP's handling: LA-UR-12-26785,
Modification of ENDF Law 4/44/61 Sampling in MCNP6.
1. Discrete line energies were not interpolated
All three samplers picked one incident energy grid
land returned that grid'stabulated line energy verbatim:
Correct for a line at a fixed energy, since both grids hold the same value.
Wrong for a line whose energy varies with incident energy: a single physical
line is emitted as two peaks for a monoenergetic beam, and as a comb
quantised onto the incident energy grid for a continuous one.
ENDF-102 §0.6.2.1 gives the rule — the method of corresponding points
interpolates along the line joining the kth points in the two distributions —
and names the failure mode of the alternative directly: artificial peaks appear
when the distributions shift as a function of energy. OpenMC already applies
unit-base interpolation to the continuum portion of the same table, so this
also removes an internal inconsistency.
Affected data. Scanning ENDF/B-VIII.1 (557 files) for tables where
e_out[k]differs between adjacent incident energies, above a 1e-5 relativetolerance that excludes six-significant-digit round-off in the ENDF fixed-format
fields, gives 13 tables:
Every MT=102 entry matches the ENDF primary-photon relation
E_gamma = E_g + A/(A+1) * E_nto four decimals, identifying these as primarycapture gammas — lines whose energy is meant to move with incident energy.
ENDF/B-VII.1 had two such tables; VIII.1 has thirteen, several with over a
thousand lines.
The effect scales with incident energy, so it is negligible for thermal capture
and largest in the fast range where these capture cross sections are small. This
is a spectrum-shape fix for fast-capture and gamma-spectroscopy work; it will not
move k-eff.
2. Out-of-bounds read when a table has no continuum
e_out[n_discrete]is the first continuum outgoing energy, so this reads pastthe end when a table is entirely discrete lines. The ACE format requires the
discrete line count to match at every incident energy but does not require a
continuum at each one, and MCNP made the same assumption:
The read is not benign:
E_i_1enters the unit-base transform with weight1 - r, so whenever the continuum is sampled from the other grid the outgoingenergy is contaminated by whatever lies past the array. A negative emission
energy is how LANL found this in MCNP, in Ac-226 (n,4n); the common case is a
silently wrong energy.
ContinuousTabularappeared guarded byif (!histogram_interp && n_energy_out > 1), but at that pointn_energy_outstill holds grid l's size and is onlyreassigned to grid
i's on the next line — so the guard tests the wrong table.Fix. Each side is checked for a continuum and the interpolation factor
collapses onto whichever side has one, following MCNP6:
MCNP additionally uses a 0–1 eV convention for photons;
AngleEnergy::samplehasno particle type available, so the neutron treatment is applied uniformly. Happy
to thread the particle type through if reviewers prefer matching MCNP exactly.
Affected data. 4557 ENDF/B-VIII.1 tables are entirely discrete lines; those
never produce a wrong value (the discrete branch returns early) but the invalid
read happens on every call, so ASan fires on any coupled neutron-photon run with
discrete level data. The tables that produce wrong numbers — a continuum at
some incident energies but not others — reproduced Appendix D of LA-UR-12-26785
exactly for ENDF/B-VII.1. VIII.1 adds isotopes that are not minor actinides:
Cs-134, Dy-159, Os-189, Os-190, Pm-147, Ta-180m, U-233. Cs-134, Pm-147 and
Dy-159 are fission products present in any burnt fuel.
Behaviour
The discrete-line fix is bitwise identical wherever a line does not move:
e_lo[k] + r * (e_hi[k] - e_lo[k])withe_hi[k] == e_lo[k]gives exactlye_lo[k]for any finiter. Verified across 33 values ofr. Histograminterpolation on the incident grid passes
0.0and likewise returns the lowergrid verbatim. Of 4591 tables with discrete lines in ENDF/B-VIII.1, only 13 can
produce a different number.
The continuum fix changes nothing where both incident energies have a continuum:
r_c == rand the bounds are identical.ContinuousTabularadditionally swapsn_energy_out > 1for a test of the actual denominator,E_hi != E_lo, which iswhat it stood in for.
Neither change alters the
prn(seed)call count on any path, so the randomnumber stream is untouched.
Follow-up
These three functions share 89 of ~100 lines between the two secondary
distributions, and 61 across all three. Every defect here was present in some
copies and not others. A separate PR consolidating the shared CDF search,
inversion and unit-base transform is prepared and will follow.
Checklist
I have followed the style guidelines for Python source files (if applicable)I have made corresponding changes to the documentation (if applicable)I have added tests that prove my fix is effective or that my feature works (if applicable)