From 60f89794f684a30a5aed43868deef8e8b45fd847 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 21 Aug 2019 10:55:43 -0500 Subject: [PATCH] Use dictionary to create FissionYieldDistribution Removes the FissionYieldDistribution.from_dict method as the most natural way to create such a distribution is with a dictionary. --- openmc/deplete/chain.py | 13 ++- openmc/deplete/nuclide.py | 79 ++++++------------- tests/unit_tests/test_deplete_chain.py | 2 +- .../unit_tests/test_deplete_fission_yields.py | 4 +- tests/unit_tests/test_deplete_nuclide.py | 6 +- 5 files changed, 38 insertions(+), 66 deletions(-) diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index d590cd8d2..ba959a3fc 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -10,7 +10,6 @@ import math import re from collections import OrderedDict, defaultdict from collections.abc import Mapping, Iterable -from numbers import Real from warnings import warn from openmc.checkvalue import check_type, check_less_than @@ -127,8 +126,8 @@ class Chain(object): fission_yields : None or iterable of dict List of effective fission yields for materials. Each dictionary should be of the form ``{parent: {product: yield}}`` with - types ``{str: {str: float}}``, where ``yield`` is the fission product yield - for isotope ``parent`` producing isotope ``product``. + types ``{str: {str: float}}``, where ``yield`` is the fission product + yield for isotope ``parent`` producing isotope ``product``. A single entry indicates yields are constant across all materials. Otherwise, an entry can be added for each material to be burned. Ordering should be identical to how the operator orders reaction @@ -222,7 +221,8 @@ class Chain(object): if mode.daughter in decay_data: target = mode.daughter else: - print('missing {} {} {}'.format(parent, ','.join(mode.modes), mode.daughter)) + print('missing {} {} {}'.format( + parent, ','.join(mode.modes), mode.daughter)) target = replace_missing(mode.daughter, decay_data) # Write branching ratio, taking care to ensure sum is unity @@ -285,7 +285,7 @@ class Chain(object): yield_replace = 0.0 yields = defaultdict(float) for product, y in table.items(): - # Handle fission products that have no decay data available + # Handle fission products that have no decay data if product not in decay_data: daughter = replace_missing(product, decay_data) product = daughter @@ -297,8 +297,7 @@ class Chain(object): missing_fp.append((parent, E, yield_replace)) yield_data[E] = yields - nuclide.yield_data = ( - FissionYieldDistribution.from_dict(yield_data)) + nuclide.yield_data = FissionYieldDistribution(yield_data) # Display warnings if missing_daughter: diff --git a/openmc/deplete/nuclide.py b/openmc/deplete/nuclide.py index d4f501d44..a3c60019e 100644 --- a/openmc/deplete/nuclide.py +++ b/openmc/deplete/nuclide.py @@ -3,16 +3,15 @@ Contains the per-nuclide components of a depletion chain. """ -from numbers import Real from collections import namedtuple -from collections.abc import Iterable, Mapping +from collections.abc import Mapping try: import lxml.etree as ET except ImportError: import xml.etree.ElementTree as ET -from numpy import asarray, empty +from numpy import empty from openmc.checkvalue import check_type @@ -132,7 +131,7 @@ class Nuclide(object): 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) + self._yield_data = FissionYieldDistribution(fission_yields) @property def yield_energies(self): @@ -245,7 +244,7 @@ class FissionYieldDistribution(Mapping): Can be used as a dictionary mapping energies and products to fission yields:: - >>> fydist = FissionYieldDistribution.from_dict({ + >>> fydist = FissionYieldDistribution{ ... {0.0253: {"Xe135": 0.021}}) >>> fydist[0.0253]["Xe135"] 0.021 @@ -266,11 +265,10 @@ class FissionYieldDistribution(Mapping): Attributes ---------- energies : tuple - Energies for which fission yields exist. Converted for - indexing + Energies for which fission yields exist. Sorted by + increasing energy products : tuple - Fission products produced at all energies. Converted - for indexing + Fission products produced at all energies. Sorted by name. yield_matrix : numpy.ndarray Array ``(n_energy, n_products)`` where ``yield_matrix[g, j]`` is the fission yield of product @@ -278,22 +276,28 @@ class FissionYieldDistribution(Mapping): See Also -------- - * :meth:`from_xml_element`, :meth:`from_dict` - Construction methods + * :meth:`from_xml_element` - Construction methods * :class:`FissionYield` - Class used for storing yields at a given energy """ - def __init__(self, energies, products, yield_matrix): - check_type("energies", energies, Iterable, Real) + def __init__(self, fission_yields): + # mapping {energy: {product: value}} + energies = sorted(fission_yields) + + # Get a consistent set of products to produce a matrix of yields + shared_prod = set.union(*(set(x) for x in fission_yields.values())) + ordered_prod = sorted(shared_prod) + + yield_matrix = empty((len(energies), len(shared_prod))) + + for g_index, energy in enumerate(energies): + prod_map = fission_yields[energy] + for prod_ix, product in enumerate(ordered_prod): + yield_val = prod_map.get(product) + yield_matrix[g_index, prod_ix] = ( + 0.0 if yield_val is None else yield_val) self.energies = tuple(energies) - check_type("products", products, Iterable, str) - self.products = tuple(products) - yield_matrix = asarray(yield_matrix, dtype=float) - if yield_matrix.shape != (len(self.energies), len(self.products)): - raise ValueError( - "Shape of yield matrix inconsistent. " - "Should be ({}, {}), is {}".format( - len(energies), len(products), - yield_matrix.shape)) + self.products = tuple(ordered_prod) self.yield_matrix = yield_matrix def __len__(self): @@ -329,38 +333,7 @@ class FissionYieldDistribution(Mapping): # Get a map of products to their corresponding yield all_yields[energy] = dict(zip(products, yields)) - return cls.from_dict(all_yields) - - @classmethod - def from_dict(cls, fission_yields): - """Construct a distribution from a dictionary of yields - - Parameters - ----------- - fission_yields : dict - Dictionary ``{energy: {product: yield}}`` - - Returns - ------- - FissionYieldDistribution - """ - # mapping {energy: {product: value}} - energies = sorted(fission_yields) - - # Get a consistent set of products to produce a matrix of yields - shared_prod = set.union(*(set(x) for x in fission_yields.values())) - ordered_prod = sorted(shared_prod) - - yield_matrix = empty((len(energies), len(shared_prod))) - - for g_index, energy in enumerate(energies): - prod_map = fission_yields[energy] - for prod_ix, product in enumerate(ordered_prod): - yield_val = prod_map.get(product) - yield_matrix[g_index, prod_ix] = ( - 0.0 if yield_val is None else yield_val) - - return cls(energies, ordered_prod, yield_matrix) + return cls(all_yields) def to_xml_element(self, root): """Write fission yield data to an xml element diff --git a/tests/unit_tests/test_deplete_chain.py b/tests/unit_tests/test_deplete_chain.py index cf6414b5c..b1ab1ae24 100644 --- a/tests/unit_tests/test_deplete_chain.py +++ b/tests/unit_tests/test_deplete_chain.py @@ -160,7 +160,7 @@ 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_data = nuclide.FissionYieldDistribution.from_dict({ + C.yield_data = nuclide.FissionYieldDistribution({ 0.0253: {"A": 0.0292737, "B": 0.002566345}}) chain = Chain() diff --git a/tests/unit_tests/test_deplete_fission_yields.py b/tests/unit_tests/test_deplete_fission_yields.py index 4b71eba69..81c3e2d42 100644 --- a/tests/unit_tests/test_deplete_fission_yields.py +++ b/tests/unit_tests/test_deplete_fission_yields.py @@ -21,11 +21,11 @@ def nuclide_bundle(): 5.0e5: {"Xe135": 7.85e-4, "Sm149": 1.71e-12}, 1.40e7: {"Xe135": 4.54e-3, "Gd155": 5.83e-8}} u235 = Nuclide("U235") - u235.yield_data = FissionYieldDistribution.from_dict(u5yield_dict) + u235.yield_data = FissionYieldDistribution(u5yield_dict) u8yield_dict = {5.00e5: {"Xe135": 1.12e-3, "Gd155": 1.32e-12}} u238 = Nuclide("U238") - u238.yield_data = FissionYieldDistribution.from_dict(u8yield_dict) + u238.yield_data = FissionYieldDistribution(u8yield_dict) xe135 = Nuclide("Xe135") diff --git a/tests/unit_tests/test_deplete_nuclide.py b/tests/unit_tests/test_deplete_nuclide.py index 4310478c9..3386e2898 100644 --- a/tests/unit_tests/test_deplete_nuclide.py +++ b/tests/unit_tests/test_deplete_nuclide.py @@ -71,7 +71,7 @@ def test_from_xml(): nuclide.ReactionTuple('(n,gamma)', 'U236', 6545200.0, 1.0), nuclide.ReactionTuple('fission', None, 193405400.0, 1.0), ] - expected_yield_data = nuclide.FissionYieldDistribution.from_dict({ + expected_yield_data = nuclide.FissionYieldDistribution({ 0.0253: {"Xe138": 0.0481413, "Zr100": 0.0497641, "Te134": 0.062155}}) assert u235.yield_data == expected_yield_data # test accessing the yield energies through the FissionYieldDistribution @@ -94,7 +94,7 @@ def test_to_xml_element(): nuclide.ReactionTuple('fission', None, 2.0e8, 1.0), nuclide.ReactionTuple('(n,gamma)', 'A', 0.0, 1.0) ] - C.yield_data = nuclide.FissionYieldDistribution.from_dict( + C.yield_data = nuclide.FissionYieldDistribution( {0.0253: {"A": 0.0292737, "B": 0.002566345}}) element = C.to_xml_element() @@ -127,7 +127,7 @@ def test_fission_yield_distribution(): 1.40e7: {"Xe135": 4.54e-3, "Gd155": 5.83e-8, "Sm149": 2.69e-8}, 5.00e5: {"Xe135": 1.12e-3, "Gd155": 1.32e-12}, # drop Sm149 } - yield_dist = nuclide.FissionYieldDistribution.from_dict(yield_dict) + yield_dist = nuclide.FissionYieldDistribution(yield_dict) assert len(yield_dist) == len(yield_dict) assert yield_dist.energies == tuple(sorted(yield_dict.keys())) for exp_ene, exp_dist in yield_dict.items():