fix: correct several neutron sampling / tally bugs#470
Conversation
- fission(): prompt-vs-delayed test compared a U[0,1) sample against nu_p (the prompt multiplicity, ~2-3) rather than the prompt *fraction*, so `xi > total` was never true and delayed neutrons were never sampled. Use nu_p / nu. - fission(): delayed emission time used neutron_fission_delayed_fractions (beta) where the decay constant is required; a neutron_fission_delayed_decay_rates getter already exists. Use it. - sample_direction(): the transverse-basis branches were swapped -- the general 1/sqrt(wx^2+wy^2) construction ran when the axis was ~parallel to z (dividing by ~0), and the fixed basis ran otherwise (not orthonormal to a general axis). Swap them (and fix the misplaced comment). - sample_white_direction(): `if nz != 1.0` let nz == -1 through, giving B = 0 and a divide-by-zero for a -z reference normal. Guard with abs(nz) != 1.0; the else branch already handles both poles. - Tally: polar_reference used `/=` against the normalized input, turning the default [0,0,1] into [0, nan, inf]; assign the normalized vector instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Hi, @steps-re. Thanks for bringing these up and fixing them! Some of the errors were introduced during the major refactoring, and the others were just not properly tested. After a parametric testing, it is found that two regression tests already cover some of the errors (failing right now since they use wrong test answers):
Errors 4 and 5 are currently not covered by the tests. We can add a unit or regression test to cover those two. Do you want to add it as part of this PR, @step-re? Or, we can make a follow-up Issue for the need for the new test. This is an important update. Once merged, we will make the patch release v0.14.2. Thanks, @steps-re!
|
|
The need for tests to cover the fixed Errors 4 and 5 will be brought up again after #472 is resolved and we proceed with completing test coverage. |
Five independent correctness fixes found while auditing the neutron physics and tally code. Each is explained below with the reasoning.
1. Delayed neutrons are never sampled (
transport/physics/neutron/native.py,fission())xiis a uniform[0,1)sample buttotalis set to the prompt multiplicitynu_p(order 2-3), soxi > totalis always false and the delayed-neutron branch is dead. The subsequent loop accumulates delayed fractions intototal, confirmingtotalis meant to be the prompt fraction. Fix:total = nu_p / nu.2. Delayed emission time uses β instead of λ (same file)
Sampling an exponential emission time needs the delayed-group decay constant λ, but this reads the delayed fraction β. A dedicated
neutron_fission_delayed_decay_ratesgetter already exists; use it.3. Swapped transverse-basis branches (
transport/distribution.py,sample_direction())The branch that builds
1/sqrt(wx^2+wy^2)ran whenabs(wz) >= 0.999(axis ~parallel to z, wherewx^2+wy^2 -> 0, dividing by ~zero), while the fixed basis(1,0,0),(0,1,0)ran otherwise (where it is not orthonormal to a general axis). The bodies were swapped and the# Axis nearly parallel to zcomment was on the wrong branch. Swapped so the fixed basis handles the pole and the general construction handles everything else.4. Divide-by-zero for a -z reference (
transport/distribution.py,sample_white_direction())if nz != 1.0:letsnz == -1.0(an ordinary -z normal) into the branch that computesB = sqrt(1 - nz**2) = 0, thenC = Ac / B. Guard withabs(nz) != 1.0; theelsebranch (usingny) already handles both poles.5. Tally polar_reference corrupted (
object_/tally.py)self.polar_referencedefaults to[0,0,1]; dividing it element-wise by the normalized input yields[0, nan, inf]for e.g.[1,0,0]. Every sibling filter assigns directly — assign the normalized vector.Testing
These are localized logic fixes; I verified each against the surrounding code and the referenced getters/constants but did not run the full numba/MPI suite locally. Happy to adjust to your conventions.