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 67402 : SolutionInvalidity::SolutionInvalidity(MooseApp & app)
28 : : ConsoleStreamInterface(app),
29 : ParallelObject(app.comm()),
30 67402 : _solution_invalidity_registry(moose::internal::getSolutionInvalidityRegistry()),
31 67402 : _has_synced(true),
32 67402 : _has_solution_warning(false),
33 67402 : _has_solution_error(false),
34 134804 : _has_recorded_issue(false)
35 : {
36 67402 : }
37 :
38 : void
39 38912 : SolutionInvalidity::flagInvalidSolutionInternal(const InvalidSolutionID _invalid_solution_id)
40 : {
41 38912 : std::lock_guard<std::mutex> lock_id(_invalid_mutex);
42 38912 : if (_counts.size() <= _invalid_solution_id)
43 2800 : _counts.resize(_invalid_solution_id + 1);
44 :
45 38912 : ++_counts[_invalid_solution_id].current_counts;
46 38912 : }
47 :
48 : bool
49 315809 : SolutionInvalidity::hasInvalidSolutionWarning() const
50 : {
51 : mooseAssert(_has_synced, "Has not synced");
52 315809 : return _has_solution_warning;
53 : }
54 :
55 : bool
56 626822 : SolutionInvalidity::hasInvalidSolutionError() const
57 : {
58 : mooseAssert(_has_synced, "Has not synced");
59 626822 : return _has_solution_error;
60 : }
61 :
62 : bool
63 315809 : SolutionInvalidity::hasInvalidSolution() const
64 : {
65 315809 : return hasInvalidSolutionWarning() || hasInvalidSolutionError();
66 : }
67 :
68 : bool
69 56035 : SolutionInvalidity::hasEverHadSolutionIssue() const
70 : {
71 : mooseAssert(_has_synced, "Has not synced");
72 56035 : return _has_recorded_issue;
73 : }
74 :
75 : void
76 4038862 : SolutionInvalidity::resetIterationOccurences()
77 : {
78 : // Zero current counts
79 4498785 : for (auto & entry : _counts)
80 459923 : entry.current_counts = 0;
81 4038862 : }
82 :
83 : void
84 311871 : SolutionInvalidity::resetTimeStepOccurences()
85 : {
86 : // Reset that we have synced because we're on a new iteration
87 311871 : _has_synced = false;
88 :
89 344999 : for (auto & entry : _counts)
90 33128 : entry.current_timestep_counts = 0;
91 311871 : }
92 :
93 : void
94 4217738 : SolutionInvalidity::accumulateIterationIntoTimeStepOccurences()
95 : {
96 4719909 : for (auto & entry : _counts)
97 502171 : entry.current_timestep_counts += entry.current_counts;
98 4217738 : }
99 :
100 : void
101 364843 : SolutionInvalidity::accumulateTimeStepIntoTotalOccurences(const unsigned int timestep_index)
102 : {
103 400380 : for (auto & entry : _counts)
104 35537 : if (entry.current_timestep_counts)
105 : {
106 3099 : if (entry.timestep_counts.empty() ||
107 668 : entry.timestep_counts.back().timestep_index != timestep_index)
108 1885 : entry.timestep_counts.emplace_back(timestep_index);
109 2431 : entry.timestep_counts.back().counts = entry.current_timestep_counts;
110 2431 : entry.total_counts += entry.current_timestep_counts;
111 : }
112 364843 : }
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 1445 : SolutionInvalidity::printHistory(const ConsoleStream & console,
123 : unsigned int & timestep_interval_size) const
124 : {
125 1445 : if (hasInvalidSolutionError())
126 45 : console << "\nSolution Invalid History:\n";
127 : else
128 1400 : console << "\nWarnings History:\n";
129 1445 : transientTable(timestep_interval_size).print(console);
130 1445 : }
131 :
132 : void
133 4636524 : SolutionInvalidity::syncIteration()
134 : {
135 : std::map<processor_id_type, std::vector<std::tuple<std::string, std::string, int, unsigned int>>>
136 4636524 : data_to_send;
137 :
138 : // Reset this as we need to see if we have new counts
139 4636524 : _has_solution_warning = false;
140 4636524 : _has_solution_error = false;
141 :
142 5175441 : for (const auto id : index_range(_counts))
143 : {
144 538917 : auto & entry = _counts[id];
145 538917 : if (entry.current_counts)
146 : {
147 7605 : const auto & info = _solution_invalidity_registry.item(id);
148 7605 : data_to_send[0].emplace_back(
149 7605 : info.object_type, info.message, info.warning, entry.current_counts);
150 7605 : entry.current_counts = 0;
151 : }
152 : }
153 :
154 4307 : 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 11912 : 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 7605 : const bool warning = warning_int;
165 :
166 7605 : InvalidSolutionID main_id = 0;
167 7605 : const moose::internal::SolutionInvalidityName name(object_type, message);
168 7605 : if (_solution_invalidity_registry.keyExists(name))
169 : {
170 7596 : 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 7605 : if (_counts.size() <= main_id)
181 9 : _counts.resize(main_id + 1);
182 :
183 7605 : _counts[main_id].current_counts += counts;
184 :
185 7605 : if (warning)
186 3371 : _has_solution_warning = true;
187 : else
188 4234 : _has_solution_error = true;
189 : }
190 4307 : };
191 :
192 : // Communicate the counts
193 4636524 : TIMPI::push_parallel_vector_data(comm(), data_to_send, receive_data);
194 :
195 : // Set the state across all processors
196 4636524 : comm().max(_has_solution_warning);
197 4636524 : comm().max(_has_solution_error);
198 :
199 : // Keep track of any occurence
200 4636524 : if (_has_solution_warning || _has_solution_error)
201 4824 : _has_recorded_issue = true;
202 :
203 : // We've now synced
204 4636524 : _has_synced = true;
205 4636524 : }
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 1445 : SolutionInvalidity::transientTable(unsigned int & step_interval) const
260 : {
261 : mooseAssert(_has_synced, "Has not synced");
262 :
263 2890 : TimeTable vtable({"Object", "Step", "Interval Count", "Total Count"}, 4);
264 :
265 2890 : 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 2890 : 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 1445 : if (processor_id() == 0)
280 : {
281 2582 : for (const auto id : index_range(_counts))
282 : {
283 1628 : const auto & entry = _counts[id];
284 1628 : const auto & info = _solution_invalidity_registry.item(id);
285 1628 : std::vector<unsigned int> interval_counts;
286 1628 : std::vector<unsigned int> total_counts;
287 :
288 1628 : if (!entry.timestep_counts.empty())
289 : {
290 : // Allow warnings from the setup step
291 3537 : for (unsigned int timestep = 0; timestep <= entry.timestep_counts.back().timestep_index;
292 1950 : timestep += step_interval)
293 : {
294 :
295 1950 : auto start_it = timestep;
296 1950 : auto end_it = (timestep + step_interval < entry.timestep_counts.back().timestep_index)
297 3719 : ? start_it + step_interval
298 1769 : : entry.timestep_counts.back().timestep_index;
299 :
300 1950 : int interval_sum = 0;
301 4513 : for (auto ts_count : entry.timestep_counts)
302 : {
303 : // Allow warnings from the setup step
304 2563 : if (ts_count.timestep_index >= start_it &&
305 2324 : (ts_count.timestep_index < end_it || start_it == end_it))
306 1721 : interval_sum += ts_count.counts;
307 : }
308 :
309 1950 : interval_counts.push_back(interval_sum);
310 : }
311 : }
312 :
313 1628 : unsigned int interval_sum = 0;
314 3578 : for (unsigned int interval_index : index_range(interval_counts))
315 : {
316 : std::string interval_index_str =
317 1974 : (step_interval > 1) ? std::to_string(interval_index) + "-" +
318 1974 : std::to_string(interval_index + step_interval - 1)
319 1998 : : std::to_string(interval_index);
320 :
321 1950 : interval_sum += interval_counts[interval_index];
322 1950 : vtable.addRow(info.object_type + " : " + info.message, // Object information
323 : interval_index_str, // Interval Index
324 1950 : interval_counts[interval_index], // Interval Counts
325 : interval_sum // Total Iteration Warnings
326 :
327 : );
328 1950 : }
329 1628 : }
330 : }
331 1445 : return vtable;
332 0 : }
333 :
334 : // Define data store structure for TimestepCounts
335 : void
336 431 : dataStore(std::ostream & stream,
337 : SolutionInvalidity::TimestepCounts & timestep_counts,
338 : void * context)
339 : {
340 431 : dataStore(stream, timestep_counts.timestep_index, context);
341 431 : dataStore(stream, timestep_counts.counts, context);
342 431 : }
343 :
344 : // Define data load structure for TimestepCounts
345 : void
346 187 : dataLoad(std::istream & stream,
347 : SolutionInvalidity::TimestepCounts & timestep_counts,
348 : void * context)
349 : {
350 187 : dataLoad(stream, timestep_counts.timestep_index, context);
351 187 : dataLoad(stream, timestep_counts.counts, context);
352 187 : }
353 :
354 : void
355 46616 : dataStore(std::ostream & stream, SolutionInvalidity & solution_invalidity, void * context)
356 : {
357 46616 : solution_invalidity.syncIteration();
358 :
359 46616 : if (solution_invalidity.processor_id() != 0)
360 6866 : return;
361 :
362 : // Build data structure for store
363 39750 : std::size_t size = solution_invalidity._counts.size();
364 39750 : dataStore(stream, size, context);
365 :
366 40173 : for (const auto id : index_range(solution_invalidity._counts))
367 : {
368 423 : auto & entry = solution_invalidity._counts[id];
369 423 : const auto & info = solution_invalidity._solution_invalidity_registry.item(id);
370 423 : std::string type = info.object_type;
371 423 : std::string message = info.message;
372 423 : bool warning = info.warning;
373 423 : dataStore(stream, type, context);
374 423 : dataStore(stream, message, context);
375 423 : dataStore(stream, warning, context);
376 423 : dataStore(stream, entry.current_counts, context);
377 423 : dataStore(stream, entry.current_timestep_counts, context);
378 423 : dataStore(stream, entry.timestep_counts, context);
379 423 : dataStore(stream, entry.total_counts, context);
380 423 : }
381 : }
382 :
383 : void
384 12935 : dataLoad(std::istream & stream, SolutionInvalidity & solution_invalidity, void * context)
385 : {
386 12935 : if (solution_invalidity.processor_id() != 0)
387 2666 : return;
388 :
389 : std::size_t num_counts;
390 : // load data block size
391 10269 : dataLoad(stream, num_counts, context);
392 :
393 10269 : std::string object_type, message;
394 : bool warning;
395 : InvalidSolutionID id;
396 :
397 : // loop over and load stored data
398 10448 : for (size_t i = 0; i < num_counts; i++)
399 : {
400 179 : dataLoad(stream, object_type, context);
401 179 : dataLoad(stream, message, context);
402 179 : dataLoad(stream, warning, context);
403 :
404 179 : const moose::internal::SolutionInvalidityName name(object_type, message);
405 179 : if (solution_invalidity._solution_invalidity_registry.keyExists(name))
406 137 : id = solution_invalidity._solution_invalidity_registry.id(name);
407 : else
408 42 : id = moose::internal::getSolutionInvalidityRegistry().registerInvalidity(
409 : object_type, message, warning);
410 :
411 179 : if (solution_invalidity._counts.size() <= id)
412 42 : solution_invalidity._counts.resize(id + 1);
413 :
414 179 : auto & entry = solution_invalidity._counts[id];
415 179 : dataLoad(stream, entry.current_counts, context);
416 179 : dataLoad(stream, entry.current_timestep_counts, context);
417 179 : dataLoad(stream, entry.timestep_counts, context);
418 179 : dataLoad(stream, entry.total_counts, context);
419 179 : }
420 10269 : }
|