https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
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
45void
46RestartableDataReader::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
55void
56RestartableDataReader::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
74std::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
183void
184RestartableDataReader::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
197 _header = readHeader(*_streams.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
224bool
225RestartableDataReader::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
235RestartableDataReader::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
245RestartableDataReader::getHeader(const std::string & data_name, const THREAD_ID tid) const
246{
247 const auto header = queryHeader(data_name, tid);
248 if (!header)
250 "RestartableDataReader::getHeader(): Failed to find a header entry for data with name '",
251 data_name,
252 "'");
253 return *header;
254}
255
256void
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
301bool
302RestartableDataReader::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
331void
333{
334 if (!_is_restoring)
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
341bool
343 const THREAD_ID tid,
345{
346 if (!isRestoring() || value.loaded())
347 return false;
348 const auto * const header_entry = queryHeader(value.name(), tid);
349 if (!header_entry)
350 return false;
351 deserializeValue(*_streams.data, value, *header_entry);
352 return true;
353}
354
355bool
357 const std::type_info & type) const
358{
359#ifndef RESTARTABLE_SKIP_CHECK_HASH_CODE
360 if (header_entry.type_hash_code == type.hash_code())
361 return true;
362#endif
363 return header_entry.type == type.name();
364}
365
367RestartableDataReader::restoreData(const std::string & data_name,
368 std::unique_ptr<RestartableDataValue> value,
369 const THREAD_ID tid)
370{
371 auto & data_map = currentData(tid);
372 if (data_map.hasData(data_name))
373 mooseError("RestartableDataReader::restoreData(): Cannot declare restartable data '",
374 data_name,
375 "' because it has already been declared");
376
377 const auto & header = getHeader(data_name, tid);
378 auto & added_value = currentData(tid).addData(std::move(value));
379 deserializeValue(*_streams.data, added_value, header);
380 return added_value;
381}
void dataLoad(std::istream &stream, LineSegment &l, void *context)
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
unsigned int THREAD_ID
Definition MooseTypes.h:237
unsigned int count
Definition MortarUtils.C:53
std::unordered_set< std::string > DataNames
Helper class that hands out input streams to an underlying, managed stream of arbitrary type.
Definition InputStream.h:23
virtual std::shared_ptr< std::istream > get() const =0
Gets an input stream to the underlying stream.
virtual std::optional< std::filesystem::path > getFilename() const
Gets the underlying filename, if any.
Definition InputStream.C:44
Base class for MOOSE-based applications.
Definition MooseApp.h:110
Class for doing restart.
static const std::string & restartableDataFile()
static const std::string & restartableHeaderFile()
RestartableDataMap & currentData(const THREAD_ID tid)
static const unsigned int CURRENT_BACKUP_FILE_VERSION
The current version for the backup file.
int COMPARE_HASH_CODE_TYPE
The type to used for comparing hash codes (sanity checking)
std::size_t dataSize() const
Storage for restartable data that is ordered based on insertion order.
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)
RestartableDataReader(MooseApp &app, RestartableDataMap &data, const bool force=false)
const HeaderEntry * queryHeader(const std::string &data_name, const THREAD_ID tid) const
bool hasData(const std::string &data_name, const THREAD_ID tid=0) const
bool _is_restoring
Whether or not we're currently restoring.
InputStreams _streams
The inputs for reading.
void deserializeValue(InputStream &data_input, RestartableDataValue &value, const HeaderEntry &header_entry) const
Internal method for deserializing (restoring from backup into a value)
bool restoreDataIfAvailable(RestartableDataValue &value, const THREAD_ID tid, Moose::PassKey< MooseApp >)
Restores value in place from the open reader if it is present in the checkpoint and has not yet been ...
const HeaderEntry & getHeader(const std::string &data_name, const THREAD_ID tid) const
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.
static bool isAvailable(const std::filesystem::path &folder_base)
InputStreams clear()
Clears the contents of the reader (header stream, data stream, header)
std::vector< std::unordered_map< std::string, HeaderEntry > > _header
The loaded headers from the restart.
const bool _force
Whether or not to forcefully attempt to read despite incompatibilities.
bool _error_on_different_number_of_processors
Whether or not to error with a different number of processors.
void restore(const DataNames &filter_names={})
Restores the restartable data.
void requireRestoring() const
Checks whether or not we're currently restoring and errors if not.
bool isSameType(const HeaderEntry &header_entry, const std::type_info &type) const
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...
Abstract definition of a RestartableData value.
processor_id_type n_processors() const
bool pathExists(const std::string &path)
Definition MooseUtils.C:258
bool checkFileReadable(const std::string &filename, bool check_line_endings, bool throw_on_unreadable, bool check_for_git_lfs_pointer)
Definition MooseUtils.C:265
Struct that describes data in the header.
std::string type
The type for this data.
std::streampos position
The position in the stream at which this data is.
std::size_t type_hash_code
The hash code for this data (typeid(T).hash_code())
std::size_t size
The size of this data.
Structure that contains the input streams for the reader.
std::unique_ptr< InputStream > header
std::unique_ptr< InputStream > data