Modified the C API to support setting material temperatures and to support setting the event based flag. Fixed failing MPI test.

This commit is contained in:
agnelson 2021-09-30 16:34:58 -05:00
parent 20c8043c1c
commit e8081207d7
11 changed files with 100 additions and 67 deletions

View file

@ -88,6 +88,7 @@ int openmc_material_set_densities(
int openmc_material_set_id(int32_t index, int32_t id);
int openmc_material_get_name(int32_t index, const char** name);
int openmc_material_set_name(int32_t index, const char* name);
int openmc_material_set_temperature(int32_t index, double temperature);
int openmc_material_set_volume(int32_t index, double volume);
int openmc_material_filter_get_bins(
int32_t index, const int32_t** bins, size_t* n);

View file

@ -158,6 +158,10 @@ public:
double density_; //!< Total atom density in [atom/b-cm]
double density_gpcc_; //!< Total atom density in [g/cm^3]
double volume_ {-1.0}; //!< Volume in [cm^3]
double temperature_ {-1}; //!< Default temperature for material
//!< A negative indicates no default
//!< temperature was specified.
bool fissionable_ {
false}; //!< Does this material contain fissionable nuclides
bool depletable_ {false}; //!< Is the material depletable?
@ -195,10 +199,6 @@ private:
// Private data members
gsl::index index_;
//! \brief Default temperature for cells containing this material.
//!
//! A negative value indicates no default temperature was specified.
double temperature_ {-1};
};
//==============================================================================

View file

@ -32,7 +32,7 @@ extern "C" bool cmfd_run; //!< is a CMFD run?
extern bool
delayed_photon_scaling; //!< Scale fission photon yield to include delayed
extern "C" bool entropy_on; //!< calculate Shannon entropy?
extern bool event_based; //!< use event-based mode (instead of history-based)
extern "C" bool event_based; //!< use event-based mode (instead of history-based)
extern bool legendre_to_tabular; //!< convert Legendre distributions to tabular?
extern bool material_cell_offsets; //!< create material cells offsets?
extern "C" bool output_summary; //!< write summary.h5?

View file

