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.
This commit is contained in:
Andrew Johnson 2019-08-08 16:42:14 -05:00
parent ec7af8f376
commit 6da0b3ec57
No known key found for this signature in database
GPG key ID: 253418E91B7F6FEB
3 changed files with 108 additions and 1 deletions

View file

@ -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

View file

@ -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")

View file

@ -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)