LCOV - code coverage report
Current view: top level - src/utils - SolutionInvalidity.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 189 191 99.0 %
Date: 2026-05-29 20:35:17 Functions: 21 21 100.0 %
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       66995 : SolutionInvalidity::SolutionInvalidity(MooseApp & app)
      28             :   : ConsoleStreamInterface(app),
      29             :     ParallelObject(app.comm()),
      30       66995 :     _solution_invalidity_registry(moose::internal::getSolutionInvalidityRegistry()),
      31       66995 :     _has_synced(true),
      32       66995 :     _has_solution_warning(false),
      33       66995 :     _has_solution_error(false),
      34      133990 :     _has_recorded_issue(false)
      35             : {
      36       66995 : }
      37             : 
      38             : void
      39       38886 : SolutionInvalidity::flagInvalidSolutionInternal(const InvalidSolutionID _invalid_solution_id)
      40             : {
      41       38886 :   std::lock_guard<std::mutex> lock_id(_invalid_mutex);
      42       38886 :   if (_counts.size() <= _invalid_solution_id)
      43        2776 :     _counts.resize(_invalid_solution_id + 1);
      44             : 
      45       38886 :   ++_counts[_invalid_solution_id].current_counts;
      46       38886 : }
      47             : 
      48             : bool
      49      315295 : SolutionInvalidity::hasInvalidSolutionWarning() const
      50             : {
      51             :   mooseAssert(_has_synced, "Has not synced");
      52      315295 :   return _has_solution_warning;
      53             : }
      54             : 
      55             : bool
      56      625774 : SolutionInvalidity::hasInvalidSolutionError() const
      57             : {
      58             :   mooseAssert(_has_synced, "Has not synced");
      59      625774 :   return _has_solution_error;
      60             : }
      61             : 
      62             : bool
      63      315295 : SolutionInvalidity::hasInvalidSolution() const
      64             : {
      65      315295 :   return hasInvalidSolutionWarning() || hasInvalidSolutionError();
      66             : }
      67             : 
      68             : bool
      69       54870 : SolutionInvalidity::hasEverHadSolutionIssue() const
      70             : {
      71             :   mooseAssert(_has_synced, "Has not synced");
      72       54870 :   return _has_recorded_issue;
      73             : }
      74             : 
      75             : void
      76     4023763 : SolutionInvalidity::resetIterationOccurences()
      77             : {
      78             :   // Zero current counts
      79     4482209 :   for (auto & entry : _counts)
      80      458446 :     entry.current_counts = 0;
      81     4023763 : }
      82             : 
      83             : void
      84      308325 : SolutionInvalidity::resetTimeStepOccurences()
      85             : {
      86             :   // Reset that we have synced because we're on a new iteration
      87      308325 :   _has_synced = false;
      88             : 
      89      341265 :   for (auto & entry : _counts)
      90       32940 :     entry.current_timestep_counts = 0;
      91      308325 : }
      92             : 
      93             : void
      94     4201989 : SolutionInvalidity::accumulateIterationIntoTimeStepOccurences()
      95             : {
      96     4702672 :   for (auto & entry : _counts)
      97      500683 :     entry.current_timestep_counts += entry.current_counts;
      98     4201989 : }
      99             : 
     100             : void
     101      360639 : SolutionInvalidity::accumulateTimeStepIntoTotalOccurences(const unsigned int timestep_index)
     102             : {
     103      395977 :   for (auto & entry : _counts)
     104       35338 :     if (entry.current_timestep_counts)
     105             :     {
     106        3091 :       if (entry.timestep_counts.empty() ||
     107         668 :           entry.timestep_counts.back().timestep_index != timestep_index)
     108        1877 :         entry.timestep_counts.emplace_back(timestep_index);
     109        2423 :       entry.timestep_counts.back().counts = entry.current_timestep_counts;
     110        2423 :       entry.total_counts += entry.current_timestep_counts;
     111             :     }
     112      360639 : }
     113             : 
     114             : void
     115         623 : SolutionInvalidity::print(const ConsoleStream & console) const
     116             : {
     117         623 :   console << "\nSolution Invalid Warnings:\n";
     118         623 :   summaryTable().print(console);
     119         623 : }
     120             : 
     121             : void
     122        1435 : SolutionInvalidity::printHistory(const ConsoleStream & console,
     123             :                                  unsigned int & timestep_interval_size) const
     124             : {
     125        1435 :   if (hasInvalidSolutionError())
     126          45 :     console << "\nSolution Invalid History:\n";
     127             :   else
     128        1390 :     console << "\nWarnings History:\n";
     129        1435 :   transientTable(timestep_interval_size).print(console);
     130        1435 : }
     131             : 
     132             : void
     133     4618084 : SolutionInvalidity::syncIteration()
     134             : {
     135             :   std::map<processor_id_type, std::vector<std::tuple<std::string, std::string, int, unsigned int>>>
     136     4618084 :       data_to_send;
     137             : 
     138             :   // Reset this as we need to see if we have new counts
     139     4618084 :   _has_solution_warning = false;
     140     4618084 :   _has_solution_error = false;
     141             : 
     142     5155339 :   for (const auto id : index_range(_counts))
     143             :   {
     144      537255 :     auto & entry = _counts[id];
     145      537255 :     if (entry.current_counts)
     146             :     {
     147        7594 :       const auto & info = _solution_invalidity_registry.item(id);
     148        7594 :       data_to_send[0].emplace_back(
     149        7594 :           info.object_type, info.message, info.warning, entry.current_counts);
     150        7594 :       entry.current_counts = 0;
     151             :     }
     152             :   }
     153             : 
     154        4296 :   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       11890 :     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        7594 :       const bool warning = warning_int;
     165             : 
     166        7594 :       InvalidSolutionID main_id = 0;
     167        7594 :       const moose::internal::SolutionInvalidityName name(object_type, message);
     168        7594 :       if (_solution_invalidity_registry.keyExists(name))
     169             :       {
     170        7585 :         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");
     177           9 :         main_id = moose::internal::getSolutionInvalidityRegistry().registerInvalidity(
     178             :             object_type, message, warning);
     179             :       }
     180        7594 :       if (_counts.size() <= main_id)
     181           9 :         _counts.resize(main_id + 1);
     182             : 
     183        7594 :       _counts[main_id].current_counts += counts;
     184             : 
     185        7594 :       if (warning)
     186        3360 :         _has_solution_warning = true;
     187             :       else
     188        4234 :         _has_solution_error = true;
     189             :     }
     190        4296 :   };
     191             : 
     192             :   // Communicate the counts
     193     4618084 :   TIMPI::push_parallel_vector_data(comm(), data_to_send, receive_data);
     194             : 
     195             :   // Set the state across all processors
     196     4618084 :   comm().max(_has_solution_warning);
     197     4618084 :   comm().max(_has_solution_error);
     198             : 
     199             :   // Keep track of any occurence
     200     4618084 :   if (_has_solution_warning || _has_solution_error)
     201        4813 :     _has_recorded_issue = true;
     202             : 
     203             :   // We've now synced
     204     4618084 :   _has_synced = true;
     205     4618084 : }
     206             : 
     207             : void
     208         576 : SolutionInvalidity::printDebug(InvalidSolutionID _invalid_solution_id) const
     209             : {
     210         576 :   const auto & info = _solution_invalidity_registry.item(_invalid_solution_id);
     211         576 :   _console << info.object_type << ": " << info.message << "\n" << std::flush;
     212         576 : }
     213             : 
     214             : SolutionInvalidity::FullTable
     215         623 : SolutionInvalidity::summaryTable() const
     216             : {
     217             :   mooseAssert(_has_synced, "Has not synced");
     218             : 
     219        1246 :   FullTable vtable({"Object", "Converged", "Timestep", "Total", "Message"}, 4);
     220             : 
     221        1246 :   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
     226             :       VariadicTableColumnFormat::AUTO, // Message
     227             :   });
     228             : 
     229        1246 :   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         623 :   if (processor_id() == 0)
     238             :   {
     239        1185 :     for (const auto id : index_range(_counts))
     240             :     {
     241         772 :       const auto & entry = _counts[id];
     242         772 :       if (entry.current_counts)
     243             :       {
     244         742 :         const auto & info = _solution_invalidity_registry.item(id);
     245         742 :         vtable.addRow(info.object_type,              // Object Type
     246         742 :                       entry.current_counts,          // Converged Iteration Warnings
     247         742 :                       entry.current_timestep_counts, // Latest Time Step Warnings
     248         742 :                       entry.total_counts,            // Total Iteration Warnings
     249         742 :                       info.message                   // Message
     250             :         );
     251             :       }
     252             :     }
     253             :   }
     254             : 
     255         623 :   return vtable;
     256           0 : }
     257             : 
     258             : SolutionInvalidity::TimeTable
     259        1435 : SolutionInvalidity::transientTable(unsigned int & step_interval) const
     260             : {
     261             :   mooseAssert(_has_synced, "Has not synced");
     262             : 
     263        2870 :   TimeTable vtable({"Object", "Step", "Interval Count", "Total Count"}, 4);
     264             : 
     265        2870 :   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        2870 :   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        1435 :   if (processor_id() == 0)
     280             :   {
     281        2568 :     for (const auto id : index_range(_counts))
     282             :     {
     283        1621 :       const auto & entry = _counts[id];
     284        1621 :       const auto & info = _solution_invalidity_registry.item(id);
     285        1621 :       std::vector<unsigned int> interval_counts;
     286        1621 :       std::vector<unsigned int> total_counts;
     287             : 
     288        1621 :       if (!entry.timestep_counts.empty())
     289             :       {
     290             :         // Allow warnings from the setup step
     291        3523 :         for (unsigned int timestep = 0; timestep <= entry.timestep_counts.back().timestep_index;
     292        1943 :              timestep += step_interval)
     293             :         {
     294             : 
     295        1943 :           auto start_it = timestep;
     296        1943 :           auto end_it = (timestep + step_interval < entry.timestep_counts.back().timestep_index)
     297        3705 :                             ? start_it + step_interval
     298        1762 :                             : entry.timestep_counts.back().timestep_index;
     299             : 
     300        1943 :           int interval_sum = 0;
     301        4499 :           for (auto ts_count : entry.timestep_counts)
     302             :           {
     303             :             // Allow warnings from the setup step
     304        2556 :             if (ts_count.timestep_index >= start_it &&
     305        2317 :                 (ts_count.timestep_index < end_it || start_it == end_it))
     306        1714 :               interval_sum += ts_count.counts;
     307             :           }
     308             : 
     309        1943 :           interval_counts.push_back(interval_sum);
     310             :         }
     311             :       }
     312             : 
     313        1621 :       unsigned int interval_sum = 0;
     314        3564 :       for (unsigned int interval_index : index_range(interval_counts))
     315             :       {
     316             :         std::string interval_index_str =
     317        1967 :             (step_interval > 1) ? std::to_string(interval_index) + "-" +
     318        1967 :                                       std::to_string(interval_index + step_interval - 1)
     319        1991 :                                 : std::to_string(interval_index);
     320             : 
     321        1943 :         interval_sum += interval_counts[interval_index];
     322        1943 :         vtable.addRow(info.object_type + " : " + info.message, // Object information
     323             :                       interval_index_str,                      // Interval Index
     324        1943 :                       interval_counts[interval_index],         // Interval Counts
     325             :                       interval_sum                             // Total Iteration Warnings
     326             : 
     327             :         );
     328        1943 :       }
     329        1621 :     }
     330             :   }
     331        1435 :   return vtable;
     332           0 : }
     333             : 
     334             : // Define data store structure for TimestepCounts
     335             : void
     336         422 : dataStore(std::ostream & stream,
     337             :           SolutionInvalidity::TimestepCounts & timestep_counts,
     338             :           void * context)
     339             : {
     340         422 :   dataStore(stream, timestep_counts.timestep_index, context);
     341         422 :   dataStore(stream, timestep_counts.counts, context);
     342         422 : }
     343             : 
     344             : // Define data load structure for TimestepCounts
     345             : void
     346         186 : dataLoad(std::istream & stream,
     347             :          SolutionInvalidity::TimestepCounts & timestep_counts,
     348             :          void * context)
     349             : {
     350         186 :   dataLoad(stream, timestep_counts.timestep_index, context);
     351         186 :   dataLoad(stream, timestep_counts.counts, context);
     352         186 : }
     353             : 
     354             : void
     355       45637 : dataStore(std::ostream & stream, SolutionInvalidity & solution_invalidity, void * context)
     356             : {
     357       45637 :   solution_invalidity.syncIteration();
     358             : 
     359       45637 :   if (solution_invalidity.processor_id() != 0)
     360        6566 :     return;
     361             : 
     362             :   // Build data structure for store
     363       39071 :   std::size_t size = solution_invalidity._counts.size();
     364       39071 :   dataStore(stream, size, context);
     365             : 
     366       39485 :   for (const auto id : index_range(solution_invalidity._counts))
     367             :   {
     368         414 :     auto & entry = solution_invalidity._counts[id];
     369         414 :     const auto & info = solution_invalidity._solution_invalidity_registry.item(id);
     370         414 :     std::string type = info.object_type;
     371         414 :     std::string message = info.message;
     372         414 :     bool warning = info.warning;
     373         414 :     dataStore(stream, type, context);
     374         414 :     dataStore(stream, message, context);
     375         414 :     dataStore(stream, warning, context);
     376         414 :     dataStore(stream, entry.current_counts, context);
     377         414 :     dataStore(stream, entry.current_timestep_counts, context);
     378         414 :     dataStore(stream, entry.timestep_counts, context);
     379         414 :     dataStore(stream, entry.total_counts, context);
     380         414 :   }
     381             : }
     382             : 
     383             : void
     384       13192 : dataLoad(std::istream & stream, SolutionInvalidity & solution_invalidity, void * context)
     385             : {
     386       13192 :   if (solution_invalidity.processor_id() != 0)
     387        2666 :     return;
     388             : 
     389             :   std::size_t num_counts;
     390             :   // load data block size
     391       10526 :   dataLoad(stream, num_counts, context);
     392             : 
     393       10526 :   std::string object_type, message;
     394             :   bool warning;
     395             :   InvalidSolutionID id;
     396             : 
     397             :   // loop over and load stored data
     398       10704 :   for (size_t i = 0; i < num_counts; i++)
     399             :   {
     400         178 :     dataLoad(stream, object_type, context);
     401         178 :     dataLoad(stream, message, context);
     402         178 :     dataLoad(stream, warning, context);
     403             : 
     404         178 :     const moose::internal::SolutionInvalidityName name(object_type, message);
     405         178 :     if (solution_invalidity._solution_invalidity_registry.keyExists(name))
     406         136 :       id = solution_invalidity._solution_invalidity_registry.id(name);
     407             :     else
     408          42 :       id = moose::internal::getSolutionInvalidityRegistry().registerInvalidity(
     409             :           object_type, message, warning);
     410             : 
     411         178 :     if (solution_invalidity._counts.size() <= id)
     412          42 :       solution_invalidity._counts.resize(id + 1);
     413             : 
     414         178 :     auto & entry = solution_invalidity._counts[id];
     415         178 :     dataLoad(stream, entry.current_counts, context);
     416         178 :     dataLoad(stream, entry.current_timestep_counts, context);
     417         178 :     dataLoad(stream, entry.timestep_counts, context);
     418         178 :     dataLoad(stream, entry.total_counts, context);
     419         178 :   }
     420       10526 : }

Generated by: LCOV version 1.14