Line data Source code
1 : //* This file is part of the MOOSE framework
2 : //* https://mooseframework.inl.gov
3 : //*
4 : //* All rights reserved, see COPYRIGHT for full restrictions
5 : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 : //*
7 : //* Licensed under LGPL 2.1, please see LICENSE for details
8 : //* https://www.gnu.org/licenses/lgpl-2.1.html
9 :
10 : #include "SolutionInvalidity.h"
11 :
12 : // MOOSE Includes
13 : #include "MooseError.h"
14 : #include "MooseApp.h"
15 : #include "VariadicTable.h"
16 :
17 : // System Includes
18 : #include <chrono>
19 : #include <memory>
20 : #include <timpi/parallel_sync.h>
21 : #include <numeric>
22 :
23 : // libMesh Includes
24 : #include "libmesh/parallel_algebra.h"
25 : #include "libmesh/parallel_sync.h"
26 :
27 66995 : SolutionInvalidity::SolutionInvalidity(MooseApp & app)
28 : : ConsoleStreamInterface(app),
29 : ParallelObject(app.comm()),
30 66995 : _solution_invalidity_registry(moose::internal::getSolutionInvalidityRegistry()),
31 66995 : _has_synced(true),
32 66995 : _has_solution_warning(false),
33 66995 : _has_solution_error(false),
34 133990 : _has_recorded_issue(false)
35 : {
36 66995 : }
37 :
38 : void
39 38886 : SolutionInvalidity::flagInvalidSolutionInternal(const InvalidSolutionID _invalid_solution_id)
40 : {
41 38886 : std::lock_guard<std::mutex> lock_id(_invalid_mutex);
42 38886 : if (_counts.size() <= _invalid_solution_id)
43 2776 : _counts.resize(_invalid_solution_id + 1);
44 :
45 38886 : ++_counts[_invalid_solution_id].current_counts;
46 38886 : }
47 :
48 : bool
49 315295 : SolutionInvalidity::hasInvalidSolutionWarning() const
50 : {
51 : mooseAssert(_has_synced, "Has not synced");
52 315295 : return _has_solution_warning;
53 : }
54 :
55 : bool
56 625774 : SolutionInvalidity::hasInvalidSolutionError() const
57 : {
58 : mooseAssert(_has_synced, "Has not synced");
59 625774 : return _has_solution_error;
60 : }
61 :
62 : bool
63 315295 : SolutionInvalidity::hasInvalidSolution() const
64 : {
65 315295 : return hasInvalidSolutionWarning() || hasInvalidSolutionError();
66 : }
67 :
68 : bool
69 54870 : SolutionInvalidity::hasEverHadSolutionIssue() const
70 : {
71 : mooseAssert(_has_synced, "Has not synced");
72 54870 : return _has_recorded_issue;
73 : }
74 :
75 : void
76 4023763 : SolutionInvalidity::resetIterationOccurences()
77 : {
78 : // Zero current counts
79 4482209 : for (auto & entry : _counts)
80 458446 : entry.current_counts = 0;
81 4023763 : }
82 :
83 : void
84 308325 : SolutionInvalidity::resetTimeStepOccurences()
85 : {
86 : // Reset that we have synced because we're on a new iteration
87 308325 : _has_synced = false;
88 :
89 341265 : for (auto & entry : _counts)
90 32940 : entry.current_timestep_counts = 0;
91 308325 : }
92 :
93 : void
94 4201989 : SolutionInvalidity::accumulateIterationIntoTimeStepOccurences()
95 : {
96 4702672 : for (auto & entry : _counts)
97 500683 : entry.current_timestep_counts += entry.current_counts;
98 4201989 : }
99 :
100 : void
101 360639 : SolutionInvalidity::accumulateTimeStepIntoTotalOccurences(const unsigned int timestep_index)
102 : {
103 395977 : for (auto & entry : _counts)
104 35338 : if (entry.current_timestep_counts)
105 : {
106 3091 : if (entry.timestep_counts.empty() ||
107 668 : entry.timestep_counts.back().timestep_index != timestep_index)
108 1877 : entry.timestep_counts.emplace_back(timestep_index);
109 2423 : entry.timestep_counts.back().counts = entry.current_timestep_counts;
110 2423 : entry.total_counts += entry.current_timestep_counts;
111 : }
112 360639 : }
113 :
114 : void
115 623 : SolutionInvalidity::print(const ConsoleStream & console) const
116 : {
117 623 : console << "\nSolution Invalid Warnings:\n";
118 623 : summaryTable().print(console);
119 623 : }
120 :
121 : void
122 1435 : SolutionInvalidity::printHistory(const ConsoleStream & console,
123 : unsigned int & timestep_interval_size) const
124 : {
125 1435 : if (hasInvalidSolutionError())
126 45 : console << "\nSolution Invalid History:\n";
127 : else
128 1390 : console << "\nWarnings History:\n";
129 1435 : transientTable(timestep_interval_size).print(console);
130 1435 : }
131 :
132 : void
133 4618084 : SolutionInvalidity::syncIteration()
134 : {
135 : std::map<processor_id_type, std::vector<std::tuple<std::string, std::string, int, unsigned int>>>
136 4618084 : data_to_send;
137 :
138 : // Reset this as we need to see if we have new counts
139 4618084 : _has_solution_warning = false;
140 4618084 : _has_solution_error = false;
141 :
142 5155339 : for (const auto id : index_range(_counts))
143 : {
144 537255 : auto & entry = _counts[id];
145 537255 : if (entry.current_counts)
146 : {
147 7594 : const auto & info = _solution_invalidity_registry.item(id);
148 7594 : data_to_send[0].emplace_back(
149 7594 : info.object_type, info.message, info.warning, entry.current_counts);
150 7594 : entry.current_counts = 0;
151 : }
152 : }
153 :
154 4296 : const auto receive_data = [this](const processor_id_type libmesh_dbg_var(pid), const auto & data)
155 : {
156 : mooseAssert(processor_id() == 0, "Should only receive on processor 0");
157 :
158 11890 : for (const auto & [object_type, message, warning_int, counts] : data)
159 : {
160 : mooseAssert(counts, "Should not send data without counts");
161 :
162 : // We transfer this as an integer (which is guaranteed by the standard to cast to a bool)
163 : // because TIMPI doesn't currently support transferring bools
164 7594 : const bool warning = warning_int;
165 :
166 7594 : InvalidSolutionID main_id = 0;
167 7594 : const moose::internal::SolutionInvalidityName name(object_type, message);
168 7594 : if (_solution_invalidity_registry.keyExists(name))
169 : {
170 7585 : main_id = _solution_invalidity_registry.id(name);
171 : mooseAssert(_solution_invalidity_registry.item(main_id).warning == warning,
172 : "Inconsistent registration of invalidity warning and error");
173 : }
174 : else
175 : {
176 : mooseAssert(pid != 0, "Should only hit on other processors");
177 9 : main_id = moose::internal::getSolutionInvalidityRegistry().registerInvalidity(
178 : object_type, message, warning);
179 : }
180 7594 : if (_counts.size() <= main_id)
181 9 : _counts.resize(main_id + 1);
182 :
183 7594 : _counts[main_id].current_counts += counts;
184 :
185 7594 : if (warning)
186 3360 : _has_solution_warning = true;
187 : else
188 4234 : _has_solution_error = true;
189 : }
190 4296 : };
191 :
192 : // Communicate the counts
193 4618084 : TIMPI::push_parallel_vector_data(comm(), data_to_send, receive_data);
194 :
195 : // Set the state across all processors
196 4618084 : comm().max(_has_solution_warning);
197 4618084 : comm().max(_has_solution_error);
198 :
199 : // Keep track of any occurence
200 4618084 : if (_has_solution_warning || _has_solution_error)
201 4813 : _has_recorded_issue = true;
202 :
203 : // We've now synced
204 4618084 : _has_synced = true;
205 4618084 : }
206 :
207 : void
208 576 : SolutionInvalidity::printDebug(InvalidSolutionID _invalid_solution_id) const
209 : {
210 576 : const auto & info = _solution_invalidity_registry.item(_invalid_solution_id);
211 576 : _console << info.object_type << ": " << info.message << "\n" << std::flush;
212 576 : }
213 :
214 : SolutionInvalidity::FullTable
215 623 : SolutionInvalidity::summaryTable() const
216 : {
217 : mooseAssert(_has_synced, "Has not synced");
218 :
219 1246 : FullTable vtable({"Object", "Converged", "Timestep", "Total", "Message"}, 4);
220 :
221 1246 : vtable.setColumnFormat({
222 : VariadicTableColumnFormat::AUTO, // Object Type
223 : VariadicTableColumnFormat::AUTO, // Converged Iteration Warnings
224 : VariadicTableColumnFormat::AUTO, // Latest Time Step Warnings
225 : VariadicTableColumnFormat::AUTO, // Total Simulation Warnings
226 : VariadicTableColumnFormat::AUTO, // Message
227 : });
228 :
229 1246 : vtable.setColumnPrecision({
230 : 1, // Object Name
231 : 0, // Converged Iteration Warnings
232 : 0, // Latest Time Step Warnings
233 : 0, // Total Simulation Warnings
234 : 1, // Message
235 : });
236 :
237 623 : if (processor_id() == 0)
238 : {
239 1185 : for (const auto id : index_range(_counts))
240 : {
241 772 : const auto & entry = _counts[id];
242 772 : if (entry.current_counts)
243 : {
244 742 : const auto & info = _solution_invalidity_registry.item(id);
245 742 : vtable.addRow(info.object_type, // Object Type
246 742 : entry.current_counts, // Converged Iteration Warnings
247 742 : entry.current_timestep_counts, // Latest Time Step Warnings
248 742 : entry.total_counts, // Total Iteration Warnings
249 742 : info.message // Message
250 : );
251 : }
252 : }
253 : }
254 :
255 623 : return vtable;
256 0 : }
257 :
258 : SolutionInvalidity::TimeTable
259 1435 : SolutionInvalidity::transientTable(unsigned int & step_interval) const
260 : {
261 : mooseAssert(_has_synced, "Has not synced");
262 :
263 2870 : TimeTable vtable({"Object", "Step", "Interval Count", "Total Count"}, 4);
264 :
265 2870 : vtable.setColumnFormat({
266 : VariadicTableColumnFormat::AUTO, // Object information
267 : VariadicTableColumnFormat::AUTO, // Simulation Time Step
268 : VariadicTableColumnFormat::AUTO, // Latest Time Step Warnings
269 : VariadicTableColumnFormat::AUTO, // Total Iteration Warnings
270 : });
271 :
272 2870 : vtable.setColumnPrecision({
273 : 1, // Object information
274 : 1, // Simulation Time Step
275 : 0, // Latest Time Step Warnings
276 : 0, // Total Iteration Warnings
277 : });
278 :
279 1435 : if (processor_id() == 0)
280 : {
281 2568 : for (const auto id : index_range(_counts))
282 : {
283 1621 : const auto & entry = _counts[id];
284 1621 : const auto & info = _solution_invalidity_registry.item(id);
285 1621 : std::vector<unsigned int> interval_counts;
286 1621 : std::vector<unsigned int> total_counts;
287 :
288 1621 : if (!entry.timestep_counts.empty())
289 : {
290 : // Allow warnings from the setup step
291 3523 : for (unsigned int timestep = 0; timestep <= entry.timestep_counts.back().timestep_index;
292 1943 : timestep += step_interval)
293 : {
294 :
295 1943 : auto start_it = timestep;
296 1943 : auto end_it = (timestep + step_interval < entry.timestep_counts.back().timestep_index)
297 3705 : ? start_it + step_interval
298 1762 : : entry.timestep_counts.back().timestep_index;
299 :
300 1943 : int interval_sum = 0;
301 4499 : for (auto ts_count : entry.timestep_counts)
302 : {
303 : // Allow warnings from the setup step
304 2556 : if (ts_count.timestep_index >= start_it &&
305 2317 : (ts_count.timestep_index < end_it || start_it == end_it))
306 1714 : interval_sum += ts_count.counts;
307 : }
308 :
309 1943 : interval_counts.push_back(interval_sum);
310 : }
311 : }
312 :
313 1621 : unsigned int interval_sum = 0;
314 3564 : for (unsigned int interval_index : index_range(interval_counts))
315 : {
316 : std::string interval_index_str =
317 1967 : (step_interval > 1) ? std::to_string(interval_index) + "-" +
318 1967 : std::to_string(interval_index + step_interval - 1)
319 1991 : : std::to_string(interval_index);
320 :
321 1943 : interval_sum += interval_counts[interval_index];
322 1943 : vtable.addRow(info.object_type + " : " + info.message, // Object information
323 : interval_index_str, // Interval Index
324 1943 : interval_counts[interval_index], // Interval Counts
325 : interval_sum // Total Iteration Warnings
326 :
327 : );
328 1943 : }
329 1621 : }
330 : }
331 1435 : return vtable;
332 0 : }
333 :
334 : // Define data store structure for TimestepCounts
335 : void
336 422 : dataStore(std::ostream & stream,
337 : SolutionInvalidity::TimestepCounts & timestep_counts,
338 : void * context)
339 : {
340 422 : dataStore(stream, timestep_counts.timestep_index, context);
341 422 : dataStore(stream, timestep_counts.counts, context);
342 422 : }
343 :
344 : // Define data load structure for TimestepCounts
345 : void
346 186 : dataLoad(std::istream & stream,
347 : SolutionInvalidity::TimestepCounts & timestep_counts,
348 : void * context)
349 : {
350 186 : dataLoad(stream, timestep_counts.timestep_index, context);
351 186 : dataLoad(stream, timestep_counts.counts, context);
352 186 : }
353 :
354 : void
355 45637 : dataStore(std::ostream & stream, SolutionInvalidity & solution_invalidity, void * context)
356 : {
357 45637 : solution_invalidity.syncIteration();
358 :
359 45637 : if (solution_invalidity.processor_id() != 0)
360 6566 : return;
361 :
362 : // Build data structure for store
363 39071 : std::size_t size = solution_invalidity._counts.size();
364 39071 : dataStore(stream, size, context);
365 :
366 39485 : for (const auto id : index_range(solution_invalidity._counts))
367 : {
368 414 : auto & entry = solution_invalidity._counts[id];
369 414 : const auto & info = solution_invalidity._solution_invalidity_registry.item(id);
370 414 : std::string type = info.object_type;
371 414 : std::string message = info.message;
372 414 : bool warning = info.warning;
373 414 : dataStore(stream, type, context);
374 414 : dataStore(stream, message, context);
375 414 : dataStore(stream, warning, context);
376 414 : dataStore(stream, entry.current_counts, context);
377 414 : dataStore(stream, entry.current_timestep_counts, context);
378 414 : dataStore(stream, entry.timestep_counts, context);
379 414 : dataStore(stream, entry.total_counts, context);
380 414 : }
381 : }
382 :
383 : void
384 13192 : dataLoad(std::istream & stream, SolutionInvalidity & solution_invalidity, void * context)
385 : {
386 13192 : if (solution_invalidity.processor_id() != 0)
387 2666 : return;
388 :
389 : std::size_t num_counts;
390 : // load data block size
391 10526 : dataLoad(stream, num_counts, context);
392 :
393 10526 : std::string object_type, message;
394 : bool warning;
395 : InvalidSolutionID id;
396 :
397 : // loop over and load stored data
398 10704 : for (size_t i = 0; i < num_counts; i++)
399 : {
400 178 : dataLoad(stream, object_type, context);
401 178 : dataLoad(stream, message, context);
402 178 : dataLoad(stream, warning, context);
403 :
404 178 : const moose::internal::SolutionInvalidityName name(object_type, message);
405 178 : if (solution_invalidity._solution_invalidity_registry.keyExists(name))
406 136 : id = solution_invalidity._solution_invalidity_registry.id(name);
407 : else
408 42 : id = moose::internal::getSolutionInvalidityRegistry().registerInvalidity(
409 : object_type, message, warning);
410 :
411 178 : if (solution_invalidity._counts.size() <= id)
412 42 : solution_invalidity._counts.resize(id + 1);
413 :
414 178 : auto & entry = solution_invalidity._counts[id];
415 178 : dataLoad(stream, entry.current_counts, context);
416 178 : dataLoad(stream, entry.current_timestep_counts, context);
417 178 : dataLoad(stream, entry.timestep_counts, context);
418 178 : dataLoad(stream, entry.total_counts, context);
419 178 : }
420 10526 : }
|