diff --git a/docs/source/pythonapi/data.rst b/docs/source/pythonapi/data.rst index 3c221906d..52dc5173b 100644 --- a/docs/source/pythonapi/data.rst +++ b/docs/source/pythonapi/data.rst @@ -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 -------------------------- diff --git a/openmc/data/data.py b/openmc/data/data.py index 523ac9769..70bc00bd9 100644 --- a/openmc/data/data.py +++ b/openmc/data/data.py @@ -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). diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index 23395329e..bcef5fff1 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -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 diff --git a/openmc/material.py b/openmc/material.py index e409d6536..351e2db27 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -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 diff --git a/tests/unit_tests/test_data_misc.py b/tests/unit_tests/test_data_misc.py index 433d34adb..7d00b7bc7 100644 --- a/tests/unit_tests/test_data_misc.py +++ b/tests/unit_tests/test_data_misc.py @@ -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) diff --git a/tests/unit_tests/test_material.py b/tests/unit_tests/test_material.py index 226521541..c251df3a6 100644 --- a/tests/unit_tests/test_material.py +++ b/tests/unit_tests/test_material.py @@ -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')