From 023f9e46900629ff36d6b13e33b39dda60846072 Mon Sep 17 00:00:00 2001 From: Mikolaj Adam Kowalski Date: Tue, 3 Mar 2020 19:02:54 +0000 Subject: [PATCH] Add support for 'distribmat' cells to Cell.atoms Provide missing atoms entry in Cell Attributes Create docstring for Cell.atoms --- openmc/cell.py | 49 ++++++++++++++++++++++++++++----- tests/unit_tests/test_cell.py | 52 +++++++++++++++++++++++------------ 2 files changed, 77 insertions(+), 24 deletions(-) diff --git a/openmc/cell.py b/openmc/cell.py index 67c224de5b..f2884e494b 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -86,7 +86,12 @@ class Cell(IDManagerMixin): volume : float Volume of the cell in cm^3. This can either be set manually or calculated in a stochastic volume calculation and added via the - :meth:`Cell.add_volume_information` method. + :meth:`Cell.add_volume_information` method. For 'distribmat' cells + it is a total volume of all instances. + atoms : dict + Mapping of nuclides to total number of atoms for each nuclide present + in the cell, or all its instances for 'sdistribmat' fill. For example, + {'U235': 1.0e22, 'U238': 5.0e22, ...}. """ @@ -189,6 +194,21 @@ class Cell(IDManagerMixin): @property def atoms(self): + """ Get total number of atoms of each nuclide in the cell + + Returns + ------- + atoms: collections.OrderedDict + Dictionary which keys are nuclides and values the number of atoms. + For example, {'H1':1.0e22, 'O16':0.5e22, ...} + + Raises + ------ + ValueError + If total volume of the cell is not set + ValueError + If cell is filled with Universe, Lattice or Void + """ if self._atoms is None: if self._volume is None: msg = ('Cannot calculate atoms content becouse no volume ' @@ -201,10 +221,10 @@ class Cell(IDManagerMixin): 'Material must be set to calculate atoms content.') raise ValueError(msg) - elif self.fill_type != 'material': - msg = ('Universe, Lattice and Distributed Material cells can ' - 'contain multiple materials. Atoms content must be ' - 'calculated with stochastic volume calculation') + elif self.fill_type in ['lattice', 'universe']: + msg = ('Universe and Lattice cells can contain multiple ' + 'materials in diffrent proportions. Atoms content must ' + 'be calculated with stochastic volume calculation') raise ValueError(msg) elif self.fill_type == 'material': @@ -213,8 +233,23 @@ class Cell(IDManagerMixin): # Convert to total number of atoms for key, nuclide in self._atoms.items(): - atom_num = nuclide[1] * self._volume * 1.0E+24 - self._atoms[key] = (nuclide[0], atom_num) + atom = nuclide[1] * self._volume * 1.0e+24 + self._atoms[key] = atom + + elif self.fill_type == 'distribmat': + # Assumes that volume is total volume of all instances + # Also assumes that all instances have the same volume + partial_volume = self.volume / len(self.fill) + self._atoms = OrderedDict() + for mat in self.fill: + for key, nuclide in mat.get_nuclide_atom_densities().items(): + # To account for overlap of nuclides between distribmat + # we need to append new atoms # to any existing value + # hence it is necessary to ask for default. + atom = self._atoms.setdefault(key, 0) + atom += nuclide[1] * partial_volume * 1.0e+24 + self._atoms[key] = atom + else: msg = 'Unrecognised fill_type:{}'.format(self.fill_type) raise ValueError(msg) diff --git a/tests/unit_tests/test_cell.py b/tests/unit_tests/test_cell.py index 03b03c6e98..d682c29c79 100644 --- a/tests/unit_tests/test_cell.py +++ b/tests/unit_tests/test_cell.py @@ -144,7 +144,7 @@ def test_volume_setting(): c.volume = u.ufloat(-0.05, 0.1) -def test_atoms_of_material_cell(uo2, water): +def test_atoms_material_cell(uo2, water): """ Test if correct number of atoms is returned. Also check if Cell.atoms still works after volume/material was changed """ @@ -153,12 +153,12 @@ def test_atoms_of_material_cell(uo2, water): expected_nucs = ['U235', 'O16'] # Precalculate the expected number of atoms - molarMass = ((atomic_mass('U235') + 2 * atomic_mass('O16'))/3) + M = ((atomic_mass('U235') + 2 * atomic_mass('O16'))/3) expected_atoms = list() - expected_atoms.append(1/3 * uo2.density/molarMass * AVOGADRO * 2.0) # U235 - expected_atoms.append(2/3 * uo2.density/molarMass * AVOGADRO * 2.0) # O16 + expected_atoms.append(1/3 * uo2.density/M * AVOGADRO * 2.0) # U235 + expected_atoms.append(2/3 * uo2.density/M * AVOGADRO * 2.0) # O16 - tuples = list(c.atoms.values()) + tuples = list(c.atoms.items()) for nuc, atom_num, t in zip(expected_nucs, expected_atoms, tuples): assert nuc == t[0] assert atom_num == t[1] @@ -166,10 +166,10 @@ def test_atoms_of_material_cell(uo2, water): # Change volume and check if OK c.volume = 3.0 expected_atoms = list() - expected_atoms.append(1/3 * uo2.density/molarMass * AVOGADRO * 3.0) # U235 - expected_atoms.append(2/3 * uo2.density/molarMass * AVOGADRO * 3.0) # O16 + expected_atoms.append(1/3 * uo2.density/M * AVOGADRO * 3.0) # U235 + expected_atoms.append(2/3 * uo2.density/M * AVOGADRO * 3.0) # O16 - tuples = list(c.atoms.values()) + tuples = list(c.atoms.items()) for nuc, atom_num, t in zip(expected_nucs, expected_atoms, tuples): assert nuc == t[0] assert atom_num == pytest.approx(t[1], rel=TOL) @@ -177,12 +177,35 @@ def test_atoms_of_material_cell(uo2, water): # Change material and check if OK c.fill = water expected_nucs = ['H1', 'O16'] - molarMass = ((2 * atomic_mass('H1') + atomic_mass('O16'))/3) + M = ((2 * atomic_mass('H1') + atomic_mass('O16'))/3) expected_atoms = list() - expected_atoms.append(2/3 * water.density/molarMass * AVOGADRO * 3.0) # H1 - expected_atoms.append(1/3 * water.density/molarMass * AVOGADRO * 3.0) # O16 + expected_atoms.append(2/3 * water.density/M * AVOGADRO * 3.0) # H1 + expected_atoms.append(1/3 * water.density/M * AVOGADRO * 3.0) # O16 - tuples = list(c.atoms.values()) + tuples = list(c.atoms.items()) + for nuc, atom_num, t in zip(expected_nucs, expected_atoms, tuples): + assert nuc == t[0] + assert atom_num == pytest.approx(t[1], rel=TOL) + + +def test_atoms_distribmat_cell(uo2, water): + """ Test if correct number of atoms is returned for a cell with + 'distribmat' fill + """ + c = openmc.Cell(fill=[uo2, water]) + c.volume = 6.0 + + # Calculate the expected number of atoms + expected_nucs = ['U235', 'O16', 'H1'] + M_uo2 = ((atomic_mass('U235') + 2 * atomic_mass('O16'))/3) + M_water = ((2 * atomic_mass('H1') + atomic_mass('O16'))/3) + expected_atoms = list() + expected_atoms.append(1/3 * uo2.density/M_uo2 * AVOGADRO * 3.0) # U235 + expected_atoms.append(2/3 * uo2.density/M_uo2 * AVOGADRO * 3.0 + + 1/3 * water.density/M_water * AVOGADRO * 3.0) # O16 + expected_atoms.append(2/3 * water.density/M_water * AVOGADRO * 3.0) # H1 + + tuples = list(c.atoms.items()) for nuc, atom_num, t in zip(expected_nucs, expected_atoms, tuples): assert nuc == t[0] assert atom_num == pytest.approx(t[1], rel=TOL) @@ -191,11 +214,6 @@ def test_atoms_of_material_cell(uo2, water): def test_atoms_errors(cell_with_lattice): cells, mats, univ, lattice = cell_with_lattice - # Distributed Material - with pytest.raises(ValueError): - cells[0].volume = 2 - cells[0].atoms - # Material Cell with no Volume with pytest.raises(ValueError): cells[1].atoms