https://mooseframework.inl.gov
RestartableDataReader.C
Go to the documentation of this file.
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 
26  RestartableDataMap & data,
27  const bool force /* = false */)
28  : RestartableDataIO(app, data),
29  _is_restoring(false),
30  _error_on_different_number_of_processors(true),
31  _force(force)
32 {
33 }
34 
36  std::vector<RestartableDataMap> & data,
37  const bool force /* = false */)
38  : RestartableDataIO(app, data),
39  _is_restoring(false),
40  _error_on_different_number_of_processors(true),
41  _force(force)
42 {
43 }
44 
45 void
46 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  _streams.header = std::make_unique<StringInputStream>(std::move(header_stream));
52  _streams.data = std::make_unique<StringInputStream>(std::move(data_stream));
53 }
54 
55 void
56 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  _streams.header = std::make_unique<FileInputStream>(restartableHeaderFile(folder_base));
61  _streams.data = std::make_unique<FileInputStream>(restartableDataFile(folder_base));
62 }
63 
66 {
67  _is_restoring = false;
68  _header.clear();
69  InputStreams streams;
70  std::swap(_streams, streams);
71  return streams;
72 }
73 
74 std::vector<std::unordered_map<std::string, RestartableDataReader::HeaderEntry>>
76 {
77  std::vector<std::unordered_map<std::string, RestartableDataReader::HeaderEntry>> header;
78 
79  auto stream_ptr = header_input.get();
80  auto & stream = *stream_ptr;
81 
82  stream.seekg(0);
83 
84  const auto error = [&header_input](auto... args)
85  {
86  std::stringstream err_prefix;
87  err_prefix << "While reading restartable data in ";
88  const auto filename = header_input.getFilename();
89  if (filename)
90  err_prefix << std::filesystem::absolute(filename->parent_path());
91  else
92  err_prefix << "memory";
93  err_prefix << ":\n\n";
94 
95  mooseError(err_prefix.str(), args...);
96  };
97 
98  // ID
99  char this_id[2];
100  stream.read(this_id, 2);
101  if (this_id[0] != 'R' || this_id[1] != 'D')
102  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  dataLoad(stream, this_file_version, nullptr);
107  if (this_file_version != CURRENT_BACKUP_FILE_VERSION)
108  error("There is a mismatch in the backup version\n\n",
109  "Current backup 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  dataLoad(stream, this_compare_hash_code, nullptr);
117 #ifndef RESTARTABLE_SKIP_CHECK_HASH_CODE
118  if (this_compare_hash_code != typeid(COMPARE_HASH_CODE_TYPE).hash_code() && !_force)
119  error("The backup is not compatible\n\nThe hash code check for a basic type (",
120  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  decltype(n_processors()) this_n_procs = 0;
130  dataLoad(stream, this_n_procs, nullptr);
132  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  decltype(dataSize()) this_num_data = 0;
139  dataLoad(stream, this_num_data, nullptr);
140  if (this_num_data != dataSize())
141  error("The number of threads is not consistent\n\nCurrent threads: ",
142  dataSize(),
143  "\nLoaded threads: ",
144  this_num_data);
145 
146  header.resize(dataSize());
147 
148  // Size of data for each thread
149  std::vector<std::size_t> tid_n_data(dataSize());
150  for (const auto tid : make_range(dataSize()))
151  dataLoad(stream, tid_n_data[tid], nullptr);
152 
153  // The position of the current data that we're loading
154  std::size_t current_data_position = 0;
155 
156  // Load the data header for each thread
157  for (const auto tid : make_range(dataSize()))
158  for (const auto i : make_range(tid_n_data[tid]))
159  {
160  std::ignore = i;
161 
162  std::string name;
163  dataLoad(stream, name, nullptr);
164  mooseAssert(name.size(), "Empty name");
165 
166  mooseAssert(!header[tid].count(name), "Data '" + name + "' is already inserted");
167  auto & entry = header[tid][name];
168 
169  dataLoad(stream, entry.size, nullptr);
170  dataLoad(stream, entry.type_hash_code, nullptr);
171  dataLoad(stream, entry.type, nullptr);
172  dataLoad(stream, entry.has_context, nullptr);
173  entry.position = current_data_position;
174 
175  current_data_position += entry.size;
176  }
177 
178  stream.seekg(0);
179 
180  return header;
181 }
182 
183 void
184 RestartableDataReader::restore(const DataNames & filter_names /* = {} */)
185 {
186  if (!_streams.header || !_streams.data)
187  mooseError("RestartableDataReader::restore(): Cannot restore because an input was not set");
188 
189  _is_restoring = true;
190 
191  // Set everything as not loaded
192  for (const auto tid : make_range(dataSize()))
193  for (auto & value : currentData(tid))
194  value.setNotLoaded({});
195 
196  // Read the header
198 
199  for (const auto tid : index_range(_header))
200  {
201  auto & data = currentData(tid);
202  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  for (auto & value : data)
207  {
208  const auto & name = value.name();
209 
210  auto find_header = header.find(name);
211  if (find_header == header.end())
212  continue;
213 
214  auto & header_entry = find_header->second;
215 
216  // Only restore values if we're either recovering or the data isn't filtered out
217  const auto is_data_in_filter = filter_names.find(name) != filter_names.end();
218  if (!is_data_in_filter)
219  deserializeValue(*_streams.data, value, header_entry);
220  }
221  }
222 }
223 
224 bool
225 RestartableDataReader::hasData(const std::string & data_name,
226  const std::type_info & type,
227  const THREAD_ID tid) const
228 {
229  if (const auto header = queryHeader(data_name, tid))
230  return isSameType(*header, type);
231  return false;
232 }
233 
235 RestartableDataReader::queryHeader(const std::string & data_name, const THREAD_ID tid) const
236 {
238  const auto it = _header[tid].find(data_name);
239  if (it == _header[tid].end())
240  return nullptr;
241  return &it->second;
242 }
243 
245 RestartableDataReader::getHeader(const std::string & data_name, const THREAD_ID tid) const
246 {
247  const auto header = queryHeader(data_name, tid);
248  if (!header)
249  mooseError(
250  "RestartableDataReader::getHeader(): Failed to find a header entry for data with name '",
251  data_name,
252  "'");
253  return *header;
254 }
255 
256 void
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  auto error = [&data_input, &value](auto... args)
265  {
266  std::stringstream err;
267  err << "While loading restartable data\n\n";
268  err << "From: ";
269  const auto filename = data_input.getFilename();
270  if (filename)
271  err << std::filesystem::absolute(filename->parent_path());
272  else
273  err << "memory";
274  err << "\nData name: " << value.name();
275  err << "\nData type: " << value.type();
276  err << "\n\n";
277  mooseError(err.str(), args...);
278  };
279 
280  if (!isSameType(header_entry, value.typeId()))
281  error("The stored type of '", header_entry.type, "' does not match");
282 
283  auto stream_ptr = data_input.get();
284  auto & stream = *stream_ptr;
285 
286  stream.seekg(header_entry.position);
287  value.load(stream);
288 
289  if (stream.tellg() == -1)
290  error("An error was encountered when reading from the stream");
291 
292  const std::size_t loaded_size = stream.tellg() - header_entry.position;
293  if (loaded_size != header_entry.size)
294  error("The data read does not match the data stored\n\n",
295  "Stored size: ",
296  header_entry.size,
297  "\nLoaded size: ",
298  loaded_size);
299 }
300 
301 bool
302 RestartableDataReader::isAvailable(const std::filesystem::path & folder_base)
303 {
304  const auto header_path = restartableDataFile(folder_base);
305  const auto data_path = restartableDataFile(folder_base);
306 
307  const auto available = [](const auto & filename)
308  { return MooseUtils::pathExists(filename) && MooseUtils::checkFileReadable(filename); };
309 
310  const bool header_available = available(header_path);
311  const bool data_available = available(data_path);
312 
313  if (header_available != data_available)
314  mooseError("The restart ",
315  header_available ? "header" : "data",
316  " is available but the corresponding ",
317  header_available ? "data" : "header",
318  " is not available\n\n",
319  "Header (",
320  header_available ? "available" : "missing",
321  "): ",
322  std::filesystem::absolute(header_path),
323  "\nData (",
324  data_available ? "available" : "missing",
325  "): ",
326  std::filesystem::absolute(header_path));
327 
328  return header_available && data_available;
329 }
330 
331 void
333 {
334  if (!_is_restoring)
335  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 }
340 
341 bool
343  const std::type_info & type) const
344 {
345 #ifndef RESTARTABLE_SKIP_CHECK_HASH_CODE
346  if (header_entry.type_hash_code == type.hash_code())
347  return true;
348 #endif
349  return header_entry.type == type.name();
350 }
351 
353 RestartableDataReader::restoreData(const std::string & data_name,
354  std::unique_ptr<RestartableDataValue> value,
355  const THREAD_ID tid)
356 {
357  auto & data_map = currentData(tid);
358  if (data_map.hasData(data_name))
359  mooseError("RestartableDataReader::restoreData(): Cannot declare restartable data '",
360  data_name,
361  "' because it has already been declared");
362 
363  const auto & header = getHeader(data_name, tid);
364  auto & added_value = currentData(tid).addData(std::move(value));
365  deserializeValue(*_streams.data, added_value, header);
366  return added_value;
367 }
std::string name(const ElemQuality q)
OStreamProxy err
static const std::string & restartableDataFile()
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:302
void setInput(std::unique_ptr< std::stringstream > header_stream, std::unique_ptr< std::stringstream > data_stream)
Sets the input stream for reading from the stringstreams header_stream and data_stream for the header...
std::streampos position
The position in the stream at which this data is.
void requireRestoring() const
Checks whether or not we&#39;re currently restoring and errors if not.
void swap(std::vector< T > &data, const std::size_t idx0, const std::size_t idx1, const libMesh::Parallel::Communicator &comm)
Swap function for serial or distributed vector of data.
Definition: Shuffle.h:494
static const unsigned int CURRENT_BACKUP_FILE_VERSION
The current version for the backup file.
Class for doing restart.
Struct that describes data in the header.
std::size_t dataSize() const
std::size_t size
The size of this data.
std::unique_ptr< InputStream > header
static const std::string & restartableHeaderFile()
Base class for MOOSE-based applications.
Definition: MooseApp.h:85
const HeaderEntry & getHeader(const std::string &data_name, const THREAD_ID tid) const
Structure that contains the input streams for the reader.
Helper class that hands out input streams to an underlying, managed stream of arbitrary type...
Definition: InputStream.h:22
void dataLoad(std::istream &stream, PenetrationInfo *&pinfo, void *context)
int COMPARE_HASH_CODE_TYPE
The type to used for comparing hash codes (sanity checking)
RestartableDataValue & addData(std::unique_ptr< RestartableDataValue > data)
Adds the restartable data data to the map.
std::vector< std::unordered_map< std::string, HeaderEntry > > readHeader(InputStream &header_input) const
Internal method for reading the header (stored by RestartableDataWriter)
RestartableDataMap & currentData(const THREAD_ID tid)
Storage for restartable data that is ordered based on insertion order.
const HeaderEntry * queryHeader(const std::string &data_name, const THREAD_ID tid) const
processor_id_type n_processors() const
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
bool checkFileReadable(const std::string &filename, bool check_line_endings=false, bool throw_on_unreadable=true, bool check_for_git_lfs_pointer=true)
Checks to see if a file is readable (exists and permissions)
Definition: MooseUtils.C:250
bool isSameType(const HeaderEntry &header_entry, const std::type_info &type) const
RestartableDataReader(MooseApp &app, RestartableDataMap &data, const bool force=false)
T & restoreData(const std::string &data_name, const THREAD_ID tid=0, void *const context=nullptr, Args &&... args)
Restores the data with name data_name of type T.
bool _is_restoring
Whether or not we&#39;re currently restoring.
std::string type
The type for this data.
const bool _force
Whether or not to forcefully attempt to read despite incompatibilities.
bool hasData(const std::string &data_name, const THREAD_ID tid=0) const
std::unique_ptr< InputStream > data
void deserializeValue(InputStream &data_input, RestartableDataValue &value, const HeaderEntry &header_entry) const
Internal method for deserializing (restoring from backup into a value)
IntRange< T > make_range(T beg, T end)
std::unordered_set< std::string > DataNames
class infix_ostream_iterator if void
Definition: InfixIterator.h:26
bool _error_on_different_number_of_processors
Whether or not to error with a different number of processors.
InputStreams clear()
Clears the contents of the reader (header stream, data stream, header)
void restore(const DataNames &filter_names={})
Restores the restartable data.
InputStreams _streams
The inputs for reading.
bool pathExists(const std::string &path)
Definition: MooseUtils.C:243
std::size_t type_hash_code
The hash code for this data (typeid(T).hash_code())
auto index_range(const T &sizable)
virtual std::shared_ptr< std::istream > get() const =0
Gets an input stream to the underlying stream.
Abstract definition of a RestartableData value.
unsigned int THREAD_ID
Definition: MooseTypes.h:209
virtual std::optional< std::filesystem::path > getFilename() const
Gets the underlying filename, if any.
Definition: InputStream.C:44
std::vector< std::unordered_map< std::string, HeaderEntry > > _header
The loaded headers from the restart.
static bool isAvailable(const std::filesystem::path &folder_base)