Remaining Opportunities to Use endf Package in OpenMC
The endf package (v0.1.12) provides MF-level section parsers that return
structured dicts, replacing the need for manual record-by-record reading.
Each PR below replaces inline get_*_record calls and surrounding parsing
logic with a single parse_mf*() call, then constructs OpenMC objects from
the resulting dict.
PR 1: Replace Evaluation class with endf.Material
Files: openmc/data/endf.py + 10 caller files (no caller changes needed)
Subclass endf.Material with a thin Evaluation adapter providing
backward-compatible section, target, info, projectile, material,
reaction_list, and gnds_name properties. The ~160-line Evaluation class
becomes a ~30-line adapter. All isinstance and check_type checks continue
to work.
|
|
| Lines saved |
~130 |
| Effort |
Small (1-2 days) |
| Difficulty |
Low |
| Risk |
Low -- adapter preserves all caller semantics, no caller changes |
PR 2: Replace get_evaluations() with endf.get_materials()
Files: openmc/data/endf.py, openmc/data/neutron.py
get_evaluations() is already imported from endf.get_materials. The one
caller in neutron.py (NJOY heating correction, line ~817) accesses
.section[3, mt] on the returned objects. After PR 1 lands the adapter
handles this; alternatively update the caller to .section_text[3, mt] and
delete the wrapper entirely.
|
|
| Lines saved |
~22 |
| Effort |
Trivial (< 1 day) |
| Difficulty |
Low |
| Risk |
Low -- single caller, straightforward |
PR 3: Use parse_mf1_mt458 in FissionEnergyRelease.from_endf()
Files: openmc/data/fission_energy.py
The 138-line from_endf() manually reads CONT and LIST records for fission
energy components. endf.parse_mf1_mt458() returns a dict with all the
polynomial coefficients and tabulated data pre-parsed.
|
|
| Lines saved |
~55-85 |
| Effort |
Small (1-2 days) |
| Difficulty |
Low-medium |
| Risk |
Low -- small focused module, good pilot PR for MF parser adoption |
PR 4: Use parse_mf4 in AngleDistribution.from_endf()
Files: openmc/data/angle_distribution.py
The 97-line from_endf() handles four LTT cases (isotropic, Legendre,
tabulated, mixed) by reading HEAD/CONT/TAB2/LIST/TAB1 records.
endf.parse_mf4() returns a structured dict covering all cases.
|
|
| Lines saved |
~40-60 |
| Effort |
Small-medium (2-3 days) |
| Difficulty |
Medium |
| Risk |
Medium -- four code paths to validate |
PR 5: Use parse_mf5 in energy distribution classes
Files: openmc/data/energy_distribution.py
Seven from_endf() methods (ArbitraryTabulated, GeneralEvaporation,
MaxwellEnergy, Evaporation, WattEnergy, MadlandNix, plus the base dispatcher)
total 187 lines of record-level parsing. endf.parse_mf5() returns parsed
data for all six LF law types.
|
|
| Lines saved |
~70-105 |
| Effort |
Medium (2-3 days) |
| Difficulty |
Medium |
| Risk |
Medium -- six law types to validate |
PR 6: Use parse_mf6 in angle-energy distribution classes
Files: openmc/data/correlated.py (47 lines), openmc/data/kalbach_mann.py
(95 lines), openmc/data/laboratory.py (37 lines), openmc/data/nbody.py
(20 lines)
Four files with from_endf() methods parsing MF=6 LAW=1,2,3,6 respectively.
endf.parse_mf6() returns structured data for all LAW types. 199 lines of
parsing combined.
|
|
| Lines saved |
~80-120 |
| Effort |
Medium (3-4 days) |
| Difficulty |
Medium |
| Risk |
Medium -- touches 4 files but all parse the same MF |
PR 7: Use parse_mf7_mt2/parse_mf7_mt4 in ThermalScattering.from_endf()
Files: openmc/data/thermal.py
The ENDF parsing section of from_endf() manually reads MF=7 MT=2,4 data
(coherent/incoherent elastic and inelastic thermal scattering). The endf
package provides parse_mf7_mt2(), parse_mf7_mt4(), and
parse_mf7_mt451(). ~163 lines of parsing.
|
|
| Lines saved |
~65-100 |
| Effort |
Medium (2-3 days) |
| Difficulty |
Medium |
| Risk |
Medium -- thermal scattering data is critical for reactor physics |
PR 8: Use parse_mf8/parse_mf8_mt457 in decay.py
Files: openmc/data/decay.py
Decay.from_endf() and FissionProductYields.from_endf() manually parse
MF=8 MT=454/457/459 using HEAD/CONT/LIST records. endf.parse_mf8(),
endf.parse_mf8_mt454(), and endf.parse_mf8_mt457() provide pre-parsed
equivalents. ~35 lines of direct parsing plus surrounding processing.
|
|
| Lines saved |
~13-20 |
| Effort |
Small (1-2 days) |
| Difficulty |
Medium |
| Risk |
Medium -- decay data feeds depletion chains |
PR 9: Use parse_mf23/parse_mf27/parse_mf28 in photon.py
Files: openmc/data/photon.py
Three parsing methods (AtomicRelaxation.from_endf, IncidentPhoton.from_endf,
photon reaction parsing) total ~172 lines of manual HEAD/CONT/LIST/TAB1 reading
for MF=23 (photo-atomic cross sections), MF=27 (form factors), and MF=28
(atomic relaxation).
|
|
| Lines saved |
~70-100 |
| Effort |
Medium (2-3 days) |
| Difficulty |
Medium |
| Risk |
Medium |
PR 10: Use parse_mf2 in resonance.py
Files: openmc/data/resonance.py
The largest block of manual parsing in OpenMC: 534 lines across six
from_endf() methods covering SLBW, MLBW, Reich-Moore, R-Matrix Limited,
and unresolved resonance formalisms. endf.parse_mf2() returns a structured
dict for all of these. The OpenMC side would still construct its rich resonance
objects but from pre-parsed dicts instead of raw records.
|
|
| Lines saved |
~200-320 |
| Effort |
Large (5-7 days) |
| Difficulty |
High |
| Risk |
High -- resonance processing is complex with many formalisms; extensive testing needed |
PR 11: Use parse_mf12/parse_mf13/parse_mf14/parse_mf15 in reaction product parsing
Files: openmc/data/reaction.py, openmc/data/neutron.py
The Reaction.from_endf() method (92 lines) orchestrates parsing of MF=3,4,5,6
and also MF=12/13 (photon production multiplicities/cross sections). After
PRs 4-6 land for the sub-parsers, this orchestration layer can be simplified
to use pre-parsed endf.Material.section_data dicts directly.
|
|
| Lines saved |
~30-50 |
| Effort |
Medium (2-3 days) |
| Difficulty |
Medium-high |
| Risk |
Medium -- depends on PRs 4-6 |
PR 12: Use parse_mf33/parse_mf34 in resonance_covariance.py
Files: openmc/data/resonance_covariance.py
316 lines of manual parsing across three from_endf() methods for MF=32
MT=151 resonance parameter covariances. Note: the endf package has no
parse_mf32 -- it only has parse_mf33 and parse_mf34 for cross-section
and angular covariances. MF=32 (resonance covariances) must remain manual
unless endf-python adds support.
The MF=33/34 parsers could still help if any MF=33/34 data is read in this
file, but currently the file only reads MF=32.
|
|
| Lines saved |
~0 (blocked -- no parse_mf32 in endf package) |
| Effort |
N/A unless endf-python adds MF=32 support |
| Difficulty |
N/A |
| Risk |
N/A |
PR 13: Use endf.Material.interpret() for high-level IncidentNeutron.from_endf()
Files: openmc/data/neutron.py
The capstone integration: instead of OpenMC's from_endf() manually iterating
over sections and dispatching to per-MF parsers, use
endf.Material.interpret() to get an endf.IncidentNeutron with fully parsed
reactions, then convert to OpenMC's richer objects. This subsumes remaining
Phase 3 work and could eliminate the orchestration layer entirely.
|
|
| Lines saved |
Hard to estimate independently; subsumes PRs 3-11 |
| Effort |
Large (1-2 weeks) |
| Difficulty |
High |
| Risk |
High -- requires mapping endf-python's entire data model to OpenMC's validated domain objects |
Summary Table
| PR |
Description |
Lines Saved |
Effort |
Difficulty |
Risk |
| 1 |
Evaluation -> Material adapter |
~130 |
Small |
Low |
Low |
| 2 |
get_evaluations -> get_materials |
~22 |
Trivial |
Low |
Low |
| 3 |
MF=1 MT=458 (fission energy) |
~55-85 |
Small |
Low-med |
Low |
| 4 |
MF=4 (angular distributions) |
~40-60 |
Small-med |
Medium |
Medium |
| 5 |
MF=5 (energy distributions) |
~70-105 |
Medium |
Medium |
Medium |
| 6 |
MF=6 (angle-energy, 4 files) |
~80-120 |
Medium |
Medium |
Medium |
| 7 |
MF=7 (thermal scattering) |
~65-100 |
Medium |
Medium |
Medium |
| 8 |
MF=8 (decay data) |
~13-20 |
Small |
Medium |
Medium |
| 9 |
MF=23/27/28 (photon) |
~70-100 |
Medium |
Medium |
Medium |
| 10 |
MF=2 (resonances) |
~200-320 |
Large |
High |
High |
| 11 |
MF=12-15 (reaction products) |
~30-50 |
Medium |
Med-high |
Medium |
| 12 |
MF=32 (resonance covariance) |
0 |
Blocked |
N/A |
N/A |
| 13 |
IncidentNeutron.interpret() |
TBD |
Large |
High |
High |
|
Total (PRs 1-11) |
~775-1110 |
|
|
|
Suggested Merge Order
Low-hanging fruit:
PR 1 (Evaluation adapter) ~130 lines
PR 2 (get_evaluations) ~22 lines
PR 3 (MF=1 -- good pilot for MF parsers) 55-85 lines
MF parser adoption (independent, any order):
PR 4 (MF=4) 40-60 lines
PR 5 (MF=5) 70-105 lines
PR 6 (MF=6) 80-120 lines
PR 7 (MF=7) 65-100 lines
PR 8 (MF=8) 13-20 lines
PR 9 (MF=23/27/28) 70-100 lines
Heavy lifts (after MF parsers land):
PR 10 (MF=2 resonances) 200-320 lines
PR 11 (MF=12-15 reaction products) 30-50 lines
PR 13 (IncidentNeutron.interpret capstone) TBD
PRs 1-2 are independent of each other. PRs 3-9 are independent of each other
but benefit from PR 1 landing first. PR 10 is the highest-value single PR but
also the riskiest. PR 13 is a capstone that subsumes the orchestration saved
by earlier PRs.
What OpenMC Should Keep
endf-python is a lightweight reader. OpenMC's higher-level infrastructure
should remain:
- Validation -- property setters with
checkvalue
- HDF5 persistence --
to_hdf5() / from_hdf5() methods
- Resonance processing -- reconstruction, Doppler broadening
- Resonance covariance -- MF=32 (no endf-python equivalent)
- Rich function types --
Polynomial, Sum, Regions1D, ResonancesWithBackground
- Transport logic -- redundant reactions, derived products, temperature handling
Remaining Opportunities to Use
endfPackage in OpenMCThe
endfpackage (v0.1.12) provides MF-level section parsers that returnstructured dicts, replacing the need for manual record-by-record reading.
Each PR below replaces inline
get_*_recordcalls and surrounding parsinglogic with a single
parse_mf*()call, then constructs OpenMC objects fromthe resulting dict.
PR 1: Replace
Evaluationclass withendf.MaterialFiles:
openmc/data/endf.py+ 10 caller files (no caller changes needed)Subclass
endf.Materialwith a thinEvaluationadapter providingbackward-compatible
section,target,info,projectile,material,reaction_list, andgnds_nameproperties. The ~160-lineEvaluationclassbecomes a ~30-line adapter. All
isinstanceandcheck_typechecks continueto work.
PR 2: Replace
get_evaluations()withendf.get_materials()Files:
openmc/data/endf.py,openmc/data/neutron.pyget_evaluations()is already imported fromendf.get_materials. The onecaller in
neutron.py(NJOY heating correction, line ~817) accesses.section[3, mt]on the returned objects. After PR 1 lands the adapterhandles this; alternatively update the caller to
.section_text[3, mt]anddelete the wrapper entirely.
PR 3: Use
parse_mf1_mt458inFissionEnergyRelease.from_endf()Files:
openmc/data/fission_energy.pyThe 138-line
from_endf()manually reads CONT and LIST records for fissionenergy components.
endf.parse_mf1_mt458()returns a dict with all thepolynomial coefficients and tabulated data pre-parsed.
PR 4: Use
parse_mf4inAngleDistribution.from_endf()Files:
openmc/data/angle_distribution.pyThe 97-line
from_endf()handles four LTT cases (isotropic, Legendre,tabulated, mixed) by reading HEAD/CONT/TAB2/LIST/TAB1 records.
endf.parse_mf4()returns a structured dict covering all cases.PR 5: Use
parse_mf5in energy distribution classesFiles:
openmc/data/energy_distribution.pySeven
from_endf()methods (ArbitraryTabulated, GeneralEvaporation,MaxwellEnergy, Evaporation, WattEnergy, MadlandNix, plus the base dispatcher)
total 187 lines of record-level parsing.
endf.parse_mf5()returns parseddata for all six LF law types.
PR 6: Use
parse_mf6in angle-energy distribution classesFiles:
openmc/data/correlated.py(47 lines),openmc/data/kalbach_mann.py(95 lines),
openmc/data/laboratory.py(37 lines),openmc/data/nbody.py(20 lines)
Four files with
from_endf()methods parsing MF=6 LAW=1,2,3,6 respectively.endf.parse_mf6()returns structured data for all LAW types. 199 lines ofparsing combined.
PR 7: Use
parse_mf7_mt2/parse_mf7_mt4inThermalScattering.from_endf()Files:
openmc/data/thermal.pyThe ENDF parsing section of
from_endf()manually reads MF=7 MT=2,4 data(coherent/incoherent elastic and inelastic thermal scattering). The endf
package provides
parse_mf7_mt2(),parse_mf7_mt4(), andparse_mf7_mt451(). ~163 lines of parsing.PR 8: Use
parse_mf8/parse_mf8_mt457indecay.pyFiles:
openmc/data/decay.pyDecay.from_endf()andFissionProductYields.from_endf()manually parseMF=8 MT=454/457/459 using HEAD/CONT/LIST records.
endf.parse_mf8(),endf.parse_mf8_mt454(), andendf.parse_mf8_mt457()provide pre-parsedequivalents. ~35 lines of direct parsing plus surrounding processing.
PR 9: Use
parse_mf23/parse_mf27/parse_mf28inphoton.pyFiles:
openmc/data/photon.pyThree parsing methods (
AtomicRelaxation.from_endf,IncidentPhoton.from_endf,photon reaction parsing) total ~172 lines of manual HEAD/CONT/LIST/TAB1 reading
for MF=23 (photo-atomic cross sections), MF=27 (form factors), and MF=28
(atomic relaxation).
PR 10: Use
parse_mf2inresonance.pyFiles:
openmc/data/resonance.pyThe largest block of manual parsing in OpenMC: 534 lines across six
from_endf()methods covering SLBW, MLBW, Reich-Moore, R-Matrix Limited,and unresolved resonance formalisms.
endf.parse_mf2()returns a structureddict for all of these. The OpenMC side would still construct its rich resonance
objects but from pre-parsed dicts instead of raw records.
PR 11: Use
parse_mf12/parse_mf13/parse_mf14/parse_mf15in reaction product parsingFiles:
openmc/data/reaction.py,openmc/data/neutron.pyThe
Reaction.from_endf()method (92 lines) orchestrates parsing of MF=3,4,5,6and also MF=12/13 (photon production multiplicities/cross sections). After
PRs 4-6 land for the sub-parsers, this orchestration layer can be simplified
to use pre-parsed
endf.Material.section_datadicts directly.PR 12: Use
parse_mf33/parse_mf34inresonance_covariance.pyFiles:
openmc/data/resonance_covariance.py316 lines of manual parsing across three
from_endf()methods for MF=32MT=151 resonance parameter covariances. Note: the endf package has no
parse_mf32-- it only hasparse_mf33andparse_mf34for cross-sectionand angular covariances. MF=32 (resonance covariances) must remain manual
unless endf-python adds support.
The MF=33/34 parsers could still help if any MF=33/34 data is read in this
file, but currently the file only reads MF=32.
parse_mf32in endf package)PR 13: Use
endf.Material.interpret()for high-levelIncidentNeutron.from_endf()Files:
openmc/data/neutron.pyThe capstone integration: instead of OpenMC's
from_endf()manually iteratingover sections and dispatching to per-MF parsers, use
endf.Material.interpret()to get anendf.IncidentNeutronwith fully parsedreactions, then convert to OpenMC's richer objects. This subsumes remaining
Phase 3 work and could eliminate the orchestration layer entirely.
Summary Table
Suggested Merge Order
PRs 1-2 are independent of each other. PRs 3-9 are independent of each other
but benefit from PR 1 landing first. PR 10 is the highest-value single PR but
also the riskiest. PR 13 is a capstone that subsumes the orchestration saved
by earlier PRs.
What OpenMC Should Keep
endf-pythonis a lightweight reader. OpenMC's higher-level infrastructureshould remain:
checkvalueto_hdf5()/from_hdf5()methodsPolynomial,Sum,Regions1D,ResonancesWithBackground