from numbers import Real
import re
from openmc.checkvalue import check_type, check_value
from openmc import Material
from openmc.data import ELEMENT_SYMBOL
[docs]class TransferRates:
"""Class for defining continuous removals and feeds.
Molten Salt Reactors (MSRs) benefit from continuous reprocessing,
which removes fission products and feeds fresh fuel into the system. MSRs
inspired the development of this class.
An instance of this class can be passed directly to an instance of one of
the :class:`openmc.deplete.Integrator` classes.
.. versionadded:: 0.14.0
Parameters
----------
operator : openmc.TransportOperator
Depletion operator
model : openmc.Model
OpenMC model containing materials and geometry. If using
:class:`openmc.deplete.CoupledOperator`, the model must also contain
a :class:`opnemc.Settings` object.
Attributes
----------
burnable_mats : list of str
All burnable material IDs.
local_mats : list of str
All burnable material IDs being managed by a single process
transfer_rates : dict of str to dict
Container of transfer rates, components (elements and/or nuclides) and
destination material
index_transfer : Set of pair of str
Pair of strings needed to build final matrix (destination_material, mat)
"""
def __init__(self, operator, model):
self.materials = model.materials
self.burnable_mats = operator.burnable_mats
self.local_mats = operator.local_mats
#initialize transfer rates container dict
self.transfer_rates = {mat: {} for mat in self.burnable_mats}
self.index_transfer = set()
def _get_material_id(self, val):
"""Helper method for getting material id from Material obj or name.
Parameters
----------
val : openmc.Material or str or int representing material name/id
Returns
-------
material_id : str
"""
if isinstance(val, Material):
check_value('Depeletable Material', str(val.id), self.burnable_mats)
val = val.id
elif isinstance(val, str):
if val.isnumeric():
check_value('Material ID', str(val), self.burnable_mats)
else:
check_value('Material name', val,
[mat.name for mat in self.materials if mat.depletable])
val = [mat.id for mat in self.materials if mat.name == val][0]
elif isinstance(val, int):
check_value('Material ID', str(val), self.burnable_mats)
return str(val)
[docs] def get_transfer_rate(self, material, component):
"""Return transfer rate for given material and element.
Parameters
----------
material : openmc.Material or str or int
Depletable material
component : str
Element or nuclide to get transfer rate value
Returns
-------
transfer_rate : list of floats
Transfer rate values
"""
material_id = self._get_material_id(material)
check_type('component', component, str)
return [i[0] for i in self.transfer_rates[material_id][component]]
[docs] def get_destination_material(self, material, component):
"""Return destination material for given material and
component, if defined.
Parameters
----------
material : openmc.Material or str or int
Depletable material
component : str
Element or nuclide that gets transferred to another material.
Returns
-------
destination_material_id : list of str
Depletable material ID to where the element or nuclide gets
transferred
"""
material_id = self._get_material_id(material)
check_type('component', component, str)
if component in self.transfer_rates[material_id]:
return [i[1] for i in self.transfer_rates[material_id][component]]
else:
return []
[docs] def get_components(self, material):
"""Extract removing elements and/or nuclides for a given material
Parameters
----------
material : openmc.Material or str or int
Depletable material
Returns
-------
elements : list
List of elements and nuclides where transfer rates exist
"""
material_id = self._get_material_id(material)
if material_id in self.transfer_rates:
return self.transfer_rates[material_id].keys()
[docs] def set_transfer_rate(self, material, components, transfer_rate,
transfer_rate_units='1/s', destination_material=None):
"""Set element and/or nuclide transfer rates in a depletable material.
Parameters
----------
material : openmc.Material or str or int
Depletable material
components : list of str
List of strings of elements and/or nuclides that share transfer rate.
Cannot add transfer rates for nuclides to a material where a
transfer rate for its element is specified and vice versa.
transfer_rate : float
Rate at which elements and/or nuclides are transferred. A positive or
negative value corresponds to a removal or feed rate, respectively.
destination_material : openmc.Material or str or int, Optional
Destination material to where nuclides get fed.
transfer_rate_units : {'1/s', '1/min', '1/h', '1/d', '1/a'}
Units for values specified in the transfer_rate argument. 's' for
seconds, 'min' for minutes, 'h' for hours, 'a' for Julian years.
"""
material_id = self._get_material_id(material)
check_type('transfer_rate', transfer_rate, Real)
check_type('components', components, list, expected_iter_type=str)
if destination_material is not None:
destination_material_id = self._get_material_id(destination_material)
if len(self.burnable_mats) > 1:
check_value('destination_material', str(destination_material_id),
self.burnable_mats)
else:
raise ValueError('Transfer to material '
f'{destination_material_id} is set, but there '
'is only one depletable material')
else:
destination_material_id = None
if transfer_rate_units in ('1/s', '1/sec'):
unit_conv = 1
elif transfer_rate_units in ('1/min', '1/minute'):
unit_conv = 60
elif transfer_rate_units in ('1/h', '1/hr', '1/hour'):
unit_conv = 60*60
elif transfer_rate_units in ('1/d', '1/day'):
unit_conv = 24*60*60
elif transfer_rate_units in ('1/a', '1/year'):
unit_conv = 365.25*24*60*60
else:
raise ValueError('Invalid transfer rate unit '
f'"{transfer_rate_units}"')
for component in components:
current_components = self.transfer_rates[material_id].keys()
split_component = re.split(r'\d+', component)
element = split_component[0]
if element not in ELEMENT_SYMBOL.values():
raise ValueError(f'{component} is not a valid nuclide or '
'element.')
else:
if len(split_component) == 1:
element_nucs = [c for c in current_components
if re.match(component + r'\d', c)]
if len(element_nucs) > 0:
nuc_str = ", ".join(element_nucs)
raise ValueError('Cannot add transfer rate for element '
f'{component} to material {material_id} '
f'with transfer rate(s) for nuclide(s) '
f'{nuc_str}.')
else:
if element in current_components:
raise ValueError('Cannot add transfer rate for nuclide '
f'{component} to material {material_id} '
f'where element {element} already has '
'a transfer rate.')
if component in self.transfer_rates[material_id]:
self.transfer_rates[material_id][component].append(
(transfer_rate / unit_conv, destination_material_id))
else:
self.transfer_rates[material_id][component] = [
(transfer_rate / unit_conv, destination_material_id)]
if destination_material_id is not None:
self.index_transfer.add((destination_material_id, material_id))