1 #include "openmc/tallies/trigger.h"
2
3 #include <cmath>
4 #include <utility> // for std::pair
5
6 #include <fmt/core.h>
7
8 #include "openmc/capi.h"
9 #include "openmc/constants.h"
10 #include "openmc/error.h"
11 #include "openmc/reaction.h"
12 #include "openmc/settings.h"
13 #include "openmc/simulation.h"
14 #include "openmc/tallies/tally.h"
15
16 namespace openmc {
17
18 //==============================================================================
19 // Global variable definitions
20 //==============================================================================
21
22 namespace settings {
23 KTrigger keff_trigger;
24 }
25
26 //==============================================================================
27 // Non-member functions
28 //==============================================================================
29
30 std::pair<double, double>
get_tally_uncertainty(int i_tally,int score_index,int filter_index)31 get_tally_uncertainty(int i_tally, int score_index, int filter_index)
32 {
33 const auto& tally {model::tallies[i_tally]};
34
35 auto sum = tally->results_(filter_index, score_index, TallyResult::SUM);
36 auto sum_sq = tally->results_(filter_index, score_index, TallyResult::SUM_SQ);
37
38 int n = tally->n_realizations_;
39 auto mean = sum / n;
40 double std_dev = std::sqrt((sum_sq/n - mean*mean) / (n-1));
41 double rel_err = (mean != 0.) ? std_dev / std::abs(mean) : 0.;
42
43 return {std_dev, rel_err};
44 }
45
46 //! Find the limiting limiting tally trigger.
47 //
48 //! param[out] ratio The uncertainty/threshold ratio for the most limiting
49 //! tally trigger
50 //! param[out] tally_id The ID number of the most limiting tally
51 //! param[out] score The most limiting tally score bin
52
53 void
check_tally_triggers(double & ratio,int & tally_id,int & score)54 check_tally_triggers(double& ratio, int& tally_id, int& score)
55 {
56 ratio = 0.;
57 for (auto i_tally = 0; i_tally < model::tallies.size(); ++i_tally) {
58 const Tally& t {*model::tallies[i_tally]};
59
60 // Ignore tallies with less than two realizations.
61 if (t.n_realizations_ < 2) continue;
62
63 for (const auto& trigger : t.triggers_) {
64 // Skip trigger if it is not active
65 if (trigger.metric == TriggerMetric::not_active) continue;
66
67 const auto& results = t.results_;
68 for (auto filter_index = 0; filter_index < results.shape()[0];
69 ++filter_index) {
70 for (auto score_index = 0; score_index < results.shape()[1];
71 ++score_index) {
72 // Compute the tally uncertainty metrics.
73 auto uncert_pair = get_tally_uncertainty(i_tally, score_index,
74 filter_index);
75 double std_dev = uncert_pair.first;
76 double rel_err = uncert_pair.second;
77
78 // Pick out the relevant uncertainty metric for this trigger.
79 double uncertainty;
80 switch (trigger.metric) {
81 case TriggerMetric::variance:
82 uncertainty = std_dev * std_dev;
83 break;
84 case TriggerMetric::standard_deviation:
85 uncertainty = std_dev;
86 break;
87 case TriggerMetric::relative_error:
88 uncertainty = rel_err;
89 break;
90 case TriggerMetric::not_active:
91 UNREACHABLE();
92 }
93
94 // Compute the uncertainty / threshold ratio.
95 double this_ratio = uncertainty / trigger.threshold;
96 if (trigger.metric == TriggerMetric::variance) {
97 this_ratio = std::sqrt(ratio);
98 }
99
100 // If this is the most uncertain value, set the output variables.
101 if (this_ratio > ratio) {
102 ratio = this_ratio;
103 score = t.scores_[trigger.score_index];
104 tally_id = t.id_;
105 }
106 }
107 }
108 }
109 }
110 }
111
112 //! Compute the uncertainty/threshold ratio for the eigenvalue trigger.
113
114 double
check_keff_trigger()115 check_keff_trigger()
116 {
117 if (settings::run_mode != RunMode::EIGENVALUE) return 0.0;
118
119 double k_combined[2];
120 openmc_get_keff(k_combined);
121
122 double uncertainty = 0.;
123 switch (settings::keff_trigger.metric) {
124 case TriggerMetric::variance:
125 uncertainty = k_combined[1] * k_combined[1];
126 break;
127 case TriggerMetric::standard_deviation:
128 uncertainty = k_combined[1];
129 break;
130 case TriggerMetric::relative_error:
131 uncertainty = k_combined[1] / k_combined[0];
132 break;
133 default:
134 // If it's an unrecognized TriggerMetric or no keff trigger is on,
135 // return 0 to stop division by zero where "ratio" is calculated.
136 return 0.0;
137 }
138
139 double ratio = uncertainty / settings::keff_trigger.threshold;
140 if (settings::keff_trigger.metric == TriggerMetric::variance)
141 ratio = std::sqrt(ratio);
142 return ratio;
143 }
144
145 //! See if tally and eigenvalue uncertainties are under trigger thresholds.
146
147 void
check_triggers()148 check_triggers()
149 {
150 // Make some aliases.
151 const auto current_batch {simulation::current_batch};
152 const auto n_batches {settings::n_batches};
153 const auto interval {settings::trigger_batch_interval};
154
155 // See if the current batch is one for which the triggers must be checked.
156 if (!settings::trigger_on) return;
157 if (current_batch < n_batches) return;
158 if (((current_batch - n_batches) % interval) != 0) return;
159
160 // Check the eigenvalue and tally triggers.
161 double keff_ratio = check_keff_trigger();
162 double tally_ratio;
163 int tally_id, score;
164 check_tally_triggers(tally_ratio, tally_id, score);
165
166 // If all the triggers are satisfied, alert the user and return.
167 if (std::max(keff_ratio, tally_ratio) <= 1.) {
168 simulation::satisfy_triggers = true;
169 write_message(7, "Triggers satisfied for batch {}", current_batch);
170 return;
171 }
172
173 // At least one trigger is unsatisfied. Let the user know which one.
174 simulation::satisfy_triggers = false;
175 std::string msg;
176 if (keff_ratio >= tally_ratio) {
177 msg = fmt::format("Triggers unsatisfied, max unc./thresh. is {} for "
178 "eigenvalue", keff_ratio);
179 } else {
180 msg = fmt::format(
181 "Triggers unsatisfied, max unc./thresh. is {} for {} in tally {}",
182 tally_ratio, reaction_name(score), tally_id);
183 }
184 write_message(msg, 7);
185
186 // Estimate batches til triggers are satisfied.
187 if (settings::trigger_predict) {
188 // This calculation assumes tally variance is proportional to 1/N where N is
189 // the number of batches.
190 auto max_ratio = std::max(keff_ratio, tally_ratio);
191 auto n_active = current_batch - settings::n_inactive;
192 auto n_pred_batches = static_cast<int>(n_active * max_ratio * max_ratio)
193 + settings::n_inactive + 1;
194
195 std::string msg = fmt::format("The estimated number of batches is {}",
196 n_pred_batches);
197 if (n_pred_batches > settings::n_max_batches) {
198 msg.append(" --- greater than max batches");
199 warning(msg);
200 } else {
201 write_message(msg, 7);
202 }
203 }
204 }
205
206 } // namespace openmc
207