Bug Description
In multigroup mode, Mgxs::calculate_xs multiplies the cached macro cross sections by the particle's cell density multiplier (src/mgxs.cpp, end of calculate_xs):
p.macro_xs().total = xs[temperature].total(angle, p.g()) * p.density_mult();
p.macro_xs().absorption = xs[temperature].absorption(angle, p.g()) * p.density_mult();
p.macro_xs().nu_fission = ... * p.density_mult();
Non-analog SCORE_TOTAL and SCORE_ABSORPTION score from that cache, so they include the multiplier. But non-analog SCORE_FISSION, SCORE_NU_FISSION, and SCORE_SCATTER go through Mgxs::get_xs(...) (src/tallies/tally_scoring.cpp, MG branches), which reads the raw XsData tensors and never applies density_mult.
Impact
For any MG cell with a density multiplier ≠ 1: transport itself is self-consistent (it uses the multiplied macros), so k is fine — but within one tally, total/absorption/flux-weighted scores and fission/nu-fission/scatter scores are computed at two different densities. The error on the affected scores is a silent flat factor equal to the multiplier. Default multipliers are 1, so standard problems don't see it.
Proposed resolution
This looks like a one-line-per-site fix, but the right location is a design choice — either
- apply
p.density_mult() inside the MG non-analog scoring paths of tally_scoring.cpp (keeps get_xs a pure data accessor), or
- thread the multiplier through
Mgxs::get_xs,
so I've filed it as an issue rather than guessing in a PR. Happy to submit whichever variant is preferred.
Found while porting the transport engine to Apple Silicon GPUs (dallonby/openmc-metal) — the port's GPU tally scores initially applied the multiplier uniformly and disagreed with the CPU reference on exactly these three scores, which led to auditing the asymmetry. (Related: #4067, #4068 from the same audit.)
🤖 Generated with Claude Code
Bug Description
In multigroup mode,
Mgxs::calculate_xsmultiplies the cached macro cross sections by the particle's cell density multiplier (src/mgxs.cpp, end ofcalculate_xs):Non-analog
SCORE_TOTALandSCORE_ABSORPTIONscore from that cache, so they include the multiplier. But non-analogSCORE_FISSION,SCORE_NU_FISSION, andSCORE_SCATTERgo throughMgxs::get_xs(...)(src/tallies/tally_scoring.cpp, MG branches), which reads the rawXsDatatensors and never appliesdensity_mult.Impact
For any MG cell with a density multiplier ≠ 1: transport itself is self-consistent (it uses the multiplied macros), so k is fine — but within one tally, total/absorption/flux-weighted scores and fission/nu-fission/scatter scores are computed at two different densities. The error on the affected scores is a silent flat factor equal to the multiplier. Default multipliers are 1, so standard problems don't see it.
Proposed resolution
This looks like a one-line-per-site fix, but the right location is a design choice — either
p.density_mult()inside the MG non-analog scoring paths oftally_scoring.cpp(keepsget_xsa pure data accessor), orMgxs::get_xs,so I've filed it as an issue rather than guessing in a PR. Happy to submit whichever variant is preferred.
Found while porting the transport engine to Apple Silicon GPUs (dallonby/openmc-metal) — the port's GPU tally scores initially applied the multiplier uniformly and disagreed with the CPU reference on exactly these three scores, which led to auditing the asymmetry. (Related: #4067, #4068 from the same audit.)
🤖 Generated with Claude Code