Add methods on Material to get mass (if volume specified)

This commit is contained in:
Paul Romano 2018-02-22 16:00:32 -06:00
parent 92697ec06e
commit 7622a1a394
6 changed files with 110 additions and 13 deletions

View file

@ -36,6 +36,7 @@ Core Functions
openmc.data.thin
openmc.data.water_density
openmc.data.write_compact_458_library
openmc.data.zam
Angle-Energy Distributions
--------------------------

View file

@ -313,6 +313,26 @@ def water_density(temperature, pressure=0.1013):
return coeff / pi / gamma1_pi
def zam(name):
"""Return tuple of (atomic number, mass number, metastable state)
Parameters
----------
name : str
Name of nuclide using GND convention, e.g., 'Am242m1'
Returns
-------
3-tuple of int
Atomic number, mass number, and metastable state
"""
symbol, A, state = re.match(r'([A-Zn][a-z]*)(\d+)((?:_[em]\d+)?)',
name).groups()
metastable = int(state[2:]) if state else 0
return (ATOMIC_NUMBER[symbol], int(A), metastable)
# Values here are from the Committee on Data for Science and Technology
# (CODATA) 2014 recommendation (doi:10.1103/RevModPhys.88.035009).

View file

@ -40,15 +40,6 @@ _REACTIONS = [
]
def _get_zai(s):
"""Get ZAI value (10000*z + 10*A + metastable state) for sorting purposes"""
symbol, A, state = re.match(r'([A-Zn][a-z]*)(\d+)((?:_[em]\d+)?)', s).groups()
Z = openmc.data.ATOMIC_NUMBER[symbol]
A = int(A)
state = int(state[2:]) if state else 0
return 10000*Z + 10*A + state
def replace_missing(product, decay_data):
"""Replace missing product with suitable decay daughter.
@ -197,7 +188,7 @@ class Chain(object):
missing_fpy = []
missing_fp = []
for idx, parent in enumerate(sorted(decay_data, key=_get_zai)):
for idx, parent in enumerate(sorted(decay_data, key=openmc.data.zam)):
data = decay_data[parent]
nuclide = Nuclide()
@ -290,7 +281,7 @@ class Chain(object):
missing_fp.append((parent, E, yield_replace))
nuclide.yield_data[E] = []
for k in sorted(yields, key=_get_zai):
for k in sorted(yields, key=openmc.data.zam):
nuclide.yield_data[E].append((k, yields[k]))
# Display warnings

View file

@ -52,8 +52,7 @@ class Material(IDManagerMixin):
'atom/b-cm', 'atom/cm3', 'sum', or 'macro'. The 'macro' unit only
applies in the case of a multi-group calculation.
depletable : bool
Indicate whether the material is depletable. This attribute can be used
by downstream depletion applications.
Indicate whether the material is depletable.
nuclides : list of tuple
List in which each item is a 3-tuple consisting of a nuclide string, the
percent density, and the percent type ('ao' or 'wo').
@ -74,6 +73,9 @@ class Material(IDManagerMixin):
:meth:`Geometry.determine_paths` method.
num_instances : int
The number of instances of this material throughout the geometry.
fissionable_mass : float
Mass of fissionable nuclides in the material in [g]. Requires that the
:attr:`volume` attribute is set.
"""
@ -245,6 +247,18 @@ class Material(IDManagerMixin):
str)
self._isotropic = list(isotropic)
@property
def fissionable_mass(self):
if self.volume is None:
raise ValueError("Volume must be set in order to determine mass.")
density = 0.0
for nuc, atoms_per_cc in self.get_nuclide_atom_densities().values():
Z = openmc.data.zam(nuc)[0]
if Z >= 90:
density += 1e24 * atoms_per_cc * openmc.data.atomic_mass(nuc) \
/ openmc.data.AVOGADRO
return density*self.volume
@classmethod
def from_hdf5(cls, group):
"""Create material from HDF5 group
@ -687,7 +701,53 @@ class Material(IDManagerMixin):
return nuclides
def get_mass_density(self, nuclide=None):
"""Return mass density of one or all nuclides
Parameters
----------
nuclides : str, optional
Nuclide for which density is desired. If not specified, the density
for the entire material is given.
Returns
-------
float
Density of the nuclide/material in [g/cm^3]
"""
mass_density = 0.0
for nuc, atoms_per_cc in self.get_nuclide_atom_densities().values():
density_i = 1e24 * atoms_per_cc * openmc.data.atomic_mass(nuc) \
/ openmc.data.AVOGADRO
if nuclide is None or nuclide == nuc:
mass_density += density_i
return mass_density
def get_mass(self, nuclide=None):
"""Return mass of one or all nuclides.
Note that this method requires that the :attr:`Material.volume` has
already been set.
Parameters
----------
nuclides : str, optional
Nuclide for which mass is desired. If not specified, the density
for the entire material is given.
Returns
-------
float
Mass of the nuclide/material in [g]
"""
if self.volume is None:
raise ValueError("Volume must be set in order to determine mass.")
return self.volume*self.get_mass_density(nuclide)
def clone(self, memo=None):
"""Create a copy of this material with a new unique ID.
Parameters

View file

@ -58,3 +58,11 @@ def test_water_density():
assert dens(300.0, 3.0) == pytest.approx(1e-3/0.100215168e-2, 1e-6)
assert dens(300.0, 80.0) == pytest.approx(1e-3/0.971180894e-3, 1e-6)
assert dens(500.0, 3.0) == pytest.approx(1e-3/0.120241800e-2, 1e-6)
def test_zam():
assert openmc.data.zam('H1') == (1, 1, 0)
assert openmc.data.zam('Zr90') == (40, 90, 0)
assert openmc.data.zam('Am242') == (95, 242, 0)
assert openmc.data.zam('Am242_m1') == (95, 242, 1)
assert openmc.data.zam('Am242_m10') == (95, 242, 10)

View file

@ -121,6 +121,23 @@ def test_get_nuclide_atom_densities(uo2):
assert density > 0
def test_mass():
m = openmc.Material()
m.add_nuclide('Zr90', 1.0, 'wo')
m.add_nuclide('U235', 1.0, 'wo')
m.set_density('g/cm3', 2.0)
m.volume = 10.0
assert m.get_mass_density('Zr90') == pytest.approx(1.0)
assert m.get_mass_density('U235') == pytest.approx(1.0)
assert m.get_mass_density() == pytest.approx(2.0)
assert m.get_mass('Zr90') == pytest.approx(10.0)
assert m.get_mass('U235') == pytest.approx(10.0)
assert m.get_mass() == pytest.approx(20.0)
assert m.fissionable_mass == pytest.approx(10.0)
def test_materials(run_in_tmpdir):
m1 = openmc.Material()
m1.add_nuclide('U235', 1.0, 'wo')