mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-21 14:35:27 -04:00
Co-authored-by: shimwell <mail@jshimwell.com> Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
156 lines
5.5 KiB
Python
156 lines
5.5 KiB
Python
from pathlib import Path
|
|
from math import exp
|
|
|
|
import numpy as np
|
|
import pytest
|
|
import openmc
|
|
import openmc.deplete
|
|
from openmc.deplete import d1s
|
|
|
|
|
|
CHAIN_PATH = Path(__file__).parents[1] / "chain_ni.xml"
|
|
|
|
|
|
@pytest.fixture
|
|
def model():
|
|
"""Simple model with natural Ni"""
|
|
mat = openmc.Material()
|
|
mat.add_element('Ni', 1.0)
|
|
geom = openmc.Geometry([openmc.Cell(fill=mat)])
|
|
return openmc.Model(geometry=geom)
|
|
|
|
|
|
def test_get_radionuclides(model):
|
|
# Check that radionuclides are correct and are unstable
|
|
chain = openmc.deplete.Chain.from_xml(CHAIN_PATH)
|
|
nuclides = d1s.get_radionuclides(model, chain)
|
|
assert sorted(nuclides) == [
|
|
'Co58', 'Co60', 'Co61', 'Co62', 'Co64',
|
|
'Fe55', 'Fe59', 'Fe61', 'Ni57', 'Ni59', 'Ni63', 'Ni65'
|
|
]
|
|
for nuc in nuclides:
|
|
assert openmc.data.half_life(nuc) is not None
|
|
|
|
|
|
@pytest.mark.parametrize("nuclide", ['Co60', 'Ni63', 'H3', 'Na24', 'K40'])
|
|
def test_time_correction_factors(nuclide):
|
|
# Irradiation schedule turning unit neutron source on and off
|
|
timesteps = [1.0, 1.0, 1.0]
|
|
source_rates = [1.0, 0.0, 1.0]
|
|
|
|
# Compute expected solution
|
|
decay_rate = openmc.data.decay_constant(nuclide)
|
|
g = exp(-decay_rate)
|
|
expected = [0.0, (1 - g), (1 - g)*g, (1 - g)*(1 + g*g)]
|
|
|
|
# Test against expected solution
|
|
tcf = d1s.time_correction_factors([nuclide], timesteps, source_rates)
|
|
assert tcf[nuclide] == pytest.approx(expected)
|
|
|
|
# Make sure all values at first timestep and onward are positive (K40 case
|
|
# has very small decay constant that stresses this)
|
|
assert np.all(tcf[nuclide][1:] > 0.0)
|
|
|
|
# Timesteps as a tuple
|
|
timesteps = [(1.0, 's'), (1.0, 's'), (1.0, 's')]
|
|
tcf = d1s.time_correction_factors([nuclide], timesteps, source_rates)
|
|
assert tcf[nuclide] == pytest.approx(expected)
|
|
|
|
# Test changing units
|
|
timesteps = [1.0/60.0, 1.0/60.0, 1.0/60.0]
|
|
tcf = d1s.time_correction_factors([nuclide], timesteps, source_rates,
|
|
timestep_units='min')
|
|
assert tcf[nuclide] == pytest.approx(expected)
|
|
|
|
|
|
def test_prepare_tallies(model):
|
|
tally = openmc.Tally()
|
|
tally.filters = [openmc.ParticleFilter('photon')]
|
|
tally.scores = ['flux']
|
|
model.tallies = [tally]
|
|
|
|
# Check that prepare_tallies adds a ParentNuclideFilter
|
|
nuclides = ['Co58', 'Co60', 'Fe55']
|
|
d1s.prepare_tallies(model, nuclides, chain_file=CHAIN_PATH)
|
|
assert tally.contains_filter(openmc.ParentNuclideFilter)
|
|
assert list(tally.filters[-1].bins) == nuclides
|
|
|
|
# Get rid of parent nuclide filter
|
|
tally.filters.pop()
|
|
|
|
# With no nuclides specified, filter should use get_radionuclides
|
|
radionuclides = d1s.get_radionuclides(model, CHAIN_PATH)
|
|
d1s.prepare_tallies(model, chain_file=CHAIN_PATH)
|
|
assert tally.contains_filter(openmc.ParentNuclideFilter)
|
|
assert sorted(tally.filters[-1].bins) == sorted(radionuclides)
|
|
|
|
assert len(tally.filters) == 2
|
|
# calling prepare_tallies twice should not add another ParentNuclideFilter
|
|
d1s.prepare_tallies(model, chain_file=CHAIN_PATH)
|
|
assert len(tally.filters) == 2
|
|
|
|
|
|
def test_apply_time_correction(run_in_tmpdir):
|
|
# Make simple sphere model with elemental Ni
|
|
mat = openmc.Material()
|
|
mat.add_element('Ni', 1.0)
|
|
sphere = openmc.Sphere(r=10.0, boundary_type='vacuum')
|
|
cell = openmc.Cell(fill=mat, region=-sphere)
|
|
model = openmc.Model()
|
|
model.geometry = openmc.Geometry([cell])
|
|
model.settings.run_mode = 'fixed source'
|
|
model.settings.batches = 3
|
|
model.settings.particles = 10
|
|
model.settings.photon_transport = True
|
|
model.settings.use_decay_photons = True
|
|
particle_filter = openmc.ParticleFilter('photon')
|
|
tally = openmc.Tally()
|
|
tally.filters = [particle_filter]
|
|
tally.scores = ['flux']
|
|
model.tallies = [tally]
|
|
|
|
# Prepare tallies for D1S and compute time correction factors
|
|
nuclides = d1s.prepare_tallies(model, chain_file=CHAIN_PATH)
|
|
factors = d1s.time_correction_factors(nuclides, [1.0e10], [1.0])
|
|
|
|
# Run OpenMC and get tally result
|
|
with openmc.config.patch('chain_file', CHAIN_PATH):
|
|
output_path = model.run()
|
|
with openmc.StatePoint(output_path) as sp:
|
|
tally = sp.tallies[tally.id]
|
|
flux = tally.mean.flatten()
|
|
|
|
# Copy attributes from original tally
|
|
tally_filters = list(tally.filters)
|
|
tally_sum = tally.sum.copy()
|
|
tally_sum_sq = tally.sum_sq.copy()
|
|
tally_mean = tally.mean.copy()
|
|
tally_std_dev = tally.std_dev.copy()
|
|
|
|
# Apply TCF and make sure results are consistent
|
|
result = d1s.apply_time_correction(tally, factors, sum_nuclides=False)
|
|
tcf = np.array([factors[nuc][-1] for nuc in nuclides])
|
|
assert result.mean.flatten() == pytest.approx(tcf * flux)
|
|
|
|
# Make sure summed results match a manual sum
|
|
result_summed = d1s.apply_time_correction(tally, factors)
|
|
assert result_summed.mean.flatten()[0] == pytest.approx(result.mean.sum())
|
|
|
|
# Make sure original tally is unchanged
|
|
assert tally.filters == tally_filters
|
|
assert np.all(tally.sum == tally_sum)
|
|
assert np.all(tally.sum_sq == tally_sum_sq)
|
|
assert np.all(tally.mean == tally_mean)
|
|
assert np.all(tally.std_dev == tally_std_dev)
|
|
|
|
# Make sure various tally methods work
|
|
result.get_values()
|
|
result_summed.get_values()
|
|
result.get_reshaped_data()
|
|
result_summed.get_reshaped_data()
|
|
result.get_pandas_dataframe()
|
|
result_summed.get_pandas_dataframe()
|
|
|
|
# The summed tally is derived, so sum/sum_sq are None
|
|
assert result_summed.sum is None
|
|
assert result_summed.sum_sq is None
|