https://mooseframework.inl.gov
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 
38 void
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 
48 bool
50 {
51  mooseAssert(_has_synced, "Has not synced");
52  return _has_solution_warning;
53 }
54 
55 bool
57 {
58  mooseAssert(_has_synced, "Has not synced");
59  return _has_solution_error;
60 }
61 
62 bool
64 {
66 }
67 
68 bool
70 {
71  mooseAssert(_has_synced, "Has not synced");
72  return _has_recorded_issue;
73 }
74 
75 void
77 {
78  // Zero current counts
79  for (auto & entry : _counts)
80  entry.current_counts = 0;
81 }
82 
83 void
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 
93 void
95 {
96  for (auto & entry : _counts)
97  entry.current_timestep_counts += entry.current_counts;
98 }
99 
100 void
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 
114 void
116 {
117  console << "\nSolution Invalid Warnings:\n";
118  summaryTable().print(console);
119 }
120 
121 void
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 
132 void
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  {
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)
186  _has_solution_warning = true;
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 
207 void
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 
259 SolutionInvalidity::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
335 void
336 dataStore(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
345 void
346 dataLoad(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 
354 void
355 dataStore(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 
383 void
384 dataLoad(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 }
std::string name(const ElemQuality q)
bool hasInvalidSolutionError() const
Whether or not an invalid solution was encountered that was an error.
bool hasEverHadSolutionIssue() const
Whether or not any warning or invalid solution has ever been encountered during the simulation...
A helper class for re-directing output streams to Console output objects form MooseObjects.
Definition: ConsoleStream.h:30
void dataStore(std::ostream &stream, SolutionInvalidity::TimestepCounts &timestep_counts, void *context)
void accumulateTimeStepIntoTotalOccurences(const unsigned int timestep_index)
Pass the number of solution invalid occurrences from current timestep to cumulative timestep counter ...
bool _has_recorded_issue
Whether or not we have ever had any warning or solution issue during the simulation.
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.
const std::vector< InvalidCounts > & counts() const
Access the private solution invalidity counts.
void print(StreamType &stream)
Pretty print the table of data.
Definition: VariadicTable.h:98
MPI_Info info
unsigned int InvalidSolutionID
Definition: MooseTypes.h:241
void accumulateIterationIntoTimeStepOccurences()
Pass the number of solution invalid occurrences from current iteration to cumulative counters...
std::vector< InvalidCounts > _counts
Store the solution invalidity counts.
void dataLoad(std::istream &stream, SolutionInvalidity::TimestepCounts &timestep_counts, void *context)
SolutionInvalidity(MooseApp &app)
Create a new SolutionInvalidity.
bool _has_synced
Whether or not we&#39;ve synced (can check counts/existance of warnings or errors)
A class for "pretty printing" a table of data.
Definition: PerfGraph.h:34
void printDebug(InvalidSolutionID _invalid_solution_id) const
Immediately print the section and message for debug purpose.
Base class for MOOSE-based applications.
Definition: MooseApp.h:108
SolutionInvalidityRegistry & _solution_invalidity_registry
Create a registry to keep track of the names and occurrences of the solution invalidity.
const Parallel::Communicator & comm() const
InvalidSolutionID registerInvalidity(const std::string &object_type, const std::string &message, const bool warning)
Call to register an invalid calculation.
void resetIterationOccurences()
Reset the number of solution invalid occurrences back to zero.
TimeTable transientTable(unsigned int &time_interval) const
Build a VariadicTable for solution invalidity history.
void push_parallel_vector_data(const Communicator &comm, MapToVectors &&data, const ActionFunctor &act_on_data)
uint8_t processor_id_type
void syncIteration()
Sync iteration counts to main processor Sum across all processors.
std::size_t id(const Key &key) const
bool _has_solution_warning
Whether or not we have a warning (only after a sync)
const Item & item(const std::size_t id) const
An inteface for the _console for outputting to the Console object.
bool hasInvalidSolution() const
Whether or not any invalid solution was encountered (error or warning).
Helper class that stores the name associated with an invalid solution.
void flagInvalidSolutionInternal(const InvalidSolutionID _invalid_solution_id)
Increments solution invalid occurrences for each solution id.
void setColumnFormat(const std::vector< VariadicTableColumnFormat > &column_format)
Set how to format numbers for each column.
bool _has_solution_error
Whether or not we have an invalid solution (only after a sync)
The SolutionInvalidity will contain all the information about the occurrence(s) of solution invalidit...
Struct used in InvalidCounts for storing the time history of invalid occurrences. ...
SolutionInvalidityRegistry & getSolutionInvalidityRegistry()
Get the global SolutionInvalidityRegistry singleton.
void max(const T &r, T &o, Request &req) const
void print(const ConsoleStream &console) const
Print the summary table of Solution Invalid warnings.
bool keyExists(const Key &key) const
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
void resetTimeStepOccurences()
Reset the number of solution invalid occurrences back to zero for the current time step...
void printHistory(const ConsoleStream &console, unsigned int &timestep_interval_size) const
Print the time history table of Solution Invalid warnings.
processor_id_type processor_id() const
auto index_range(const T &sizable)
FullTable summaryTable() const
Build a VariadicTable for solution invalidity.