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  // Allow warnings from the setup step
289  for (unsigned int timestep = 0; timestep <= entry.timestep_counts.back().timestep_index;
290  timestep += step_interval)
291  {
292 
293  auto start_it = timestep;
294  auto end_it = (timestep + step_interval < entry.timestep_counts.back().timestep_index)
295  ? start_it + step_interval
296  : entry.timestep_counts.back().timestep_index;
297 
298  int interval_sum = 0;
299  for (auto ts_count : entry.timestep_counts)
300  {
301  // Allow warnings from the setup step
302  if (ts_count.timestep_index >= start_it &&
303  (ts_count.timestep_index < end_it || start_it == end_it))
304  interval_sum += ts_count.counts;
305  }
306 
307  interval_counts.push_back(interval_sum);
308  }
309  }
310 
311  unsigned int interval_sum = 0;
312  for (unsigned int interval_index : index_range(interval_counts))
313  {
314  std::string interval_index_str =
315  std::to_string(interval_index) + "-" + std::to_string(interval_index + step_interval);
316 
317  interval_sum += interval_counts[interval_index];
318  vtable.addRow(info.object_type + " : " + info.message, // Object information
319  interval_index_str, // Interval Index
320  interval_counts[interval_index], // Interval Counts
321  interval_sum // Total Iteration Warnings
322 
323  );
324  }
325  }
326  }
327  return vtable;
328 }
329 
330 // Define data store structure for TimestepCounts
331 void
332 dataStore(std::ostream & stream,
333  SolutionInvalidity::TimestepCounts & timestep_counts,
334  void * context)
335 {
336  dataStore(stream, timestep_counts.timestep_index, context);
337  dataStore(stream, timestep_counts.counts, context);
338 }
339 
340 // Define data load structure for TimestepCounts
341 void
342 dataLoad(std::istream & stream,
343  SolutionInvalidity::TimestepCounts & timestep_counts,
344  void * context)
345 {
346  dataLoad(stream, timestep_counts.timestep_index, context);
347  dataLoad(stream, timestep_counts.counts, context);
348 }
349 
350 void
351 dataStore(std::ostream & stream, SolutionInvalidity & solution_invalidity, void * context)
352 {
353  solution_invalidity.syncIteration();
354 
355  if (solution_invalidity.processor_id() != 0)
356  return;
357 
358  // Build data structure for store
359  std::size_t size = solution_invalidity._counts.size();
360  dataStore(stream, size, context);
361 
362  for (const auto id : index_range(solution_invalidity._counts))
363  {
364  auto & entry = solution_invalidity._counts[id];
365  const auto & info = solution_invalidity._solution_invalidity_registry.item(id);
366  std::string type = info.object_type;
367  std::string message = info.message;
368  bool warning = info.warning;
369  dataStore(stream, type, context);
370  dataStore(stream, message, context);
371  dataStore(stream, warning, context);
372  dataStore(stream, entry.current_counts, context);
373  dataStore(stream, entry.current_timestep_counts, context);
374  dataStore(stream, entry.timestep_counts, context);
375  dataStore(stream, entry.total_counts, context);
376  }
377 }
378 
379 void
380 dataLoad(std::istream & stream, SolutionInvalidity & solution_invalidity, void * context)
381 {
382  if (solution_invalidity.processor_id() != 0)
383  return;
384 
385  std::size_t num_counts;
386  // load data block size
387  dataLoad(stream, num_counts, context);
388 
389  std::string object_type, message;
390  bool warning;
392 
393  // loop over and load stored data
394  for (size_t i = 0; i < num_counts; i++)
395  {
396  dataLoad(stream, object_type, context);
397  dataLoad(stream, message, context);
398  dataLoad(stream, warning, context);
399 
400  const moose::internal::SolutionInvalidityName name(object_type, message);
401  if (solution_invalidity._solution_invalidity_registry.keyExists(name))
402  id = solution_invalidity._solution_invalidity_registry.id(name);
403  else
405  object_type, message, warning);
406 
407  if (solution_invalidity._counts.size() <= id)
408  solution_invalidity._counts.resize(id + 1);
409 
410  auto & entry = solution_invalidity._counts[id];
411  dataLoad(stream, entry.current_counts, context);
412  dataLoad(stream, entry.current_timestep_counts, context);
413  dataLoad(stream, entry.timestep_counts, context);
414  dataLoad(stream, entry.total_counts, context);
415  }
416 }
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:103
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.