From c2137187a4b0eb5c6bfaf2278a80c403e6c9bc4b Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 6 Aug 2019 11:48:57 -0500 Subject: [PATCH 01/11] Add Te129 to CASL chain Closes: #1307 This serves as a path way for production in I129 without impacting transport because 1) Te129 has no ground state cross section data, due to 2) Te129 decays to I129 with a half life of about an hour Running scripts/openmc-make-depletion-chain-casl now has the following changes: 1) Te129 is present 2) Te129_m1 decays to Te129 with branching ratio of 2/3 using ENDFB 7.1 data [pulled using tools/ci/download-xs.sh] --- scripts/casl_chain.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/casl_chain.py b/scripts/casl_chain.py index 86389e3ce..2477a5e89 100755 --- a/scripts/casl_chain.py +++ b/scripts/casl_chain.py @@ -187,6 +187,7 @@ CASL_CHAIN = { 'Sb127': (False, 3, 2, None), 'Te127': (False, 3, -1, None), 'Te127_m1': (False, 3, -1, None), + 'Te129': (False, 3, 2, None), 'Te129_m1': (False, 3, 2, None), 'Te132': (False, 3, 2, None), 'I127': (True, 3, 1, None), From ec7af8f376c0c8830bcb878c59b10a9c375297b5 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 8 Aug 2019 15:27:57 -0500 Subject: [PATCH 02/11] Add validation for deplete.Nuclide branching ratios, fission yields Method Nuclide.validate traverses over decay mode and reaction lists, as well as all fission yield data to check for inconsistencies. The following checks are performed: 1) For all non-fission reactions and decay modes, do the sum of branching ratios equal about 1? 2) For fission reactions, do the sum of fission yield fractions equal about 2? Users are allowed to supply three control arguments: - strict: bool that controls if errors are raised [True] or warnings [False] - quiet: bool that controls if warnings are printed [False] or supressed [True] - tolerance: float that provides some wiggle room on the comparisons ``y - tol <= x <= y + tol`` The method returns a boolean if 1) no inconsistencies were found and strict evaluates to True, 2) strict evaluates to False regardless of quiet. If ``strict`` evaluates to False and ``quiet`` evaluates to True, the method will return at the first inconsistency. No type checking is done because this will potentially be called by ``openmc.deplete.Chain`` for many nuclides as the primary entry point. Type and value checking will be done there. A unit test was added that creates a valid nuclide, and then strategically invalidates it to check for the various exceptions and error messages. --- openmc/deplete/nuclide.py | 101 ++++++++++++++++++++++- tests/unit_tests/test_deplete_nuclide.py | 91 ++++++++++++++++++++ 2 files changed, 191 insertions(+), 1 deletion(-) diff --git a/openmc/deplete/nuclide.py b/openmc/deplete/nuclide.py index 88d5ac6ea..9c07a91a9 100644 --- a/openmc/deplete/nuclide.py +++ b/openmc/deplete/nuclide.py @@ -3,7 +3,9 @@ Contains the per-nuclide components of a depletion chain. """ -from collections import namedtuple +from collections import namedtuple, defaultdict +from operator import attrgetter, itemgetter +from warnings import warn try: import lxml.etree as ET except ImportError: @@ -222,3 +224,100 @@ class Nuclide(object): data_elem.text = ' '.join(str(x[1]) for x in self.yield_data[E]) return elem + + def validate(self, strict=True, quiet=False, tolerance=1e-4): + """Search for possible inconsistencies + + The following checks are performed: + + 1) for all non-fission reactions and decay modes, + do the sum of branching ratios equal about 1? + 2) for fission reactions, do the sum of fission yield + fractions equal about 2? + + Parameters + ---------- + strict : bool, optional + Raise exceptions at the first inconsistency if true. + Otherwise mark a warning + quiet : bool, optional + Flag to supress warnings and return immediately at + the first inconsistency. Used only if + ``strict`` does not evaluate to ``True``. + tolerance : float, optional + Absolute tolerance for comparisons. Used to compare computed + value ``x`` to intended value ``y`` as:: + + valid = (y - tolerance <= x <= y + tolerance) + + Returns + ------- + valid : bool + True if no inconsistencies were found + + Raises + ------ + ValueError + If ``strict`` evaluates to ``True`` and an inconistency was + found + """ + + branch_getter = attrgetter("branching_ratio") + msg_func = ("Nuclide {name} has {prop} that sum to {actual} " + "instead of {expected} +/- {tol:7.4e}").format + type_map = defaultdict(set) + valid = True + + # check decay modes + if len(self.decay_modes) > 0: + sum_br = sum(map(branch_getter, self.decay_modes)) + stat = 1.0 - tolerance <= sum_br <= 1.0 + tolerance + if not stat: + msg = msg_func( + name=self.name, actual=sum_br, expected=1.0, tol=tolerance, + prop="decay mode branch ratios") + if strict: + raise ValueError(msg) + elif quiet: + return False + warn(msg) + valid = False + + if len(self.reactions) > 0: + type_map.clear() + for reaction in self.reactions: + type_map[reaction.type].add(reaction) + for rxn_type, reactions in type_map.items(): + sum_rxn = sum(map(branch_getter, reactions)) + stat = 1.0 - tolerance <= sum_rxn <= 1.0 + tolerance + if stat: + continue + msg = msg_func( + name=self.name, actual=sum_br, expected=1.0, tol=tolerance, + prop="{} reaction branch ratios".format(rxn_type)) + if strict: + raise ValueError(msg) + elif quiet: + return False + warn(msg) + valid = False + + if len(self.yield_data) > 0: + yield_getter = itemgetter(1) + for energy, yield_list in self.yield_data.items(): + sum_yield = sum(map(yield_getter, yield_list)) + stat = 2.0 - tolerance <= sum_yield <= 2.0 + tolerance + if stat: + continue + msg = msg_func( + name=self.name, actual=sum_yield, + expected=2.0, tol=tolerance, + prop="fission yields (E = {:7.4e} eV)".format(energy)) + if strict: + raise ValueError(msg) + elif quiet: + return False + warn(msg) + valid = False + + return valid diff --git a/tests/unit_tests/test_deplete_nuclide.py b/tests/unit_tests/test_deplete_nuclide.py index f2a101d2a..a93f6bb4c 100644 --- a/tests/unit_tests/test_deplete_nuclide.py +++ b/tests/unit_tests/test_deplete_nuclide.py @@ -2,6 +2,8 @@ import xml.etree.ElementTree as ET +import pytest + from openmc.deplete import nuclide @@ -114,3 +116,92 @@ def test_to_xml_element(): assert float(rx_elems[1].get("Q")) == 0.0 assert element.find('neutron_fission_yields') is not None + + +def test_validate(): + + nuc = nuclide.Nuclide() + nuc.name = "Test" + + # decay modes: type, target, branching_ratio + + nuc.decay_modes = [ + nuclide.DecayTuple("type 0", "0", 0.5), + nuclide.DecayTuple("type 1", "1", 0.5), + ] + + # reactions: type, target, Q, branching_ratio + nuc.reactions = [ + nuclide.ReactionTuple("0", "0", 1000, 0.3), + nuclide.ReactionTuple("0", "1", 1000, 0.3), + nuclide.ReactionTuple("1", "2", 1000, 1.0), + nuclide.ReactionTuple("0", "3", 1000, 0.4), + ] + + # fission yields + + nuc.yield_data = { + 0.0253: [("0", 1.5), ("1", 0.5)], + 1e6: [("0", 1.5), ("1", 0.5)], + } + + # nuclide is good and should have no warnings raise + with pytest.warns(None) as record: + assert nuc.validate(strict=True, quiet=False, tolerance=0.0) + assert len(record) == 0 + + # invalidate decay modes + decay = nuc.decay_modes.pop() + with pytest.raises(ValueError, match="decay mode"): + nuc.validate(strict=True, quiet=False, tolerance=0.0) + + with pytest.warns(UserWarning) as record: + assert not nuc.validate(strict=False, quiet=False, tolerance=0.0) + assert not nuc.validate(strict=False, quiet=True, tolerance=0.0) + assert len(record) == 1 + assert "decay mode" in record[0].message.args[0] + + # restore decay modes, invalidate reactions + nuc.decay_modes.append(decay) + reaction = nuc.reactions.pop() + + with pytest.raises(ValueError, match="0 reaction"): + nuc.validate(strict=True, quiet=False, tolerance=0.0) + + with pytest.warns(UserWarning) as record: + assert not nuc.validate(strict=False, quiet=False, tolerance=0.0) + assert not nuc.validate(strict=False, quiet=True, tolerance=0.0) + assert len(record) == 1 + assert "0 reaction" in record[0].message.args[0] + + # restore reactions, invalidate fission yields + nuc.reactions.append(reaction) + fission_yields = nuc.yield_data[1e6].pop() + + with pytest.raises(ValueError, match=r"fission yields.*1\.0*e"): + nuc.validate(strict=True, quiet=False, tolerance=0.0) + + with pytest.warns(UserWarning) as record: + assert not nuc.validate(strict=False, quiet=False, tolerance=0.0) + assert not nuc.validate(strict=False, quiet=True, tolerance=0.0) + assert len(record) == 1 + assert "1.0" in record[0].message.args[0] + + # invalidate everything, check that error is raised at decay modes + + decay = nuc.decay_modes.pop() + reaction = nuc.reactions.pop() + + with pytest.raises(ValueError, match="decay mode"): + nuc.validate(strict=True, quiet=False, tolerance=0.0) + + # check for warnings + # should be one warning for decay modes, reactions, fission yields + + with pytest.warns(UserWarning) as record: + assert not nuc.validate(strict=False, quiet=False, tolerance=0.0) + assert not nuc.validate(strict=False, quiet=True, tolerance=0.0) + assert len(record) == 3 + assert "decay mode" in record[0].message.args[0] + assert "0 reaction" in record[1].message.args[0] + assert "1.0" in record[2].message.args[0] From 6da0b3ec5758a74abddc30ce25512646caf9362f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 8 Aug 2019 16:42:14 -0500 Subject: [PATCH 03/11] Add openmc.deplete.Chain.validate method Closes #1308 by providing a way to validate the contents of the depletion chain. This method iterates over all the nuclides and calls their validation method with the same input arguments, (strict, quiet, and tolerance). For the case where strict == False, quiet == False, and a nuclide fails the validation, the method returns early rather than continuing to iterate over all other nuclides. Type and value checking are also done on the tolerance argument. Two tests were added to test_deplete_chain.py The more interesting test works with the simple chain and various manipulations to check the robustness of the method. The other just checks the type and value checking on tolerance. --- openmc/deplete/chain.py | 53 +++++++++++++++++++++++++- openmc/deplete/nuclide.py | 4 ++ tests/unit_tests/test_deplete_chain.py | 52 +++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 1 deletion(-) diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index 1f16d9caf..d2ccf227c 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -10,9 +10,10 @@ import math import re from collections import OrderedDict, defaultdict from collections.abc import Mapping +from numbers import Real from warnings import warn -from openmc.checkvalue import check_type, check_less_than +from openmc.checkvalue import check_type, check_less_than, check_greater_than from openmc.data import gnd_name, zam # Try to use lxml if it is available. It preserves the order of attributes and @@ -613,3 +614,53 @@ class Chain(object): new_ratios[ground_tgt] = ground_br parent.reactions.append(ReactionTuple( "(n,gamma)", ground_tgt, capt_Q, ground_br)) + + def validate(self, strict=True, quiet=False, tolerance=1e-4): + """Search for possible inconsistencies + + The following checks are performed for all nuclides present: + + 1) For all non-fission reactions, do the sum of branching + ratios equal about 1? + 2) For fission reactions, do the sum of fission yield + fractions equal about 2? + + Parameters + ---------- + strict : bool, optional + Raise exceptions at the first inconsistency if true. + Otherwise mark a warning + quiet : bool, optional + Flag to supress warnings and return immediately at + the first inconsistency. Used only if + ``strict`` does not evaluate to ``True``. + tolerance : float, optional + Absolute tolerance for comparisons. Used to compare computed + value ``x`` to intended value ``y`` as:: + + valid = (y - tolerance <= x <= y + tolerance) + Returns + ------- + valid : bool + True if no inconsistencies were found + + Raises + ------ + ValueError + If ``strict`` evaluates to ``True`` and an inconistency was + found + + See Also + -------- + openmc.deplete.Nuclide.validate + """ + check_type("tolerance", tolerance, Real) + check_greater_than("tolerance", tolerance, 0.0, True) + valid = True + # Sort through nuclides by name + for name in sorted(self.nuclide_dict): + stat = self[name].validate(strict, quiet, tolerance) + if quiet and not stat: + return stat + valid &= stat + return valid diff --git a/openmc/deplete/nuclide.py b/openmc/deplete/nuclide.py index 9c07a91a9..63503cbfb 100644 --- a/openmc/deplete/nuclide.py +++ b/openmc/deplete/nuclide.py @@ -260,6 +260,10 @@ class Nuclide(object): ValueError If ``strict`` evaluates to ``True`` and an inconistency was found + + See Also + -------- + openmc.deplete.Chain.validate """ branch_getter = attrgetter("branching_ratio") diff --git a/tests/unit_tests/test_deplete_chain.py b/tests/unit_tests/test_deplete_chain.py index 33b13b596..294e56637 100644 --- a/tests/unit_tests/test_deplete_chain.py +++ b/tests/unit_tests/test_deplete_chain.py @@ -329,3 +329,55 @@ def test_capture_branch_failures(simple_chain): br = {"C": {"A": 1.0, "B": 1.0}} with pytest.raises(ValueError, match="C ratios"): simple_chain.set_capture_branches(br) + + +def test_validate(simple_chain): + """Test the validate method""" + + # current chain is invalid + # fission yields do not sum to 2.0 + with pytest.raises(ValueError, match="Nuclide C.*fission yields"): + simple_chain.validate(strict=True, tolerance=0.0) + + with pytest.warns(UserWarning) as record: + assert not simple_chain.validate(strict=False, quiet=False, tolerance=0.0) + assert not simple_chain.validate(strict=False, quiet=True, tolerance=0.0) + assert len(record) == 1 + assert "Nuclide C" in record[0].message.args[0] + + # Fix fission yields but keep to restore later + old_yields = simple_chain["C"].yield_data + simple_chain["C"].yield_data = {0.0253: [("A", 1.4), ("B", 0.6)]} + + assert simple_chain.validate(strict=True, tolerance=0.0) + with pytest.warns(None) as record: + assert simple_chain.validate(strict=False, quiet=False, tolerance=0.0) + assert len(record) == 0 + + # Mess up "earlier" nuclide's reactions + decay_mode = simple_chain["A"].decay_modes.pop() + + with pytest.raises(ValueError, match="Nuclide A.*decay mode"): + simple_chain.validate(strict=True, tolerance=0.0) + + # restore old fission yields + simple_chain["C"].yield_data = old_yields + + with pytest.warns(UserWarning) as record: + assert not simple_chain.validate(strict=False, quiet=False, tolerance=0.0) + assert len(record) == 2 + assert "Nuclide A" in record[0].message.args[0] + assert "Nuclide C" in record[1].message.args[0] + + # restore decay modes + simple_chain["A"].decay_modes.append(decay_mode) + + +def test_validate_inputs(): + c = Chain() + + with pytest.raises(TypeError, match="tolerance"): + c.validate(tolerance=None) + + with pytest.raises(ValueError, match="tolerance"): + c.validate(tolerance=-1) From 58313c4ed8c6c6008f38ccdc8598507834c375aa Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 8 Aug 2019 16:43:32 -0500 Subject: [PATCH 04/11] Check for downloaded ENDF data in openmc-make-depletion-chain Mirror to openmc-make-depletion-chain-casl, but uses Path objects --- scripts/openmc-make-depletion-chain | 32 +++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/scripts/openmc-make-depletion-chain b/scripts/openmc-make-depletion-chain index 7c08f984e..8627e62ec 100755 --- a/scripts/openmc-make-depletion-chain +++ b/scripts/openmc-make-depletion-chain @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -import glob import os +from pathlib import Path from zipfile import ZipFile from openmc._utils import download @@ -15,15 +15,29 @@ URLS = [ ] def main(): - for url in URLS: - basename = download(url) - with ZipFile(basename, 'r') as zf: - print('Extracting {}...'.format(basename)) - zf.extractall() + endf_dir = os.environ.get("OPENMC_ENDF_DATA") + if endf_dir is not None: + endf_dir = Path(endf_dir) + elif all(os.path.isdir(lib) for lib in ("neutrons", "decay", "nfy")): + endf_dir = Path(".") + else: + for url in URLS: + basename = download(url) + with ZipFile(basename, 'r') as zf: + print('Extracting {}...'.format(basename)) + zf.extractall() + endf_dir = Path(".") - decay_files = glob.glob(os.path.join('decay', '*.endf')) - nfy_files = glob.glob(os.path.join('nfy', '*.endf')) - neutron_files = glob.glob(os.path.join('neutrons', '*.endf')) + decay_files = tuple((endf_dir / "decay").glob("*endf")) + neutron_files = tuple((endf_dir / "neutrons").glob("*endf")) + nfy_files = tuple((endf_dir / "nfy").glob("*endf")) + + # check files exist + for flist, ftype in zip( + (decay_files, neutron_files, nfy_files), + ("decay", "neutron", "nfy")): + if len(flist) == 0: + raise IOError("No {} endf files found in {}".format(ftype, endf_dir)) chain = openmc.deplete.Chain.from_endf(decay_files, nfy_files, neutron_files) chain.export_to_xml('chain_endfb71.xml') From 3b348116d1a4030b34c08a3e1086897f5d59a757 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 8 Aug 2019 16:50:34 -0500 Subject: [PATCH 05/11] Document Te-129 addition in scripts/casl_chain.py --- scripts/casl_chain.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/casl_chain.py b/scripts/casl_chain.py index 2477a5e89..cecf88b1d 100755 --- a/scripts/casl_chain.py +++ b/scripts/casl_chain.py @@ -6,6 +6,8 @@ # Note 32 of the 255 nuclides appeare twice as they are both activation # nuclides (category 1) and fission product nuclides (category 3). +# Te-129 has been added due to it's link to I129 production. + CASL_CHAIN = { # Nuclide: (Stable, CAT, IFPY, Special yield treatment) # Stable: True if nuclide has no decay reactions From 066885ddec08641df638aad322810e48a3844dca Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 8 Aug 2019 16:53:12 -0500 Subject: [PATCH 06/11] Document openmc-make-depletion-chain[-casl] scripts --- docs/source/usersguide/scripts.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/source/usersguide/scripts.rst b/docs/source/usersguide/scripts.rst index 76c7c2f22..d64632683 100644 --- a/docs/source/usersguide/scripts.rst +++ b/docs/source/usersguide/scripts.rst @@ -106,6 +106,36 @@ Compton profile data using an existing data library from `Geant4 `_. Note that OpenMC includes this data file by default so it should not be necessary in practice to generate it yourself. + +.. _scripts_depletion_chain: + +------------------------------- +``openmc-make-depletion-chain`` +------------------------------- + +This script generates a depletion chain file called ``chain_endfb71.xml`` +using ENDF/B 7.1 nuclear data. If the :envvar:`OPENMC_ENDF_DATA` variable +is not set, and ``"neutron"``, ``"decay"``, ``"nfy"`` directories +to not exist, then ENDF/B 7.1 293 K data will be downloaded. + +.. _scripts_depletion_chain_casl: + +------------------------------------ +``openmc-make-depletion-chain-casl`` +------------------------------------ + +This script generates a depletion chain called ``chain_casl.xml`` +using a ENDF/B 7.1 nuclear data for a simplified chain. +The nuclides were chosen by CASL-ORIGEN, which can be found in +Appendix A of Kang Seog Kim, "Specification for the VERA Depletion +Benchmark Suite", CASL-U-2015-1014-000, Rev. 0, ORNL/TM-2016/53, 2016. +``Te129`` has been added into this chain due to it's link to +``I129`` production. + +If the :envvar:`OPENMC_ENDF_DATA` variable is not set, +and ``"neutron"``, ``"decay"``, ``"nfy"`` directories +to not exist, then ENDF/B 7.1 293 K data will be downloaded. + .. _scripts_stopping: ------------------------------- From 6b87ada75274e3d9cca3b49715f474981744c3e9 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 13 Aug 2019 16:59:25 -0500 Subject: [PATCH 07/11] Apply suggestions from code review Co-Authored-By: Paul Romano --- docs/source/usersguide/scripts.rst | 10 +++++----- openmc/deplete/chain.py | 10 +++++----- openmc/deplete/nuclide.py | 20 ++++++++++---------- scripts/casl_chain.py | 2 +- scripts/openmc-make-depletion-chain | 2 +- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/source/usersguide/scripts.rst b/docs/source/usersguide/scripts.rst index d64632683..c97e7f1bd 100644 --- a/docs/source/usersguide/scripts.rst +++ b/docs/source/usersguide/scripts.rst @@ -114,9 +114,9 @@ so it should not be necessary in practice to generate it yourself. ------------------------------- This script generates a depletion chain file called ``chain_endfb71.xml`` -using ENDF/B 7.1 nuclear data. If the :envvar:`OPENMC_ENDF_DATA` variable +using ENDF/B-VII.1 nuclear data. If the :envvar:`OPENMC_ENDF_DATA` variable is not set, and ``"neutron"``, ``"decay"``, ``"nfy"`` directories -to not exist, then ENDF/B 7.1 293 K data will be downloaded. +do not exist, then ENDF/B-VII.1 data will be downloaded. .. _scripts_depletion_chain_casl: @@ -125,16 +125,16 @@ to not exist, then ENDF/B 7.1 293 K data will be downloaded. ------------------------------------ This script generates a depletion chain called ``chain_casl.xml`` -using a ENDF/B 7.1 nuclear data for a simplified chain. +using ENDF/B-VII.1 nuclear data for a simplified chain. The nuclides were chosen by CASL-ORIGEN, which can be found in Appendix A of Kang Seog Kim, "Specification for the VERA Depletion Benchmark Suite", CASL-U-2015-1014-000, Rev. 0, ORNL/TM-2016/53, 2016. -``Te129`` has been added into this chain due to it's link to +``Te129`` has been added into this chain due to its link to ``I129`` production. If the :envvar:`OPENMC_ENDF_DATA` variable is not set, and ``"neutron"``, ``"decay"``, ``"nfy"`` directories -to not exist, then ENDF/B 7.1 293 K data will be downloaded. +to not exist, then ENDF/B-VII.1 data will be downloaded. .. _scripts_stopping: diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index d2ccf227c..bfbfcc2be 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -620,9 +620,9 @@ class Chain(object): The following checks are performed for all nuclides present: - 1) For all non-fission reactions, do the sum of branching - ratios equal about 1? - 2) For fission reactions, do the sum of fission yield + 1) For all non-fission reactions, does the sum of branching + ratios equal about one? + 2) For fission reactions, does the sum of fission yield fractions equal about 2? Parameters @@ -631,7 +631,7 @@ class Chain(object): Raise exceptions at the first inconsistency if true. Otherwise mark a warning quiet : bool, optional - Flag to supress warnings and return immediately at + Flag to suppress warnings and return immediately at the first inconsistency. Used only if ``strict`` does not evaluate to ``True``. tolerance : float, optional @@ -662,5 +662,5 @@ class Chain(object): stat = self[name].validate(strict, quiet, tolerance) if quiet and not stat: return stat - valid &= stat + valid = valid and stat return valid diff --git a/openmc/deplete/nuclide.py b/openmc/deplete/nuclide.py index 63503cbfb..95af6d03d 100644 --- a/openmc/deplete/nuclide.py +++ b/openmc/deplete/nuclide.py @@ -231,9 +231,9 @@ class Nuclide(object): The following checks are performed: 1) for all non-fission reactions and decay modes, - do the sum of branching ratios equal about 1? - 2) for fission reactions, do the sum of fission yield - fractions equal about 2? + does the sum of branching ratios equal about one? + 2) for fission reactions, does the sum of fission yield + fractions equal about two? Parameters ---------- @@ -241,7 +241,7 @@ class Nuclide(object): Raise exceptions at the first inconsistency if true. Otherwise mark a warning quiet : bool, optional - Flag to supress warnings and return immediately at + Flag to suppress warnings and return immediately at the first inconsistency. Used only if ``strict`` does not evaluate to ``True``. tolerance : float, optional @@ -273,8 +273,8 @@ class Nuclide(object): valid = True # check decay modes - if len(self.decay_modes) > 0: - sum_br = sum(map(branch_getter, self.decay_modes)) + if self.decay_modes: + sum_br = sum(m.branching_ratio for m in self.decay_modes) stat = 1.0 - tolerance <= sum_br <= 1.0 + tolerance if not stat: msg = msg_func( @@ -287,12 +287,12 @@ class Nuclide(object): warn(msg) valid = False - if len(self.reactions) > 0: + if self.reactions: type_map.clear() for reaction in self.reactions: type_map[reaction.type].add(reaction) for rxn_type, reactions in type_map.items(): - sum_rxn = sum(map(branch_getter, reactions)) + sum_rxn = sum(rx.branching_ratio for rx in reactions) stat = 1.0 - tolerance <= sum_rxn <= 1.0 + tolerance if stat: continue @@ -306,10 +306,10 @@ class Nuclide(object): warn(msg) valid = False - if len(self.yield_data) > 0: + if self.yield_data > 0: yield_getter = itemgetter(1) for energy, yield_list in self.yield_data.items(): - sum_yield = sum(map(yield_getter, yield_list)) + sum_yield = sum(y[1] for y in yield_list) stat = 2.0 - tolerance <= sum_yield <= 2.0 + tolerance if stat: continue diff --git a/scripts/casl_chain.py b/scripts/casl_chain.py index cecf88b1d..61f5da9bc 100755 --- a/scripts/casl_chain.py +++ b/scripts/casl_chain.py @@ -6,7 +6,7 @@ # Note 32 of the 255 nuclides appeare twice as they are both activation # nuclides (category 1) and fission product nuclides (category 3). -# Te-129 has been added due to it's link to I129 production. +# Te129 has been added due to it's link to I129 production. CASL_CHAIN = { # Nuclide: (Stable, CAT, IFPY, Special yield treatment) diff --git a/scripts/openmc-make-depletion-chain b/scripts/openmc-make-depletion-chain index 8627e62ec..5ad0bc66a 100755 --- a/scripts/openmc-make-depletion-chain +++ b/scripts/openmc-make-depletion-chain @@ -36,7 +36,7 @@ def main(): for flist, ftype in zip( (decay_files, neutron_files, nfy_files), ("decay", "neutron", "nfy")): - if len(flist) == 0: + if not flist: raise IOError("No {} endf files found in {}".format(ftype, endf_dir)) chain = openmc.deplete.Chain.from_endf(decay_files, nfy_files, neutron_files) From 0de7618f955a8e07a734ea7f257fc75b99163abc Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 19 Aug 2019 10:02:14 -0500 Subject: [PATCH 08/11] Use independent FP yields for Te129 in CASL chain --- scripts/casl_chain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/casl_chain.py b/scripts/casl_chain.py index 61f5da9bc..94fc9dd96 100755 --- a/scripts/casl_chain.py +++ b/scripts/casl_chain.py @@ -189,7 +189,7 @@ CASL_CHAIN = { 'Sb127': (False, 3, 2, None), 'Te127': (False, 3, -1, None), 'Te127_m1': (False, 3, -1, None), - 'Te129': (False, 3, 2, None), + 'Te129': (False, 3, 1, None), 'Te129_m1': (False, 3, 2, None), 'Te132': (False, 3, 2, None), 'I127': (True, 3, 1, None), From 32f3c27cf64b904d879db1fbd907868d131d8157 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 19 Aug 2019 10:07:20 -0500 Subject: [PATCH 09/11] Add minor changes from code review to deplete.Nuclide.validate --- openmc/deplete/chain.py | 3 ++- openmc/deplete/nuclide.py | 8 ++------ tests/unit_tests/test_deplete_nuclide.py | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index bfbfcc2be..4706bfabb 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -623,7 +623,7 @@ class Chain(object): 1) For all non-fission reactions, does the sum of branching ratios equal about one? 2) For fission reactions, does the sum of fission yield - fractions equal about 2? + fractions equal about two? Parameters ---------- @@ -639,6 +639,7 @@ class Chain(object): value ``x`` to intended value ``y`` as:: valid = (y - tolerance <= x <= y + tolerance) + Returns ------- valid : bool diff --git a/openmc/deplete/nuclide.py b/openmc/deplete/nuclide.py index 95af6d03d..538b69790 100644 --- a/openmc/deplete/nuclide.py +++ b/openmc/deplete/nuclide.py @@ -4,7 +4,6 @@ Contains the per-nuclide components of a depletion chain. """ from collections import namedtuple, defaultdict -from operator import attrgetter, itemgetter from warnings import warn try: import lxml.etree as ET @@ -266,10 +265,8 @@ class Nuclide(object): openmc.deplete.Chain.validate """ - branch_getter = attrgetter("branching_ratio") msg_func = ("Nuclide {name} has {prop} that sum to {actual} " "instead of {expected} +/- {tol:7.4e}").format - type_map = defaultdict(set) valid = True # check decay modes @@ -288,7 +285,7 @@ class Nuclide(object): valid = False if self.reactions: - type_map.clear() + type_map = defaultdict(set) for reaction in self.reactions: type_map[reaction.type].add(reaction) for rxn_type, reactions in type_map.items(): @@ -306,8 +303,7 @@ class Nuclide(object): warn(msg) valid = False - if self.yield_data > 0: - yield_getter = itemgetter(1) + if self.yield_data: for energy, yield_list in self.yield_data.items(): sum_yield = sum(y[1] for y in yield_list) stat = 2.0 - tolerance <= sum_yield <= 2.0 + tolerance diff --git a/tests/unit_tests/test_deplete_nuclide.py b/tests/unit_tests/test_deplete_nuclide.py index a93f6bb4c..38848e9c1 100644 --- a/tests/unit_tests/test_deplete_nuclide.py +++ b/tests/unit_tests/test_deplete_nuclide.py @@ -176,7 +176,7 @@ def test_validate(): # restore reactions, invalidate fission yields nuc.reactions.append(reaction) - fission_yields = nuc.yield_data[1e6].pop() + nuc.yield_data[1e6].pop() with pytest.raises(ValueError, match=r"fission yields.*1\.0*e"): nuc.validate(strict=True, quiet=False, tolerance=0.0) From 37c871cbad673b125420c91ebf7059f675e1a9b1 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 19 Aug 2019 10:08:15 -0500 Subject: [PATCH 10/11] Add hyperlink to CASL depletion benchmark in scripts documentation https://doi.org/10.2172/1256820 --- docs/source/usersguide/scripts.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/source/usersguide/scripts.rst b/docs/source/usersguide/scripts.rst index c97e7f1bd..388130947 100644 --- a/docs/source/usersguide/scripts.rst +++ b/docs/source/usersguide/scripts.rst @@ -127,8 +127,9 @@ do not exist, then ENDF/B-VII.1 data will be downloaded. This script generates a depletion chain called ``chain_casl.xml`` using ENDF/B-VII.1 nuclear data for a simplified chain. The nuclides were chosen by CASL-ORIGEN, which can be found in -Appendix A of Kang Seog Kim, "Specification for the VERA Depletion -Benchmark Suite", CASL-U-2015-1014-000, Rev. 0, ORNL/TM-2016/53, 2016. +Appendix A of Kang Seog Kim, `"Specification for the VERA Depletion +Benchmark Suite" `_, +CASL-U-2015-1014-000, Rev. 0, ORNL/TM-2016/53, 2016. ``Te129`` has been added into this chain due to its link to ``I129`` production. From 68b3fd6ec8299cc903a3c4ce347a45175881fe32 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 19 Aug 2019 10:15:25 -0500 Subject: [PATCH 11/11] Improve file checking and reporting in openmc-make-depletion-chain --- scripts/openmc-make-depletion-chain | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/openmc-make-depletion-chain b/scripts/openmc-make-depletion-chain index 5ad0bc66a..51092a722 100755 --- a/scripts/openmc-make-depletion-chain +++ b/scripts/openmc-make-depletion-chain @@ -33,9 +33,8 @@ def main(): nfy_files = tuple((endf_dir / "nfy").glob("*endf")) # check files exist - for flist, ftype in zip( - (decay_files, neutron_files, nfy_files), - ("decay", "neutron", "nfy")): + for flist, ftype in [(decay_files, "decay"), (neutron_files, "neutron"), + (nfy_files, "neutron fission product yield")]: if not flist: raise IOError("No {} endf files found in {}".format(ftype, endf_dir))