Added tests of the openmc.Model class. Still need to do tests of deplete and the methods which change python and C attributes. Pushing this now to verify the CI shows passes for the MPI configs

This commit is contained in:
agnelson 2021-09-27 08:18:54 -05:00
parent 24bca06310
commit 0d2f923be8
3 changed files with 308 additions and 3 deletions

View file

@ -193,6 +193,8 @@ class Model:
def from_xml(cls, geometry='geometry.xml', materials='materials.xml',
settings='settings.xml'):
"""Create model from existing XML files
When initializing this way, the user must manually load plots, tallies,
the chain_file and fission_q attributes.
Parameters
----------
@ -428,7 +430,9 @@ class Model:
Parameters
----------
**kwargs
Keyword arguments passed to :func:`openmc.run`
Keyword arguments passed to :func:`openmc.run`. Note that these are
ignored if running via the C-API. Instead the parameters should be
set via the Settings object.
Returns
-------

View file

@ -1,9 +1,129 @@
from math import pi
import openmc
import pytest
from tests.regression_tests import config
@pytest.fixture(scope='module')
def pin_model_attributes():
uo2 = openmc.Material(name='UO2')
uo2.set_density('g/cm3', 10.29769)
uo2.add_element('U', 1., enrichment=2.4)
uo2.add_element('O', 2.)
zirc = openmc.Material(name='Zirc')
zirc.set_density('g/cm3', 6.55)
zirc.add_element('Zr', 1.)
borated_water = openmc.Material(name='Borated water')
borated_water.set_density('g/cm3', 0.740582)
borated_water.add_element('B', 4.0e-5)
borated_water.add_element('H', 5.0e-2)
borated_water.add_element('O', 2.4e-2)
borated_water.add_s_alpha_beta('c_H_in_H2O')
mats = openmc.Materials([uo2, zirc, borated_water])
pitch = 1.25984
fuel_or = openmc.ZCylinder(r=0.39218, name='Fuel OR')
clad_or = openmc.ZCylinder(r=0.45720, name='Clad OR')
box = openmc.model.rectangular_prism(pitch, pitch, boundary_type='reflective')
# Define cells
fuel = openmc.Cell(name='fuel', fill=uo2, region=-fuel_or)
clad = openmc.Cell(fill=zirc, region=+fuel_or & -clad_or)
water = openmc.Cell(fill=borated_water, region=+clad_or & box)
# Define overall geometry
geom = openmc.Geometry([fuel, clad, water])
uo2.volume = pi * fuel_or.r**2
settings = openmc.Settings()
settings.batches = 100
settings.inactive = 10
settings.particles = 1000
# Create an initial uniform spatial source distribution over fissionable zones
bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1]
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)
settings.source = openmc.source.Source(space=uniform_dist)
entropy_mesh = openmc.RegularMesh()
entropy_mesh.lower_left = [-0.39218, -0.39218, -1.e50]
entropy_mesh.upper_right = [0.39218, 0.39218, 1.e50]
entropy_mesh.dimension = [10, 10, 1]
settings.entropy_mesh = entropy_mesh
tals = openmc.Tallies()
tal = openmc.Tally(name='test')
tal.scores = ['flux']
tals.append(tal)
plot = openmc.Plot()
plot.origin = (0., 0., 0.)
plot.width = (pitch, pitch)
plot.pixels = (300, 300)
plot.color_by = 'material'
plots = openmc.Plots((plot,))
chain = './chain_simple.xml'
fission_q = {'U235': 200e6}
chain_file_xml = """<?xml version="1.0"?>
<depletion_chain>
<nuclide name="I135" decay_modes="1" reactions="1" half_life="2.36520E+04">
<decay type="beta" target="Xe135" branching_ratio="1.0" />
<reaction type="(n,gamma)" Q="0.0" target="Xe136" /> <!-- Not precisely true, but whatever -->
</nuclide>
<nuclide name="Xe135" decay_modes="1" reactions="1" half_life="3.29040E+04">
<decay type=" beta" target="Cs135" branching_ratio="1.0" />
<reaction type="(n,gamma)" Q="0.0" target="Xe136" />
</nuclide>
<nuclide name="Xe136" decay_modes="0" reactions="0" />
<nuclide name="Cs135" decay_modes="0" reactions="0" />
<nuclide name="Gd157" decay_modes="0" reactions="1" >
<reaction type="(n,gamma)" Q="0.0" target="Nothing" />
</nuclide>
<nuclide name="Gd156" decay_modes="0" reactions="1">
<reaction type="(n,gamma)" Q="0.0" target="Gd157" />
</nuclide>
<nuclide name="U234" decay_modes="0" reactions="1">
<reaction type="fission" Q="191840000."/>
<neutron_fission_yields>
<energies>2.53000e-02</energies>
<fission_yields energy="2.53000e-02">
<products>Gd157 Gd156 I135 Xe135 Xe136 Cs135</products>
<data>1.093250e-04 2.087260e-04 2.780820e-02 6.759540e-03 2.392300e-02 4.356330e-05</data>
</fission_yields>
</neutron_fission_yields>
</nuclide>
<nuclide name="U235" decay_modes="0" reactions="1">
<reaction type="fission" Q="193410000."/>
<neutron_fission_yields>
<energies>2.53000e-02</energies>
<fission_yields energy="2.53000e-02">
<products>Gd157 Gd156 I135 Xe135 Xe136 Cs135</products>
<data>6.142710e-5 1.483250e-04 0.0292737 0.002566345 0.0219242 4.9097e-6</data>
</fission_yields>
</neutron_fission_yields>
</nuclide>
<nuclide name="U238" decay_modes="0" reactions="1">
<reaction type="fission" Q="197790000."/>
<neutron_fission_yields>
<energies>2.53000e-02</energies>
<fission_yields energy="2.53000e-02">
<products>Gd157 Gd156 I135 Xe135 Xe136 Cs135</products>
<data>4.141120e-04 7.605360e-04 0.0135457 0.00026864 0.0024432 3.7100E-07</data>
</fission_yields>
</neutron_fission_yields>
</nuclide>
</depletion_chain>
"""
return (mats, geom, settings, tals, plots, chain, fission_q,
chain_file_xml)
@pytest.fixture(scope='module')
def mpi_intracomm():
if config['mpi']:

View file

@ -1,18 +1,162 @@
from typing import Type
import pytest
from pathlib import Path
import openmc
import openmc.lib
def test_init(run_in_tmpdir, pin_model_attributes):
mats, geom, settings, tals, plots, chain, fission_q, _ = \
pin_model_attributes
openmc.reset_auto_ids()
# Check blank initialization of a model
test_model = openmc.Model()
assert test_model.geometry.root_universe is None
assert len(test_model.materials) == 0
ref_settings = openmc.Settings()
assert sorted(test_model.settings.__dict__.keys()) == \
sorted(ref_settings.__dict__.keys())
for ref_k, ref_v in ref_settings.__dict__.items():
assert test_model.settings.__dict__[ref_k] == ref_v
assert len(test_model.tallies) == 0
assert len(test_model.plots) == 0
assert test_model.chain_file is None
assert test_model.fission_q is None
assert test_model._materials_by_id == {}
assert test_model._materials_by_name == {}
assert test_model._cells_by_id == {}
assert test_model._cells_by_name == {}
assert test_model.C_init is False
# Now check proper init of an actual model. Assume no interference between
# parameters and so we can apply them all at once instead of testing one
# parameter initialization at a time
test_model = openmc.Model(geom, mats, settings, tals, plots, chain,
fission_q)
assert test_model.geometry is geom
assert test_model.materials is mats
assert test_model.settings is settings
assert test_model.tallies is tals
assert test_model.plots is plots
assert test_model.chain_file == Path(chain).resolve()
assert test_model.fission_q is fission_q
assert test_model._materials_by_id == {1: mats[0], 2: mats[1], 3: mats[2]}
assert test_model._materials_by_name == {'UO2': mats[0], 'Zirc': mats[1],
'Borated water': mats[2]}
assert test_model._cells_by_id == {1: geom.root_universe.cells[1],
2: geom.root_universe.cells[2],
3: geom.root_universe.cells[3]}
# No cell name for 2 and 3, so we expect a blank name to be assigned to
# cell 3 due to overwriting
assert test_model._cells_by_name == {
'fuel': geom.root_universe.cells[1], '': geom.root_universe.cells[3]}
assert test_model.C_init is False
# Finally test the parameter type checking by passing bad types and
# obtaining the right exception types
def_params = [geom, mats, settings, tals, plots, chain, fission_q]
for i in range(len(def_params)):
args = def_params.copy()
# Try an integer, as that is a bad type for all arguments
args[i] = i
with pytest.raises(TypeError):
test_model = openmc.Model(*args)
def test_from_xml(run_in_tmpdir, pin_model_attributes):
mats, geom, settings, tals, plots, _, _, _ = pin_model_attributes
# This test will write the individual files to xml and then init that way
# and run the same sort of test as in test_init
mats.export_to_xml()
geom.export_to_xml()
settings.export_to_xml()
tals.export_to_xml()
plots.export_to_xml()
# This from_xml method cannot load chain and fission_q
test_model = openmc.Model.from_xml()
assert test_model.geometry.root_universe.cells.keys() == \
geom.root_universe.cells.keys()
assert [c.fill.name for c in test_model.geometry.root_universe.cells.values()] == \
[c.fill.name for c in geom.root_universe.cells.values()]
assert [mat.name for mat in test_model.materials] == [mat.name for mat in mats]
# We will assume the attributes of settings that are custom objects are
# OK if the others are so we dotn need to implement explicit comparisons
no_test = ['_source', '_entropy_mesh']
assert sorted(k for k in test_model.settings.__dict__.keys() if k not in no_test) == \
sorted(k for k in settings.__dict__.keys() if k not in no_test)
keys = sorted(k for k in settings.__dict__.keys() if k not in no_test)
for ref_k in keys:
assert test_model.settings.__dict__[ref_k] == settings.__dict__[ref_k]
assert len(test_model.tallies) == 0
assert len(test_model.plots) == 0
assert test_model.chain_file is None
assert test_model.fission_q is None
assert test_model._materials_by_id == \
{1: test_model.materials[0], 2: test_model.materials[1],
3: test_model.materials[2]}
assert test_model._materials_by_name == {
'UO2': test_model.materials[0], 'Zirc': test_model.materials[1],
'Borated water': test_model.materials[2]}
assert test_model._cells_by_id == {\
1: test_model.geometry.root_universe.cells[1],
2: test_model.geometry.root_universe.cells[2],
3: test_model.geometry.root_universe.cells[3]}
# No cell name for 2 and 3, so we expect a blank name to be assigned to
# cell 3 due to overwriting
assert test_model._cells_by_name == {
'fuel': test_model.geometry.root_universe.cells[1],
'': test_model.geometry.root_universe.cells[3]}
assert test_model.C_init is False
def test_init_clear_C_api(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
# We are going to init and then make sure data is loaded
mats, geom, settings, tals, plots, _, _, _ = pin_model_attributes
test_model = openmc.Model(geom, mats, settings, tals, plots)
# Set the depletion module to use the test intracomm as the depletion mods
# intracomm is the one that init uses. We will not perturb system state
# outside of this test by re-setting the openmc.deplete.comm to what it was
# before when we exist
if mpi_intracomm is not None:
orig_comm = openmc.deplete.comm
openmc.deplete.comm = mpi_intracomm
test_model.init_C_api()
# First check that the API is advertised as initialized
assert openmc.lib.LIB_INIT is True
assert test_model.C_init is True
# Now make sure it actually is initialized by making a call to the lib
c_mat = openmc.lib.find_material((0.6, 0., 0.))
# This should be Borated water
assert c_mat.name == 'Borated water'
assert c_mat.id == 3
# Ok, now lets test that we can clear the data and check that it is cleared
test_model.clear_C_api()
# First check that the API is advertised as initialized
assert openmc.lib.LIB_INIT is False
assert test_model.C_init is False
# Note we cant actually test that a sys call fails because we should get a
# seg fault
# And before done, reset the deplete communicator
if mpi_intracomm is not None:
openmc.deplete.comm = orig_comm
def test_import_properties(run_in_tmpdir, mpi_intracomm):
"""Test importing properties on the Model class """
# Create PWR pin cell model and write XML files
openmc.reset_auto_ids()
model = openmc.examples.pwr_pin_cell()
model.export_to_xml()
model.init_C_api()
# Change fuel temperature and density and export properties
openmc.lib.init(intracomm=mpi_intracomm)
cell = openmc.lib.cells[1]
cell.set_temperature(600.0)
cell.fill.set_density(5.0, 'g/cm3')
@ -32,3 +176,40 @@ def test_import_properties(run_in_tmpdir, mpi_intracomm):
cell = model_with_properties.geometry.get_all_cells()[1]
assert cell.temperature == [600.0]
assert cell.fill.get_mass_density() == pytest.approx(5.0)
def test_run(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
mats, geom, settings, tals, plots, _, _, _ = pin_model_attributes
test_model = openmc.Model(geom, mats, settings, tals, plots)
# Set the depletion module to use the test intracomm as the depletion mods
# intracomm is the one that init uses. We will not perturb system state
# outside of this test by re-setting the openmc.deplete.comm to what it was
# before when we exist
# This case will run by getting the k-eff and tallies for command-line and
# C-API execution modes and ensuring they give the same result.
sp_path = test_model.run(output=False)
with openmc.StatePoint(sp_path) as sp:
cli_keff = sp.k_combined
cli_flux = sp.get_tally(id=1).get_values()[0, 0, 0]
if mpi_intracomm is not None:
orig_comm = openmc.deplete.comm
openmc.deplete.comm = mpi_intracomm
test_model.settings.verbosity = 1
test_model.init_C_api()
sp_path = test_model.run()
with openmc.StatePoint(sp_path) as sp:
C_keff = sp.k_combined
C_flux = sp.get_tally(id=1).get_values()[0, 0, 0]
test_model.clear_C_api()
# and lets compare results
assert (C_keff - cli_keff) < 1e-15
assert (C_flux - cli_flux) < 1e-15
# And before done, reset the deplete communicator
if mpi_intracomm is not None:
openmc.deplete.comm = orig_comm