LCOV - code coverage report
Current view: top level - src/restart - RestartableDataReader.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 146 176 83.0 %
Date: 2025-07-17 01:28:37 Functions: 18 24 75.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 "RestartableDataReader.h"
      11             : 
      12             : #include "StringInputStream.h"
      13             : #include "FileInputStream.h"
      14             : #include "RestartableDataMap.h"
      15             : #include "MooseUtils.h"
      16             : 
      17             : #include <fstream>
      18             : 
      19             : // Type hash codes can't be relied on for older clang...
      20             : // not sure why, and also don't care why
      21             : #if defined(__clang__) && __clang_major__ < 12
      22             : #define RESTARTABLE_SKIP_CHECK_HASH_CODE
      23             : #endif
      24             : 
      25        3224 : RestartableDataReader::RestartableDataReader(MooseApp & app,
      26             :                                              RestartableDataMap & data,
      27        3224 :                                              const bool force /* = false */)
      28             :   : RestartableDataIO(app, data),
      29        3224 :     _is_restoring(false),
      30        3224 :     _error_on_different_number_of_processors(true),
      31        3224 :     _force(force)
      32             : {
      33        3224 : }
      34             : 
      35       62755 : RestartableDataReader::RestartableDataReader(MooseApp & app,
      36             :                                              std::vector<RestartableDataMap> & data,
      37       62755 :                                              const bool force /* = false */)
      38             :   : RestartableDataIO(app, data),
      39       62755 :     _is_restoring(false),
      40       62755 :     _error_on_different_number_of_processors(true),
      41       62755 :     _force(force)
      42             : {
      43       62755 : }
      44             : 
      45             : void
      46        9906 : RestartableDataReader::setInput(std::unique_ptr<std::stringstream> header_stream,
      47             :                                 std::unique_ptr<std::stringstream> data_stream)
      48             : {
      49             :   mooseAssert(!_streams.header, "Header input already set");
      50             :   mooseAssert(!_streams.data, "Data stream already set");
      51        9906 :   _streams.header = std::make_unique<StringInputStream>(std::move(header_stream));
      52        9906 :   _streams.data = std::make_unique<StringInputStream>(std::move(data_stream));
      53        9906 : }
      54             : 
      55             : void
      56        6539 : RestartableDataReader::setInput(const std::filesystem::path & folder_base)
      57             : {
      58             :   mooseAssert(!_streams.header, "Header input already set");
      59             :   mooseAssert(!_streams.data, "Data stream already set");
      60        6539 :   _streams.header = std::make_unique<FileInputStream>(restartableHeaderFile(folder_base));
      61        6539 :   _streams.data = std::make_unique<FileInputStream>(restartableDataFile(folder_base));
      62        6539 : }
      63             : 
      64             : RestartableDataReader::InputStreams
      65       13182 : RestartableDataReader::clear()
      66             : {
      67       13182 :   _is_restoring = false;
      68       13182 :   _header.clear();
      69       13182 :   InputStreams streams;
      70       13182 :   std::swap(_streams, streams);
      71       13182 :   return streams;
      72             : }
      73             : 
      74             : std::vector<std::unordered_map<std::string, RestartableDataReader::HeaderEntry>>
      75       16445 : RestartableDataReader::readHeader(InputStream & header_input) const
      76             : {
      77       16445 :   std::vector<std::unordered_map<std::string, RestartableDataReader::HeaderEntry>> header;
      78             : 
      79       16445 :   auto stream_ptr = header_input.get();
      80       16445 :   auto & stream = *stream_ptr;
      81             : 
      82       16445 :   stream.seekg(0);
      83             : 
      84          28 :   const auto error = [&header_input](auto... args)
      85             :   {
      86          14 :     std::stringstream err_prefix;
      87          14 :     err_prefix << "While reading restartable data in ";
      88          14 :     const auto filename = header_input.getFilename();
      89          14 :     if (filename)
      90          14 :       err_prefix << std::filesystem::absolute(filename->parent_path());
      91             :     else
      92           0 :       err_prefix << "memory";
      93          14 :     err_prefix << ":\n\n";
      94             : 
      95          14 :     mooseError(err_prefix.str(), args...);
      96       16445 :   };
      97             : 
      98             :   // ID
      99             :   char this_id[2];
     100       16445 :   stream.read(this_id, 2);
     101       16445 :   if (this_id[0] != 'R' || this_id[1] != 'D')
     102           0 :     error("The data is invalid or corrupted (unexpected header)");
     103             : 
     104             :   // File version
     105             :   std::remove_const<decltype(CURRENT_BACKUP_FILE_VERSION)>::type this_file_version;
     106       16445 :   dataLoad(stream, this_file_version, nullptr);
     107       16445 :   if (this_file_version != CURRENT_BACKUP_FILE_VERSION)
     108           0 :     error("There is a mismatch in the backup version\n\n",
     109             :           "Current backup version: ",
     110             :           CURRENT_BACKUP_FILE_VERSION,
     111             :           "\nLoaded backup version: ",
     112             :           this_file_version);
     113             : 
     114             :   // Type id for a basic type
     115             :   std::size_t this_compare_hash_code;
     116       16445 :   dataLoad(stream, this_compare_hash_code, nullptr);
     117             : #ifndef RESTARTABLE_SKIP_CHECK_HASH_CODE
     118       16445 :   if (this_compare_hash_code != typeid(COMPARE_HASH_CODE_TYPE).hash_code() && !_force)
     119           0 :     error("The backup is not compatible\n\nThe hash code check for a basic type (",
     120           0 :           MooseUtils::prettyCppType<COMPARE_HASH_CODE_TYPE>(),
     121             :           ") failed.\nIt is possible that this backup was stored with a different architecture or "
     122             :           "operating system.\n\nTo forcefully attempt loading the backup, use the command line "
     123             :           "option --force-restart");
     124             : #else
     125             :   (void)_force;
     126             : #endif
     127             : 
     128             :   // Number of procs
     129       16445 :   decltype(n_processors()) this_n_procs = 0;
     130       16445 :   dataLoad(stream, this_n_procs, nullptr);
     131       16445 :   if (_error_on_different_number_of_processors && this_n_procs != n_processors())
     132           7 :     error("The number of MPI ranks is not consistent\n\nCurrent MPI ranks: ",
     133             :           n_processors(),
     134             :           "\nLoaded MPI ranks: ",
     135             :           this_n_procs);
     136             : 
     137             :   // Number of data
     138       16438 :   decltype(dataSize()) this_num_data = 0;
     139       16438 :   dataLoad(stream, this_num_data, nullptr);
     140       16438 :   if (this_num_data != dataSize())
     141           7 :     error("The number of threads is not consistent\n\nCurrent threads: ",
     142             :           dataSize(),
     143             :           "\nLoaded threads: ",
     144             :           this_num_data);
     145             : 
     146       16431 :   header.resize(dataSize());
     147             : 
     148             :   // Size of data for each thread
     149       16431 :   std::vector<std::size_t> tid_n_data(dataSize());
     150       33750 :   for (const auto tid : make_range(dataSize()))
     151       17319 :     dataLoad(stream, tid_n_data[tid], nullptr);
     152             : 
     153             :   // The position of the current data that we're loading
     154       16431 :   std::size_t current_data_position = 0;
     155             : 
     156             :   // Load the data header for each thread
     157       33750 :   for (const auto tid : make_range(dataSize()))
     158      552537 :     for (const auto i : make_range(tid_n_data[tid]))
     159             :     {
     160      535218 :       std::ignore = i;
     161             : 
     162      535218 :       std::string name;
     163      535218 :       dataLoad(stream, name, nullptr);
     164             :       mooseAssert(name.size(), "Empty name");
     165             : 
     166             :       mooseAssert(!header[tid].count(name), "Data '" + name + "' is already inserted");
     167      535218 :       auto & entry = header[tid][name];
     168             : 
     169      535218 :       dataLoad(stream, entry.size, nullptr);
     170      535218 :       dataLoad(stream, entry.type_hash_code, nullptr);
     171      535218 :       dataLoad(stream, entry.type, nullptr);
     172      535218 :       dataLoad(stream, entry.has_context, nullptr);
     173      535218 :       entry.position = current_data_position;
     174             : 
     175      535218 :       current_data_position += entry.size;
     176      535218 :     }
     177             : 
     178       16431 :   stream.seekg(0);
     179             : 
     180       32862 :   return header;
     181       16431 : }
     182             : 
     183             : void
     184       16446 : RestartableDataReader::restore(const DataNames & filter_names /* = {} */)
     185             : {
     186       16446 :   if (!_streams.header || !_streams.data)
     187           1 :     mooseError("RestartableDataReader::restore(): Cannot restore because an input was not set");
     188             : 
     189       16445 :   _is_restoring = true;
     190             : 
     191             :   // Set everything as not loaded
     192       33786 :   for (const auto tid : make_range(dataSize()))
     193      553326 :     for (auto & value : currentData(tid))
     194      535985 :       value.setNotLoaded({});
     195             : 
     196             :   // Read the header
     197       16445 :   _header = readHeader(*_streams.header);
     198             : 
     199       33717 :   for (const auto tid : index_range(_header))
     200             :   {
     201       17313 :     auto & data = currentData(tid);
     202       17313 :     const auto & header = _header[tid];
     203             : 
     204             :     // TODO: Think about what to do with missing data
     205             :     // Load the data in the order that it was requested
     206      551956 :     for (auto & value : data)
     207             :     {
     208      534670 :       const auto & name = value.name();
     209             : 
     210      534670 :       auto find_header = header.find(name);
     211      534670 :       if (find_header == header.end())
     212        1060 :         continue;
     213             : 
     214      533610 :       auto & header_entry = find_header->second;
     215             : 
     216             :       // Only restore values if we're either recovering or the data isn't filtered out
     217      533610 :       const auto is_data_in_filter = filter_names.find(name) != filter_names.end();
     218      533610 :       if (!is_data_in_filter)
     219      526639 :         deserializeValue(*_streams.data, value, header_entry);
     220             :     }
     221             :   }
     222       16404 : }
     223             : 
     224             : bool
     225          21 : RestartableDataReader::hasData(const std::string & data_name,
     226             :                                const std::type_info & type,
     227             :                                const THREAD_ID tid) const
     228             : {
     229          21 :   if (const auto header = queryHeader(data_name, tid))
     230          20 :     return isSameType(*header, type);
     231           1 :   return false;
     232             : }
     233             : 
     234             : const RestartableDataReader::HeaderEntry *
     235          26 : RestartableDataReader::queryHeader(const std::string & data_name, const THREAD_ID tid) const
     236             : {
     237          26 :   requireRestoring();
     238          26 :   const auto it = _header[tid].find(data_name);
     239          26 :   if (it == _header[tid].end())
     240           1 :     return nullptr;
     241          25 :   return &it->second;
     242             : }
     243             : 
     244             : const RestartableDataReader::HeaderEntry &
     245           5 : RestartableDataReader::getHeader(const std::string & data_name, const THREAD_ID tid) const
     246             : {
     247           5 :   const auto header = queryHeader(data_name, tid);
     248           5 :   if (!header)
     249           0 :     mooseError(
     250             :         "RestartableDataReader::getHeader(): Failed to find a header entry for data with name '",
     251             :         data_name,
     252             :         "'");
     253           5 :   return *header;
     254             : }
     255             : 
     256             : void
     257      526644 : RestartableDataReader::deserializeValue(
     258             :     InputStream & data_input,
     259             :     RestartableDataValue & value,
     260             :     const RestartableDataReader::HeaderEntry & header_entry) const
     261             : {
     262             :   mooseAssert(!value.loaded(), value.name() + " is already loaded");
     263             : 
     264           0 :   auto error = [&data_input, &value](auto... args)
     265             :   {
     266           0 :     std::stringstream err;
     267           0 :     err << "While loading restartable data\n\n";
     268           0 :     err << "From: ";
     269           0 :     const auto filename = data_input.getFilename();
     270           0 :     if (filename)
     271           0 :       err << std::filesystem::absolute(filename->parent_path());
     272             :     else
     273           0 :       err << "memory";
     274           0 :     err << "\nData name: " << value.name();
     275           0 :     err << "\nData type: " << value.type();
     276           0 :     err << "\n\n";
     277           0 :     mooseError(err.str(), args...);
     278      526644 :   };
     279             : 
     280      526644 :   if (!isSameType(header_entry, value.typeId()))
     281           0 :     error("The stored type of '", header_entry.type, "' does not match");
     282             : 
     283      526644 :   auto stream_ptr = data_input.get();
     284      526644 :   auto & stream = *stream_ptr;
     285             : 
     286      526644 :   stream.seekg(header_entry.position);
     287      526644 :   value.load(stream);
     288             : 
     289      526617 :   if (stream.tellg() == -1)
     290           0 :     error("An error was encountered when reading from the stream");
     291             : 
     292      526617 :   const std::size_t loaded_size = stream.tellg() - header_entry.position;
     293      526617 :   if (loaded_size != header_entry.size)
     294           0 :     error("The data read does not match the data stored\n\n",
     295             :           "Stored size: ",
     296           0 :           header_entry.size,
     297             :           "\nLoaded size: ",
     298             :           loaded_size);
     299      526617 : }
     300             : 
     301             : bool
     302        9631 : RestartableDataReader::isAvailable(const std::filesystem::path & folder_base)
     303             : {
     304        9631 :   const auto header_path = restartableDataFile(folder_base);
     305        9631 :   const auto data_path = restartableDataFile(folder_base);
     306             : 
     307       19262 :   const auto available = [](const auto & filename)
     308       19262 :   { return MooseUtils::pathExists(filename) && MooseUtils::checkFileReadable(filename); };
     309             : 
     310        9631 :   const bool header_available = available(header_path);
     311        9631 :   const bool data_available = available(data_path);
     312             : 
     313        9631 :   if (header_available != data_available)
     314           0 :     mooseError("The restart ",
     315           0 :                header_available ? "header" : "data",
     316             :                " is available but the corresponding ",
     317           0 :                header_available ? "data" : "header",
     318             :                " is not available\n\n",
     319             :                "Header (",
     320           0 :                header_available ? "available" : "missing",
     321             :                "): ",
     322           0 :                std::filesystem::absolute(header_path),
     323             :                "\nData (",
     324           0 :                data_available ? "available" : "missing",
     325             :                "): ",
     326           0 :                std::filesystem::absolute(header_path));
     327             : 
     328       19262 :   return header_available && data_available;
     329        9631 : }
     330             : 
     331             : void
     332          26 : RestartableDataReader::requireRestoring() const
     333             : {
     334          26 :   if (!_is_restoring)
     335           0 :     mooseError(
     336             :         "The RestartableDataReader is not available for querying as it is not currently restoring");
     337             :   mooseAssert(_streams.header, "Header not available");
     338             :   mooseAssert(_streams.data, "Data not available");
     339          26 : }
     340             : 
     341             : bool
     342      526664 : RestartableDataReader::isSameType(const RestartableDataReader::HeaderEntry & header_entry,
     343             :                                   const std::type_info & type) const
     344             : {
     345             : #ifndef RESTARTABLE_SKIP_CHECK_HASH_CODE
     346      526664 :   if (header_entry.type_hash_code == type.hash_code())
     347      526654 :     return true;
     348             : #endif
     349          10 :   return header_entry.type == type.name();
     350             : }
     351             : 
     352             : RestartableDataValue &
     353          10 : RestartableDataReader::restoreData(const std::string & data_name,
     354             :                                    std::unique_ptr<RestartableDataValue> value,
     355             :                                    const THREAD_ID tid)
     356             : {
     357          10 :   auto & data_map = currentData(tid);
     358          10 :   if (data_map.hasData(data_name))
     359           5 :     mooseError("RestartableDataReader::restoreData(): Cannot declare restartable data '",
     360             :                data_name,
     361             :                "' because it has already been declared");
     362             : 
     363           5 :   const auto & header = getHeader(data_name, tid);
     364           5 :   auto & added_value = currentData(tid).addData(std::move(value));
     365           5 :   deserializeValue(*_streams.data, added_value, header);
     366           5 :   return added_value;
     367             : }

Generated by: LCOV version 1.14