@ -7,8 +7,8 @@ import openmc
def process_CLI_arguments(volume=False, geometry_debug=False, particles=None,
plot=False, restart_file=None, threads=None,
tracks=False, event_based=False,
openmc_exec='openmc', mpi_args=None):
tracks=False, event_based=None, openmc_exec='openmc',
mpi_args=None):
"""Run an OpenMC simulation.
Parameters
@ -30,8 +30,9 @@ def process_CLI_arguments(volume=False, geometry_debug=False, particles=None,
:envvar:`OMP_NUM_THREADS` environment variable).
tracks : bool, optional
Write tracks for all particles. Defaults to False.
event_based : bool, optional
Turns on event-based parallelism, instead of default history-based
event_based : None or bool, optional
Turns on event-based parallelism if True. If None, the value in
the Settings will be used.
openmc_exec : str, optional
Path to OpenMC executable. Defaults to 'openmc'.
mpi_args : list of str, optional
@ -61,8 +62,9 @@ def process_CLI_arguments(volume=False, geometry_debug=False, particles=None,
if geometry_debug:
args.append('-g')
if event_based:
args.append('-e')
if event_based is not None:
if event_based:
args.append('-e')
if isinstance(restart_file, str):
args += ['-r', restart_file]
@ -70,6 +72,9 @@ def process_CLI_arguments(volume=False, geometry_debug=False, particles=None,
if tracks:
args.append('-t')
if plot:
args.append('-p')
if mpi_args is not None:
args = mpi_args + args

View file

@ -61,4 +61,6 @@ from .math import *
from .plot import *
# Flag to denote whether or not openmc.lib.init has been called
# TODO: Establish and use a flag in the C++ code to represent the status of the
# openmc_init and openmc_finalize methods
is_initialized = False

View file

@ -57,6 +57,9 @@ _dll.openmc_material_get_name.errcheck = _error_handler
_dll.openmc_material_set_name.argtypes = [c_int32, c_char_p]
_dll.openmc_material_set_name.restype = c_int
_dll.openmc_material_set_name.errcheck = _error_handler
_dll.openmc_material_set_temperature.argtypes = [c_int32, c_double]
_dll.openmc_material_set_temperature.restype = c_int
_dll.openmc_material_set_temperature.errcheck = _error_handler
_dll.openmc_material_set_volume.argtypes = [c_int32, c_double]
_dll.openmc_material_set_volume.restype = c_int
_dll.openmc_material_set_volume.errcheck = _error_handler
@ -165,6 +168,10 @@ class Material(_FortranObjectWithID):
return None
return volume.value
@temperature.setter
def temperature(self, temperature):
_dll.openmc_material_set_temperature(self._index, temperature)
@volume.setter
def volume(self, volume):
_dll.openmc_material_set_volume(self._index, volume)

View file

@ -34,6 +34,7 @@ class _Settings:
restart_run = _DLLGlobal(c_bool, 'restart_run')
run_CE = _DLLGlobal(c_bool, 'run_CE')
verbosity = _DLLGlobal(c_int, 'verbosity')
event_based = _DLLGlobal(c_bool, 'event_based')
@property
def run_mode(self):

View file

@ -73,7 +73,7 @@ class Model:
"""
def __init__(self, geometry=None, materials=None, settings=None,
tallies=None, plots=None, intracomm=None):
tallies=None, plots=None):
self.geometry = openmc.Geometry()
self.materials = openmc.Materials()
self.settings = openmc.Settings()
@ -145,8 +145,6 @@ class Model:
@property
def is_initialized(self):
# TODO: Replace openmc.lib.is_initialized with a direct ctypes access
# to simulation::initialized
return openmc.lib.is_initialized
@geometry.setter
@ -222,7 +220,7 @@ class Model:
return cls(geometry, materials, settings)
def init_lib(self, threads=None, geometry_debug=False, restart_file=None,
tracks=False, output=True, event_based=False):
tracks=False, output=True, event_based=None):
"""Initializes the model in memory via the C API
.. versionadded:: 0.13.0
@ -243,8 +241,9 @@ class Model:
Write tracks for all particles. Defaults to False.
output : bool
Capture OpenMC output from standard out
event_based : bool, optional
Turns on event-based parallelism, instead of default history-based
event_based : None or bool, optional
Turns on event-based parallelism if True. If None, the value in
the Settings will be used.
"""
# TODO: right now the only way to set most of the above parameters via
@ -456,7 +455,7 @@ class Model:
def run(self, particles=None, threads=None, geometry_debug=False,
restart_file=None, tracks=False, output=True, cwd='.',
openmc_exec='openmc', mpi_args=None, event_based=False):
openmc_exec='openmc', mpi_args=None, event_based=None):
"""Runs OpenMC. If the C API has been initialized, then the C API is
used, otherwise, this method creates the XML files and runs OpenMC via
a system call. In both cases this method returns the path to the last
@ -494,8 +493,9 @@ class Model:
mpi_args : list of str, optional
MPI execute command and any additional MPI arguments to pass,
e.g. ['mpiexec', '-n', '8'].
event_based : bool, optional
Turns on event-based parallelism, instead of default history-based
event_based : None or bool, optional
Turns on event-based parallelism if True. If None, the value in
the Settings will be used.
Returns
-------
@ -523,21 +523,21 @@ class Model:
msg = f"{arg_name} must be set via Model.is_initialized(...)"
raise ValueError(msg)
init_particles = openmc.lib.settings.particles
if particles is not None:
init_particles = openmc.lib.settings.particles
if isinstance(particles, Integral) and particles > 0:
openmc.lib.settings.particles = particles
# Event-based can be set at init-time or on a case-basis.
# Handle the argument here.
# TODO This will be dealt with in a future change to the C API
init_event_based = openmc.lib.settings.event_based
if event_based is not None:
openmc.lib.settings.event_based = event_based
# Then run using the C API
openmc.lib.run(output)
# Reset changes for the openmc.run kwargs handling
if particles is not None:
openmc.lib.settings.particles = init_particles
openmc.lib.settings.particles = init_particles
openmc.lib.settings.event_based = init_event_based
else:
# Then run via the command line
@ -697,10 +697,7 @@ class Model:
if obj_type == 'cell' and attrib_name == 'volume':
raise NotImplementedError(
'Setting a Cell volume is not supported!')
# Same with setting temperatures, TODO: update C API for this
if obj_type == 'material' and attrib_name == 'temperature':
raise NotImplementedError(
'Setting a Material temperature is not yet supported!')
# And some items just dont make sense
if obj_type == 'cell' and attrib_name == 'density':
raise ValueError('Cannot set a Cell density!')
@ -751,10 +748,10 @@ class Model:
# Next lets keep what is in C API memory up to date as well
if self.is_initialized:
lib_obj = obj_by_id[id_]
if attrib_name == 'temperature':
lib_obj.set_temperature(value)
elif attrib_name == 'density':
if attrib_name == 'density':
lib_obj.set_density(value, density_units)
elif attrib_name == 'temperature' and obj_type == 'cell':
lib_obj.set_temperature(value)
else:
setattr(lib_obj, attrib_name, value)
@ -800,7 +797,8 @@ class Model:
"""
self._change_py_lib_attribs(names_or_ids, vector, 'cell', 'translation')
self._change_py_lib_attribs(names_or_ids, vector, 'cell',
'translation')
def update_densities(self, names_or_ids, density, density_units='atom/b-cm'):
"""Update the density of a given set of materials to a new value
@ -822,8 +820,8 @@ class Model:
"""
self._change_py_lib_attribs(names_or_ids, density, 'material', 'density',
density_units)
self._change_py_lib_attribs(names_or_ids, density, 'material',
'density', density_units)
def update_cell_temperatures(self, names_or_ids, temperature):
"""Update the temperature of a set of cells to the given value
@ -844,7 +842,7 @@ class Model:
"""
self._change_py_lib_attribs(names_or_ids, temperature, 'cell',
'temperature')
'temperature')
def update_material_temperatures(self, names_or_ids, temperature):
"""Update the temperature of a set of materials to the given value
@ -865,7 +863,7 @@ class Model:
"""
self._change_py_lib_attribs(names_or_ids, temperature, 'material',
'temperature')
'temperature')
def update_material_volumes(self, names_or_ids, volume):
"""Update the volume of a set of materials to the given value

View file

@ -1480,6 +1480,18 @@ extern "C" int openmc_material_set_name(int32_t index, const char* name)
return 0;
}
extern "C" int openmc_material_set_temperature(int32_t index, double temperature)
{
if (index >= 0 && index < model::materials.size()) {
auto& m {model::materials[index]};
m->temperature_ = temperature;
return 0;
} else {
set_errmsg("Index in materials array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
}
extern "C" int openmc_material_set_volume(int32_t index, double volume)
{
if (index >= 0 && index < model::materials.size()) {

View file

@ -207,6 +207,11 @@ def test_material(lib_init):
m.name = "Not hot borated water"
assert m.name == "Not hot borated water"
assert m.temperature == 293.6
m.temperature = 400.
assert m.temperature == 400.
m.temperature == 293.6
def test_properties_density(lib_init):
m = openmc.lib.materials[1]
@ -263,6 +268,7 @@ def test_settings(lib_init):
assert settings.generations_per_batch == 1
assert settings.particles == 100
assert settings.seed == 1
assert settings.event_based is False
settings.seed = 11

View file

@ -5,7 +5,6 @@ from pathlib import Path
from shutil import which
import openmc
import openmc.lib
from openmc.mpi import DummyCommunicator
@pytest.fixture(scope='function')
@ -128,12 +127,14 @@ def test_init(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
assert test_model._cells_by_id == {}
assert test_model._cells_by_name == {}
assert test_model.is_initialized is False
assert isinstance(test_model.intracomm, DummyCommunicator)
assert hasattr(test_model.intracomm, 'rank')
# 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, mpi_intracomm)
test_model = openmc.Model(geom, mats, settings, tals, plots)
if mpi_intracomm is not None:
test_model.intracomm = mpi_intracomm
assert test_model.geometry is geom
assert test_model.materials is mats
assert test_model.settings is settings
@ -154,17 +155,10 @@ def test_init(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
'': [geom.root_universe.cells[3], geom.root_universe.cells[4]],
'inf fuel': [geom.root_universe.cells[2].fill.cells[1]]}
assert test_model.is_initialized is False
if mpi_intracomm is None:
assert isinstance(test_model.intracomm, DummyCommunicator)
else:
assert test_model.intracomm == mpi_intracomm
# Finally test the parameter type checking by passing bad types and
# obtaining the right exception types
if mpi_intracomm is not None:
def_params = [geom, mats, settings, tals, plots, mpi_intracomm]
else:
def_params = [geom, mats, settings, tals, plots]
def_params = [geom, mats, settings, tals, plots]
for i in range(len(def_params)):
args = def_params.copy()
# Try an integer, as that is a bad type for all arguments
@ -223,13 +217,14 @@ def test_from_xml(run_in_tmpdir, pin_model_attributes):
test_model.geometry.root_universe.cells[4]],
'inf fuel': [test_model.geometry.root_universe.cells[2].fill.cells[1]]}
assert test_model.is_initialized is False
assert isinstance(test_model.intracomm, DummyCommunicator)
def test_init_finalize_lib(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, mpi_intracomm)
test_model = openmc.Model(geom, mats, settings, tals, plots)
if mpi_intracomm is not None:
test_model.intracomm = mpi_intracomm
test_model.init_lib(output=False)
# First check that the API is advertised as initialized
@ -257,6 +252,8 @@ def test_import_properties(run_in_tmpdir, mpi_intracomm):
# Create PWR pin cell model and write XML files
openmc.reset_auto_ids()
model = openmc.examples.pwr_pin_cell()
if mpi_intracomm is not None:
model.intracomm = mpi_intracomm
model.init_lib(output=False)
# Change fuel temperature and density and export properties
@ -296,7 +293,9 @@ def test_import_properties(run_in_tmpdir, mpi_intracomm):
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, mpi_intracomm)
test_model = openmc.Model(geom, mats, settings, tals, plots)
if mpi_intracomm is not None:
test_model.intracomm = mpi_intracomm
# 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.
@ -334,7 +333,9 @@ def test_run(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
def test_plots(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, mpi_intracomm)
test_model = openmc.Model(geom, mats, settings, tals, plots)
if mpi_intracomm is not None:
test_model.intracomm = mpi_intracomm
# This test cannot check the correctness of the plot, but it can
# check that a plot was made and that the expected ppm and png files are
@ -367,7 +368,9 @@ def test_plots(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
def test_py_lib_attributes(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, mpi_intracomm)
test_model = openmc.Model(geom, mats, settings, tals, plots)
if mpi_intracomm is not None:
test_model.intracomm = mpi_intracomm
test_model.init_lib(output=False)
@ -439,19 +442,13 @@ def test_py_lib_attributes(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
# Now lets do the material temperature updates.
# Check initial conditions
with pytest.raises(NotImplementedError):
test_model.update_material_temperatures(['UO2'], 600.)
# TODO: When C API material.set_temperature is implemented, uncomment below
# assert abs(openmc.lib.materials[1].temperature - 293.6) < 1e-13
# # The temperature on the material will be None because its just the
# # default assert test_model.materials[0].temperature is None
# # Change the temperature
# test_model.update_material_temperatures(['UO2'], 600.)
# assert abs(openmc.lib.materials[1].temperature - 600.) < 1e-13
# assert abs(test_model.materials[0].temperature - 600.) < 1e-13
assert abs(openmc.lib.materials[1].temperature - 293.6) < 1e-13
# Change the temperature
test_model.update_material_temperatures(['UO2'], 600.)
assert abs(openmc.lib.materials[1].temperature - 600.) < 1e-13
assert abs(test_model.materials[0].temperature - 600.) < 1e-13
# And finally material volume
# import pdb; pdb.set_trace()
assert abs(openmc.lib.materials[1].volume - 0.4831931368640985) < 1e-13
# The temperature on the material will be None because its just the default
assert abs(test_model.materials[0].volume - 0.4831931368640985) < 1e-13
@ -468,7 +465,9 @@ def test_deplete(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
pin_model_attributes
with open('test_chain.xml', 'w') as f:
f.write(chain_file_xml)
test_model = openmc.Model(geom, mats, settings, tals, plots, mpi_intracomm)
test_model = openmc.Model(geom, mats, settings, tals, plots)
if mpi_intracomm is not None:
test_model.intracomm = mpi_intracomm
initial_mat = mats[0].clone()
initial_u = initial_mat.get_nuclide_atom_densities()['U235'][1]
@ -518,7 +517,9 @@ def test_deplete(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
def test_calc_volumes(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, mpi_intracomm)
test_model = openmc.Model(geom, mats, settings, tals, plots)
if mpi_intracomm is not None:
test_model.intracomm = mpi_intracomm
# With no vol calcs, it should fail
with pytest.raises(ValueError):