https://mooseframework.inl.gov
Loading...
Searching...
No Matches
SolutionInvalidity.C
Go to the documentation of this file.
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
29 ParallelObject(app.comm()),
30 _solution_invalidity_registry(moose::internal::getSolutionInvalidityRegistry()),
31 _has_synced(true),
32 _has_solution_warning(false),
33 _has_solution_error(false),
34 _has_recorded_issue(false)
35{
36}
37
38void
40{
41 std::lock_guard<std::mutex> lock_id(_invalid_mutex);
42 if (_counts.size() <= _invalid_solution_id)
43 _counts.resize(_invalid_solution_id + 1);
44
45 ++_counts[_invalid_solution_id].current_counts;
46}
47
48bool
50{
51 mooseAssert(_has_synced, "Has not synced");
53}
54
55bool
57{
58 mooseAssert(_has_synced, "Has not synced");
60}
61
62bool
67
68bool
70{
71 mooseAssert(_has_synced, "Has not synced");
73}
74
75void
77{
78 // Zero current counts
79 for (auto & entry : _counts)
80 entry.current_counts = 0;
81}
82
83void
85{
86 // Reset that we have synced because we're on a new iteration
87 _has_synced = false;
88
89 for (auto & entry : _counts)
90 entry.current_timestep_counts = 0;
91}
92
93void
95{
96 for (auto & entry : _counts)
97 entry.current_timestep_counts += entry.current_counts;
98}
99
100void
102{
103 for (auto & entry : _counts)
104 if (entry.current_timestep_counts)
105 {
106 if (entry.timestep_counts.empty() ||
107 entry.timestep_counts.back().timestep_index != timestep_index)
108 entry.timestep_counts.emplace_back(timestep_index);
109 entry.timestep_counts.back().counts = entry.current_timestep_counts;
110 entry.total_counts += entry.current_timestep_counts;
111 }
112}
113
114void
116{
117 console << "\nSolution Invalid Warnings:\n";
118 summaryTable().print(console);
119}
120
121void
123 unsigned int & timestep_interval_size) const
124{
126 console << "\nSolution Invalid History:\n";
127 else
128 console << "\nWarnings History:\n";
129 transientTable(timestep_interval_size).print(console);
130}
131
132void
134{
135 std::map<processor_id_type, std::vector<std::tuple<std::string, std::string, int, unsigned int>>>
136 data_to_send;
137
138 // Reset this as we need to see if we have new counts
139 _has_solution_warning = false;
140 _has_solution_error = false;
141
142 for (const auto id : index_range(_counts))
143 {
144 auto & entry = _counts[id];
145 if (entry.current_counts)
146 {
147 const auto & info = _solution_invalidity_registry.item(id);
148 data_to_send[0].emplace_back(
149 info.object_type, info.message, info.warning, entry.current_counts);
150 entry.current_counts = 0;
151 }
152 }
153
154 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 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 const bool warning = warning_int;
165
166 InvalidSolutionID main_id = 0;
167 const moose::internal::SolutionInvalidityName name(object_type, message);
169 {
170 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");
178 object_type, message, warning);
179 }
180 if (_counts.size() <= main_id)
181 _counts.resize(main_id + 1);
182
183 _counts[main_id].current_counts += counts;
184
185 if (warning)
187 else
188 _has_solution_error = true;
189 }
190 };
191
192 // Communicate the counts
193 TIMPI::push_parallel_vector_data(comm(), data_to_send, receive_data);
194
195 // Set the state across all processors
198
199 // Keep track of any occurence
201 _has_recorded_issue = true;
202
203 // We've now synced
204 _has_synced = true;
205}
206
207void
209{
210 const auto & info = _solution_invalidity_registry.item(_invalid_solution_id);
211 _console << info.object_type << ": " << info.message << "\n" << std::flush;
212}
213
216{
217 mooseAssert(_has_synced, "Has not synced");
218
219 FullTable vtable({"Object", "Converged", "Timestep", "Total", "Message"}, 4);
220
221 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
227 });
228
229 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 if (processor_id() == 0)
238 {
239 for (const auto id : index_range(_counts))
240 {
241 const auto & entry = _counts[id];
242 if (entry.current_counts)
243 {
244 const auto & info = _solution_invalidity_registry.item(id);
245 vtable.addRow(info.object_type, // Object Type
246 entry.current_counts, // Converged Iteration Warnings
247 entry.current_timestep_counts, // Latest Time Step Warnings
248 entry.total_counts, // Total Iteration Warnings
249 info.message // Message
250 );
251 }
252 }
253 }
254
255 return vtable;
256}
257
259SolutionInvalidity::transientTable(unsigned int & step_interval) const
260{
261 mooseAssert(_has_synced, "Has not synced");
262
263 TimeTable vtable({"Object", "Step", "Interval Count", "Total Count"}, 4);
264
265 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 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 if (processor_id() == 0)
280 {
281 for (const auto id : index_range(_counts))
282 {
283 const auto & entry = _counts[id];
284 const auto & info = _solution_invalidity_registry.item(id);
285 std::vector<unsigned int> interval_counts;
286 std::vector<unsigned int> total_counts;
287
288 if (!entry.timestep_counts.empty())
289 {
290 // Allow warnings from the setup step
291 for (unsigned int timestep = 0; timestep <= entry.timestep_counts.back().timestep_index;
292 timestep += step_interval)
293 {
294
295 auto start_it = timestep;
296 auto end_it = (timestep + step_interval < entry.timestep_counts.back().timestep_index)
297 ? start_it + step_interval
298 : entry.timestep_counts.back().timestep_index;
299
300 int interval_sum = 0;
301 for (auto ts_count : entry.timestep_counts)
302 {
303 // Allow warnings from the setup step
304 if (ts_count.timestep_index >= start_it &&
305 (ts_count.timestep_index < end_it || start_it == end_it))
306 interval_sum += ts_count.counts;
307 }
308
309 interval_counts.push_back(interval_sum);
310 }
311 }
312
313 unsigned int interval_sum = 0;
314 for (unsigned int interval_index : index_range(interval_counts))
315 {
316 std::string interval_index_str =
317 (step_interval > 1) ? std::to_string(interval_index) + "-" +
318 std::to_string(interval_index + step_interval - 1)
319 : std::to_string(interval_index);
320
321 interval_sum += interval_counts[interval_index];
322 vtable.addRow(info.object_type + " : " + info.message, // Object information
323 interval_index_str, // Interval Index
324 interval_counts[interval_index], // Interval Counts
325 interval_sum // Total Iteration Warnings
326
327 );
328 }
329 }
330 }
331 return vtable;
332}
333
334// Define data store structure for TimestepCounts
335void
336dataStore(std::ostream & stream,
337 SolutionInvalidity::TimestepCounts & timestep_counts,
338 void * context)
339{
340 dataStore(stream, timestep_counts.timestep_index, context);
341 dataStore(stream, timestep_counts.counts, context);
342}
343
344// Define data load structure for TimestepCounts
345void
346dataLoad(std::istream & stream,
347 SolutionInvalidity::TimestepCounts & timestep_counts,
348 void * context)
349{
350 dataLoad(stream, timestep_counts.timestep_index, context);
351 dataLoad(stream, timestep_counts.counts, context);
352}
353
354void
355dataStore(std::ostream & stream, SolutionInvalidity & solution_invalidity, void * context)
356{
357 solution_invalidity.syncIteration();
358
359 if (solution_invalidity.processor_id() != 0)
360 return;
361
362 // Build data structure for store
363 std::size_t size = solution_invalidity._counts.size();
364 dataStore(stream, size, context);
365
366 for (const auto id : index_range(solution_invalidity._counts))
367 {
368 auto & entry = solution_invalidity._counts[id];
369 const auto & info = solution_invalidity._solution_invalidity_registry.item(id);
370 std::string type = info.object_type;
371 std::string message = info.message;
372 bool warning = info.warning;
373 dataStore(stream, type, context);
374 dataStore(stream, message, context);
375 dataStore(stream, warning, context);
376 dataStore(stream, entry.current_counts, context);
377 dataStore(stream, entry.current_timestep_counts, context);
378 dataStore(stream, entry.timestep_counts, context);
379 dataStore(stream, entry.total_counts, context);
380 }
381}
382
383void
384dataLoad(std::istream & stream, SolutionInvalidity & solution_invalidity, void * context)
385{
386 if (solution_invalidity.processor_id() != 0)
387 return;
388
389 std::size_t num_counts;
390 // load data block size
391 dataLoad(stream, num_counts, context);
392
393 std::string object_type, message;
394 bool warning;
396
397 // loop over and load stored data
398 for (size_t i = 0; i < num_counts; i++)
399 {
400 dataLoad(stream, object_type, context);
401 dataLoad(stream, message, context);
402 dataLoad(stream, warning, context);
403
404 const moose::internal::SolutionInvalidityName name(object_type, message);
405 if (solution_invalidity._solution_invalidity_registry.keyExists(name))
406 id = solution_invalidity._solution_invalidity_registry.id(name);
407 else
409 object_type, message, warning);
410
411 if (solution_invalidity._counts.size() <= id)
412 solution_invalidity._counts.resize(id + 1);
413
414 auto & entry = solution_invalidity._counts[id];
415 dataLoad(stream, entry.current_counts, context);
416 dataLoad(stream, entry.current_timestep_counts, context);
417 dataLoad(stream, entry.timestep_counts, context);
418 dataLoad(stream, entry.total_counts, context);
419 }
420}
unsigned int InvalidSolutionID
Definition MooseTypes.h:241
void dataStore(std::ostream &stream, SolutionInvalidity::TimestepCounts &timestep_counts, void *context)
void dataLoad(std::istream &stream, SolutionInvalidity::TimestepCounts &timestep_counts, void *context)
An inteface for the _console for outputting to the Console object.
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
A helper class for re-directing output streams to Console output objects form MooseObjects.
bool keyExists(const Key &key) const
std::size_t id(const Key &key) const
const Item & item(const std::size_t id) const
Base class for MOOSE-based applications.
Definition MooseApp.h:110
The SolutionInvalidity will contain all the information about the occurrence(s) of solution invalidit...
SolutionInvalidityRegistry & _solution_invalidity_registry
Create a registry to keep track of the names and occurrences of the solution invalidity.
void printDebug(InvalidSolutionID _invalid_solution_id) const
Immediately print the section and message for debug purpose.
void flagInvalidSolutionInternal(const InvalidSolutionID _invalid_solution_id)
Increments solution invalid occurrences for each solution id.
SolutionInvalidity(MooseApp &app)
Create a new SolutionInvalidity.
const std::vector< InvalidCounts > & counts() const
Access the private solution invalidity counts.
bool _has_solution_error
Whether or not we have an invalid solution (only after a sync)
std::vector< InvalidCounts > _counts
Store the solution invalidity counts.
bool _has_recorded_issue
Whether or not we have ever had any warning or solution issue during the simulation.
bool hasInvalidSolutionError() const
Whether or not an invalid solution was encountered that was an error.
void accumulateIterationIntoTimeStepOccurences()
Pass the number of solution invalid occurrences from current iteration to cumulative counters.
TimeTable transientTable(unsigned int &time_interval) const
Build a VariadicTable for solution invalidity history.
void resetTimeStepOccurences()
Reset the number of solution invalid occurrences back to zero for the current time step.
void resetIterationOccurences()
Reset the number of solution invalid occurrences back to zero.
void print(const ConsoleStream &console) const
Print the summary table of Solution Invalid warnings.
void accumulateTimeStepIntoTotalOccurences(const unsigned int timestep_index)
Pass the number of solution invalid occurrences from current timestep to cumulative timestep counter ...
void syncIteration()
Sync iteration counts to main processor Sum across all processors.
bool hasInvalidSolutionWarning() const
Whether or not an invalid solution was encountered that was a warning.
std::mutex _invalid_mutex
Mutex for locking access to the invalid counts TODO: These can be changed to shared_mutexes.
bool hasEverHadSolutionIssue() const
Whether or not any warning or invalid solution has ever been encountered during the simulation.
bool _has_solution_warning
Whether or not we have a warning (only after a sync)
bool _has_synced
Whether or not we've synced (can check counts/existance of warnings or errors)
bool hasInvalidSolution() const
Whether or not any invalid solution was encountered (error or warning).
FullTable summaryTable() const
Build a VariadicTable for solution invalidity.
void printHistory(const ConsoleStream &console, unsigned int &timestep_interval_size) const
Print the time history table of Solution Invalid warnings.
void max(const T &r, T &o, Request &req) const
A class for "pretty printing" a table of data.
void print(StreamType &stream)
Pretty print the table of data.
void setColumnFormat(const std::vector< VariadicTableColumnFormat > &column_format)
Set how to format numbers for each column.
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
Helper class that stores the name associated with an invalid solution.
InvalidSolutionID registerInvalidity(const std::string &object_type, const std::string &message, const bool warning)
Call to register an invalid calculation.
void push_parallel_vector_data(const Communicator &comm, MapToVectors &&data, const ActionFunctor &act_on_data)
SolutionInvalidityRegistry & getSolutionInvalidityRegistry()
Get the global SolutionInvalidityRegistry singleton.
Struct used in InvalidCounts for storing the time history of invalid occurrences.