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 {
35 }
36 
37 void
39 {
40  std::lock_guard<std::mutex> lock_id(_invalid_mutex);
41  if (_counts.size() <= _invalid_solution_id)
42  _counts.resize(_invalid_solution_id + 1);
43 
44  ++_counts[_invalid_solution_id].current_counts;
45 }
46 
47 bool
49 {
50  mooseAssert(_has_synced, "Has not synced");
51  return _has_solution_warning;
52 }
53 
54 bool
56 {
57  mooseAssert(_has_synced, "Has not synced");
58  return _has_solution_error;
59 }
60 
61 bool
63 {
65 }
66 
67 void
69 {
70  // Zero current counts
71  for (auto & entry : _counts)
72  entry.current_counts = 0;
73 }
74 
75 void
77 {
78  // Reset that we have synced because we're on a new iteration
79  _has_synced = false;
80 
81  for (auto & entry : _counts)
82  entry.current_timestep_counts = 0;
83 }
84 
85 void
87 {
88  for (auto & entry : _counts)
89  entry.current_timestep_counts += entry.current_counts;
90 }
91 
92 void
94 {
95  for (auto & entry : _counts)
96  if (entry.current_timestep_counts)
97  {
98  if (entry.timestep_counts.empty() ||
99  entry.timestep_counts.back().timestep_index != timestep_index)
100  entry.timestep_counts.emplace_back(timestep_index);
101  entry.timestep_counts.back().counts = entry.current_timestep_counts;
102  entry.total_counts += entry.current_timestep_counts;
103  }
104 }
105 
106 void
108 {
109  mooseAssert(_has_synced, "Has not synced");
110 
111  for (auto & entry : _counts)
112  {
113  entry.total_counts = 0;
114  for (auto & time_counts : entry.timestep_counts)
115  entry.total_counts += time_counts.counts;
116  }
117 }
118 
119 void
121 {
122  console << "\nSolution Invalid Warnings:\n";
123  summaryTable().print(console);
124 }
125 
126 void
128  unsigned int & timestep_interval_size) const
129 {
130  console << "\nSolution Invalid Warnings History:\n";
131  transientTable(timestep_interval_size).print(console);
132 }
133 
134 void
136 {
137  std::map<processor_id_type, std::vector<std::tuple<std::string, std::string, int, unsigned int>>>
138  data_to_send;
139 
140  // Reset this as we need to see if we have new counts
141  _has_solution_warning = false;
142  _has_solution_error = false;
143 
144  for (const auto id : index_range(_counts))
145  {
146  auto & entry = _counts[id];
147  if (entry.current_counts)
148  {
149  const auto & info = _solution_invalidity_registry.item(id);
150  data_to_send[0].emplace_back(
151  info.object_type, info.message, info.warning, entry.current_counts);
152  entry.current_counts = 0;
153  }
154  }
155 
156  const auto receive_data = [this](const processor_id_type libmesh_dbg_var(pid), const auto & data)
157  {
158  mooseAssert(processor_id() == 0, "Should only receive on processor 0");
159 
160  for (const auto & [object_type, message, warning_int, counts] : data)
161  {
162  mooseAssert(counts, "Should not send data without counts");
163 
164  // We transfer this as an integer (which is guaranteed by the standard to cast to a bool)
165  // because TIMPI doesn't currently support transferring bools
166  const bool warning = warning_int;
167 
168  InvalidSolutionID main_id = 0;
169  const moose::internal::SolutionInvalidityName name(object_type, message);
171  {
173  mooseAssert(_solution_invalidity_registry.item(main_id).warning == warning,
174  "Inconsistent registration of invalidity warning and error");
175  }
176  else
177  {
178  mooseAssert(pid != 0, "Should only hit on other processors");
180  object_type, message, warning);
181  }
182  if (_counts.size() <= main_id)
183  _counts.resize(main_id + 1);
184 
185  _counts[main_id].current_counts += counts;
186 
187  if (warning)
188  _has_solution_warning = true;
189  else
190  _has_solution_error = true;
191  }
192  };
193 
194  // Communicate the counts
195  TIMPI::push_parallel_vector_data(comm(), data_to_send, receive_data);
196 
197  // Set the state across all processors
200 
201  // We've now synced
202  _has_synced = true;
203 }
204 
205 void
207 {
208  const auto & info = _solution_invalidity_registry.item(_invalid_solution_id);
209  _console << info.object_type << ": " << info.message << "\n" << std::flush;
210 }
211 
214 {
215  mooseAssert(_has_synced, "Has not synced");
216 
217  FullTable vtable({"Object", "Converged", "Timestep", "Total", "Message"}, 4);
218 
219  vtable.setColumnFormat({
220  VariadicTableColumnFormat::AUTO, // Object Type
221  VariadicTableColumnFormat::AUTO, // Converged Iteration Warnings
222  VariadicTableColumnFormat::AUTO, // Latest Time Step Warnings
223  VariadicTableColumnFormat::AUTO, // Total Simulation Warnings
225  });
226 
227  vtable.setColumnPrecision({
228  1, // Object Name
229  0, // Converged Iteration Warnings
230  0, // Latest Time Step Warnings
231  0, // Total Simulation Warnings
232  1, // Message
233  });
234 
235  if (processor_id() == 0)
236  {
237  for (const auto id : index_range(_counts))
238  {
239  const auto & entry = _counts[id];
240  if (entry.current_counts)
241  {
242  const auto & info = _solution_invalidity_registry.item(id);
243  vtable.addRow(info.object_type, // Object Type
244  entry.current_counts, // Converged Iteration Warnings
245  entry.current_timestep_counts, // Latest Time Step Warnings
246  entry.total_counts, // Total Iteration Warnings
247  info.message // Message
248  );
249  }
250  }
251  }
252 
253  return vtable;
254 }
255 
257 SolutionInvalidity::transientTable(unsigned int & step_interval) const
258 {
259  mooseAssert(_has_synced, "Has not synced");
260 
261  TimeTable vtable({"Object", "Time", "Stepinterval Count", "Total Count"}, 4);
262 
263  vtable.setColumnFormat({
264  VariadicTableColumnFormat::AUTO, // Object information
265  VariadicTableColumnFormat::AUTO, // Simulation Time Step
266  VariadicTableColumnFormat::AUTO, // Latest Time Step Warnings
267  VariadicTableColumnFormat::AUTO, // Total Iteration Warnings
268  });
269 
270  vtable.setColumnPrecision({
271  1, // Object information
272  1, // Simulation Time Step
273  0, // Latest Time Step Warnings
274  0, // Total Iteration Warnings
275  });
276 
277  if (processor_id() == 0)
278  {
279  for (const auto id : index_range(_counts))
280  {
281  const auto & entry = _counts[id];
282  const auto & info = _solution_invalidity_registry.item(id);
283  std::vector<unsigned int> interval_counts;
284  std::vector<unsigned int> total_counts;
285 
286  if (!entry.timestep_counts.empty())
287  {
288  for (unsigned int timestep = 0; timestep < entry.timestep_counts.back().timestep_index;
289  timestep += step_interval)
290  {
291 
292  auto start_it = timestep;
293  auto end_it = (timestep + step_interval < entry.timestep_counts.back().timestep_index)
294  ? start_it + step_interval
295  : entry.timestep_counts.back().timestep_index;
296 
297  int interval_sum = 0;
298  for (auto ts_count : entry.timestep_counts)
299  {
300  if (ts_count.timestep_index >= start_it && ts_count.timestep_index < end_it)
301  interval_sum += ts_count.counts;
302  }
303 
304  interval_counts.push_back(interval_sum);
305  }
306  }
307 
308  unsigned int interval_sum = 0;
309  for (unsigned int interval_index : index_range(interval_counts))
310  {
311  std::string interval_index_str =
312  std::to_string(interval_index) + "-" + std::to_string(interval_index + step_interval);
313 
314  interval_sum += interval_counts[interval_index];
315  vtable.addRow(info.object_type + " : " + info.message, // Object information
316  interval_index_str, // Interval Index
317  interval_counts[interval_index], // Interval Counts
318  interval_sum // Total Iteration Warnings
319 
320  );
321  }
322  }
323  }
324  return vtable;
325 }
326 
327 // Define data store structure for TimestepCounts
328 void
329 dataStore(std::ostream & stream,
330  SolutionInvalidity::TimestepCounts & timestep_counts,
331  void * context)
332 {
333  dataStore(stream, timestep_counts.timestep_index, context);
334  dataStore(stream, timestep_counts.counts, context);
335 }
336 
337 // Define data load structure for TimestepCounts
338 void
339 dataLoad(std::istream & stream,
340  SolutionInvalidity::TimestepCounts & timestep_counts,
341  void * context)
342 {
343  dataLoad(stream, timestep_counts.timestep_index, context);
344  dataLoad(stream, timestep_counts.counts, context);
345 }
346 
347 void
348 dataStore(std::ostream & stream, SolutionInvalidity & solution_invalidity, void * context)
349 {
350  solution_invalidity.syncIteration();
351 
352  if (solution_invalidity.processor_id() != 0)
353  return;
354 
355  // Build data structure for store
356  std::size_t size = solution_invalidity._counts.size();
357  dataStore(stream, size, context);
358 
359  for (const auto id : index_range(solution_invalidity._counts))
360  {
361  auto & entry = solution_invalidity._counts[id];
362  const auto & info = solution_invalidity._solution_invalidity_registry.item(id);
363  std::string type = info.object_type;
364  std::string message = info.message;
365  bool warning = info.warning;
366  dataStore(stream, type, context);
367  dataStore(stream, message, context);
368  dataStore(stream, warning, context);
369  dataStore(stream, entry.current_counts, context);
370  dataStore(stream, entry.current_timestep_counts, context);
371  dataStore(stream, entry.timestep_counts, context);
372  dataStore(stream, entry.total_counts, context);
373  }
374 }
375 
376 void
377 dataLoad(std::istream & stream, SolutionInvalidity & solution_invalidity, void * context)
378 {
379  if (solution_invalidity.processor_id() != 0)
380  return;
381 
382  std::size_t num_counts;
383  // load data block size
384  dataLoad(stream, num_counts, context);
385 
386  std::string object_type, message;
387  bool warning;
389 
390  // loop over and load stored data
391  for (size_t i = 0; i < num_counts; i++)
392  {
393  dataLoad(stream, object_type, context);
394  dataLoad(stream, message, context);
395  dataLoad(stream, warning, context);
396 
397  const moose::internal::SolutionInvalidityName name(object_type, message);
398  if (solution_invalidity._solution_invalidity_registry.keyExists(name))
399  id = solution_invalidity._solution_invalidity_registry.id(name);
400  else
402  object_type, message, warning);
403 
404  if (solution_invalidity._counts.size() <= id)
405  solution_invalidity._counts.resize(id + 1);
406 
407  auto & entry = solution_invalidity._counts[id];
408  dataLoad(stream, entry.current_counts, context);
409  dataLoad(stream, entry.current_timestep_counts, context);
410  dataLoad(stream, entry.timestep_counts, context);
411  dataLoad(stream, entry.total_counts, context);
412  }
413 }
std::string name(const ElemQuality q)
void computeTotalCounts()
Compute the total number of solution invalid occurrences.
bool hasInvalidSolutionError() const
Whether or not an invalid solution was encountered that was an error.
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 solutionInvalidAccumulationTimeStep(const unsigned int timestep_index)
Pass the number of solution invalid occurrences from current iteration to cumulative time iteration c...
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:97
MPI_Info info
unsigned int InvalidSolutionID
Definition: MooseTypes.h:213
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 resetSolutionInvalidTimeStep()
Reset the number of solution invalid occurrences back to zero for the current time step...
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:96
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.
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)
void solutionInvalidAccumulation()
Pass the number of solution invalid occurrences from current iteration to cumulative counters...
uint8_t processor_id_type
void syncIteration()
Sync iteration counts to main processor.
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 resetSolutionInvalidCurrentIteration()
Reset the number of solution invalid occurrences back to zero.
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.