Skip to content

Commit 587e11b

Browse files
GuyStenpaulromano
andauthored
Fix delayed group filter indexing for kinetic parameters calculation (#4077)
Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
1 parent 1b74220 commit 587e11b

2 files changed

Lines changed: 104 additions & 2 deletions

File tree

‎src/tallies/tally_scoring.cpp‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -968,8 +968,13 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index,
968968
const DelayedGroupFilter& filt {
969969
*dynamic_cast<DelayedGroupFilter*>(
970970
model::tally_filters[i_dg_filt].get())};
971-
score_fission_delayed_dg(i_tally, delayed_groups[0] - 1,
972-
score, score_index, p.filter_matches());
971+
for (int d_bin = 0; d_bin < filt.n_bins(); ++d_bin) {
972+
if (filt.groups()[d_bin] == delayed_groups[0]) {
973+
score_fission_delayed_dg(
974+
i_tally, d_bin, score, score_index, p.filter_matches());
975+
break;
976+
}
977+
}
973978
continue;
974979
}
975980
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
"""Test that delayed group filter bins follow the groups they name rather than
2+
their position in the filter's list of bins."""
3+
4+
import pytest
5+
import openmc
6+
7+
8+
GROUPS_ASCENDING = [1, 2, 3, 4, 5, 6]
9+
GROUPS_SHUFFLED = [4, 1, 6, 3, 5, 2]
10+
GROUPS_SUBSET = [2, 4]
11+
12+
SCORES = ["delayed-nu-fission", "ifp-beta-numerator"]
13+
14+
15+
@pytest.fixture(scope="module")
16+
def geometry():
17+
openmc.reset_auto_ids()
18+
material = openmc.Material()
19+
material.add_nuclide("U235", 1.0)
20+
material.set_density("g/cm3", 16.0)
21+
sphere = openmc.Sphere(r=10.0, boundary_type="vacuum")
22+
cell = openmc.Cell(region=-sphere, fill=material)
23+
return openmc.Geometry([cell])
24+
25+
26+
def build_model(geometry, score):
27+
"""Model tallying one score four ways: three delayed group filters that
28+
differ only in how the bins are listed, plus an unfiltered reference."""
29+
model = openmc.Model(geometry=geometry)
30+
model.settings.particles = 5000
31+
model.settings.batches = 20
32+
model.settings.inactive = 5
33+
model.settings.ifp_n_generation = 5
34+
35+
for name, groups in [
36+
("ascending", GROUPS_ASCENDING),
37+
("shuffled", GROUPS_SHUFFLED),
38+
("subset", GROUPS_SUBSET),
39+
]:
40+
tally = openmc.Tally(name=name)
41+
tally.scores = [score]
42+
tally.filters = [openmc.DelayedGroupFilter(groups)]
43+
model.tallies.append(tally)
44+
45+
tally = openmc.Tally(name="unfiltered")
46+
tally.scores = [score]
47+
model.tallies.append(tally)
48+
49+
return model
50+
51+
52+
def means_by_group(statepoint, name):
53+
"""Map delayed group number to tallied mean.
54+
55+
The group order is read back from the filter stored in the statepoint, so
56+
a mismatch between the reported bin labels and the values fails here
57+
instead of being hidden by assuming an order.
58+
"""
59+
tally = statepoint.get_tally(name=name)
60+
delayed_group_filter = tally.find_filter(openmc.DelayedGroupFilter)
61+
means = tally.mean.ravel()
62+
assert len(means) == len(delayed_group_filter.bins)
63+
return dict(zip(delayed_group_filter.bins, means))
64+
65+
66+
@pytest.mark.parametrize("score", SCORES)
67+
def test_delayed_group_bins(run_in_tmpdir, geometry, score):
68+
"""Delayed group labels determine values independently of bin order.
69+
70+
All tallies come from a single run, so they see the same histories and the
71+
same scoring events. Corresponding groups must therefore agree within
72+
statistics, and all selected groups must sum to the unfiltered result.
73+
"""
74+
model = build_model(geometry, score)
75+
sp_file = model.run()
76+
with openmc.StatePoint(sp_file) as sp:
77+
ascending = means_by_group(sp, "ascending")
78+
shuffled = means_by_group(sp, "shuffled")
79+
subset = means_by_group(sp, "subset")
80+
unfiltered = sp.get_tally(name="unfiltered").mean.ravel()[0]
81+
82+
# Guard against a vacuous pass if the run were too short to populate bins.
83+
assert all(value > 0.0 for value in ascending.values())
84+
85+
# Listing groups out of order must not move the corresponding results.
86+
assert sorted(shuffled) == sorted(ascending)
87+
for group in GROUPS_ASCENDING:
88+
assert shuffled[group] == pytest.approx(ascending[group], rel=1e-6)
89+
90+
# A partial selection must reproduce those groups from the full list. A
91+
# group-to-bin index assumption would write out of bounds for group 4.
92+
assert sorted(subset) == sorted(GROUPS_SUBSET)
93+
for group in GROUPS_SUBSET:
94+
assert subset[group] == pytest.approx(ascending[group], rel=1e-6)
95+
96+
# Every delayed neutron lands in exactly one bin and none are lost.
97+
assert sum(ascending.values()) == pytest.approx(unfiltered, rel=1e-6)

0 commit comments

Comments
 (0)