LCOV - code coverage report
Current view: top level - src/utils - SolutionInvalidity.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 180 187 96.3 %
Date: 2025-07-17 01:28:37 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       62755 : SolutionInvalidity::SolutionInvalidity(MooseApp & app)
      28             :   : ConsoleStreamInterface(app),
      29             :     ParallelObject(app.comm()),
      30       62755 :     _solution_invalidity_registry(moose::internal::getSolutionInvalidityRegistry()),
      31       62755 :     _has_synced(true),
      32       62755 :     _has_solution_warning(false),
      33      125510 :     _has_solution_error(false)
      34             : {
      35       62755 : }
      36             : 
      37             : void
      38       94744 : SolutionInvalidity::flagInvalidSolutionInternal(const InvalidSolutionID _invalid_solution_id)
      39             : {
      40       94744 :   std::lock_guard<std::mutex> lock_id(_invalid_mutex);
      41       94744 :   if (_counts.size() <= _invalid_solution_id)
      42         280 :     _counts.resize(_invalid_solution_id + 1);
      43             : 
      44       94744 :   ++_counts[_invalid_solution_id].current_counts;
      45       94744 : }
      46             : 
      47             : bool
      48      346131 : SolutionInvalidity::hasInvalidSolutionWarning() const
      49             : {
      50             :   mooseAssert(_has_synced, "Has not synced");
      51      346131 :   return _has_solution_warning;
      52             : }
      53             : 
      54             : bool
      55      650455 : SolutionInvalidity::hasInvalidSolutionError() const
      56             : {
      57             :   mooseAssert(_has_synced, "Has not synced");
      58      650455 :   return _has_solution_error;
      59             : }
      60             : 
      61             : bool
      62      346131 : SolutionInvalidity::hasInvalidSolution() const
      63             : {
      64      346131 :   return hasInvalidSolutionWarning() || hasInvalidSolutionError();
      65             : }
      66             : 
      67             : void
      68     3621545 : SolutionInvalidity::resetSolutionInvalidCurrentIteration()
      69             : {
      70             :   // Zero current counts
      71     3625424 :   for (auto & entry : _counts)
      72        3879 :     entry.current_counts = 0;
      73     3621545 : }
      74             : 
      75             : void
      76      255948 : SolutionInvalidity::resetSolutionInvalidTimeStep()
      77             : {
      78             :   // Reset that we have synced because we're on a new iteration
      79      255948 :   _has_synced = false;
      80             : 
      81      257041 :   for (auto & entry : _counts)
      82        1093 :     entry.current_timestep_counts = 0;
      83      255948 : }
      84             : 
      85             : void
      86     3521569 : SolutionInvalidity::solutionInvalidAccumulation()
      87             : {
      88     3525639 :   for (auto & entry : _counts)
      89        4070 :     entry.current_timestep_counts += entry.current_counts;
      90     3521569 : }
      91             : 
      92             : void
      93      285162 : SolutionInvalidity::solutionInvalidAccumulationTimeStep(const unsigned int timestep_index)
      94             : {
      95      286446 :   for (auto & entry : _counts)
      96        1284 :     if (entry.current_timestep_counts)
      97             :     {
      98        1589 :       if (entry.timestep_counts.empty() ||
      99         728 :           entry.timestep_counts.back().timestep_index != timestep_index)
     100         245 :         entry.timestep_counts.emplace_back(timestep_index);
     101         861 :       entry.timestep_counts.back().counts = entry.current_timestep_counts;
     102         861 :       entry.total_counts += entry.current_timestep_counts;
     103             :     }
     104      285162 : }
     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         686 : SolutionInvalidity::print(const ConsoleStream & console) const
     121             : {
     122         686 :   console << "\nSolution Invalid Warnings:\n";
     123         686 :   summaryTable().print(console);
     124         686 : }
     125             : 
     126             : void
     127         625 : SolutionInvalidity::printHistory(const ConsoleStream & console,
     128             :                                  unsigned int & timestep_interval_size) const
     129             : {
     130         625 :   console << "\nSolution Invalid Warnings History:\n";
     131         625 :   transientTable(timestep_interval_size).print(console);
     132         625 : }
     133             : 
     134             : void
     135     3857334 : SolutionInvalidity::syncIteration()
     136             : {
     137             :   std::map<processor_id_type, std::vector<std::tuple<std::string, std::string, int, unsigned int>>>
     138     3857334 :       data_to_send;
     139             : 
     140             :   // Reset this as we need to see if we have new counts
     141     3857334 :   _has_solution_warning = false;
     142     3857334 :   _has_solution_error = false;
     143             : 
     144     3863733 :   for (const auto id : index_range(_counts))
     145             :   {
     146        6399 :     auto & entry = _counts[id];
     147        6399 :     if (entry.current_counts)
     148             :     {
     149        5252 :       const auto & info = _solution_invalidity_registry.item(id);
     150        5252 :       data_to_send[0].emplace_back(
     151        5252 :           info.object_type, info.message, info.warning, entry.current_counts);
     152        5252 :       entry.current_counts = 0;
     153             :     }
     154             :   }
     155             : 
     156       29159 :   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        8151 :     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        5252 :       const bool warning = warning_int;
     167             : 
     168        5252 :       InvalidSolutionID main_id = 0;
     169        5252 :       const moose::internal::SolutionInvalidityName name(object_type, message);
     170        5252 :       if (_solution_invalidity_registry.keyExists(name))
     171             :       {
     172        5245 :         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        5252 :       if (_counts.size() <= main_id)
     183           7 :         _counts.resize(main_id + 1);
     184             : 
     185        5252 :       _counts[main_id].current_counts += counts;
     186             : 
     187        5252 :       if (warning)
     188         497 :         _has_solution_warning = true;
     189             :       else
     190        4755 :         _has_solution_error = true;
     191             :     }
     192        2899 :   };
     193             : 
     194             :   // Communicate the counts
     195     3857334 :   TIMPI::push_parallel_vector_data(comm(), data_to_send, receive_data);
     196             : 
     197             :   // Set the state across all processors
     198     3857334 :   comm().max(_has_solution_warning);
     199     3857334 :   comm().max(_has_solution_error);
     200             : 
     201             :   // We've now synced
     202     3857334 :   _has_synced = true;
     203     3857334 : }
     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         686 : SolutionInvalidity::summaryTable() const
     214             : {
     215             :   mooseAssert(_has_synced, "Has not synced");
     216             : 
     217        4116 :   FullTable vtable({"Object", "Converged", "Timestep", "Total", "Message"}, 4);
     218             : 
     219         686 :   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         686 :   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         686 :   if (processor_id() == 0)
     236             :   {
     237        1331 :     for (const auto id : index_range(_counts))
     238             :     {
     239         857 :       const auto & entry = _counts[id];
     240         857 :       if (entry.current_counts)
     241             :       {
     242         857 :         const auto & info = _solution_invalidity_registry.item(id);
     243         857 :         vtable.addRow(info.object_type,              // Object Type
     244         857 :                       entry.current_counts,          // Converged Iteration Warnings
     245         857 :                       entry.current_timestep_counts, // Latest Time Step Warnings
     246         857 :                       entry.total_counts,            // Total Iteration Warnings
     247         857 :                       info.message                   // Message
     248             :         );
     249             :       }
     250             :     }
     251             :   }
     252             : 
     253         686 :   return vtable;
     254        1372 : }
     255             : 
     256             : SolutionInvalidity::TimeTable
     257         625 : SolutionInvalidity::transientTable(unsigned int & step_interval) const
     258             : {
     259             :   mooseAssert(_has_synced, "Has not synced");
     260             : 
     261        3125 :   TimeTable vtable({"Object", "Time", "Stepinterval Count", "Total Count"}, 4);
     262             : 
     263         625 :   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         625 :   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         625 :   if (processor_id() == 0)
     278             :   {
     279        1246 :     for (const auto id : index_range(_counts))
     280             :     {
     281         823 :       const auto & entry = _counts[id];
     282         823 :       const auto & info = _solution_invalidity_registry.item(id);
     283         823 :       std::vector<unsigned int> interval_counts;
     284         823 :       std::vector<unsigned int> total_counts;
     285             : 
     286         823 :       if (!entry.timestep_counts.empty())
     287             :       {
     288        1775 :         for (unsigned int timestep = 0; timestep < entry.timestep_counts.back().timestep_index;
     289         954 :              timestep += step_interval)
     290             :         {
     291             : 
     292         954 :           auto start_it = timestep;
     293         954 :           auto end_it = (timestep + step_interval < entry.timestep_counts.back().timestep_index)
     294        1775 :                             ? start_it + step_interval
     295         821 :                             : entry.timestep_counts.back().timestep_index;
     296             : 
     297         954 :           int interval_sum = 0;
     298        2405 :           for (auto ts_count : entry.timestep_counts)
     299             :           {
     300        1451 :             if (ts_count.timestep_index >= start_it && ts_count.timestep_index < end_it)
     301         126 :               interval_sum += ts_count.counts;
     302             :           }
     303             : 
     304         954 :           interval_counts.push_back(interval_sum);
     305             :         }
     306             :       }
     307             : 
     308         823 :       unsigned int interval_sum = 0;
     309        1777 :       for (unsigned int interval_index : index_range(interval_counts))
     310             :       {
     311             :         std::string interval_index_str =
     312         954 :             std::to_string(interval_index) + "-" + std::to_string(interval_index + step_interval);
     313             : 
     314         954 :         interval_sum += interval_counts[interval_index];
     315         954 :         vtable.addRow(info.object_type + " : " + info.message, // Object information
     316             :                       interval_index_str,                      // Interval Index
     317         954 :                       interval_counts[interval_index],         // Interval Counts
     318             :                       interval_sum                             // Total Iteration Warnings
     319             : 
     320             :         );
     321         954 :       }
     322         823 :     }
     323             :   }
     324         625 :   return vtable;
     325        1250 : }
     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       41839 : dataStore(std::ostream & stream, SolutionInvalidity & solution_invalidity, void * context)
     349             : {
     350       41839 :   solution_invalidity.syncIteration();
     351             : 
     352       41839 :   if (solution_invalidity.processor_id() != 0)
     353        5703 :     return;
     354             : 
     355             :   // Build data structure for store
     356       36136 :   std::size_t size = solution_invalidity._counts.size();
     357       36136 :   dataStore(stream, size, context);
     358             : 
     359       36175 :   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       12748 : dataLoad(std::istream & stream, SolutionInvalidity & solution_invalidity, void * context)
     378             : {
     379       12748 :   if (solution_invalidity.processor_id() != 0)
     380        2665 :     return;
     381             : 
     382             :   std::size_t num_counts;
     383             :   // load data block size
     384       10083 :   dataLoad(stream, num_counts, context);
     385             : 
     386       10083 :   std::string object_type, message;
     387             :   bool warning;
     388             :   InvalidSolutionID id;
     389             : 
     390             :   // loop over and load stored data
     391       10108 :   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       10083 : }

Generated by: LCOV version 1.14