From d7bcf60eb4fd4031abf4dd0f2cfac423ca694155 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 30 Jul 2019 10:13:54 -0500 Subject: [PATCH 01/14] Store min/max data temperatures. Use in Cell::set_temperature to check bounds --- include/openmc/nuclide.h | 6 ++++++ src/cell.cpp | 12 +++++++++++- src/cross_sections.cpp | 6 ++++++ src/finalize.cpp | 2 ++ src/nuclide.cpp | 10 +++++++++- 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/include/openmc/nuclide.h b/include/openmc/nuclide.h index 4be898a64e..a936ac0102 100644 --- a/include/openmc/nuclide.h +++ b/include/openmc/nuclide.h @@ -126,6 +126,12 @@ namespace data { extern std::array energy_min; extern std::array energy_max; +//! Minimum temperature in [K] that nuclide data is available at +extern double temperature_min; + +//! Maximum temperature in [K] that nuclide data is available at +extern double temperature_max; + extern std::vector> nuclides; extern std::unordered_map nuclide_map; diff --git a/src/cell.cpp b/src/cell.cpp index 3c079f7ae5..cfb717c85e 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -16,6 +16,7 @@ #include "openmc/hdf5_interface.h" #include "openmc/lattice.h" #include "openmc/material.h" +#include "openmc/nuclide.h" #include "openmc/settings.h" #include "openmc/surface.h" #include "openmc/xml_interface.h" @@ -244,6 +245,16 @@ Cell::temperature(int32_t instance) const void Cell::set_temperature(double T, int32_t instance) { + if (settings::temperature_method == TEMPERATURE_INTERPOLATION) { + if (T < data::temperature_min) { + throw std::runtime_error{"Temperature is below minimum temperature at " + "which data is available."}; + } else if (T > data::temperature_max) { + throw std::runtime_error{"Temperature is above maximum temperature at " + "which data is available."}; + } + } + if (instance >= 0) { sqrtkT_.at(instance) = std::sqrt(K_BOLTZMANN * T); } else { @@ -1057,7 +1068,6 @@ openmc_cell_set_fill(int32_t index, int type, int32_t n, return 0; } -//TODO: make sure data is loaded for this temperature extern "C" int openmc_cell_set_temperature(int32_t index, double T, const int32_t* instance) { diff --git a/src/cross_sections.cpp b/src/cross_sections.cpp index 8ea59c1f08..f9bfe1d229 100644 --- a/src/cross_sections.cpp +++ b/src/cross_sections.cpp @@ -357,6 +357,12 @@ read_ce_cross_sections(const std::vector>& nuc_temps, } } + // Show minimum/maximum temperature + write_message("Minimum neutron data temperature: " + + std::to_string(data::temperature_min) + " K"); + write_message("Maximum neutron data temperature: " + + std::to_string(data::temperature_max) + " K"); + // If the user wants multipole, make sure we found a multipole library. if (settings::temperature_multipole) { bool mp_found = false; diff --git a/src/finalize.cpp b/src/finalize.cpp index 633cf2991d..120903f3fe 100644 --- a/src/finalize.cpp +++ b/src/finalize.cpp @@ -117,6 +117,8 @@ int openmc_finalize() data::energy_max = {INFTY, INFTY}; data::energy_min = {0.0, 0.0}; + data::temperature_min = 0.0; + data::temperature_max = INFTY; model::root_universe = -1; openmc::openmc_set_seed(DEFAULT_SEED); diff --git a/src/nuclide.cpp b/src/nuclide.cpp index 6094b79831..3b092ddf4b 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -18,7 +18,7 @@ #include "xtensor/xbuilder.hpp" #include "xtensor/xview.hpp" -#include // for sort +#include // for sort, min_element #include // for to_string, stoi namespace openmc { @@ -30,6 +30,8 @@ namespace openmc { namespace data { std::array energy_min {0.0, 0.0}; std::array energy_max {INFTY, INFTY}; +double temperature_min {0.0}; +double temperature_max {INFTY}; std::vector> nuclides; std::unordered_map nuclide_map; } // namespace data @@ -154,6 +156,12 @@ Nuclide::Nuclide(hid_t group, const std::vector& temperature, int i_nucl // Sort temperatures to read std::sort(temps_to_read.begin(), temps_to_read.end()); + double T_min_read = *std::min_element(temps_to_read.cbegin(), temps_to_read.cend()); + double T_max_read = *std::max_element(temps_to_read.cbegin(), temps_to_read.cend()); + + data::temperature_min = std::max(data::temperature_min, T_min_read); + data::temperature_max = std::min(data::temperature_max, T_max_read); + hid_t energy_group = open_group(group, "energy"); for (const auto& T : temps_to_read) { std::string dset {std::to_string(T) + "K"}; From c2137187a4b0eb5c6bfaf2278a80c403e6c9bc4b Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 6 Aug 2019 11:48:57 -0500 Subject: [PATCH 02/14] 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 86389e3ced..2477a5e892 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 03/14] 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 88d5ac6eae..9c07a91a9a 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 f2a101d2a9..a93f6bb4c9 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 04/14] 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 1f16d9cafa..d2ccf227c4 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 9c07a91a9a..63503cbfb4 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 33b13b596d..294e56637c 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 05/14] 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 7c08f984e9..8627e62ec6 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 06/14] 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 2477a5e892..cecf88b1dd 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 07/14] 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 76c7c2f222..d64632683e 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 fe2a87e7597427f2ecc1c03e885d76223771053a Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 13 Aug 2019 08:32:18 -0500 Subject: [PATCH 08/14] Add a few papers in publications list --- docs/source/publications.rst | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/docs/source/publications.rst b/docs/source/publications.rst index d554fe5a14..f33cf79587 100644 --- a/docs/source/publications.rst +++ b/docs/source/publications.rst @@ -57,6 +57,9 @@ Benchmarking Coupling and Multi-physics -------------------------- +- Miriam A. Kreher, Benoit Forget, and Kord Smith, "Single-Batch Monte Carlo + Multiphysics Coupling," *Proc. M&C*, Portland, Oregon, Aug. 25-29 (2019). + - Ze-Long Zhao, Yongwei Yang, and Shuang Hong, "`Application of FLUKA and OpenMC in coupled physics calculation of target and subcritical reactor for ADS `_," *Nucl. Sci. Tech.*, **30**: 10 @@ -113,6 +116,10 @@ Coupling and Multi-physics Geometry and Visualization -------------------------- +- Sterling Harper, Paul Romano, Benoit Forget, and Kord Smith, "Efficient + dynamic threadsafe neighbor lists for Monte Carlo ray tracing," *Proc. M&C*, + Portland, Oregon, Aug. 25-29 (2019). + - Jin-Yang Li, Long Gu, Hu-Shan Xu, Nadezha Korepanova, Rui Yu, Yan-Lei Zhu, and Chang-Ping Qin, "`CAD modeling study on FLUKA and OpenMC for accelerator driven system simulation `_", @@ -303,6 +310,11 @@ Multigroup Cross Section Generation Doppler Broadening ------------------ +- Jonathan A. Walsh, Benoit Forget, Kord S. Smith, and Forrest B. Brown, + "`On-the-fly Doppler broadening of unresolved resonance region cross sections + `_," *Prog. Nucl. Energy*, + **101**, 444-460 (2017). + - Colin Josey, Pablo Ducru, Benoit Forget, and Kord Smith, "`Windowed multipole for cross section Doppler broadening `_," *J. Comput. Phys.*, **307**, @@ -313,6 +325,12 @@ Doppler Broadening via Probability Band Interpolation," *Proc. PHYSOR*, Sun Valley, Idaho, May 1-5, 2016. +- Jonathan A. Walsh, Benoit Forget, Kord S. Smith, Brian C. Kiedrowski, and + Forrest B. Brown, "`Direct, on-the-fly calculation of unresolved resonance + region cross sections in Monte Carlo simulations + `_," *Proc. Joint Int. Conf. M&C+SNA+MC*, + Nashville, Tennessee, Apr. 19--23 (2015). + - Colin Josey, Benoit Forget, and Kord Smith, "`Windowed multipole sensitivity to target accuracy of the optimization procedure `_," @@ -366,11 +384,6 @@ Nuclear Data `_", *Comput. Phys. Commun.*, **196**, 134-142 (2015). -- Jonathan A. Walsh, Benoit Forget, Kord S. Smith, Brian C. Kiedrowski, and - Forrest B. Brown, "Direct, on-the-fly calculation of unresolved resonance - region cross sections in Monte Carlo simulations," *Proc. Joint - Int. Conf. M&C+SNA+MC*, Nashville, Tennessee, Apr. 19--23 (2015). - - Amanda L. Lund, Andrew R. Siegel, Benoit Forget, Colin Josey, and Paul K. Romano, "Using fractional cascading to accelerate cross section lookups in Monte Carlo particle transport calculations," *Proc. Joint @@ -481,6 +494,10 @@ Parallelism Depletion --------- +- Jose L. Salcedo-Perez, Benoit Forget, Kord Smith, and Paul Romano, "Hybrid + tallies to improve performance in depletion Monte Carlo simulations," *Proc. + M&C*, Aug. 25-29 (2019). + - Zhao-Qing Liu, Ze-Long Zhao, Yong-Wei Yang, Yu-Cui Gao, Hai-Yan Meng, and Qing-Yu Gao, "`Development and validation of depletion code system IMPC-Burnup for ADS `_," *Nucl. Sci. Tech.*, From fc2c80628b93ecb4667f98740085692f30134168 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 13 Aug 2019 08:36:09 -0500 Subject: [PATCH 09/14] Remove list of developers, update copyright year in a few places --- docs/source/developers.rst | 28 ---------------------------- docs/source/index.rst | 1 - docs/source/license.rst | 2 +- man/man1/openmc.1 | 2 +- 4 files changed, 2 insertions(+), 31 deletions(-) delete mode 100644 docs/source/developers.rst diff --git a/docs/source/developers.rst b/docs/source/developers.rst deleted file mode 100644 index 7d32b5831c..0000000000 --- a/docs/source/developers.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. _developers: - -================ -Development Team -================ - -The following people have contributed to development of the OpenMC Monte Carlo -code: - -* `Paul Romano `_ -* `Bryan Herman `_ -* `Nick Horelik `_ -* `Adam Nelson `_ -* `Jon Walsh `_ -* `Sterling Harper `_ -* `Will Boyd `_ -* `Samuel Shaner `_ -* `Jingang Liang `_ -* `Colin Josey `_ -* `Amanda Lund `_ -* `Guillaume Giudicelli `_ -* `Isaac Meyer `_ -* `Patrick Shriwise `_ -* `Shikhar Kumar `_ -* `Andrew Davis `_ -* `Benoit Forget `_ -* `Kord Smith `_ -* `Andrew Siegel `_ diff --git a/docs/source/index.rst b/docs/source/index.rst index 5500890e2b..2ce5072d01 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -44,4 +44,3 @@ list `_. io_formats/index publications license - developers diff --git a/docs/source/license.rst b/docs/source/license.rst index c0728e1a8c..98f6655bc2 100644 --- a/docs/source/license.rst +++ b/docs/source/license.rst @@ -4,7 +4,7 @@ License Agreement ================= -Copyright © 2011-2018 Massachusetts Institute of Technology and OpenMC contributors +Copyright © 2011-2019 Massachusetts Institute of Technology and OpenMC contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/man/man1/openmc.1 b/man/man1/openmc.1 index f574ea10ae..229b64fd1a 100644 --- a/man/man1/openmc.1 +++ b/man/man1/openmc.1 @@ -54,7 +54,7 @@ Indicates the default path to an HDF5 file that contains multi-group cross section libraries if the user has not specified the tag in .I materials.xml\fP. .SH LICENSE -Copyright \(co 2011-2018 Massachusetts Institute of Technology and OpenMC +Copyright \(co 2011-2019 Massachusetts Institute of Technology and OpenMC contributors. .PP Permission is hereby granted, free of charge, to any person obtaining a copy of From 6b87ada75274e3d9cca3b49715f474981744c3e9 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 13 Aug 2019 16:59:25 -0500 Subject: [PATCH 10/14] 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 d64632683e..c97e7f1bd5 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 d2ccf227c4..bfbfcc2bef 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 63503cbfb4..95af6d03de 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 cecf88b1dd..61f5da9bc3 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 8627e62ec6..5ad0bc66a1 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 11/14] 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 61f5da9bc3..94fc9dd966 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 12/14] 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 bfbfcc2bef..4706bfabb9 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 95af6d03de..538b697904 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 a93f6bb4c9..38848e9c19 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 13/14] 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 c97e7f1bd5..388130947c 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 14/14] 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 5ad0bc66a1..51092a7224 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))