mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-27 21:55:41 -04:00
Allow zero bins in tally triggers (#2928)
Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
This commit is contained in:
parent
d1366c0545
commit
c976653abf
6 changed files with 90 additions and 7 deletions
|
|
@ -100,6 +100,18 @@ The ``<tally>`` element accepts the following sub-elements:
|
|||
|
||||
*Default*: None
|
||||
|
||||
:ignore_zeros:
|
||||
Whether to allow zero tally bins to be ignored when assessing the
|
||||
convergece of the precision trigger. If True, only nonzero tally scores
|
||||
will be compared to the trigger's threshold.
|
||||
|
||||
.. note:: The ``ignore_zeros`` option can cause the tally trigger to fire
|
||||
prematurely if there are no hits in any bins at the first
|
||||
evalulation. It is the user's responsibility to specify enough
|
||||
particles per batch to get a nonzero score in at least one bin.
|
||||
|
||||
*Default*: False
|
||||
|
||||
:scores:
|
||||
The score(s) in this tally to which the trigger should be applied.
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ enum class TriggerMetric {
|
|||
struct Trigger {
|
||||
TriggerMetric metric; //!< The type of uncertainty (e.g. std dev) measured
|
||||
double threshold; //!< Uncertainty value below which trigger is satisfied
|
||||
bool ignore_zeros; //!< Whether to allow zero tally bins to be ignored
|
||||
int score_index; //!< Index of the relevant score in the tally's arrays
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@ class Trigger(EqualityMixin):
|
|||
relative error of scores.
|
||||
threshold : float
|
||||
The threshold for the trigger type.
|
||||
ignore_zeros : bool
|
||||
Whether to allow zero tally bins to be ignored. Note that this option
|
||||
can cause the trigger to fire prematurely if there are zero scores in
|
||||
any bin at the first evaluation.
|
||||
|
||||
.. versionadded:: 0.14.1
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
|
@ -27,18 +33,22 @@ class Trigger(EqualityMixin):
|
|||
The threshold for the trigger type.
|
||||
scores : list of str
|
||||
Scores which should be checked against the trigger
|
||||
ignore_zeros : bool
|
||||
Whether to allow zero tally bins to be ignored.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, trigger_type: str, threshold: float):
|
||||
def __init__(self, trigger_type: str, threshold: float, ignore_zeros: bool = False):
|
||||
self.trigger_type = trigger_type
|
||||
self.threshold = threshold
|
||||
self.ignore_zeros = ignore_zeros
|
||||
self._scores = []
|
||||
|
||||
def __repr__(self):
|
||||
string = 'Trigger\n'
|
||||
string += '{: <16}=\t{}\n'.format('\tType', self._trigger_type)
|
||||
string += '{: <16}=\t{}\n'.format('\tThreshold', self._threshold)
|
||||
string += '{: <16}=\t{}\n'.format('\tIgnore Zeros', self._ignore_zeros)
|
||||
string += '{: <16}=\t{}\n'.format('\tScores', self._scores)
|
||||
return string
|
||||
|
||||
|
|
@ -61,6 +71,15 @@ class Trigger(EqualityMixin):
|
|||
cv.check_type('tally trigger threshold', threshold, Real)
|
||||
self._threshold = threshold
|
||||
|
||||
@property
|
||||
def ignore_zeros(self):
|
||||
return self._ignore_zeros
|
||||
|
||||
@ignore_zeros.setter
|
||||
def ignore_zeros(self, ignore_zeros):
|
||||
cv.check_type('tally trigger ignores zeros', ignore_zeros, bool)
|
||||
self._ignore_zeros = ignore_zeros
|
||||
|
||||
@property
|
||||
def scores(self):
|
||||
return self._scores
|
||||
|
|
@ -88,6 +107,8 @@ class Trigger(EqualityMixin):
|
|||
element = ET.Element("trigger")
|
||||
element.set("type", self._trigger_type)
|
||||
element.set("threshold", str(self._threshold))
|
||||
if self._ignore_zeros:
|
||||
element.set("ignore_zeros", "true")
|
||||
if len(self._scores) != 0:
|
||||
element.set("scores", ' '.join(self._scores))
|
||||
return element
|
||||
|
|
@ -110,7 +131,10 @@ class Trigger(EqualityMixin):
|
|||
# Generate trigger object
|
||||
trigger_type = elem.get("type")
|
||||
threshold = float(elem.get("threshold"))
|
||||
trigger = cls(trigger_type, threshold)
|
||||
ignore_zeros = str(elem.get("ignore_zeros", "false")).lower()
|
||||
# Try to convert to bool. Let Trigger error out on instantiation.
|
||||
ignore_zeros = ignore_zeros in ('true', '1')
|
||||
trigger = cls(trigger_type, threshold, ignore_zeros)
|
||||
|
||||
# Add scores if present
|
||||
scores = elem.get("scores")
|
||||
|
|
|
|||
|
|
@ -690,6 +690,12 @@ void Tally::init_triggers(pugi::xml_node node)
|
|||
"Must specify trigger threshold for tally {} in tally XML file", id_));
|
||||
}
|
||||
|
||||
// Read whether to allow zero-tally bins to be ignored.
|
||||
bool ignore_zeros = false;
|
||||
if (check_for_node(trigger_node, "ignore_zeros")) {
|
||||
ignore_zeros = get_node_value_bool(trigger_node, "ignore_zeros");
|
||||
}
|
||||
|
||||
// Read the trigger scores.
|
||||
vector<std::string> trigger_scores;
|
||||
if (check_for_node(trigger_node, "scores")) {
|
||||
|
|
@ -703,7 +709,7 @@ void Tally::init_triggers(pugi::xml_node node)
|
|||
if (score_str == "all") {
|
||||
triggers_.reserve(triggers_.size() + this->scores_.size());
|
||||
for (auto i_score = 0; i_score < this->scores_.size(); ++i_score) {
|
||||
triggers_.push_back({metric, threshold, i_score});
|
||||
triggers_.push_back({metric, threshold, ignore_zeros, i_score});
|
||||
}
|
||||
} else {
|
||||
int i_score = 0;
|
||||
|
|
@ -717,7 +723,7 @@ void Tally::init_triggers(pugi::xml_node node)
|
|||
"{} but it was listed in a trigger on that tally",
|
||||
score_str, id_));
|
||||
}
|
||||
triggers_.push_back({metric, threshold, i_score});
|
||||
triggers_.push_back({metric, threshold, ignore_zeros, i_score});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,9 +77,9 @@ void check_tally_triggers(double& ratio, int& tally_id, int& score)
|
|||
auto uncert_pair =
|
||||
get_tally_uncertainty(i_tally, trigger.score_index, filter_index);
|
||||
|
||||
// if there is a score without contributions, set ratio to inf and
|
||||
// exit early
|
||||
if (uncert_pair.first == -1) {
|
||||
// If there is a score without contributions, set ratio to inf and
|
||||
// exit early, unless zero scores are ignored for this trigger.
|
||||
if (uncert_pair.first == -1 && !trigger.ignore_zeros) {
|
||||
ratio = INFINITY;
|
||||
score = t.scores_[trigger.score_index];
|
||||
tally_id = t.id_;
|
||||
|
|
|
|||
|
|
@ -73,3 +73,43 @@ def test_tally_trigger_null_score(run_in_tmpdir):
|
|||
total_batches = sp.n_realizations + sp.n_inactive
|
||||
assert total_batches == pincell.settings.trigger_max_batches
|
||||
|
||||
|
||||
def test_tally_trigger_zero_ignored(run_in_tmpdir):
|
||||
pincell = openmc.examples.pwr_pin_cell()
|
||||
|
||||
# create an energy filter below and around the O-16(n,p) threshold (1.02e7 eV)
|
||||
e_filter = openmc.EnergyFilter([0.0, 1e7, 2e7])
|
||||
|
||||
# create a tally with triggers applied
|
||||
tally = openmc.Tally()
|
||||
tally.filters = [e_filter]
|
||||
tally.scores = ['(n,p)']
|
||||
tally.nuclides = ["O16"]
|
||||
|
||||
# 100% relative error: should be immediately satisfied in nonzero bin
|
||||
trigger = openmc.Trigger('rel_err', 1.0)
|
||||
trigger.scores = ['(n,p)']
|
||||
trigger.ignore_zeros = True
|
||||
|
||||
tally.triggers = [trigger]
|
||||
|
||||
pincell.tallies = [tally]
|
||||
|
||||
pincell.settings.particles = 1000 # we need a few more particles for this
|
||||
pincell.settings.trigger_active = True
|
||||
pincell.settings.trigger_max_batches = 50
|
||||
pincell.settings.trigger_batch_interval = 20
|
||||
|
||||
sp_file = pincell.run()
|
||||
|
||||
with openmc.StatePoint(sp_file) as sp:
|
||||
# verify that the first bin is zero and the second is nonzero
|
||||
tally_out = sp.get_tally(id=tally.id)
|
||||
below, above = tally_out.mean.squeeze()
|
||||
assert below == 0.0, "Tally events observed below expected threshold"
|
||||
assert above > 0, "No tally events observed. Test with more particles."
|
||||
|
||||
# we expect that the trigger fires before max batches are hit
|
||||
total_batches = sp.n_realizations + sp.n_inactive
|
||||
assert total_batches < pincell.settings.trigger_max_batches
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue