From f2fa86fd5264432c89907c40109bbbb429746baf Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 14 Aug 2019 16:09:41 -0500 Subject: [PATCH] Delegate Nuclide yield properties to FissionYieldDistribution For consistency across the various fission yield helpers, the Nuclide object now sets the yield data to always be a FissionYieldDistribution or an empty dictionary. Nuclide.yield_energies is now a property that returns the energies from the FissionYieldDistribution. When setting the yield_data, the Nuclide enforces the object to be a Mapping instance. If the incoming yield_data is a FissionYieldDistribution, no conversions are made. Otherwise, a new distribution is created with the from_dict method. The Chain.from_xml method now creates the yield_data for nuclides using FissionYieldDistribution.from_dict Changes were made to some unit tests that attempted to set the yield_energies, which do not have a setter. --- openmc/deplete/chain.py | 14 +++++----- openmc/deplete/nuclide.py | 27 +++++++++++++++---- tests/unit_tests/test_deplete_chain.py | 3 +-- .../unit_tests/test_deplete_fission_yields.py | 2 -- tests/unit_tests/test_deplete_nuclide.py | 8 ++++-- 5 files changed, 37 insertions(+), 17 deletions(-) diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index a204e4821..dd13a338b 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -15,6 +15,7 @@ from warnings import warn from openmc.checkvalue import check_type, check_less_than from openmc.data import gnd_name, zam +from .nuclide import FissionYieldDistribution # Try to use lxml if it is available. It preserves the order of attributes and # provides a pretty-printer by default. If not available, @@ -276,11 +277,12 @@ class Chain(object): fpy = fpy_data[parent] if fpy.energies is not None: - nuclide.yield_energies = fpy.energies + yield_energies = fpy.energies else: - nuclide.yield_energies = [0.0] + yield_energies = [0.0] - for E, table in zip(nuclide.yield_energies, fpy.independent): + yield_data = {} + for E, table in zip(yield_energies, fpy.independent): yield_replace = 0.0 yields = defaultdict(float) for product, y in table.items(): @@ -294,10 +296,10 @@ class Chain(object): if yield_replace > 0.0: missing_fp.append((parent, E, yield_replace)) + yield_data[E] = yields - nuclide.yield_data[E] = [] - for k in sorted(yields, key=openmc.data.zam): - nuclide.yield_data[E].append((k, yields[k])) + nuclide.yield_data = ( + FissionYieldDistribution.from_dict(yield_data)) # Display warnings if missing_daughter: diff --git a/openmc/deplete/nuclide.py b/openmc/deplete/nuclide.py index 5217831f4..649b6700f 100644 --- a/openmc/deplete/nuclide.py +++ b/openmc/deplete/nuclide.py @@ -86,10 +86,10 @@ class Nuclide(object): reactions : list of openmc.deplete.ReactionTuple Reaction information. Each element of the list is a named tuple with attribute 'type', 'target', 'Q', and 'branching_ratio'. - yield_data : dict of float to :class:`openmc.deplete.FissionYieldDistribution` + yield_data : FissionYieldDistribution or None Fission product yields at tabulated energies for this nuclide. Can be treated as a nested dictionary ``{energy: {product: yield}}`` - yield_energies : list of float + yield_energies : tuple of float or None Energies at which fission product yields exist """ @@ -107,8 +107,7 @@ class Nuclide(object): self.reactions = [] # Neutron fission yields, if present - self.yield_data = {} - self.yield_energies = [] + self._yield_data = None @property def n_decay_modes(self): @@ -118,6 +117,25 @@ class Nuclide(object): def n_reaction_paths(self): return len(self.reactions) + @property + def yield_data(self): + if self._yield_data is None: + return {} + return self._yield_data + + @yield_data.setter + def yield_data(self, fission_yields): + check_type("fission_yields", fission_yields, Mapping) + if isinstance(fission_yields, FissionYieldDistribution): + self._yield_data = fission_yields + self._yield_data = FissionYieldDistribution.from_dict(fission_yields) + + @property + def yield_energies(self): + if self._yield_data is None: + return None + return self.yield_data.energies + @classmethod def from_xml(cls, element, fission_q=None): """Read nuclide from an XML element. @@ -173,7 +191,6 @@ class Nuclide(object): fpy_elem = element.find('neutron_fission_yields') if fpy_elem is not None: nuc.yield_data = FissionYieldDistribution.from_xml_element(fpy_elem) - nuc.yield_energies = list(sorted(nuc.yield_data.keys())) return nuc diff --git a/tests/unit_tests/test_deplete_chain.py b/tests/unit_tests/test_deplete_chain.py index dcb5127cb..98ca023f0 100644 --- a/tests/unit_tests/test_deplete_chain.py +++ b/tests/unit_tests/test_deplete_chain.py @@ -129,7 +129,7 @@ def test_from_xml(simple_chain): assert [r.branching_ratio for r in nuc.reactions] == [1.0, 0.7, 0.3] # Yield tests - assert nuc.yield_energies == [0.0253] + assert nuc.yield_energies == (0.0253, ) assert list(nuc.yield_data) == [0.0253] assert nuc.yield_data[0.0253].products == ("A", "B") assert (nuc.yield_data[0.0253].yields == [0.0292737, 0.002566345]).all() @@ -163,7 +163,6 @@ def test_export_to_xml(run_in_tmpdir): nuclide.ReactionTuple("(n,gamma)", "A", 0.0, 0.7), nuclide.ReactionTuple("(n,gamma)", "B", 0.0, 0.3) ] - C.yield_energies = [0.0253] C.yield_data = nuclide.FissionYieldDistribution.from_dict({ 0.0253: {"A": 0.0292737, "B": 0.002566345}}) diff --git a/tests/unit_tests/test_deplete_fission_yields.py b/tests/unit_tests/test_deplete_fission_yields.py index bf0bc6b8f..233074236 100644 --- a/tests/unit_tests/test_deplete_fission_yields.py +++ b/tests/unit_tests/test_deplete_fission_yields.py @@ -19,13 +19,11 @@ def nuclide_bundle(): 1.40e7: {"Xe135": 4.54e-3, "Gd155": 5.83e-8}} u235 = Nuclide() u235.name = "U235" - u235.yield_energies = (0.0253, 5.0e5, 1.40e7) u235.yield_data = FissionYieldDistribution.from_dict(u5yield_dict) u8yield_dict = {5.00e5: {"Xe135": 1.12e-3, "Gd155": 1.32e-12}} u238 = Nuclide() u238.name = "U238" - u238.yield_energies = (5.00e5,) u238.yield_data = FissionYieldDistribution.from_dict(u8yield_dict) xe135 = Nuclide() diff --git a/tests/unit_tests/test_deplete_nuclide.py b/tests/unit_tests/test_deplete_nuclide.py index 2a3993921..ef5c3d1ca 100644 --- a/tests/unit_tests/test_deplete_nuclide.py +++ b/tests/unit_tests/test_deplete_nuclide.py @@ -3,6 +3,7 @@ import xml.etree.ElementTree as ET import numpy +import pytest from openmc.deplete import nuclide @@ -73,8 +74,12 @@ def test_from_xml(): ] expected_yield_data = nuclide.FissionYieldDistribution.from_dict({ 0.0253: {"Xe138": 0.0481413, "Zr100": 0.0497641, "Te134": 0.062155}}) - assert u235.yield_energies == [0.0253] assert u235.yield_data == expected_yield_data + # test accessing the yield energies through the FissionYieldDistribution + assert u235.yield_energies == (0.0253, ) + assert u235.yield_energies is u235.yield_data.energies + with pytest.raises(AttributeError): # not settable + u235.yield_energies = [0.0253, 5e5] def test_to_xml_element(): @@ -91,7 +96,6 @@ def test_to_xml_element(): nuclide.ReactionTuple('fission', None, 2.0e8, 1.0), nuclide.ReactionTuple('(n,gamma)', 'A', 0.0, 1.0) ] - C.yield_energies = [0.0253] C.yield_data = nuclide.FissionYieldDistribution.from_dict( {0.0253: {"A": 0.0292737, "B": 0.002566345}}) element = C.to_xml_element()