https://mooseframework.inl.gov
MaterialPropertyStorage.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 #include "MaterialProperty.h"
12 #include "Material.h"
13 #include "MaterialData.h"
14 #include "MooseMesh.h"
15 #include "MaterialBase.h"
16 #include "DataIO.h"
17 
18 #include "libmesh/fe_interface.h"
19 #include "libmesh/quadrature.h"
20 
21 #include <optional>
22 
24  FEProblemBase & problem)
25  : _problem(problem),
26  _max_state(0),
27  _spin_mtx(libMesh::Threads::spin_mtx),
28  _registry(registry),
29  _restart_in_place(false),
30  _recovering(false)
31 {
33  for (const auto tid : make_range(libMesh::n_threads()))
34  _material_data.emplace_back(*this, tid);
35 }
36 
37 void
38 MaterialPropertyStorage::shallowSwapData(const std::vector<unsigned int> & stateful_prop_ids,
39  MaterialProperties & data,
40  MaterialProperties & data_from)
41 {
42  for (const auto i : make_range(std::min(stateful_prop_ids.size(), data_from.size())))
43  if (stateful_prop_ids[i] < data.size() && data.hasValue(stateful_prop_ids[i]) &&
44  data_from.hasValue(i))
45  {
46  auto & prop = data[stateful_prop_ids[i]];
47  auto & prop_from = data_from[i];
48  prop.swap(prop_from);
49  }
50 }
51 
52 void
53 MaterialPropertyStorage::shallowSwapDataBack(const std::vector<unsigned int> & stateful_prop_ids,
54  MaterialProperties & data,
55  MaterialProperties & data_from)
56 {
57  for (const auto i : make_range(std::min(stateful_prop_ids.size(), data.size())))
58  if (stateful_prop_ids[i] < data_from.size() && data.hasValue(i) &&
59  data_from.hasValue(stateful_prop_ids[i]))
60  {
61  auto & prop = data[i];
62  auto & prop_from = data_from[stateful_prop_ids[i]];
63  prop.swap(prop_from);
64  }
65 }
66 
67 std::optional<std::string>
69 {
70  if (_prop_records.size() > id && _prop_records[id] && _prop_records[id]->stateful())
71  return _registry.getName(id);
72  return {};
73 }
74 
75 void
77 {
78  for (const auto state : stateIndexRange())
79  setProps(state).erase(elem);
80 }
81 
82 void
84 {
85  _restart_in_place = true;
86  _restartable_map.clear();
87 }
88 
90 MaterialPropertyStorage::getPropRecord(const unsigned int id) const
91 {
92  mooseAssert(_prop_records.size() > id, "Invalid ID");
93  auto & prop_record_ptr = _prop_records[id];
94  mooseAssert(prop_record_ptr, "Not initialized");
95  return *prop_record_ptr;
96 }
97 
98 bool
99 MaterialPropertyStorage::isRestoredProperty(const std::string & name) const
100 {
101  const auto & record = getPropRecord(_registry.getID(name));
102  if (!record.stateful())
103  mooseAssert(!record.restored, "Stateful properties should not be restored");
104  return record.restored;
105 }
106 
107 const std::set<const MooseObject *> &
109 {
110  static const std::set<const MooseObject *> empty;
111 
112  const auto it = _consumers.find(type);
113 
114  if (it != _consumers.end())
115  return it->second;
116 
117  return empty;
118 }
119 
120 void
122  const processor_id_type libmesh_dbg_var(pid),
123  const std::vector<QpMap> & p_refinement_map,
124  const QBase & qrule,
125  const QBase & qrule_face,
126  const THREAD_ID tid,
127  const Elem & elem,
128  const int input_side)
129 {
130  unsigned int n_qpoints = 0;
131 
132  // If we passed in -1 for these then we really need to store properties at 0
133  unsigned int side = input_side == -1 ? 0 : input_side;
134 
135  if (input_side == -1) // Not doing side projection (ie, doing volume projection)
136  n_qpoints = qrule.n_points();
137  else
138  n_qpoints = qrule_face.n_points();
139 
140  _material_data[tid].resize(n_qpoints);
141 
142  mooseAssert(elem.active(), "We should be doing p-refinement on active elements only");
143  mooseAssert(elem.processor_id() == pid, "Prolongation should be occurring locally");
144  mooseAssert(p_refinement_map.size() == n_qpoints, "Refinement map not proper size");
145 
146  auto current_p_level_props = initProps(tid, &elem, side, n_qpoints);
147 
148  for (const auto state : stateIndexRange())
149  for (const auto i : index_range(_stateful_prop_id_to_prop_id))
150  {
151  auto & current_p_level_prop = (*current_p_level_props[state])[i];
152  // We need to clone this property in order to not overwrite the values we're going to be
153  // reading from
154  auto previous_p_level_prop = current_p_level_prop.clone(current_p_level_prop.size());
155  // Cloning, despite its name, does not copy the data. Luckily since we are about to overwrite
156  // all of the current_p_level_prop data, we can just swap its data over to our
157  // previous_p_level_prop
158  previous_p_level_prop->swap(current_p_level_prop);
159  current_p_level_prop.resize(n_qpoints);
160  for (const auto qp : index_range(p_refinement_map))
161  current_p_level_prop.qpCopy(qp, *previous_p_level_prop, p_refinement_map[qp]._to);
162  }
163 }
164 
165 void
167  processor_id_type pid,
168  const std::vector<std::vector<QpMap>> & refinement_map,
169  const QBase & qrule,
170  const QBase & qrule_face,
171  MaterialPropertyStorage & parent_material_props,
172  const THREAD_ID tid,
173  const Elem & elem,
174  const int input_parent_side,
175  const int input_child,
176  const int input_child_side)
177 {
178  mooseAssert(input_child != -1 || input_parent_side == input_child_side, "Invalid inputs!");
179 
180  unsigned int n_qpoints = 0;
181 
182  // If we passed in -1 for these then we really need to store properties at 0
183  unsigned int parent_side = input_parent_side == -1 ? 0 : input_parent_side;
184  unsigned int child_side = input_child_side == -1 ? 0 : input_child_side;
185 
186  if (input_child_side == -1) // Not doing side projection (ie, doing volume projection)
187  n_qpoints = qrule.n_points();
188  else
189  n_qpoints = qrule_face.n_points();
190 
191  _material_data[tid].resize(n_qpoints);
192 
193  unsigned int n_children = elem.n_children();
194 
195  std::vector<unsigned int> children;
196 
197  if (input_child != -1) // Passed in a child explicitly
198  children.push_back(input_child);
199  else
200  {
201  children.resize(n_children);
202  for (unsigned int child = 0; child < n_children; child++)
203  children[child] = child;
204  }
205 
206  for (const auto & child : children)
207  {
208  // If we're not projecting an internal child side, but we are projecting sides, see if this
209  // child is on that side
210  if (input_child == -1 && input_child_side != -1 && !elem.is_child_on_side(child, parent_side))
211  continue;
212 
213  const Elem * child_elem = elem.child_ptr(child);
214 
215  // If it's not a local child then it'll be prolonged where it is
216  // local
217  if (child_elem->processor_id() != pid)
218  continue;
219 
220  mooseAssert(child < refinement_map.size(), "Refinement_map vector not initialized");
221  const std::vector<QpMap> & child_map = refinement_map[child];
222 
223  auto child_props = initProps(tid, child_elem, child_side, n_qpoints);
224 
225  for (const auto state : stateIndexRange())
226  {
227  const auto & parent_props = parent_material_props.props(&elem, parent_side, state);
228  for (const auto i : index_range(_stateful_prop_id_to_prop_id))
229  for (const auto qp : index_range(refinement_map[child]))
230  (*child_props[state])[i].qpCopy(qp, parent_props[i], child_map[qp]._to);
231  }
232  }
233 }
234 
235 void
237  const std::vector<std::pair<unsigned int, QpMap>> & coarsening_map,
238  const std::vector<const Elem *> & coarsened_element_children,
239  const QBase & qrule,
240  const QBase & qrule_face,
241  const THREAD_ID tid,
242  const Elem & elem,
243  int input_side)
244 {
245  unsigned int side;
246 
247  bool doing_a_side = input_side != -1;
248 
249  unsigned int n_qpoints = 0;
250 
251  if (!doing_a_side)
252  {
253  side = 0; // Use 0 for the elem
254  n_qpoints = qrule.n_points();
255  }
256  else
257  {
258  side = input_side;
259  n_qpoints = qrule_face.n_points();
260  }
261 
262  auto parent_props = initProps(tid, &elem, side, n_qpoints);
263 
264  // Copy from the child stateful properties
265  for (const auto qp : index_range(coarsening_map))
266  {
267  const std::pair<unsigned int, QpMap> & qp_pair = coarsening_map[qp];
268  unsigned int child = qp_pair.first;
269 
270  mooseAssert(child < coarsened_element_children.size(),
271  "Coarsened element children vector not initialized");
272  const Elem * child_elem = coarsened_element_children[child];
273  const QpMap & qp_map = qp_pair.second;
274 
275  for (const auto state : stateIndexRange())
276  {
277  const auto & child_props = props(child_elem, side, state);
278  for (const auto i : index_range(_stateful_prop_id_to_prop_id))
279  (*parent_props[state])[i].qpCopy(qp, child_props[i], qp_map._to);
280  }
281  }
282 }
283 
284 void
286  const std::vector<std::shared_ptr<MaterialBase>> & mats,
287  const unsigned int n_qpoints,
288  const Elem & elem,
289  const unsigned int side /* = 0*/)
290 {
291  // Material -> stateful properties that need to be restarted for said material
292  // We need this because we need to initialize everything in dependency the dependency ordering
293  // as given in mats, which means that we can't just do these all up front
294  std::unordered_map<const MaterialBase *,
295  std::vector<std::pair<unsigned int, std::vector<std::stringstream> *>>>
296  materials_to_restart;
297  // Find the entry in the restartable data (binary data waiting to be restarted)
298  // for this [elem, side], if any
299  RestartableMapType::mapped_type * restartable_entry = nullptr;
300  if (_restartable_map.size())
301  if (auto it = _restartable_map.find(std::make_pair(&elem, side)); it != _restartable_map.end())
302  restartable_entry = &it->second;
303 
304  // The stateful objects that we're going to initialize. This is needed for materials that are
305  // passed in as mat that are not stateful, who we don't want to call initStatefulProperties() on.
306  // We unfortunately can only determine this here due to block/boundary restriction, which is
307  // not known without mapping property -> material
308  std::vector<MaterialBase *> stateful_mats;
309  // The stateful IDs that we need to copy back
310  std::vector<unsigned int> stateful_ids_to_copy;
311 
312 #ifndef NDEBUG
313  // All of the stateful IDs; used to make sure that we're not supplying one property
314  // across multiple materials
315  std::set<unsigned int> stateful_ids;
316 #endif
317 
318  // Work through all of the materials that we were passed to figure out which ones are stateful
319  // that we need to initialize, and also to figure out which ones we need to restart
320  for (const auto & mat : mats)
321  {
322  // For keeping track of this material in stateful_mats
323  bool stateful = false;
324 
325  // Check each material that was declared
326  for (const auto id : mat->getSuppliedPropIDs())
327  {
328  const auto & record = getPropRecord(id);
329  if (record.stateful())
330  {
331  const auto stateful_id = record.stateful_id;
332  stateful = true;
333 
334 #ifndef NDEBUG
335  const auto it_inserted_pair = stateful_ids.insert(stateful_id);
336  mooseAssert(it_inserted_pair.second,
337  "Material property '" + _registry.getName(id) +
338  "' supplied by multiple materials at the same point");
339 #endif
340 
341  bool restarting = false;
342  // We have restartable data for this [elem, side]; see if we have it for this prop
343  if (restartable_entry)
344  {
345  if (auto id_datum_pair_it = restartable_entry->find(stateful_id);
346  id_datum_pair_it != restartable_entry->end())
347  {
348  restarting = true;
349  materials_to_restart[mat.get()].emplace_back(stateful_id, &id_datum_pair_it->second);
350  }
351  }
352 
353  if (!restarting)
354  stateful_ids_to_copy.push_back(record.stateful_id);
355  }
356  }
357 
358  if (stateful || mat->forceStatefulInit())
359  stateful_mats.push_back(mat.get());
360  }
361 
362  // This currently initializes all of the stateful properties for this [elem, side],
363  // even though we didn't necessarily need to init all of them. Future work?
364  auto props = initProps(tid, &elem, side, n_qpoints);
365 
366  // Whether or not we have swapped material data from _storage to _material_data
367  // When we're initializing from initStatefulProperties, the MaterialBase objects
368  // need to be able to access the data in _material_data. When we're initalizing
369  // from restart, we need to be able to access from _storage.
370  bool swapped = false;
371 
372  // Initialize properties
373  for (auto mat : stateful_mats)
374  {
375  const auto materials_to_restart_find = materials_to_restart.find(mat);
376  const bool restart = materials_to_restart_find != materials_to_restart.end();
377 
378  if (!restart || mat->forceStatefulInit())
379  {
380  // Need stateful _material_data in the material to store
381  if (!swapped)
382  {
383  swap(tid, elem, side);
384  swapped = true;
385  }
386 
387  mat->initStatefulProperties(n_qpoints);
388  }
389 
390  if (restart)
391  {
392  // Need data in _storage
393  if (swapped)
394  {
395  swapBack(tid, elem, side);
396  swapped = false;
397  }
398 
399  // Load from the cached binary backup into place
400  for (auto & [stateful_id, datum_ptr] : materials_to_restart_find->second)
401  for (const auto state : index_range(*datum_ptr))
402  {
403  (*datum_ptr)[state].seekg(0, std::ios::beg);
404  dataLoad((*datum_ptr)[state], (*props[state])[stateful_id], nullptr);
405  }
406  }
407  }
408 
409  // Done with accessing from _material_data
410  if (swapped)
411  swapBack(tid, elem, side);
412 
413  // Copy to older states as needed, only for non-restarted data
414  for (const auto stateful_id : stateful_ids_to_copy)
415  for (const auto state : statefulIndexRange())
416  for (const auto qp : make_range(n_qpoints))
417  (*props[state])[stateful_id].qpCopy(qp, (*props[0])[stateful_id], qp);
418 }
419 
420 void
422 {
423  mooseAssert(hasStatefulProperties(), "Doesn't have stateful props");
424 
431  for (unsigned int state = maxState(); state != 0; state--)
432  std::swap(setProps(state), setProps(state - 1));
433 }
434 
435 void
437  const Elem & elem_to,
438  const Elem & elem_from,
439  unsigned int side,
440  unsigned int n_qpoints)
441 {
442  copy(tid, &elem_to, &elem_from, side, n_qpoints);
443 }
444 
445 void
447  const Elem * elem_to,
448  const Elem * elem_from,
449  unsigned int side,
450  unsigned int n_qpoints)
451 {
452  auto to_props = initProps(tid, elem_to, side, n_qpoints);
453 
454  for (const auto state : stateIndexRange())
455  {
456  const auto & from_props = props(elem_from, side, state);
457  for (const auto i : index_range(_stateful_prop_id_to_prop_id))
458  for (const auto qp : make_range(n_qpoints))
459  (*to_props[state])[i].qpCopy(qp, from_props[i], qp);
460  }
461 }
462 
463 void
464 MaterialPropertyStorage::swap(const THREAD_ID tid, const Elem & elem, unsigned int side)
465 {
466  Threads::spin_mutex::scoped_lock lock(this->_spin_mtx);
467 
468  for (const auto state : stateIndexRange())
470  _material_data[tid].props(state),
471  // Would be nice to make this setProps()
472  initAndSetProps(&elem, side, state));
473 }
474 
475 void
476 MaterialPropertyStorage::swapBack(const THREAD_ID tid, const Elem & elem, unsigned int side)
477 {
478  Threads::spin_mutex::scoped_lock lock(this->_spin_mtx);
479 
480  for (const auto state : stateIndexRange())
482  setProps(&elem, side, state),
483  _material_data[tid].props(state));
484 
485  // Workaround for MOOSE difficulties in keeping materialless
486  // elements (e.g. Lower D elements in Mortar code) materials
487  for (const auto state : stateIndexRange())
488  if (props(&elem, side, state).empty())
489  setProps(state)[&elem].erase(side);
490 }
491 
492 unsigned int
493 MaterialPropertyStorage::addProperty(const std::string & prop_name,
494  const std::type_info & type,
495  const unsigned int state,
496  const MaterialBase * const declarer)
497 {
498  if (state > MaterialData::max_state)
499  mooseError("Material property state of ",
500  state,
501  " is not supported. Max state supported: ",
503 
504  // Increment state as needed
505  if (maxState() < state)
506  _max_state = state;
507 
508  // Register the property
509  const auto prop_id = _registry.addOrGetID(prop_name, {});
510 
511  // Instantiate the record if needed
512  if (_prop_records.size() < _registry.size())
513  _prop_records.resize(_registry.size());
514  if (!_prop_records[prop_id])
515  _prop_records[prop_id] = PropRecord();
516 
517  // Fill the record
518  auto & record = *_prop_records[prop_id];
519  record.type = type.name();
520  if (declarer)
521  record.declarers.emplace(declarer->typeAndName());
522  record.state = std::max(state, record.state);
523 
524  // Keep track of stateful props by quick access
525  if (state > 0 && std::find(_stateful_prop_id_to_prop_id.begin(),
527  prop_id) == _stateful_prop_id_to_prop_id.end())
528  {
529  _stateful_prop_id_to_prop_id.push_back(prop_id);
530  record.stateful_id = _stateful_prop_id_to_prop_id.size() - 1;
531  }
532 
533  return prop_id;
534 }
535 
536 std::vector<MaterialProperties *>
538  const Elem * elem,
539  unsigned int side,
540  unsigned int n_qpoints)
541 {
542  std::vector<MaterialProperties *> props(numStates());
543  for (const auto state : stateIndexRange())
544  props[state] = &this->initProps(tid, state, elem, side, n_qpoints);
545  return props;
546 }
547 
550  const unsigned int state,
551  const Elem * elem,
552  unsigned int side,
553  unsigned int n_qpoints)
554 {
555  auto & material_data = _material_data[tid];
556  material_data.resize(n_qpoints);
557 
558  auto & mat_props = initAndSetProps(elem, side, state);
559 
560  // In some special cases, material_data might be larger than n_qpoints
561  if (material_data.isOnlyResizeIfSmaller())
562  n_qpoints = material_data.nQPoints();
563 
564  const auto n_props = _stateful_prop_id_to_prop_id.size();
565  if (mat_props.size() < n_props)
566  mat_props.resize(n_props, {});
567 
568  // init properties (allocate memory. etc)
569  for (const auto i : index_range(_stateful_prop_id_to_prop_id))
570  if (!mat_props.hasValue(i))
571  {
572  const auto prop_id = _stateful_prop_id_to_prop_id[i];
573  mat_props.setPointer(i, material_data.props(0)[prop_id].clone(n_qpoints), {});
574  mooseAssert(mat_props[i].id() == prop_id, "Inconsistent id");
575  }
576 
577  return mat_props;
578 }
579 
580 void
581 dataStore(std::ostream & stream, MaterialPropertyStorage & storage, void * context)
582 {
583  // Store the material property ID -> name map for mapping back
584  const auto & registry = storage.getMaterialPropertyRegistry();
585  std::vector<std::string> ids_to_names(registry.idsToNamesBegin(), registry.idsToNamesEnd());
586  dataStore(stream, ids_to_names, nullptr);
587 
588  // Store the stateful ID -> property ID map for mapping back
589  dataStore(stream, storage._stateful_prop_id_to_prop_id, nullptr);
590 
591  // Store the prop -> record map
592  dataStore(stream, storage._prop_records, nullptr);
593 
594  // Store the number of states
595  auto num_states = storage.numStates();
596  dataStore(stream, num_states, nullptr);
597 
598  // Store every property
599  for (const auto state : storage.stateIndexRange())
600  {
601  std::size_t num_elems = storage.setProps(state).size();
602  dataStore(stream, num_elems, nullptr);
603 
604  for (auto & elem_side_map_pair : storage.setProps(state))
605  {
606  const Elem * elem = elem_side_map_pair.first;
607  mooseAssert(elem, "Null element");
608  dataStore(stream, elem, context);
609 
610  auto & side_map = elem_side_map_pair.second;
611  std::size_t num_sides = side_map.size();
612  dataStore(stream, num_sides, nullptr);
613 
614  for (auto & [side, props] : side_map)
615  {
616  dataStore(stream, side, nullptr);
617 
618  std::size_t num_props = props.size();
619  dataStore(stream, num_props, nullptr);
620  mooseAssert(num_props > 0, "No properties");
621 
622  std::size_t n_q_points = 0;
623  for (const auto & entry : props)
624  if (entry.size() > n_q_points)
625  n_q_points = entry.size();
626  dataStore(stream, n_q_points, nullptr);
627 
628  // Here we actually store a stringstream of the data instead of the data directly, because
629  // upon load we don't know if we can load it immediately into place or if we need to cache
630  // it to load later. We also store it as skippable so that we can support not loading a
631  // property if it no longer exists in restart
632  for (auto & entry : props)
633  {
634  std::stringstream out;
635  dataStore(out, entry, nullptr);
636  dataStore(stream, out, nullptr);
637  }
638  }
639  }
640  }
641 }
642 
643 void
644 dataLoad(std::istream & stream, MaterialPropertyStorage & storage, void * context)
645 {
646  storage._restartable_map.clear();
647 
648  const auto & registry = storage.getMaterialPropertyRegistry();
649 
650  std::vector<std::string> from_prop_ids_to_names;
651  dataLoad(stream, from_prop_ids_to_names, nullptr);
652 
653  decltype(storage._stateful_prop_id_to_prop_id) from_stateful_prop_id_to_prop_id;
654  dataLoad(stream, from_stateful_prop_id_to_prop_id, nullptr);
655 
656  decltype(storage._prop_records) from_prop_records;
657  dataLoad(stream, from_prop_records, nullptr);
658 
659  decltype(storage.numStates()) num_states;
660  dataLoad(stream, num_states, nullptr);
661 
662  {
663  // Build maps of material object -> properties and property -> material objects
664  const auto build_maps = [](const auto & prop_records, const auto & ids_to_names)
665  {
666  std::map<std::string, std::set<std::string>> object_to_props, prop_to_objects;
667  for (const auto i : index_range(prop_records))
668  if (prop_records[i] && prop_records[i]->stateful())
669  {
670  const auto & prop = ids_to_names[i];
671  for (const auto & declarer : (*prop_records[i]).declarers)
672  {
673  object_to_props[declarer].insert(prop);
674  prop_to_objects[prop].insert(declarer);
675  }
676  }
677 
678  return std::make_pair(std::move(object_to_props), std::move(prop_to_objects));
679  };
680  // Maps for the current stateful properties
681  const std::vector<std::string> prop_ids_to_names(registry.idsToNamesBegin(),
682  registry.idsToNamesEnd());
683  const auto [object_to_props, prop_to_objects] =
684  build_maps(storage._prop_records, prop_ids_to_names);
685  // Maps for the stored stateful properties
686  const auto [from_object_to_props, from_prop_to_objects] =
687  build_maps(from_prop_records, from_prop_ids_to_names);
688 
689  // Enforce our stateful requirements
690  for (const auto & [object, props] : object_to_props)
691  {
692  const auto find_from_object = from_object_to_props.find(object);
693 
694  // We have a material object that was stored with the same name that
695  // had stateful material properties. Here, we enforce that the stateful
696  // properties stored match exactly the ones that we have declared in
697  // the new run
698  if (find_from_object != from_object_to_props.end())
699  {
700  const auto & from_props = find_from_object->second;
701  if (props != from_props)
702  {
703  std::stringstream error;
704  error << "The stateful material properties in " << object
705  << " that are being restarted do not match the stored properties in the same "
706  "material object from the checkpoint.\n\n";
707  error << "Checkpointed stateful properties:\n";
708  for (const auto & prop : from_props)
709  error << " - " << prop << "\n";
710  error << "\nCurrent stateful properties:\n";
711  for (const auto & prop : props)
712  error << " - " << prop << "\n";
713  mooseError(error.str());
714  }
715  }
716  // We're recovering and we haven't found a stateful material object. We require a
717  // one-to-one stateful mapping when recovering
718  else if (storage._recovering)
719  {
720  mooseError("The ",
721  object,
722  " was stored in restart but no longer exists. This is not supported when "
723  "recovering stateful material properties.");
724  }
725  }
726 
727  // We can easily support this, but have chosen not to due to ambiguity and we
728  // don't yet know how to express this ambiguity. Just removing this error
729  // _should_ make it work without issue because to_stateful_ids below for this
730  // property will be an empty optional, which means simply don't load it. Or,
731  // we could load it into the new property.
732  for (const auto & [from_prop, from_objects] : from_prop_to_objects)
733  if (const auto find_objects = prop_to_objects.find(from_prop);
734  find_objects != prop_to_objects.end())
735  for (const auto & object : find_objects->second)
736  if (!from_objects.count(object))
737  mooseError(
738  "The stateful material property '",
739  from_prop,
740  "' was declared in ",
741  object,
742  " but was not declared in that object on checkpoint.\n\nThis is not currently "
743  "supported due to ambiguity.\n\nPlease contact the development team on "
744  "GitHub if you desire this capability.");
745  }
746 
747  std::vector<std::optional<unsigned int>> to_stateful_ids(from_stateful_prop_id_to_prop_id.size());
748 
749  auto & to_prop_records = storage._prop_records;
750 
751  // Mark everything as not restored in the event that we call this again
752  for (auto & record_ptr : to_prop_records)
753  if (record_ptr)
754  record_ptr->restored = false;
755 
756  // Fill the mapping from previous ID to current stateful ID
757  for (const auto from_stateful_id : index_range(from_stateful_prop_id_to_prop_id))
758  {
759  const auto from_prop_id = from_stateful_prop_id_to_prop_id[from_stateful_id];
760 
761  mooseAssert(from_prop_id < from_prop_records.size(), "Invalid record map");
762  mooseAssert(from_prop_records[from_prop_id], "Not set");
763  const auto & from_record = *from_prop_records[from_prop_id];
764  mooseAssert(from_record.stateful(), "Not stateful");
765 
766  mooseAssert(from_prop_id < from_prop_ids_to_names.size(), "Invalid ID map");
767  const auto & name = from_prop_ids_to_names[from_prop_id];
768 
769  if (const auto query_to_prop_id = registry.queryID(name))
770  {
771  const auto to_prop_id = *query_to_prop_id;
772 
773  mooseAssert(to_prop_id < to_prop_records.size(), "Invalid record map");
774  mooseAssert(to_prop_records[to_prop_id], "Not set");
775  auto & to_record = *to_prop_records[to_prop_id];
776 
777  if (to_record.stateful())
778  {
779  if (from_record.type != to_record.type)
780  mooseError(
781  "The type for the restarted stateful material property '", name, "' does not match");
782 
783  // I'm not sure if we need to enforce this one, but I don't want to think
784  // about it deeply so we'll just make it an error until someone complains
785  // we have time to think
786  if (from_record.state != to_record.state)
787  mooseError("The number of states for the restarted stateful material property '",
788  name,
789  "' do not match.\n\n",
790  "Checkpointed states: ",
791  from_record.state,
792  "\nCurrent states: ",
793  to_record.state);
794 
795  const auto to_stateful_id = storage.getPropRecord(to_prop_id).stateful_id;
796  mooseAssert(to_stateful_id != invalid_uint, "Not stateful");
797 
798  to_stateful_ids[from_stateful_id] = to_stateful_id;
799 
800  // Mark that we're going to restore this property
801  to_record.restored = true;
802  mooseAssert(storage.isRestoredProperty(name), "Restored mismatch");
803 
804  if (storage._recovering)
805  mooseAssert(from_stateful_id == to_stateful_id, "Does not have direct mapping");
806  }
807  }
808  }
809 
810  // Load the properties
811  for (const auto state : make_range(num_states))
812  {
813  std::size_t num_elems;
814  dataLoad(stream, num_elems, nullptr);
815 
816  for (std::size_t i_elem = 0; i_elem < num_elems; ++i_elem)
817  {
818  const Elem * elem;
819  dataLoad(stream, elem, context);
820  mooseAssert(elem, "Null element");
821 
822  std::size_t num_sides;
823  dataLoad(stream, num_sides, nullptr);
824 
825  for (std::size_t i_side = 0; i_side < num_sides; ++i_side)
826  {
827  unsigned int side;
828  dataLoad(stream, side, nullptr);
829 
830  std::size_t num_props;
831  dataLoad(stream, num_props, nullptr);
832  mooseAssert(num_props <= to_stateful_ids.size(), "Missized map");
833 
834  std::size_t num_q_points;
835  dataLoad(stream, num_q_points, nullptr);
836 
837  // Avoid multiple map lookups for entries pertaining to [elem, side]
838  MaterialPropertyStorage::RestartableMapType::mapped_type * restart_entry = nullptr;
839  MaterialProperties * in_place_entry = nullptr;
840 
841  // Load each stateful property
842  for (const auto from_stateful_id : make_range(num_props))
843  {
844  // Load the binary data, which lets us choose in a moment whether
845  // or not to load it in place or to cache it to load later
846  std::stringstream data;
847  dataLoad(stream, data, nullptr);
848  data.seekg(0, std::ios::beg);
849 
850  // We have a property to load into
851  if (const auto to_stateful_id_ptr = to_stateful_ids[from_stateful_id])
852  {
853  const auto to_stateful_id = *to_stateful_id_ptr;
854 
855  // Load the data directly into _storage
856  if (storage._restart_in_place)
857  {
858  if (!in_place_entry)
859  in_place_entry = &storage.setProps(elem, side, state);
860 
861  dataLoad(data, (*in_place_entry)[to_stateful_id], nullptr);
862  }
863  // Properties aren't initialized, so load the data into
864  // _restartable_map to be loaded later in initStatefulProps()
865  else
866  {
867  if (!restart_entry)
868  restart_entry = &storage._restartable_map[std::make_pair(elem, side)];
869 
870  auto & mat_entry = (*restart_entry)[to_stateful_id];
871  if (state >= mat_entry.size())
872  mat_entry.resize(state + 1);
873 
874  mat_entry[state] = std::move(data);
875  }
876  }
877  }
878  }
879  }
880  }
881 }
882 
883 void
884 dataStore(std::ostream & stream, MaterialPropertyStorage::PropRecord & record, void *)
885 {
886  dataStore(stream, record.declarers, nullptr);
887  dataStore(stream, record.type, nullptr);
888  dataStore(stream, record.stateful_id, nullptr);
889  dataStore(stream, record.state, nullptr);
890 }
891 
892 void
893 dataLoad(std::istream & stream, MaterialPropertyStorage::PropRecord & record, void *)
894 {
895  dataLoad(stream, record.declarers, nullptr);
896  dataLoad(stream, record.type, nullptr);
897  dataLoad(stream, record.stateful_id, nullptr);
898  dataLoad(stream, record.state, nullptr);
899 }
std::string name(const ElemQuality q)
MaterialProperties & setProps(const Elem *elem, unsigned int side, const unsigned int state=0)
void shift()
Shift the material properties in time.
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
Definition: KokkosUtils.h:30
unsigned int n_threads()
std::vector< MaterialData > _material_data
The threaded material data.
const unsigned int invalid_uint
MaterialPropertyStorage(MaterialPropertyRegistry &registry, FEProblemBase &problem)
Registry class for material property IDs and names.
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:323
void eraseProperty(const Elem *elem)
Remove the property storage and element pointer from internal data structures Use this when elements ...
static Moose::ParameterRegistry & registry
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
MaterialDataType
MaterialData types.
Definition: MooseTypes.h:692
Stores the stateful material properties computed by materials.
std::vector< unsigned int > _stateful_prop_id_to_prop_id
the vector of stateful property ids (the vector index is the map to stateful prop_id) ...
const MaterialPropertyRegistry & getMaterialPropertyRegistry() const
Helper object for holding qp mapping info.
Definition: MooseMesh.h:73
libMesh::Threads::spin_mutex & _spin_mtx
std::vector< std::optional< PropRecord > > _prop_records
Property records indexed by property id (may be null)
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
bool restored
Whether or not this property was restored (stateful only)
static void shallowSwapData(const std::vector< unsigned int > &stateful_prop_ids, MaterialProperties &data, MaterialProperties &data_from)
Shallow copies of material properties.
static void shallowSwapDataBack(const std::vector< unsigned int > &stateful_prop_ids, MaterialProperties &data, MaterialProperties &data_from)
unsigned int _to
The qp to map to.
Definition: MooseMesh.h:82
Specialization of SubProblem for solving nonlinear equations plus auxiliary equations.
std::size_t size() const
auto max(const L &left, const R &right)
uint8_t processor_id_type
void setRestartInPlace()
Sets the loading of stateful material properties in place.
void prolongStatefulProps(processor_id_type pid, const std::vector< std::vector< QpMap >> &refinement_map, const libMesh::QBase &qrule, const libMesh::QBase &qrule_face, MaterialPropertyStorage &parent_material_props, const THREAD_ID tid, const Elem &elem, const int input_parent_side, const int input_child, const int input_child_side)
Creates storage for newly created elements from mesh Adaptivity.
libMesh::IntRange< unsigned int > stateIndexRange() const
void swapBack(const THREAD_ID tid, const Elem &elem, unsigned int side)
Swap (shallow copy) material properties in MaterialPropertyStorage and MaterialDat Thread safe...
std::optional< std::string > queryStatefulPropName(const unsigned int id) const
bool isRestoredProperty(const std::string &name) const
bool _recovering
Whether or not we&#39;re recovering; enforces a one-to-one mapping of stateful properties.
unsigned int numStates() const
unsigned int addProperty(const std::string &prop_name, const std::type_info &type, const unsigned int state, const MaterialBase *const declarer)
Adds a property with the name prop_name, type type, and state state (0 = current, 1 = old...
unsigned int addOrGetID(const std::string &name, const WriteKey)
unsigned int state
The max state requrested for this property (0 = current, 1 = old, ...)
std::string typeAndName() const
Get the class&#39;s combined type and name; useful in error handling.
Definition: MooseBase.C:57
static constexpr unsigned int max_state
The max time state supported (2 = older)
Definition: MaterialData.h:43
std::vector< MaterialProperties * > initProps(const THREAD_ID tid, const Elem *elem, unsigned int side, unsigned int n_qpoints)
Initializes hashmap entries for element and side to proper qpoint and property count sizes...
RestartableMapType _restartable_map
The restartable data to be loaded in initStatefulProps() later.
void updateStatefulPropsForPRefinement(const processor_id_type pid, const std::vector< QpMap > &p_refinement_map, const libMesh::QBase &qrule, const libMesh::QBase &qrule_face, const THREAD_ID tid, const Elem &elem, const int input_side)
Based on the p-refinement flag of elem, either prolong (for refinement) or restrict (for coarsening) ...
Basic structure for storing information about a property.
const PropsType & props(const unsigned int state=0) const
Access methods to the stored material property data with the given state state.
const std::string & getName(const unsigned int id) const
OStreamProxy out
unsigned int getID(const std::string &name) const
IntRange< T > make_range(T beg, T end)
const std::set< const MooseObject * > & getConsumers(Moose::MaterialDataType type) const
void dataStore(std::ostream &stream, MaterialPropertyStorage &storage, void *context)
MaterialPropertyRegistry & _registry
Shared registry (across storage objects) for property names and IDs.
friend void dataLoad(std::istream &, MaterialPropertyStorage &, void *)
void restrictStatefulProps(const std::vector< std::pair< unsigned int, QpMap >> &coarsening_map, const std::vector< const Elem *> &coarsened_element_children, const libMesh::QBase &qrule, const libMesh::QBase &qrule_face, const THREAD_ID tid, const Elem &elem, int input_side=-1)
Creates storage for newly created elements from mesh Adaptivity.
void swap(const THREAD_ID tid, const Elem &elem, unsigned int side)
Swap (shallow copy) material properties in MaterialData and MaterialPropertyStorage Thread safe...
void dataLoad(std::istream &stream, MaterialPropertyStorage &storage, void *context)
std::map< Moose::MaterialDataType, std::set< const MooseObject * > > _consumers
The consumers of this storage.
void initStatefulProps(const THREAD_ID tid, const std::vector< std::shared_ptr< MaterialBase >> &mats, const unsigned int n_qpoints, const Elem &elem, const unsigned int side=0)
Initialize stateful material properties.
bool _restart_in_place
Whether or not we want to restart stateful properties in place.
void copy(const THREAD_ID tid, const Elem &elem_to, const Elem &elem_from, unsigned int side, unsigned int n_qpoints)
Copy material properties from elem_from to elem_to.
auto min(const L &left, const R &right)
const PropRecord & getPropRecord(const unsigned int id) const
Get the property record associated with the material with id id.
MaterialBases compute MaterialProperties.
Definition: MaterialBase.h:62
bool hasValue(const std::size_t i) const
for(PetscInt i=0;i< nvars;++i)
auto index_range(const T &sizable)
unsigned int _max_state
The maximum state (0 = current, 1 = old, 2 = older)
unsigned int stateful_id
The stateful id in _storage used for this property, if any.
unsigned int THREAD_ID
Definition: MooseTypes.h:209
std::set< std::string > declarers
The material (type,name) that have declared this property.
MaterialProperties & initAndSetProps(const Elem *elem, const unsigned int side, const unsigned int state)
libMesh::IntRange< unsigned int > statefulIndexRange() const
spin_mutex spin_mtx
std::string type
The type of this property.