Enable "hybrid" tallies in get_microxs_and_flux (#3831)

This commit is contained in:
Paul Romano 2026-03-02 21:06:52 -06:00 committed by GitHub
parent 823b4c96c9
commit 49b896b0eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 279 additions and 16 deletions

View file

@ -34,6 +34,7 @@ def model():
[
("materials", "direct"),
("materials", "flux"),
("materials", "hybrid"),
("mesh", "direct"),
("mesh", "flux"),
]
@ -49,12 +50,21 @@ def test_from_model(model, domain_type, rr_mode):
domains = mesh
nuclides = ['U235', 'O16', 'Xe135']
kwargs = {
'reaction_rate_mode': rr_mode,
'chain_file': CHAIN_FILE,
'path_statepoint': 'neutron_transport.h5',
}
if rr_mode == 'flux':
kwargs['reaction_rate_mode'] = 'flux'
kwargs['energies'] = 'CASMO-40'
elif rr_mode == 'hybrid':
kwargs['reaction_rate_mode'] = 'flux'
kwargs['energies'] = 'CASMO-40'
kwargs['reaction_rate_opts'] = {
'nuclides': ['U235'],
'reactions': ['fission'],
}
else:
kwargs['reaction_rate_mode'] = rr_mode
_, test_xs = get_microxs_and_flux(model, domains, nuclides, **kwargs)
if config['update']:
test_xs[0].to_csv(f'test_reference_{domain_type}_{rr_mode}.csv')

View file

@ -0,0 +1,7 @@
nuclides,reactions,groups,xs
U235,"(n,gamma)",1,0.1500301670375847
U235,fission,1,1.2578145727734291
O16,"(n,gamma)",1,0.00012069778439640312
O16,fission,1,0.0
Xe135,"(n,gamma)",1,0.014820264774863558
Xe135,fission,1,0.0
1 nuclides reactions groups xs
2 U235 (n,gamma) 1 0.1500301670375847
3 U235 fission 1 1.2578145727734291
4 O16 (n,gamma) 1 0.00012069778439640312
5 O16 fission 1 0.0
6 Xe135 (n,gamma) 1 0.014820264774863558
7 Xe135 fission 1 0.0

View file

@ -6,12 +6,15 @@ to a custom file with new depletion_chain node
from os import remove
from pathlib import Path
from unittest.mock import patch
import pytest
from openmc.deplete import MicroXS
import openmc
from openmc.deplete import MicroXS, get_microxs_and_flux
import numpy as np
ONE_GROUP_XS = Path(__file__).parents[1] / "micro_xs_simple.csv"
CHAIN_FILE = Path(__file__).parents[1] / "chain_simple.xml"
def test_from_array():
@ -124,3 +127,127 @@ def test_microxs_zero_flux():
# All microscopic cross sections should be zero
assert np.all(microxs.data == 0.0)
def test_hybrid_tally_setup():
"""In hybrid mode a 1-group RR tally is added alongside the flux tally."""
# Create a simple model with one material and a few nuclides for testing
model = openmc.Model()
mat = openmc.Material(components={'U235': 1.0, 'O16': 2.0})
sphere = openmc.Sphere(r=10.0, boundary_type='vacuum')
cell = openmc.Cell(region=-sphere, fill=mat)
model.geometry = openmc.Geometry([cell])
model.settings.batches = 2
model.settings.particles = 10
# Define 2-group energy structure for the test
energies = [0., 0.625, 2.0e7]
# Function to replace Model.run and capture the tallies that were created
captured = {}
def capture_run(**kwargs):
captured['tallies'] = list(model.tallies)
raise StopIteration
# Call get_microxs_and_flux but replace Model.run with a function that
# captures the tallies and raises StopIteration to exit early
with patch.object(model, 'run', side_effect=capture_run):
with pytest.raises(StopIteration):
get_microxs_and_flux(
model, [mat],
nuclides=['U235', 'O16'],
reactions=['fission', '(n,gamma)'],
energies=energies,
reaction_rate_mode='flux',
reaction_rate_opts={'nuclides': ['U235'], 'reactions': ['fission']},
chain_file=CHAIN_FILE,
)
# Check that both tallies were created with the expected properties
tally_names = [t.name for t in captured['tallies']]
assert 'MicroXS flux' in tally_names
assert 'MicroXS RR' in tally_names
# Check that the RR tally has the expected nuclides and reactions
rr = next(t for t in captured['tallies'] if t.name == 'MicroXS RR')
assert rr.nuclides == ['U235']
assert rr.scores == ['fission']
# RR tally must use a 1-group energy filter spanning the full energy range
ef = next(f for f in rr.filters if isinstance(f, openmc.EnergyFilter))
assert len(ef.values) == 2
assert ef.values[0] == pytest.approx(energies[0])
assert ef.values[-1] == pytest.approx(energies[-1])
# ---------------------------------------------------------------------------
# Tests for MicroXS.merge()
# ---------------------------------------------------------------------------
def _make_microxs(nuclides, reactions, values, groups=1):
"""Helper: build a MicroXS from a flat list of values (nuclide-major order)."""
data = np.array(values, dtype=float).reshape(
len(nuclides), len(reactions), groups)
return MicroXS(data, nuclides, reactions)
def test_merge_disjoint():
"""Merging two MicroXS with no overlapping nuclides or reactions."""
m1 = _make_microxs(['U235', 'U238'], ['fission', '(n,gamma)'],
[1., 2., 3., 4.])
m2 = _make_microxs(['Pu239'], ['(n,2n)'], [5.])
merged = m1.merge(m2)
assert merged.nuclides == ['U235', 'U238', 'Pu239']
assert merged.reactions == ['fission', '(n,gamma)', '(n,2n)']
assert merged.data.shape == (3, 3, 1)
# Self data preserved
assert merged['U235', 'fission'] == pytest.approx([1.])
assert merged['U238', '(n,gamma)'] == pytest.approx([4.])
# New data from other
assert merged['Pu239', '(n,2n)'] == pytest.approx([5.])
# Cross-terms that had no data should be zero
assert merged['U235', '(n,2n)'] == pytest.approx([0.])
assert merged['Pu239', 'fission'] == pytest.approx([0.])
def test_merge_prefer_other():
"""prefer='other': other overwrites shared entries, adds new ones."""
# m1: U235 and U238, reactions fission and (n,gamma)
m1 = _make_microxs(['U235', 'U238'], ['fission', '(n,gamma)'],
[1., 2., 3., 4.])
# m2: only U235, reactions fission (overlap) and (n,2n) (new)
m2 = _make_microxs(['U235'], ['fission', '(n,2n)'], [9., 5.])
merged = m1.merge(m2)
# Nuclide/reaction sets
assert set(merged.nuclides) == {'U235', 'U238'}
assert set(merged.reactions) == {'fission', '(n,gamma)', '(n,2n)'}
# U235/fission overwritten by m2
assert merged['U235', 'fission'] == pytest.approx([9.])
# U235/(n,gamma) untouched
assert merged['U235', '(n,gamma)'] == pytest.approx([2.])
# New reaction added from m2
assert merged['U235', '(n,2n)'] == pytest.approx([5.])
# U238 data preserved
assert merged['U238', 'fission'] == pytest.approx([3.])
assert merged['U238', '(n,gamma)'] == pytest.approx([4.])
# U238/(n,2n) not in either → zero
assert merged['U238', '(n,2n)'] == pytest.approx([0.])
def test_merge_prefer_self():
"""prefer='self': shared pairs keep self's value; new entries use other's value."""
m1 = _make_microxs(['U235', 'U238'], ['fission', '(n,gamma)'],
[1., 2., 3., 4.])
m2 = _make_microxs(['U235'], ['fission', '(n,2n)'], [9., 5.])
merged = m1.merge(m2, prefer='self')
# U235/fission: self wins
assert merged['U235', 'fission'] == pytest.approx([1.])
# U235/(n,2n): other used
assert merged['U235', '(n,2n)'] == pytest.approx([5.])