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 67006 : SolutionInvalidity::SolutionInvalidity(MooseApp & app)
28 : : ConsoleStreamInterface(app),
29 : ParallelObject(app.comm()),
30 67006 : _solution_invalidity_registry(moose::internal::getSolutionInvalidityRegistry()),
31 67006 : _has_synced(true),
32 67006 : _has_solution_warning(false),
33 67006 : _has_solution_error(false),
34 134012 : _has_recorded_issue(false)
35 : {
36 67006 : }
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 315302 : SolutionInvalidity::hasInvalidSolutionWarning() const
50 : {
51 : mooseAssert(_has_synced, "Has not synced");
52 315302 : return _has_solution_warning;
53 : }
54 :
55 : bool
56 625779 : SolutionInvalidity::hasInvalidSolutionError() const
57 : {
58 : mooseAssert(_has_synced, "Has not synced");
59 625779 : return _has_solution_error;
60 : }
61 :
62 : bool
63 315302 : SolutionInvalidity::hasInvalidSolution() const
64 : {
65 315302 : return hasInvalidSolutionWarning() || hasInvalidSolutionError();
66 : }
67 :
68 : bool
69 54881 : SolutionInvalidity::hasEverHadSolutionIssue() const
70 : {
71 : mooseAssert(_has_synced, "Has not synced");
72 54881 : return _has_recorded_issue;
73 : }
74 :
75 : void
76 4026157 : SolutionInvalidity::resetIterationOccurences()
77 : {
78 : // Zero current counts
79 4484649 : for (auto & entry : _counts)
80 458492 : entry.current_counts = 0;
81 4026157 : }
82 :
83 : void
84 308332 : SolutionInvalidity::resetTimeStepOccurences()
85 : {
86 : // Reset that we have synced because we're on a new iteration
87 308332 : _has_synced = false;
88 :
89 341272 : for (auto & entry : _counts)
90 32940 : entry.current_timestep_counts = 0;
91 308332 : }
92 :
93 : void
94 4204383 : SolutionInvalidity::accumulateIterationIntoTimeStepOccurences()
95 : {
96 4705112 : for (auto & entry : _counts)
97 500729 : entry.current_timestep_counts += entry.current_counts;
98 4204383 : }
99 :
100 : void
101 360646 : SolutionInvalidity::accumulateTimeStepIntoTotalOccurences(const unsigned int timestep_index)
102 : {
103 395984 : 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 360646 : }
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 1426 : SolutionInvalidity::printHistory(const ConsoleStream & console,
123 : unsigned int & timestep_interval_size) const
124 : {
125 1426 : if (hasInvalidSolutionError())
126 45 : console << "\nSolution Invalid History:\n";
127 : else
128 1381 : console << "\nWarnings History:\n";
129 1426 : transientTable(timestep_interval_size).print(console);
130 1426 : }
131 :
132 : void
133 4565619 : SolutionInvalidity::syncIteration()
134 : {
135 : std::map<processor_id_type, std::vector<std::tuple<std::string, std::string, int, unsigned int>>>
136 4565619 : data_to_send;
137 :
138 : // Reset this as we need to see if we have new counts
139 4565619 : _has_solution_warning = false;
140 4565619 : _has_solution_error = false;
141 :
142 5100457 : for (const auto id : index_range(_counts))
143 : {
144 534838 : auto & entry = _counts[id];
145 534838 : if (entry.current_counts)
146 : {
147 7512 : const auto & info = _solution_invalidity_registry.item(id);
148 7512 : data_to_send[0].emplace_back(
149 7512 : info.object_type, info.message, info.warning, entry.current_counts);
150 7512 : entry.current_counts = 0;
151 : }
152 : }
153 :
154 4249 : 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 11761 : 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 7512 : const bool warning = warning_int;
165 :
166 7512 : InvalidSolutionID main_id = 0;
167 7512 : const moose::internal::SolutionInvalidityName name(object_type, message);
168 7512 : if (_solution_invalidity_registry.keyExists(name))
169 : {
170 7503 : 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 7512 : if (_counts.size() <= main_id)
181 9 : _counts.resize(main_id + 1);
182 :
183 7512 : _counts[main_id].current_counts += counts;
184 :
185 7512 : if (warning)
186 3326 : _has_solution_warning = true;
187 : else
188 4186 : _has_solution_error = true;
189 : }
190 4249 : };
191 :
192 : // Communicate the counts
193 4565619 : TIMPI::push_parallel_vector_data(comm(), data_to_send, receive_data);
194 :
195 : // Set the state across all processors
196 4565619 : comm().max(_has_solution_warning);
197 4565619 : comm().max(_has_solution_error);
198 :
199 : // Keep track of any occurence
200 4565619 : if (_has_solution_warning || _has_solution_error)
201 4738 : _has_recorded_issue = true;
202 :
203 : // We've now synced
204 4565619 : _has_synced = true;
205 4565619 : }
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 1426 : SolutionInvalidity::transientTable(unsigned int & step_interval) const
260 : {
261 : mooseAssert(_has_synced, "Has not synced");
262 :
263 2852 : TimeTable vtable({"Object", "Step", "Interval Count", "Total Count"}, 4);
264 :
265 2852 : 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 2852 : 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 1426 : if (processor_id() == 0)
280 : {
281 2544 : for (const auto id : index_range(_counts))
282 : {
283 1605 : const auto & entry = _counts[id];
284 1605 : const auto & info = _solution_invalidity_registry.item(id);
285 1605 : std::vector<unsigned int> interval_counts;
286 1605 : std::vector<unsigned int> total_counts;
287 :
288 1605 : if (!entry.timestep_counts.empty())
289 : {
290 : // Allow warnings from the setup step
291 3491 : for (unsigned int timestep = 0; timestep <= entry.timestep_counts.back().timestep_index;
292 1927 : timestep += step_interval)
293 : {
294 :
295 1927 : auto start_it = timestep;
296 1927 : auto end_it = (timestep + step_interval < entry.timestep_counts.back().timestep_index)
297 3673 : ? start_it + step_interval
298 1746 : : entry.timestep_counts.back().timestep_index;
299 :
300 1927 : int interval_sum = 0;
301 4467 : for (auto ts_count : entry.timestep_counts)
302 : {
303 : // Allow warnings from the setup step
304 2540 : if (ts_count.timestep_index >= start_it &&
305 2301 : (ts_count.timestep_index < end_it || start_it == end_it))
306 1698 : interval_sum += ts_count.counts;
307 : }
308 :
309 1927 : interval_counts.push_back(interval_sum);
310 : }
311 : }
312 :
313 1605 : unsigned int interval_sum = 0;
314 3532 : for (unsigned int interval_index : index_range(interval_counts))
315 : {
316 : std::string interval_index_str =
317 1951 : (step_interval > 1) ? std::to_string(interval_index) + "-" +
318 1951 : std::to_string(interval_index + step_interval - 1)
319 1975 : : std::to_string(interval_index);
320 :
321 1927 : interval_sum += interval_counts[interval_index];
322 1927 : vtable.addRow(info.object_type + " : " + info.message, // Object information
323 : interval_index_str, // Interval Index
324 1927 : interval_counts[interval_index], // Interval Counts
325 : interval_sum // Total Iteration Warnings
326 :
327 : );
328 1927 : }
329 1605 : }
330 : }
331 1426 : 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 : }
|