https://mooseframework.inl.gov
RestartableEquationSystems.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 "DataIO.h"
13 
14 #include "libmesh/dof_map.h"
15 #include "libmesh/dof_object.h"
16 #include "libmesh/elem.h"
17 #include "libmesh/node.h"
18 
20  "SYSTEM_SOLUTION";
21 
23  : _es(mesh), _load_all_vectors(true)
24 {
25 }
26 
29  const std::vector<const libMesh::DofObject *> & ordered_objects) const
30 {
31  EquationSystemsHeader es_header;
32 
33  // Systems
34  for (const auto sys_num : make_range(_es.n_systems()))
35  {
36  const auto & sys = _es.get_system(sys_num);
37 
38  SystemHeader sys_header;
39  sys_header.name = sys.name();
40  sys_header.type = sys.system_type();
41 
42  // Variables in the system
43  for (const auto var_num : make_range(sys.n_vars()))
44  {
45  const auto & var = sys.variable(var_num);
46 
47  VariableHeader var_header;
48  var_header.name = var.name();
49  var_header.type = var.type();
50  var_header.size = 0;
51  var_header.variable = &var;
52 
53  mooseAssert(_es.comm().verify("sys_" + sys.name() + "_var_" + var.name()),
54  "Out of order in parallel");
55  mooseAssert(!sys_header.variables.count(var.name()), "Already inserted");
56 
57  // Non-SCALAR variable
58  if (var.type().family != SCALAR)
59  {
60  for (const auto & obj : ordered_objects)
61  var_header.size += sizeof(Real) * obj->n_comp(sys.number(), var.number());
62  }
63  // SCALAR variable on the last rank
64  else if (_es.processor_id() == _es.n_processors() - 1)
65  {
66  std::vector<dof_id_type> scalar_dofs;
67  sys.get_dof_map().SCALAR_dof_indices(scalar_dofs, var.number());
68  var_header.size += sizeof(Real) * scalar_dofs.size();
69  }
70 
71  sys_header.variables.emplace(var.name(), var_header);
72  }
73 
74  // System vector
75  auto & sys_vec_header = sys_header.vectors[SystemHeader::system_solution_name];
76  sys_vec_header.name = SystemHeader::system_solution_name;
77  sys_vec_header.vector = sys.solution.get();
78  sys_vec_header.type = sys_vec_header.vector->type();
79  for (const auto vec_num : make_range(sys.n_vectors()))
80  {
81  mooseAssert(_es.comm().verify("sys_" + sys.name() + "_vec_" + sys.vector_name(vec_num)),
82  "Out of order in parallel");
83  const auto & name = sys.vector_name(vec_num);
84  mooseAssert(!sys_header.vectors.count(name), "Already inserted");
85  auto & vec_header = sys_header.vectors[name];
86  vec_header.name = sys.vector_name(vec_num);
87  vec_header.projections = sys.vector_preservation(vec_header.name);
88  vec_header.vector = &sys.get_vector(vec_header.name);
89  vec_header.type = vec_header.vector->type();
90  }
91 
92  // System in this EquationSystems
93  mooseAssert(!es_header.systems.count(sys.name()), "Already inserted");
94  es_header.systems.emplace(sys.name(), sys_header);
95  }
96 
97  // Setup the positions in each vector for easy access later
98  std::size_t offset = 0;
99  for (auto & sys_name_header_pair : es_header.systems)
100  {
101  auto & sys_header = sys_name_header_pair.second;
102  for (auto & vec_name_header_pair : sys_header.vectors)
103  {
104  auto & vec_header = vec_name_header_pair.second;
105  for (const auto & var_name_header_pair : sys_header.variables)
106  {
107  const auto & var_header = var_name_header_pair.second;
108  mooseAssert(!vec_header.variable_offset.count(var_header.name), "Already inserted");
109  vec_header.variable_offset[var_header.name] = offset;
110  offset += var_header.size;
111  }
112  }
113  }
114 
115  es_header.data_size = offset;
116 
117  return es_header;
118 }
119 
120 std::vector<const libMesh::DofObject *>
122 {
123  std::vector<const libMesh::DofObject *> objects;
124  auto add = [&objects](const auto begin, const auto end)
125  {
126  std::set<const libMesh::DofObject *, libMesh::CompareDofObjectsByID> ordered(begin, end);
127  objects.insert(objects.end(), ordered.begin(), ordered.end());
128  };
129 
130  const auto & mesh = _es.get_mesh();
131  add(mesh.local_elements_begin(), mesh.local_elements_end());
132  add(mesh.local_nodes_begin(), mesh.local_nodes_end());
133 
134  return objects;
135 }
136 
137 void
138 RestartableEquationSystems::store(std::ostream & stream) const
139 {
140  // Order objects (elements and then nodes) by ID for storing
141  const auto ordered_objects = orderDofObjects();
142 
143  // Store the header (systems, variables, vectors)
144  EquationSystemsHeader es_header = buildHeader(ordered_objects);
145  dataStore(stream, es_header, nullptr);
146 
147  // Store the ordered objects so we can do a sanity check on if we're
148  // loading the same thing
149  {
150  std::vector<dof_id_type> ordered_objects_ids(ordered_objects.size());
151  for (const auto i : index_range(ordered_objects))
152  ordered_objects_ids[i] = ordered_objects[i]->id();
153  dataStore(stream, ordered_objects_ids, nullptr);
154  }
155 
156 #ifndef NDEBUG
157  const std::size_t data_initial_position = static_cast<std::size_t>(stream.tellp());
158 #endif
159 
160  // Store each system
161  for (const auto & sys_name_header_pair : es_header.systems)
162  {
163  const auto & sys_header = sys_name_header_pair.second;
164  const auto & sys = _es.get_system(sys_header.name);
165 
166  // Store each vector and variable
167  for (const auto & vec_name_header_pair : sys_header.vectors)
168  {
169  const auto & vec_header = vec_name_header_pair.second;
170  const auto & vec = *vec_header.vector;
171  for (const auto & var_name_header_pair : sys_header.variables)
172  {
173  const auto & var_header = var_name_header_pair.second;
174  const auto & var = *var_header.variable;
175 
176 #ifndef NDEBUG
177  const std::size_t var_initial_position = stream.tellp();
178 #endif
179 
180  // Non-SCALAR variable
181  if (var.type().family != SCALAR)
182  {
183  // Store for each component of each element and node
184  for (const auto & obj : ordered_objects)
185  for (const auto comp : make_range(obj->n_comp(sys.number(), var.number())))
186  {
187  auto val = vec(obj->dof_number(sys.number(), var.number(), comp));
188  dataStore(stream, val, nullptr);
189  }
190  }
191  // SCALAR variable on the last rank
192  else if (_es.processor_id() == _es.n_processors() - 1)
193  {
194  const auto & dof_map = sys.get_dof_map();
195  std::vector<dof_id_type> scalar_dofs;
196  dof_map.SCALAR_dof_indices(scalar_dofs, var.number());
197  for (const auto dof : scalar_dofs)
198  {
199  auto val = vec(dof);
200  dataStore(stream, val, nullptr);
201  }
202  }
203 
204 #ifndef NDEBUG
205  const std::size_t data_offset = var_initial_position - data_initial_position;
206  mooseAssert(vec_header.variable_offset.at(var_header.name) == data_offset,
207  "Invalid offset");
208 
209  const std::size_t current_position = static_cast<std::size_t>(stream.tellp());
210  const std::size_t var_size = current_position - var_initial_position;
211  mooseAssert(var_size == sys_header.variables.at(var.name()).size, "Incorrect assumed size");
212 #endif
213  }
214  }
215  }
216 
217  mooseAssert((data_initial_position + es_header.data_size) ==
218  static_cast<std::size_t>(stream.tellp()),
219  "Incorrect assumed size");
220 }
221 
222 void
223 RestartableEquationSystems::load(std::istream & stream)
224 {
225  // Load the header (systems, variables, vectors)
226  // We do this first so that the loader can make informed decisions
227  // on what to put where based on everything that is available
228  _loaded_header.systems.clear();
229  dataLoad(stream, _loaded_header, nullptr);
230 
231  // Order objects (elements and then node) by ID for storing
233 
234  // Clear previously loaded variables to ensure a clean state
235  _loaded_variables.clear();
236 
237  // Sanity check on if we're loading the same thing
238  {
239  const std::string mesh_mismatch_message =
240  ". This means that equation-system data is being restored onto a different mesh topology. "
241  "If this occurs while restoring a MultiApp sub-application that uses mesh adaptivity, the "
242  "backup may not contain mesh checkpoint data for the restored topology, or the "
243  "sub-application mesh may have changed after the backup was made. Use clone_parent_mesh = "
244  "true when the sub-application should share the parent application's mesh topology, or "
245  "avoid changing the sub-application mesh between backup and restore.";
246 
247  std::vector<dof_id_type> from_ordered_objects_ids;
248  dataLoad(stream, from_ordered_objects_ids, nullptr);
249  if (_loaded_ordered_objects.size() != from_ordered_objects_ids.size())
250  mooseError("RestartableEquationSystems::load(): Number of previously stored elements/nodes (",
251  from_ordered_objects_ids.size(),
252  ") does not match the current number of elements/nodes (",
254  mesh_mismatch_message);
255  for (const auto i : index_range(_loaded_ordered_objects))
256  if (_loaded_ordered_objects[i]->id() != from_ordered_objects_ids[i])
257  mooseError("RestartableEquationSystems::load(): Id of previously stored element/node (",
258  from_ordered_objects_ids[i],
259  ") does not match the current element/node id (",
260  _loaded_ordered_objects[i]->id(),
261  mesh_mismatch_message);
262  }
263 
264  _loaded_stream_data_begin = static_cast<std::size_t>(stream.tellg());
265 
266  // Load everything that we have available at the moment
267  for (const auto & sys_name_header_pair : _loaded_header.systems)
268  {
269  const auto & sys_header = sys_name_header_pair.second;
270  if (!_es.has_system(sys_header.name))
271  continue;
272  auto & sys = _es.get_system(sys_header.name);
273 
274  bool modified_sys = false;
275 
276  for (const auto & vec_name_header_pair : sys_header.vectors)
277  {
278  bool modified_vec = false;
279 
280  const auto & vec_header = vec_name_header_pair.second;
281  const bool is_solution = vec_header.name == SystemHeader::system_solution_name;
282 
283  if (!is_solution && !sys.have_vector(vec_header.name))
284  {
285  if (_load_all_vectors)
286  sys.add_vector(vec_header.name, vec_header.projections, vec_header.type);
287  else
288  continue;
289  }
290 
291  auto & vec = is_solution ? *sys.solution : sys.get_vector(vec_header.name);
292 
293  for (const auto & var_name_header_pair : sys_header.variables)
294  {
295  const auto & var_header = var_name_header_pair.second;
296  if (!sys.has_variable(var_header.name))
297  continue;
298  const auto & var = sys.variable(sys.variable_number(var_header.name));
299  if (var.type() != var_header.type)
300  continue;
301 
302  restore(sys_header, vec_header, var_header, sys, vec, var, stream);
303  modified_vec = true;
304  }
305 
306  if (modified_vec)
307  {
308  vec.close();
309  modified_sys = true;
310  }
311  }
312 
313  if (modified_sys)
314  sys.update();
315  }
316 
317  // Move the stream to the end of our data so that we make RestartableDataReader happy
319 }
320 
321 bool
322 RestartableEquationSystems::isVariableRestored(const std::string & system_name,
323  const std::string & vector_name,
324  const std::string & variable_name) const
325 {
326  std::tuple<std::string, std::string, std::string> key(system_name, vector_name, variable_name);
327  return _loaded_variables.count(key);
328 }
329 
330 void
332  const VectorHeader & from_vec_header,
333  const VariableHeader & from_var_header,
334  const libMesh::System & to_sys,
336  const libMesh::Variable & to_var,
337  std::istream & stream)
338 {
339 #ifndef NDEBUG
340  const auto sys_it = _loaded_header.systems.find(from_sys_header.name);
341  mooseAssert(sys_it != _loaded_header.systems.end(), "System does not exist");
342  const auto & sys_header = sys_it->second;
343  mooseAssert(sys_header == from_sys_header, "Not my system");
344  const auto vec_it = sys_header.vectors.find(from_vec_header.name);
345  mooseAssert(vec_it != sys_header.vectors.end(), "Vector does not exist");
346  const auto & vec_header = vec_it->second;
347  mooseAssert(vec_header == from_vec_header, "Not my vector");
348  const auto var_it = sys_header.variables.find(from_var_header.name);
349  mooseAssert(var_it != sys_header.variables.end(), "Variable does not exist");
350  const auto & var_header = var_it->second;
351  mooseAssert(var_header == from_var_header, "Not my variable");
352  mooseAssert(!isVariableRestored(from_sys_header.name, from_vec_header.name, from_var_header.name),
353  "Variable already restored");
354 #endif
355 
356  const auto error =
357  [&from_sys_header, &from_vec_header, &from_var_header, &to_sys, &to_var](auto... args)
358  {
359  mooseError("An error occured while restoring a system:\n",
360  args...,
361  "\n\nFrom system: ",
362  from_sys_header.name,
363  "\nFrom vector: ",
364  from_vec_header.name,
365  "\nFrom variable: ",
366  from_var_header.name,
367  "\nTo system: ",
368  to_sys.name(),
369  "\nTo variable: ",
370  to_var.name());
371  };
372 
373  if (from_var_header.type != to_var.type())
374  error("Cannot restore to a variable of a different type");
375 
376  const auto offset = from_vec_header.variable_offset.at(from_var_header.name);
377  stream.seekg(_loaded_stream_data_begin + offset);
378 
379  // Non-SCALAR variable
380  if (to_var.type().family != SCALAR)
381  {
382  for (const auto & obj : _loaded_ordered_objects)
383  for (const auto comp : make_range(obj->n_comp(to_sys.number(), to_var.number())))
384  {
385  Real val;
386  dataLoad(stream, val, nullptr);
387  to_vec.set(obj->dof_number(to_sys.number(), to_var.number(), comp), val);
388  }
389  }
390  // SCALAR variable on the last rank
391  else if (_es.processor_id() == _es.n_processors() - 1)
392  {
393  const auto & dof_map = to_sys.get_dof_map();
394  std::vector<dof_id_type> scalar_dofs;
395  dof_map.SCALAR_dof_indices(scalar_dofs, to_var.number());
396  for (const auto dof : scalar_dofs)
397  {
398  Real val;
399  dataLoad(stream, val, nullptr);
400  to_vec.set(dof, val);
401  }
402  }
403 
404  // insert into the member variable
405  std::tuple<std::string, std::string, std::string> _loaded_variable = {
406  from_sys_header.name, from_vec_header.name, from_var_header.name};
407 
408  _loaded_variables.insert(_loaded_variable);
409 
410  mooseAssert(isVariableRestored(from_sys_header.name, from_vec_header.name, from_var_header.name),
411  "Variable not marked as restored");
412 }
413 
414 void
415 dataStore(std::ostream & stream, RestartableEquationSystems & res, void *)
416 {
417  res.store(stream);
418 }
419 
420 void
421 dataLoad(std::istream & stream, RestartableEquationSystems & res, void *)
422 {
423  res.load(stream);
424 }
425 
426 void
427 dataStore(std::ostream & stream, RestartableEquationSystems::EquationSystemsHeader & header, void *)
428 {
429  dataStore(stream, header.systems, nullptr);
430  dataStore(stream, header.data_size, nullptr);
431 }
432 
433 void
434 dataLoad(std::istream & stream, RestartableEquationSystems::EquationSystemsHeader & header, void *)
435 {
436  dataLoad(stream, header.systems, nullptr);
437  dataLoad(stream, header.data_size, nullptr);
438 }
439 
440 void
441 dataStore(std::ostream & stream, RestartableEquationSystems::SystemHeader & header, void *)
442 {
443  dataStore(stream, header.name, nullptr);
444  dataStore(stream, header.type, nullptr);
445  dataStore(stream, header.variables, nullptr);
446  dataStore(stream, header.vectors, nullptr);
447 }
448 
449 void
450 dataLoad(std::istream & stream, RestartableEquationSystems::SystemHeader & header, void *)
451 {
452  dataLoad(stream, header.name, nullptr);
453  dataLoad(stream, header.type, nullptr);
454  dataLoad(stream, header.variables, nullptr);
455  dataLoad(stream, header.vectors, nullptr);
456 }
457 
458 void
459 dataStore(std::ostream & stream, RestartableEquationSystems::VariableHeader & header, void *)
460 {
461  dataStore(stream, header.name, nullptr);
462  dataStore(stream, header.type, nullptr);
463 }
464 void
465 dataLoad(std::istream & stream, RestartableEquationSystems::VariableHeader & header, void *)
466 {
467  dataLoad(stream, header.name, nullptr);
468  dataLoad(stream, header.type, nullptr);
469 }
470 
471 void
472 dataStore(std::ostream & stream, RestartableEquationSystems::VectorHeader & header, void *)
473 {
474  dataStore(stream, header.name, nullptr);
475  dataStore(stream, header.projections, nullptr);
476  dataStore(stream, header.type, nullptr);
477  dataStore(stream, header.variable_offset, nullptr);
478 }
479 void
480 dataLoad(std::istream & stream, RestartableEquationSystems::VectorHeader & header, void *)
481 {
482  dataLoad(stream, header.name, nullptr);
483  dataLoad(stream, header.projections, nullptr);
484  dataLoad(stream, header.type, nullptr);
485  dataLoad(stream, header.variable_offset, nullptr);
486 }
487 
488 void
489 to_json(nlohmann::json & json, const RestartableEquationSystems & res)
490 {
491 
492  nlohmann::json loaded_vars = nlohmann::json::array();
493 
494  for (const auto & [system, vector, variable] : res.getLoadedVariables())
495  {
496  loaded_vars.push_back({{"system", system}, {"vector", vector}, {"variable", variable}});
497  }
498 
499  json["loaded_variables"] = loaded_vars;
500 }
std::string name(const ElemQuality q)
libMesh::FEType type
The type of the stored variable.
void store(std::ostream &stream) const
Stores the EquationSystems to the given stream.
void restore(const SystemHeader &from_sys_header, const VectorHeader &from_vec_header, const VariableHeader &from_var_header, const libMesh::System &to_sys, libMesh::NumericVector< libMesh::Number > &to_vec, const libMesh::Variable &to_var, std::istream &stream)
unsigned int n_systems() const
std::size_t data_size
The total size of data for this EquationSystems.
void to_json(nlohmann::json &json, const RestartableEquationSystems &res)
SCALAR
std::size_t _loaded_stream_data_begin
The starting position for the vector data in the input stream.
const std::set< std::tuple< std::string, std::string, std::string > > & getLoadedVariables() const
Returns the set of variables that were loaded during the load() function. Each set of variables conta...
bool has_system(std::string_view name) const
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
std::size_t size
The size of this variable&#39;s data.
MeshBase & mesh
const Parallel::Communicator & comm() const
const T_sys & get_system(std::string_view name) const
bool projections
The projection flag (whether or not it should be projected or zeroed)
std::string name
The name of the stored vector.
libMesh::EquationSystems _es
The underlying EquationSystems.
std::string name
The name of the stored variable.
processor_id_type n_processors() const
std::map< std::string, RestartableEquationSystems::SystemHeader > systems
The stored systems in the equation systems.
RestartableEquationSystems(libMesh::MeshBase &mesh)
std::string name
The name of the stored system.
std::map< std::string, RestartableEquationSystems::VectorHeader > vectors
The stored vectors in the system.
static const std::string system_solution_name
Special name for a vector that is the system solution vector.
std::map< std::string, RestartableEquationSystems::VariableHeader > variables
The stored variables in the system.
bool isVariableRestored(const std::string &system_name, const std::string &vector_name, const std::string &variable_name) const
Checks whether variable was successfully restored from a restart file.
void load(std::istream &stream)
Loads the EquationSystems from the given stream.
Represents a stored system in restart.
std::string type
The type of the stored system.
EquationSystemsHeader _loaded_header
The loaded header.
libMesh::ParallelType type
The type of the stored vector.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
bool _load_all_vectors
Whether or not to load all of the vectors, including ones that haven&#39;t been added yet...
timpi_pure bool verify(const T &r) const
void dataLoad(std::istream &stream, RestartableEquationSystems &res, void *)
const libMesh::Variable * variable
The underlying variable (only valid during store, not used in load)
Wrapper class that owns a libMesh EquationSystem and adds advanced restart capability to it...
std::vector< const libMesh::DofObject * > _loaded_ordered_objects
The object ordering for this data.
const MeshBase & get_mesh() const
IntRange< T > make_range(T beg, T end)
void dataStore(std::ostream &stream, RestartableEquationSystems &res, void *)
std::vector< const libMesh::DofObject * > orderDofObjects() const
Internal method for ordering the DofObjects by ID (elems and the nodes)
virtual void set(const numeric_index_type i, const T value)=0
const std::string & name() const
std::set< std::tuple< std::string, std::string, std::string > > _loaded_variables
The variables that were loaded in load(); [system name, vector name, variable name].
std::map< std::string, std::size_t > variable_offset
The position of each variable for this vector (relative to the start of the data) ...
unsigned int number() const
Represents a stored EquationSystems in restart.
Represents a stored variable in restart.
processor_id_type processor_id() const
EquationSystemsHeader buildHeader(const std::vector< const libMesh::DofObject *> &ordered_objects) const
Internal method for building the header struct.
auto index_range(const T &sizable)
const FEType & type() const
Represents a stored variable in restart.