LCOV - code coverage report
Current view: top level - src/utils - SolutionInvalidity.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #31730 (e8b711) with base e0c998 Lines: 180 188 95.7 %
Date: 2025-10-29 16:49:47 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       69941 : SolutionInvalidity::SolutionInvalidity(MooseApp & app)
      28             :   : ConsoleStreamInterface(app),
      29             :     ParallelObject(app.comm()),
      30       69941 :     _solution_invalidity_registry(moose::internal::getSolutionInvalidityRegistry()),
      31       69941 :     _has_synced(true),
      32       69941 :     _has_solution_warning(false),
      33      139882 :     _has_solution_error(false)
      34             : {
      35       69941 : }
      36             : 
      37             : void
      38      106083 : SolutionInvalidity::flagInvalidSolutionInternal(const InvalidSolutionID _invalid_solution_id)
      39             : {
      40      106083 :   std::lock_guard<std::mutex> lock_id(_invalid_mutex);
      41      106083 :   if (_counts.size() <= _invalid_solution_id)
      42        3083 :     _counts.resize(_invalid_solution_id + 1);
      43             : 
      44      106083 :   ++_counts[_invalid_solution_id].current_counts;
      45      106083 : }
      46             : 
      47             : bool
      48      384675 : SolutionInvalidity::hasInvalidSolutionWarning() const
      49             : {
      50             :   mooseAssert(_has_synced, "Has not synced");
      51      384675 :   return _has_solution_warning;
      52             : }
      53             : 
      54             : bool
      55      724448 : SolutionInvalidity::hasInvalidSolutionError() const
      56             : {
      57             :   mooseAssert(_has_synced, "Has not synced");
      58      724448 :   return _has_solution_error;
      59             : }
      60             : 
      61             : bool
      62      384675 : SolutionInvalidity::hasInvalidSolution() const
      63             : {
      64      384675 :   return hasInvalidSolutionWarning() || hasInvalidSolutionError();
      65             : }
      66             : 
      67             : void
      68     3977702 : SolutionInvalidity::resetSolutionInvalidCurrentIteration()
      69             : {
      70             :   // Zero current counts
      71     4444901 :   for (auto & entry : _counts)
      72      467199 :     entry.current_counts = 0;
      73     3977702 : }
      74             : 
      75             : void
      76      283776 : SolutionInvalidity::resetSolutionInvalidTimeStep()
      77             : {
      78             :   // Reset that we have synced because we're on a new iteration
      79      283776 :   _has_synced = false;
      80             : 
      81      318104 :   for (auto & entry : _counts)
      82       34328 :     entry.current_timestep_counts = 0;
      83      283776 : }
      84             : 
      85             : void
      86     3926666 : SolutionInvalidity::solutionInvalidAccumulation()
      87             : {
      88     4396497 :   for (auto & entry : _counts)
      89      469831 :     entry.current_timestep_counts += entry.current_counts;
      90     3926666 : }
      91             : 
      92             : void
      93      372622 : SolutionInvalidity::solutionInvalidAccumulationTimeStep(const unsigned int timestep_index)
      94             : {
      95      410513 :   for (auto & entry : _counts)
      96       37891 :     if (entry.current_timestep_counts)
      97             :     {
      98        4672 :       if (entry.timestep_counts.empty() ||
      99        1357 :           entry.timestep_counts.back().timestep_index != timestep_index)
     100        2519 :         entry.timestep_counts.emplace_back(timestep_index);
     101        3315 :       entry.timestep_counts.back().counts = entry.current_timestep_counts;
     102        3315 :       entry.total_counts += entry.current_timestep_counts;
     103             :     }
     104      372622 : }
     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         714 : SolutionInvalidity::printHistory(const ConsoleStream & console,
     128             :                                  unsigned int & timestep_interval_size) const
     129             : {
     130         714 :   console << "\nSolution Invalid Warnings History:\n";
     131         714 :   transientTable(timestep_interval_size).print(console);
     132         714 : }
     133             : 
     134             : void
     135     4298414 : SolutionInvalidity::syncIteration()
     136             : {
     137             :   std::map<processor_id_type, std::vector<std::tuple<std::string, std::string, int, unsigned int>>>
     138     4298414 :       data_to_send;
     139             : 
     140             :   // Reset this as we need to see if we have new counts
     141     4298414 :   _has_solution_warning = false;
     142     4298414 :   _has_solution_error = false;
     143             : 
     144     4805053 :   for (const auto id : index_range(_counts))
     145             :   {
     146      506639 :     auto & entry = _counts[id];
     147      506639 :     if (entry.current_counts)
     148             :     {
     149        7764 :       const auto & info = _solution_invalidity_registry.item(id);
     150        7764 :       data_to_send[0].emplace_back(
     151        7764 :           info.object_type, info.message, info.warning, entry.current_counts);
     152        7764 :       entry.current_counts = 0;
     153             :     }
     154             :   }
     155             : 
     156        4366 :   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       12130 :     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        7764 :       const bool warning = warning_int;
     167             : 
     168        7764 :       InvalidSolutionID main_id = 0;
     169        7764 :       const moose::internal::SolutionInvalidityName name(object_type, message);
     170        7764 :       if (_solution_invalidity_registry.keyExists(name))
     171             :       {
     172        7757 :         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        7764 :       if (_counts.size() <= main_id)
     183           7 :         _counts.resize(main_id + 1);
     184             : 
     185        7764 :       _counts[main_id].current_counts += counts;
     186             : 
     187        7764 :       if (warning)
     188        3001 :         _has_solution_warning = true;
     189             :       else
     190        4763 :         _has_solution_error = true;
     191             :     }
     192        4366 :   };
     193             : 
     194             :   // Communicate the counts
     195     4298414 :   TIMPI::push_parallel_vector_data(comm(), data_to_send, receive_data);
     196             : 
     197             :   // Set the state across all processors
     198     4298414 :   comm().max(_has_solution_warning);
     199     4298414 :   comm().max(_has_solution_error);
     200             : 
     201             :   // We've now synced
     202     4298414 :   _has_synced = true;
     203     4298414 : }
     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        1374 :   FullTable vtable({"Object", "Converged", "Timestep", "Total", "Message"}, 4);
     218             : 
     219        1374 :   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        1374 :   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           0 : }
     255             : 
     256             : SolutionInvalidity::TimeTable
     257         714 : SolutionInvalidity::transientTable(unsigned int & step_interval) const
     258             : {
     259             :   mooseAssert(_has_synced, "Has not synced");
     260             : 
     261        1428 :   TimeTable vtable({"Object", "Time", "Stepinterval Count", "Total Count"}, 4);
     262             : 
     263        1428 :   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        1428 :   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         714 :   if (processor_id() == 0)
     278             :   {
     279        1426 :     for (const auto id : index_range(_counts))
     280             :     {
     281         938 :       const auto & entry = _counts[id];
     282         938 :       const auto & info = _solution_invalidity_registry.item(id);
     283         938 :       std::vector<unsigned int> interval_counts;
     284         938 :       std::vector<unsigned int> total_counts;
     285             : 
     286         938 :       if (!entry.timestep_counts.empty())
     287             :       {
     288             :         // Allow warnings from the setup step
     289        2828 :         for (unsigned int timestep = 0; timestep <= entry.timestep_counts.back().timestep_index;
     290        1892 :              timestep += step_interval)
     291             :         {
     292             : 
     293        1892 :           auto start_it = timestep;
     294        1892 :           auto end_it = (timestep + step_interval < entry.timestep_counts.back().timestep_index)
     295        3651 :                             ? start_it + step_interval
     296        1759 :                             : entry.timestep_counts.back().timestep_index;
     297             : 
     298        1892 :           int interval_sum = 0;
     299        4407 :           for (auto ts_count : entry.timestep_counts)
     300             :           {
     301             :             // Allow warnings from the setup step
     302        2515 :             if (ts_count.timestep_index >= start_it &&
     303        2277 :                 (ts_count.timestep_index < end_it || start_it == end_it))
     304        1062 :               interval_sum += ts_count.counts;
     305             :           }
     306             : 
     307        1892 :           interval_counts.push_back(interval_sum);
     308             :         }
     309             :       }
     310             : 
     311         938 :       unsigned int interval_sum = 0;
     312        2830 :       for (unsigned int interval_index : index_range(interval_counts))
     313             :       {
     314             :         std::string interval_index_str =
     315        1892 :             std::to_string(interval_index) + "-" + std::to_string(interval_index + step_interval);
     316             : 
     317        1892 :         interval_sum += interval_counts[interval_index];
     318        1892 :         vtable.addRow(info.object_type + " : " + info.message, // Object information
     319             :                       interval_index_str,                      // Interval Index
     320        1892 :                       interval_counts[interval_index],         // Interval Counts
     321             :                       interval_sum                             // Total Iteration Warnings
     322             : 
     323             :         );
     324        1892 :       }
     325         938 :     }
     326             :   }
     327         714 :   return vtable;
     328           0 : }
     329             : 
     330             : // Define data store structure for TimestepCounts
     331             : void
     332         426 : dataStore(std::ostream & stream,
     333             :           SolutionInvalidity::TimestepCounts & timestep_counts,
     334             :           void * context)
     335             : {
     336         426 :   dataStore(stream, timestep_counts.timestep_index, context);
     337         426 :   dataStore(stream, timestep_counts.counts, context);
     338         426 : }
     339             : 
     340             : // Define data load structure for TimestepCounts
     341             : void
     342         226 : dataLoad(std::istream & stream,
     343             :          SolutionInvalidity::TimestepCounts & timestep_counts,
     344             :          void * context)
     345             : {
     346         226 :   dataLoad(stream, timestep_counts.timestep_index, context);
     347         226 :   dataLoad(stream, timestep_counts.counts, context);
     348         226 : }
     349             : 
     350             : void
     351       45800 : dataStore(std::ostream & stream, SolutionInvalidity & solution_invalidity, void * context)
     352             : {
     353       45800 :   solution_invalidity.syncIteration();
     354             : 
     355       45800 :   if (solution_invalidity.processor_id() != 0)
     356        5778 :     return;
     357             : 
     358             :   // Build data structure for store
     359       40022 :   std::size_t size = solution_invalidity._counts.size();
     360       40022 :   dataStore(stream, size, context);
     361             : 
     362       40396 :   for (const auto id : index_range(solution_invalidity._counts))
     363             :   {
     364         374 :     auto & entry = solution_invalidity._counts[id];
     365         374 :     const auto & info = solution_invalidity._solution_invalidity_registry.item(id);
     366         374 :     std::string type = info.object_type;
     367         374 :     std::string message = info.message;
     368         374 :     bool warning = info.warning;
     369         374 :     dataStore(stream, type, context);
     370         374 :     dataStore(stream, message, context);
     371         374 :     dataStore(stream, warning, context);
     372         374 :     dataStore(stream, entry.current_counts, context);
     373         374 :     dataStore(stream, entry.current_timestep_counts, context);
     374         374 :     dataStore(stream, entry.timestep_counts, context);
     375         374 :     dataStore(stream, entry.total_counts, context);
     376         374 :   }
     377             : }
     378             : 
     379             : void
     380       13984 : dataLoad(std::istream & stream, SolutionInvalidity & solution_invalidity, void * context)
     381             : {
     382       13984 :   if (solution_invalidity.processor_id() != 0)
     383        2670 :     return;
     384             : 
     385             :   std::size_t num_counts;
     386             :   // load data block size
     387       11314 :   dataLoad(stream, num_counts, context);
     388             : 
     389       11314 :   std::string object_type, message;
     390             :   bool warning;
     391             :   InvalidSolutionID id;
     392             : 
     393             :   // loop over and load stored data
     394       11502 :   for (size_t i = 0; i < num_counts; i++)
     395             :   {
     396         188 :     dataLoad(stream, object_type, context);
     397         188 :     dataLoad(stream, message, context);
     398         188 :     dataLoad(stream, warning, context);
     399             : 
     400         188 :     const moose::internal::SolutionInvalidityName name(object_type, message);
     401         188 :     if (solution_invalidity._solution_invalidity_registry.keyExists(name))
     402         136 :       id = solution_invalidity._solution_invalidity_registry.id(name);
     403             :     else
     404          52 :       id = moose::internal::getSolutionInvalidityRegistry().registerInvalidity(
     405             :           object_type, message, warning);
     406             : 
     407         188 :     if (solution_invalidity._counts.size() <= id)
     408          52 :       solution_invalidity._counts.resize(id + 1);
     409             : 
     410         188 :     auto & entry = solution_invalidity._counts[id];
     411         188 :     dataLoad(stream, entry.current_counts, context);
     412         188 :     dataLoad(stream, entry.current_timestep_counts, context);
     413         188 :     dataLoad(stream, entry.timestep_counts, context);
     414         188 :     dataLoad(stream, entry.total_counts, context);
     415         188 :   }
     416       11314 : }

Generated by: LCOV version 1.14