mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 06:05:58 -04:00
Added generic ability to update Models volumes, temperatures, rotations, translations, densities for cells and materials as applicable. Other various changes based on local testing to make sure Model.deplete works and that it works when called in separate instances while keeping the model in C-API memory
This commit is contained in:
parent
3f24c3d4e1
commit
b0bb2493b9
2 changed files with 189 additions and 107 deletions
|
|
@ -202,6 +202,7 @@ class Operator(TransportOperator):
|
|||
self.settings = settings
|
||||
self.geometry = geometry
|
||||
self.diff_burnable_mats = diff_burnable_mats
|
||||
self.cleanup_when_done = True
|
||||
|
||||
# Reduce the chain before we create more materials
|
||||
if reduce_chain:
|
||||
|
|
@ -555,7 +556,8 @@ class Operator(TransportOperator):
|
|||
|
||||
def finalize(self):
|
||||
"""Finalize a depletion simulation and release resources."""
|
||||
openmc.lib.finalize()
|
||||
if self.cleanup_when_done:
|
||||
openmc.lib.finalize()
|
||||
|
||||
def _update_materials(self):
|
||||
"""Updates material compositions in OpenMC on all processes."""
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
from collections.abc import Iterable
|
||||
import os
|
||||
from pathlib import Path
|
||||
import time
|
||||
import warnings
|
||||
|
|
@ -90,13 +91,13 @@ class Model:
|
|||
|
||||
self.chain_file = chain_file
|
||||
self.fission_q = fission_q
|
||||
self.depletion_operator = None
|
||||
|
||||
if self.materials is None:
|
||||
if materials is None:
|
||||
mats = self.geometry.get_all_materials().values()
|
||||
else:
|
||||
mats = self.materials
|
||||
self._materials_by_id = {mat.id: mat for mat in mats}
|
||||
self._materials_by_name = {mat.name: mat for mat in mats}
|
||||
cells = self.geometry.get_all_cells()
|
||||
self._cells_by_id = {cell.id: cell for cell in cells.values()}
|
||||
self._cells_by_name = {cell.name: cell for cell in cells.values()}
|
||||
|
|
@ -129,10 +130,6 @@ class Model:
|
|||
def fission_q(self):
|
||||
return self._fission_q
|
||||
|
||||
@property
|
||||
def depletion_operator(self):
|
||||
return self._depletion_operator
|
||||
|
||||
@property
|
||||
def C_init(self):
|
||||
return openmc.lib.LIB_INIT
|
||||
|
|
@ -181,21 +178,15 @@ class Model:
|
|||
def chain_file(self, chain_file):
|
||||
check_type('chain_file', chain_file, (type(None), str, Path))
|
||||
if isinstance(chain_file, str):
|
||||
self._chain_file = Path(chain_file)
|
||||
self._chain_file = Path(chain_file).resolve()
|
||||
else:
|
||||
self._chain_file = chain_file
|
||||
self._chain_file = chain_file.resolve()
|
||||
|
||||
@fission_q.setter
|
||||
def fission_q(self, fission_q):
|
||||
check_type('fission_q', fission_q, (type(None), dict))
|
||||
self._fission_q = fission_q
|
||||
|
||||
@depletion_operator.setter
|
||||
def depletion_operator(self, depletion_operator):
|
||||
check_type('depletion_operator', depletion_operator,
|
||||
(type(None), dep.Operator))
|
||||
self._depletion_operator = depletion_operator
|
||||
|
||||
@classmethod
|
||||
def from_xml(cls, geometry='geometry.xml', materials='materials.xml',
|
||||
settings='settings.xml'):
|
||||
|
|
@ -221,28 +212,11 @@ class Model:
|
|||
settings = openmc.Settings.from_xml(settings)
|
||||
return cls(geometry, materials, settings)
|
||||
|
||||
def init_C_api(self, use_depletion_operator=False):
|
||||
"""Initializes the model in memory via the C-API
|
||||
def init_C_api(self):
|
||||
"""Initializes the model in memory via the C-API"""
|
||||
|
||||
Parameters
|
||||
----------
|
||||
directory : str
|
||||
Directory to write XML files to. If it doesn't exist already, it
|
||||
will be created.
|
||||
use_depletion_operator : bool, optional
|
||||
If True, the model will be loaded using the depletion operator
|
||||
including all isotopes necessary from fission. This parameter will
|
||||
use the :attr:`Model.chain_file` and :attr:`Model.fission_q`
|
||||
attributes.
|
||||
"""
|
||||
self.clear_C_api()
|
||||
|
||||
if use_depletion_operator:
|
||||
# Create OpenMC transport operator
|
||||
self.depletion_operator = \
|
||||
dep.Operator(self.geometry, self.settings,
|
||||
str(self.chain_file), fission_q=self.fission_q)
|
||||
else:
|
||||
openmc.lib.hard_reset()
|
||||
if dep.comm.rank == 0:
|
||||
self.export_to_xml()
|
||||
dep.comm.barrier()
|
||||
|
|
@ -253,7 +227,8 @@ class Model:
|
|||
openmc.lib.finalize()
|
||||
|
||||
def deplete(self, timesteps, chain_file=None, method='cecm',
|
||||
fission_q=None, final_step=True, **kwargs):
|
||||
fission_q=None, final_step=True, directory='.',
|
||||
**kwargs):
|
||||
"""Deplete model using specified timesteps/power
|
||||
|
||||
Parameters
|
||||
|
|
@ -265,57 +240,63 @@ class Model:
|
|||
Path to the depletion chain XML file. Defaults to the chain
|
||||
found under the ``depletion_chain`` in the
|
||||
:envvar:`OPENMC_CROSS_SECTIONS` environment variable if it exists.
|
||||
method : str
|
||||
Integration method used for depletion (e.g., 'cecm', 'predictor')
|
||||
method : str, optional
|
||||
Integration method used for depletion (e.g., 'cecm', 'predictor').
|
||||
Defaults to 'cecm'.
|
||||
fission_q : dict, optional
|
||||
Dictionary of nuclides and their fission Q values [eV].
|
||||
If not given, values will be pulled from the ``chain_file``.
|
||||
Defaults to pulling from the ``chain_file``.
|
||||
final_step : bool, optional
|
||||
Indicate whether or not a transport solve should be run at the end
|
||||
of the last timestep.
|
||||
|
||||
.. versionadded:: 0.12.3
|
||||
of the last timestep. Defaults to running this transport solve.
|
||||
directory : str, optional
|
||||
Directory to write XML files to. If it doesn't exist already, it
|
||||
will be created. Defaults to the current working directory
|
||||
**kwargs
|
||||
Keyword arguments passed to integration function (e.g.,
|
||||
:func:`openmc.deplete.integrator.cecm`)
|
||||
|
||||
"""
|
||||
|
||||
if self.C_init and self.depletion_operator is not None:
|
||||
# Then the user has properly initialized the information and we can
|
||||
# just carry forward
|
||||
pass
|
||||
elif self.C_init and self.depletion_operator is None:
|
||||
# Then the user has initialzed the C-API but without the depletion
|
||||
# isotopes loaded. We would have to reset and reload data, but
|
||||
# doing so could lose user information. Therefore let us just
|
||||
# provide an error and quit.
|
||||
msg = "Model.deplete(...) cannot be called after " \
|
||||
"Model.init_C_api(...) if the use_depletion_operator " \
|
||||
"argument to Model.init_C_api(...) is False."
|
||||
raise SetupError(msg)
|
||||
# To keep Model.deplete(...) API compatibility, we will allow the
|
||||
# chain_file and fission_q params to be set if provided while we set
|
||||
# the depletion operator
|
||||
if chain_file is not None:
|
||||
this_chain_file = Path(chain_file).resolve()
|
||||
warnings.warn("The chain_file argument of Model.deplete(...) "
|
||||
"has been deprecated and may be removed in a "
|
||||
"future version. The Model.chain_file should be"
|
||||
"used instead.", DeprecationWarning)
|
||||
else:
|
||||
# To get here, the C-API is not initialized. So we can do that now
|
||||
# To keep Model.deplete(...) API compatibility, we will allow the
|
||||
# chain_file and fission_q params to be set since we havent loaded
|
||||
# the API anyways
|
||||
if chain_file is not None:
|
||||
self.chain_file = chain_file
|
||||
warnings.warn("The chain_file argument of Model.deplete(...) "
|
||||
"has been deprecated and may be removed in a "
|
||||
"future version. The Model.chain_file should be"
|
||||
"used instead.", DeprecationWarning)
|
||||
if fission_q is not None:
|
||||
warnings.warn("The fission_q argument of Model.deplete(...) "
|
||||
"has been deprecated and may be removed in a "
|
||||
"future version. The Model.fission_q should be"
|
||||
"used instead.", DeprecationWarning)
|
||||
self.fission_q = fission_q
|
||||
self.init_C_api(use_depletion_operator=True)
|
||||
this_chain_file = self.chain_file
|
||||
if fission_q is not None:
|
||||
warnings.warn("The fission_q argument of Model.deplete(...) "
|
||||
"has been deprecated and may be removed in a "
|
||||
"future version. The Model.fission_q should be"
|
||||
"used instead.", DeprecationWarning)
|
||||
this_fission_q = fission_q
|
||||
else:
|
||||
this_fission_q = fission_q
|
||||
|
||||
# Create directory if required
|
||||
d = Path(directory)
|
||||
if not d.is_dir():
|
||||
d.mkdir(parents=True)
|
||||
start_dir = Path.cwd()
|
||||
os.chdir(d)
|
||||
|
||||
depletion_operator = \
|
||||
dep.Operator(self.geometry, self.settings,
|
||||
str(this_chain_file.absolute()),
|
||||
fission_q=this_fission_q)
|
||||
# Tell depletion_operator.finalize NOT to clear C-API memory when it is
|
||||
# done
|
||||
depletion_operator.cleanup_when_done = False
|
||||
|
||||
# Set up the integrator
|
||||
integrator_class = dep.integrators.integrator_factory(method)
|
||||
integrator = integrator_class(self.depletion_operator,
|
||||
integrator = integrator_class(depletion_operator,
|
||||
timesteps, **kwargs)
|
||||
|
||||
# Now perform the depletion
|
||||
|
|
@ -324,7 +305,7 @@ class Model:
|
|||
# If we did not perform a transport calculation on the final step, then
|
||||
# make the code update the C-API material inventory
|
||||
if not final_step:
|
||||
self.depletion_operator._update_materials()
|
||||
depletion_operator._update_materials()
|
||||
|
||||
# Now make the python Materials match the C-API material data
|
||||
for mat_id, mat in self._materials_by_id.items():
|
||||
|
|
@ -340,6 +321,8 @@ class Model:
|
|||
atom_density += density
|
||||
mat.set_density('atom/b-cm', atom_density)
|
||||
|
||||
os.chdir(start_dir)
|
||||
|
||||
def export_to_xml(self, directory='.'):
|
||||
"""Export model to XML files.
|
||||
|
||||
|
|
@ -376,6 +359,9 @@ class Model:
|
|||
def import_properties(self, filename):
|
||||
"""Import physical properties
|
||||
|
||||
.. versionchanged:: 0.12.3
|
||||
This method now updates values as loaded in memory with the C-API
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str
|
||||
|
|
@ -404,6 +390,9 @@ class Model:
|
|||
cell = cells[cell_id]
|
||||
if cell.fill_type in ('material', 'distribmat'):
|
||||
cell.temperature = group['temperature'][()]
|
||||
if self.C_init:
|
||||
C_cell = openmc.lib.cells[cell_id]
|
||||
C_cell.set_temperature(group['temperature'][()])
|
||||
|
||||
# Make sure number of materials matches
|
||||
mats_group = fh['materials']
|
||||
|
|
@ -417,6 +406,9 @@ class Model:
|
|||
mat_id = int(name.split()[1])
|
||||
atom_density = group.attrs['atom_density']
|
||||
materials[mat_id].set_density('atom/b-cm', atom_density)
|
||||
if self.C_init:
|
||||
C_mat = openmc.lib.materials[mat_id]
|
||||
C_mat.set_density(atom_density, 'atom/b-cm')
|
||||
|
||||
def run(self, **kwargs):
|
||||
"""Runs OpenMC. If the C-API has been initialized, then the C-API is
|
||||
|
|
@ -469,54 +461,92 @@ class Model:
|
|||
last_statepoint = sp
|
||||
return last_statepoint
|
||||
|
||||
def _move_cell(self, cell_names_or_ids, vector, attrib_name):
|
||||
# Method to do the same work whether it is a rotation or translation
|
||||
check_type('cell_names_or_ids', cell_names_or_ids, Iterable,
|
||||
(np.int, int, str))
|
||||
check_type('vector', vector, Iterable, (np.float, float))
|
||||
check_length('vector', vector, 3)
|
||||
check_value('attrib_name', attrib_name, ('rotation', 'translation'))
|
||||
def _change_py_C_attribs(self, names_or_ids, value, obj_type, attrib_name,
|
||||
density_units='atom/b-cm'):
|
||||
# Method to do the same work whether it is a cell or material and
|
||||
# a temperature or volume
|
||||
check_type('names_or_ids', names_or_ids, Iterable, (np.int, int, str))
|
||||
check_type('obj_type', obj_type, str)
|
||||
obj_type = obj_type.lower()
|
||||
check_value('obj_type', obj_type, ('material', 'cell'))
|
||||
check_value('attrib_name', attrib_name,
|
||||
('temperature', 'volume', 'density', 'rotation',
|
||||
'translation'))
|
||||
# The C-API only allows setting density units of atom/b-cm and g/cm3
|
||||
check_value('density_units', density_units, ('atom/b-cm', 'g/cm3'))
|
||||
# The C-API has no way to set cell volume so lets raise an exception
|
||||
if obj_type == 'cell' and attrib_name == 'volume':
|
||||
raise NotImplementedError(
|
||||
'Setting a Cell volume 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!')
|
||||
if obj_type == 'material' and attrib_name in ('rotation',
|
||||
'translation'):
|
||||
raise ValueError('Cannot set a material rotation/translation!')
|
||||
|
||||
# Get the list of cell ids to use y converting from names and accepting
|
||||
# Set the
|
||||
if obj_type == 'cell':
|
||||
by_name = self._cells_by_name
|
||||
by_id = self._cells_by_id
|
||||
C_by_id = openmc.lib.cells
|
||||
else:
|
||||
by_name = self._materials_by_name
|
||||
by_id = self._materials_by_id
|
||||
C_by_id = openmc.lib.materials
|
||||
|
||||
# Get the list of ids to use if converting from names and accepting
|
||||
# only values that have actual ids
|
||||
cell_ids = [None] * len(cell_names_or_ids)
|
||||
for c, cell_name_or_id in enumerate(cell_names_or_ids):
|
||||
if isinstance(cell_name_or_id, (int, np.int)):
|
||||
if cell_name_or_id in self._cells_by_id:
|
||||
cell_ids[c] = int(cell_name_or_id)
|
||||
msg = 'Cell ID {} is not present in the model!'.format(
|
||||
cell_name_or_id)
|
||||
ids = [None] * len(names_or_ids)
|
||||
for i, name_or_id in enumerate(names_or_ids):
|
||||
if isinstance(name_or_id, (int, np.int)):
|
||||
if name_or_id in by_id:
|
||||
ids[i] = int(name_or_id)
|
||||
msg = '{} ID {} is not present in the model!'.format(
|
||||
obj_type.capitalize(), name_or_id)
|
||||
raise InvalidIDError(msg)
|
||||
elif isinstance(cell_name_or_id, str):
|
||||
if cell_name_or_id in self._cells_by_name:
|
||||
cell_ids[c] = self._cells_by_name[cell_name_or_id]
|
||||
elif isinstance(name_or_id, str):
|
||||
if name_or_id in by_name:
|
||||
ids[i] = by_name[name_or_id].id
|
||||
else:
|
||||
msg = 'Cell {} is not present in the model!'.format(
|
||||
cell_name_or_id)
|
||||
msg = '{} {} is not present in the model!'.format(
|
||||
obj_type.capitalize(), name_or_id)
|
||||
raise InvalidIDError(msg)
|
||||
|
||||
# Now perform the motion
|
||||
for cell_id in cell_ids:
|
||||
cell = self._cells_by_id[cell_id]
|
||||
# Now perform the change to both python and C-API
|
||||
for id_ in ids:
|
||||
obj = by_id[id_]
|
||||
if attrib_name == 'rotation':
|
||||
cell.rotation = vector
|
||||
obj.rotation = value
|
||||
elif attrib_name == 'translation':
|
||||
cell.translation = vector
|
||||
obj.translation = value
|
||||
elif attrib_name == 'volume':
|
||||
obj.volume = value
|
||||
elif attrib_name == 'temperature':
|
||||
obj.temperature = value
|
||||
elif attrib_name == 'density':
|
||||
obj.set_density(value, density_units)
|
||||
# Next lets keep what is in C-API memory up to date as well
|
||||
if self.C_init:
|
||||
C_cell = openmc.lib.cells[cell_id]
|
||||
C_obj = C_by_id[id_]
|
||||
if attrib_name == 'rotation':
|
||||
C_cell.rotation = vector
|
||||
C_obj.rotation = value
|
||||
elif attrib_name == 'translation':
|
||||
C_cell.translation = vector
|
||||
C_obj.translation = value
|
||||
elif attrib_name == 'volume':
|
||||
C_obj.set_volume = value
|
||||
elif attrib_name == 'temperature':
|
||||
C_obj.set_temperature = value
|
||||
elif attrib_name == 'density':
|
||||
C_obj.set_density(value, density_units)
|
||||
|
||||
def rotate_cells(self, cell_names_or_ids, vector):
|
||||
def rotate_cells(self, names_or_ids, vector):
|
||||
"""Rotate the identified cell(s) by the specified rotation vector.
|
||||
The rotation is only applied to cells filled with a universe.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cell_names_or_ids : Iterable of str or int
|
||||
names_or_ids : Iterable of str or int
|
||||
The cell names (if str) or id (if int) that are to be translated
|
||||
or rotated. This parameter can include a mix of names and ids.
|
||||
vector : Iterable of float
|
||||
|
|
@ -525,15 +555,15 @@ class Model:
|
|||
|
||||
"""
|
||||
|
||||
self._move_cell(cell_names_or_ids, vector, 'rotation')
|
||||
self._change_py_C_attribs(names_or_ids, vector, 'cell', 'rotation')
|
||||
|
||||
def translate_cells(self, cell_names_or_ids, vector):
|
||||
def translate_cells(self, names_or_ids, vector):
|
||||
"""Translate the identified cell(s) by the specified translation vector.
|
||||
The translation is only applied to cells filled with a universe.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cell_names_or_ids : Iterable of str or int
|
||||
names_or_ids : Iterable of str or int
|
||||
The cell names (if str) or id (if int) that are to be translated
|
||||
or rotated. This parameter can include a mix of names and ids.
|
||||
vector : Iterable of float
|
||||
|
|
@ -542,4 +572,54 @@ class Model:
|
|||
|
||||
"""
|
||||
|
||||
self._move_cell(cell_names_or_ids, vector, 'translation')
|
||||
self._change_py_C_attribs(names_or_ids, vector, 'cell', 'translation')
|
||||
|
||||
def update_densities(self, names_or_ids, density, density_units):
|
||||
"""Update the density of a given set of materials to a new value
|
||||
|
||||
Parameters
|
||||
----------
|
||||
names_or_ids : Iterable of str or int
|
||||
The material names (if str) or id (if int) that are to be updated.
|
||||
This parameter can include a mix of names and ids.
|
||||
density : float
|
||||
The density to apply in the units specified by `density_units`
|
||||
density_units : {'atom/b-cm', 'g/cm3'}
|
||||
Units for `density`
|
||||
|
||||
"""
|
||||
|
||||
self._change_py_C_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
|
||||
|
||||
Parameters
|
||||
----------
|
||||
names_or_ids : Iterable of str or int
|
||||
The cell names (if str) or id (if int) that are to be updated.
|
||||
This parameter can include a mix of names and ids.
|
||||
temperature : float
|
||||
The temperature to apply in units of Kelvin
|
||||
|
||||
"""
|
||||
|
||||
self._change_py_C_attribs(names_or_ids, temperature, 'cell',
|
||||
'temperature')
|
||||
|
||||
def update_material_temperatures(self, names_or_ids, temperature):
|
||||
"""Update the temperature of a set of materials to the given value
|
||||
|
||||
Parameters
|
||||
----------
|
||||
names_or_ids : Iterable of str or int
|
||||
The material names (if str) or id (if int) that are to be updated.
|
||||
This parameter can include a mix of names and ids.
|
||||
temperature : float
|
||||
The temperature to apply in units of Kelvin
|
||||
|
||||
"""
|
||||
|
||||
self._change_py_C_attribs(names_or_ids, temperature, 'material',
|
||||
'temperature')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue