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