LCOV - code coverage report
Current view: top level - src/utils - SolutionInvalidity.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 419b9d Lines: 180 187 96.3 %
Date: 2025-08-08 20:01:16 Functions: 20 21 95.2 %
Legend: Lines: hit not hit

          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       67610 : SolutionInvalidity::SolutionInvalidity(MooseApp & app)
      28             :   : ConsoleStreamInterface(app),
      29             :     ParallelObject(app.comm()),
      30       67610 :     _solution_invalidity_registry(moose::internal::getSolutionInvalidityRegistry()),
      31       67610 :     _has_synced(true),
      32       67610 :     _has_solution_warning(false),
      33      135220 :     _has_solution_error(false)
      34             : {
      35       67610 : }
      36             : 
      37             : void
      38      102073 : SolutionInvalidity::flagInvalidSolutionInternal(const InvalidSolutionID _invalid_solution_id)
      39             : {
      40      102073 :   std::lock_guard<std::mutex> lock_id(_invalid_mutex);
      41      102073 :   if (_counts.size() <= _invalid_solution_id)
      42         291 :     _counts.resize(_invalid_solution_id + 1);
      43             : 
      44      102073 :   ++_counts[_invalid_solution_id].current_counts;
      45      102073 : }
      46             : 
      47             : bool
      48      377312 : SolutionInvalidity::hasInvalidSolutionWarning() const
      49             : {
      50             :   mooseAssert(_has_synced, "Has not synced");
      51      377312 :   return _has_solution_warning;
      52             : }
      53             : 
      54             : bool
      55      711889 : SolutionInvalidity::hasInvalidSolutionError() const
      56             : {
      57             :   mooseAssert(_has_synced, "Has not synced");
      58      711889 :   return _has_solution_error;
      59             : }
      60             : 
      61             : bool
      62      377312 : SolutionInvalidity::hasInvalidSolution() const
      63             : {
      64      377312 :   return hasInvalidSolutionWarning() || hasInvalidSolutionError();
      65             : }
      66             : 
      67             : void
      68     3907319 : SolutionInvalidity::resetSolutionInvalidCurrentIteration()
      69             : {
      70             :   // Zero current counts
      71     3911258 :   for (auto & entry : _counts)
      72        3939 :     entry.current_counts = 0;
      73     3907319 : }
      74             : 
      75             : void
      76      279417 : SolutionInvalidity::resetSolutionInvalidTimeStep()
      77             : {
      78             :   // Reset that we have synced because we're on a new iteration
      79      279417 :   _has_synced = false;
      80             : 
      81      280514 :   for (auto & entry : _counts)
      82        1097 :     entry.current_timestep_counts = 0;
      83      279417 : }
      84             : 
      85             : void
      86     3799686 : SolutionInvalidity::solutionInvalidAccumulation()
      87             : {
      88     3803818 :   for (auto & entry : _counts)
      89        4132 :     entry.current_timestep_counts += entry.current_counts;
      90     3799686 : }
      91             : 
      92             : void
      93      311110 : SolutionInvalidity::solutionInvalidAccumulationTimeStep(const unsigned int timestep_index)
      94             : {
      95      312400 :   for (auto & entry : _counts)
      96        1290 :     if (entry.current_timestep_counts)
      97             :     {
      98        1591 :       if (entry.timestep_counts.empty() ||
      99         728 :           entry.timestep_counts.back().timestep_index != timestep_index)
     100         247 :         entry.timestep_counts.emplace_back(timestep_index);
     101         863 :       entry.timestep_counts.back().counts = entry.current_timestep_counts;
     102         863 :       entry.total_counts += entry.current_timestep_counts;
     103             :     }
     104      311110 : }
     105             : 
     106             : void
     107           0 : SolutionInvalidity::computeTotalCounts()
     108             : {
     109             :   mooseAssert(_has_synced, "Has not synced");
     110             : 
     111           0 :   for (auto & entry : _counts)
     112             :   {
     113           0 :     entry.total_counts = 0;
     114           0 :     for (auto & time_counts : entry.timestep_counts)
     115           0 :       entry.total_counts += time_counts.counts;
     116             :   }
     117           0 : }
     118             : 
     119             : void
     120         687 : SolutionInvalidity::print(const ConsoleStream & console) const
     121             : {
     122         687 :   console << "\nSolution Invalid Warnings:\n";
     123         687 :   summaryTable().print(console);
     124         687 : }
     125             : 
     126             : void
     127         626 : SolutionInvalidity::printHistory(const ConsoleStream & console,
     128             :                                  unsigned int & timestep_interval_size) const
     129             : {
     130         626 :   console << "\nSolution Invalid Warnings History:\n";
     131         626 :   transientTable(timestep_interval_size).print(console);
     132         626 : }
     133             : 
     134             : void
     135     4165226 : SolutionInvalidity::syncIteration()
     136             : {
     137             :   std::map<processor_id_type, std::vector<std::tuple<std::string, std::string, int, unsigned int>>>
     138     4165226 :       data_to_send;
     139             : 
     140             :   // Reset this as we need to see if we have new counts
     141     4165226 :   _has_solution_warning = false;
     142     4165226 :   _has_solution_error = false;
     143             : 
     144     4171693 :   for (const auto id : index_range(_counts))
     145             :   {
     146        6467 :     auto & entry = _counts[id];
     147        6467 :     if (entry.current_counts)
     148             :     {
     149        5260 :       const auto & info = _solution_invalidity_registry.item(id);
     150        5260 :       data_to_send[0].emplace_back(
     151        5260 :           info.object_type, info.message, info.warning, entry.current_counts);
     152        5260 :       entry.current_counts = 0;
     153             :     }
     154             :   }
     155             : 
     156       29203 :   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        8163 :     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        5260 :       const bool warning = warning_int;
     167             : 
     168        5260 :       InvalidSolutionID main_id = 0;
     169        5260 :       const moose::internal::SolutionInvalidityName name(object_type, message);
     170        5260 :       if (_solution_invalidity_registry.keyExists(name))
     171             :       {
     172        5253 :         main_id = _solution_invalidity_registry.id(name);
     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");
     179           7 :         main_id = moose::internal::getSolutionInvalidityRegistry().registerInvalidity(
     180             :             object_type, message, warning);
     181             :       }
     182        5260 :       if (_counts.size() <= main_id)
     183           7 :         _counts.resize(main_id + 1);
     184             : 
     185        5260 :       _counts[main_id].current_counts += counts;
     186             : 
     187        5260 :       if (warning)
     188         497 :         _has_solution_warning = true;
     189             :       else
     190        4763 :         _has_solution_error = true;
     191             :     }
     192        2903 :   };
     193             : 
     194             :   // Communicate the counts
     195     4165226 :   TIMPI::push_parallel_vector_data(comm(), data_to_send, receive_data);
     196             : 
     197             :   // Set the state across all processors
     198     4165226 :   comm().max(_has_solution_warning);
     199     4165226 :   comm().max(_has_solution_error);
     200             : 
     201             :   // We've now synced
     202     4165226 :   _has_synced = true;
     203     4165226 : }
     204             : 
     205             : void
     206         672 : SolutionInvalidity::printDebug(InvalidSolutionID _invalid_solution_id) const
     207             : {
     208         672 :   const auto & info = _solution_invalidity_registry.item(_invalid_solution_id);
     209         672 :   _console << info.object_type << ": " << info.message << "\n" << std::flush;
     210         672 : }
     211             : 
     212             : SolutionInvalidity::FullTable
     213         687 : SolutionInvalidity::summaryTable() const
     214             : {
     215             :   mooseAssert(_has_synced, "Has not synced");
     216             : 
     217        4122 :   FullTable vtable({"Object", "Converged", "Timestep", "Total", "Message"}, 4);
     218             : 
     219         687 :   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
     224             :       VariadicTableColumnFormat::AUTO, // Message
     225             :   });
     226             : 
     227         687 :   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         687 :   if (processor_id() == 0)
     236             :   {
     237        1334 :     for (const auto id : index_range(_counts))
     238             :     {
     239         859 :       const auto & entry = _counts[id];
     240         859 :       if (entry.current_counts)
     241             :       {
     242         859 :         const auto & info = _solution_invalidity_registry.item(id);
     243         859 :         vtable.addRow(info.object_type,              // Object Type
     244         859 :                       entry.current_counts,          // Converged Iteration Warnings
     245         859 :                       entry.current_timestep_counts, // Latest Time Step Warnings
     246         859 :                       entry.total_counts,            // Total Iteration Warnings
     247         859 :                       info.message                   // Message
     248             :         );
     249             :       }
     250             :     }
     251             :   }
     252             : 
     253         687 :   return vtable;
     254        1374 : }
     255             : 
     256             : SolutionInvalidity::TimeTable
     257         626 : SolutionInvalidity::transientTable(unsigned int & step_interval) const
     258             : {
     259             :   mooseAssert(_has_synced, "Has not synced");
     260             : 
     261        3130 :   TimeTable vtable({"Object", "Time", "Stepinterval Count", "Total Count"}, 4);
     262             : 
     263         626 :   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         626 :   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         626 :   if (processor_id() == 0)
     278             :   {
     279        1249 :     for (const auto id : index_range(_counts))
     280             :     {
     281         825 :       const auto & entry = _counts[id];
     282         825 :       const auto & info = _solution_invalidity_registry.item(id);
     283         825 :       std::vector<unsigned int> interval_counts;
     284         825 :       std::vector<unsigned int> total_counts;
     285             : 
     286         825 :       if (!entry.timestep_counts.empty())
     287             :       {
     288        1779 :         for (unsigned int timestep = 0; timestep < entry.timestep_counts.back().timestep_index;
     289         956 :              timestep += step_interval)
     290             :         {
     291             : 
     292         956 :           auto start_it = timestep;
     293         956 :           auto end_it = (timestep + step_interval < entry.timestep_counts.back().timestep_index)
     294        1779 :                             ? start_it + step_interval
     295         823 :                             : entry.timestep_counts.back().timestep_index;
     296             : 
     297         956 :           int interval_sum = 0;
     298        2409 :           for (auto ts_count : entry.timestep_counts)
     299             :           {
     300        1453 :             if (ts_count.timestep_index >= start_it && ts_count.timestep_index < end_it)
     301         126 :               interval_sum += ts_count.counts;
     302             :           }
     303             : 
     304         956 :           interval_counts.push_back(interval_sum);
     305             :         }
     306             :       }
     307             : 
     308         825 :       unsigned int interval_sum = 0;
     309        1781 :       for (unsigned int interval_index : index_range(interval_counts))
     310             :       {
     311             :         std::string interval_index_str =
     312         956 :             std::to_string(interval_index) + "-" + std::to_string(interval_index + step_interval);
     313             : 
     314         956 :         interval_sum += interval_counts[interval_index];
     315         956 :         vtable.addRow(info.object_type + " : " + info.message, // Object information
     316             :                       interval_index_str,                      // Interval Index
     317         956 :                       interval_counts[interval_index],         // Interval Counts
     318             :                       interval_sum                             // Total Iteration Warnings
     319             : 
     320             :         );
     321         956 :       }
     322         825 :     }
     323             :   }
     324         626 :   return vtable;
     325        1252 : }
     326             : 
     327             : // Define data store structure for TimestepCounts
     328             : void
     329          44 : dataStore(std::ostream & stream,
     330             :           SolutionInvalidity::TimestepCounts & timestep_counts,
     331             :           void * context)
     332             : {
     333          44 :   dataStore(stream, timestep_counts.timestep_index, context);
     334          44 :   dataStore(stream, timestep_counts.counts, context);
     335          44 : }
     336             : 
     337             : // Define data load structure for TimestepCounts
     338             : void
     339          30 : dataLoad(std::istream & stream,
     340             :          SolutionInvalidity::TimestepCounts & timestep_counts,
     341             :          void * context)
     342             : {
     343          30 :   dataLoad(stream, timestep_counts.timestep_index, context);
     344          30 :   dataLoad(stream, timestep_counts.counts, context);
     345          30 : }
     346             : 
     347             : void
     348       45214 : dataStore(std::ostream & stream, SolutionInvalidity & solution_invalidity, void * context)
     349             : {
     350       45214 :   solution_invalidity.syncIteration();
     351             : 
     352       45214 :   if (solution_invalidity.processor_id() != 0)
     353        5749 :     return;
     354             : 
     355             :   // Build data structure for store
     356       39465 :   std::size_t size = solution_invalidity._counts.size();
     357       39465 :   dataStore(stream, size, context);
     358             : 
     359       39504 :   for (const auto id : index_range(solution_invalidity._counts))
     360             :   {
     361          39 :     auto & entry = solution_invalidity._counts[id];
     362          39 :     const auto & info = solution_invalidity._solution_invalidity_registry.item(id);
     363          39 :     std::string type = info.object_type;
     364          39 :     std::string message = info.message;
     365          39 :     bool warning = info.warning;
     366          39 :     dataStore(stream, type, context);
     367          39 :     dataStore(stream, message, context);
     368          39 :     dataStore(stream, warning, context);
     369          39 :     dataStore(stream, entry.current_counts, context);
     370          39 :     dataStore(stream, entry.current_timestep_counts, context);
     371          39 :     dataStore(stream, entry.timestep_counts, context);
     372          39 :     dataStore(stream, entry.total_counts, context);
     373          39 :   }
     374             : }
     375             : 
     376             : void
     377       13740 : dataLoad(std::istream & stream, SolutionInvalidity & solution_invalidity, void * context)
     378             : {
     379       13740 :   if (solution_invalidity.processor_id() != 0)
     380        2652 :     return;
     381             : 
     382             :   std::size_t num_counts;
     383             :   // load data block size
     384       11088 :   dataLoad(stream, num_counts, context);
     385             : 
     386       11088 :   std::string object_type, message;
     387             :   bool warning;
     388             :   InvalidSolutionID id;
     389             : 
     390             :   // loop over and load stored data
     391       11113 :   for (size_t i = 0; i < num_counts; i++)
     392             :   {
     393          25 :     dataLoad(stream, object_type, context);
     394          25 :     dataLoad(stream, message, context);
     395          25 :     dataLoad(stream, warning, context);
     396             : 
     397          25 :     const moose::internal::SolutionInvalidityName name(object_type, message);
     398          25 :     if (solution_invalidity._solution_invalidity_registry.keyExists(name))
     399           0 :       id = solution_invalidity._solution_invalidity_registry.id(name);
     400             :     else
     401          25 :       id = moose::internal::getSolutionInvalidityRegistry().registerInvalidity(
     402             :           object_type, message, warning);
     403             : 
     404          25 :     if (solution_invalidity._counts.size() <= id)
     405          25 :       solution_invalidity._counts.resize(id + 1);
     406             : 
     407          25 :     auto & entry = solution_invalidity._counts[id];
     408          25 :     dataLoad(stream, entry.current_counts, context);
     409          25 :     dataLoad(stream, entry.current_timestep_counts, context);
     410          25 :     dataLoad(stream, entry.timestep_counts, context);
     411          25 :     dataLoad(stream, entry.total_counts, context);
     412          25 :   }
     413       11088 : }

Generated by: LCOV version 1.14