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 320343 : MaterialPropertyStorage::MaterialPropertyStorage(MaterialPropertyRegistry & registry,
24 320343 : FEProblemBase & problem)
25 320343 : : _problem(problem),
26 320343 : _max_state(0),
27 320343 : _spin_mtx(libMesh::Threads::spin_mtx),
28 320343 : _registry(registry),
29 320343 : _restart_in_place(false),
30 320343 : _recovering(false)
31 : {
32 320343 : _material_data.reserve(libMesh::n_threads());
33 677892 : for (const auto tid : make_range(libMesh::n_threads()))
34 357549 : _material_data.emplace_back(*this, tid);
35 320343 : }
36 :
37 : void
38 13185614 : MaterialPropertyStorage::shallowSwapData(const std::vector<unsigned int> & stateful_prop_ids,
39 : MaterialProperties & data,
40 : MaterialProperties & data_from)
41 : {
42 32281990 : for (const auto i : make_range(std::min(stateful_prop_ids.size(), data_from.size())))
43 38192689 : if (stateful_prop_ids[i] < data.size() && data.hasValue(stateful_prop_ids[i]) &&
44 19096313 : data_from.hasValue(i))
45 : {
46 19096313 : auto & prop = data[stateful_prop_ids[i]];
47 19096313 : auto & prop_from = data_from[i];
48 19096313 : prop.swap(prop_from);
49 : }
50 13185614 : }
51 :
52 : void
53 13185569 : MaterialPropertyStorage::shallowSwapDataBack(const std::vector<unsigned int> & stateful_prop_ids,
54 : MaterialProperties & data,
55 : MaterialProperties & data_from)
56 : {
57 32281900 : for (const auto i : make_range(std::min(stateful_prop_ids.size(), data.size())))
58 38192662 : if (stateful_prop_ids[i] < data_from.size() && data.hasValue(i) &&
59 19096331 : data_from.hasValue(stateful_prop_ids[i]))
60 : {
61 19096268 : auto & prop = data[i];
62 19096268 : auto & prop_from = data_from[stateful_prop_ids[i]];
63 19096268 : prop.swap(prop_from);
64 : }
65 13185569 : }
66 :
67 : std::optional<std::string>
68 0 : MaterialPropertyStorage::queryStatefulPropName(const unsigned int id) const
69 : {
70 0 : if (_prop_records.size() > id && _prop_records[id] && _prop_records[id]->stateful())
71 0 : return _registry.getName(id);
72 0 : return {};
73 : }
74 :
75 : void
76 35856 : MaterialPropertyStorage::eraseProperty(const Elem * elem)
77 : {
78 125571 : for (const auto state : stateIndexRange())
79 89715 : setProps(state).erase(elem);
80 35856 : }
81 :
82 : void
83 184032 : MaterialPropertyStorage::setRestartInPlace()
84 : {
85 184032 : _restart_in_place = true;
86 184032 : _restartable_map.clear();
87 184032 : }
88 :
89 : const MaterialPropertyStorage::PropRecord &
90 2555634 : MaterialPropertyStorage::getPropRecord(const unsigned int id) const
91 : {
92 : mooseAssert(_prop_records.size() > id, "Invalid ID");
93 2555634 : auto & prop_record_ptr = _prop_records[id];
94 : mooseAssert(prop_record_ptr, "Not initialized");
95 2555634 : return *prop_record_ptr;
96 : }
97 :
98 : bool
99 1676 : MaterialPropertyStorage::isRestoredProperty(const std::string & name) const
100 : {
101 1676 : const auto & record = getPropRecord(_registry.getID(name));
102 1676 : if (!record.stateful())
103 : mooseAssert(!record.restored, "Stateful properties should not be restored");
104 1676 : return record.restored;
105 : }
106 :
107 : const std::set<const MooseObject *> &
108 693 : MaterialPropertyStorage::getConsumers(Moose::MaterialDataType type) const
109 : {
110 693 : static const std::set<const MooseObject *> empty;
111 :
112 693 : const auto it = _consumers.find(type);
113 :
114 693 : if (it != _consumers.end())
115 189 : return it->second;
116 :
117 504 : return empty;
118 : }
119 :
120 : void
121 900 : MaterialPropertyStorage::updateStatefulPropsForPRefinement(
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 900 : unsigned int n_qpoints = 0;
131 :
132 : // If we passed in -1 for these then we really need to store properties at 0
133 900 : unsigned int side = input_side == -1 ? 0 : input_side;
134 :
135 900 : if (input_side == -1) // Not doing side projection (ie, doing volume projection)
136 900 : n_qpoints = qrule.n_points();
137 : else
138 0 : n_qpoints = qrule_face.n_points();
139 :
140 900 : _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 900 : auto current_p_level_props = initProps(tid, &elem, side, n_qpoints);
147 :
148 2997 : for (const auto state : stateIndexRange())
149 4194 : for (const auto i : index_range(_stateful_prop_id_to_prop_id))
150 : {
151 2097 : 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 2097 : 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 2097 : previous_p_level_prop->swap(current_p_level_prop);
159 2097 : current_p_level_prop.resize(n_qpoints);
160 51885 : for (const auto qp : index_range(p_refinement_map))
161 49788 : current_p_level_prop.qpCopy(qp, *previous_p_level_prop, p_refinement_map[qp]._to);
162 2097 : }
163 900 : }
164 :
165 : void
166 3980 : MaterialPropertyStorage::prolongStatefulProps(
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 3980 : unsigned int n_qpoints = 0;
181 :
182 : // If we passed in -1 for these then we really need to store properties at 0
183 3980 : unsigned int parent_side = input_parent_side == -1 ? 0 : input_parent_side;
184 3980 : unsigned int child_side = input_child_side == -1 ? 0 : input_child_side;
185 :
186 3980 : if (input_child_side == -1) // Not doing side projection (ie, doing volume projection)
187 3398 : n_qpoints = qrule.n_points();
188 : else
189 582 : n_qpoints = qrule_face.n_points();
190 :
191 3980 : _material_data[tid].resize(n_qpoints);
192 :
193 3980 : unsigned int n_children = elem.n_children();
194 :
195 3980 : std::vector<unsigned int> children;
196 :
197 3980 : if (input_child != -1) // Passed in a child explicitly
198 0 : children.push_back(input_child);
199 : else
200 : {
201 3980 : children.resize(n_children);
202 24684 : for (unsigned int child = 0; child < n_children; child++)
203 20704 : children[child] = child;
204 : }
205 :
206 24684 : 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 20704 : if (input_child == -1 && input_child_side != -1 && !elem.is_child_on_side(child, parent_side))
211 1728 : continue;
212 :
213 19540 : 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 19540 : if (child_elem->processor_id() != pid)
218 564 : continue;
219 :
220 : mooseAssert(child < refinement_map.size(), "Refinement_map vector not initialized");
221 18976 : const std::vector<QpMap> & child_map = refinement_map[child];
222 :
223 18976 : auto child_props = initProps(tid, child_elem, child_side, n_qpoints);
224 :
225 66144 : for (const auto state : stateIndexRange())
226 : {
227 47168 : const auto & parent_props = parent_material_props.props(&elem, parent_side, state);
228 94336 : for (const auto i : index_range(_stateful_prop_id_to_prop_id))
229 358592 : for (const auto qp : index_range(refinement_map[child]))
230 311424 : (*child_props[state])[i].qpCopy(qp, parent_props[i], child_map[qp]._to);
231 : }
232 18976 : }
233 3980 : }
234 :
235 : void
236 1509 : MaterialPropertyStorage::restrictStatefulProps(
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 1509 : bool doing_a_side = input_side != -1;
248 :
249 1509 : unsigned int n_qpoints = 0;
250 :
251 1509 : if (!doing_a_side)
252 : {
253 1285 : side = 0; // Use 0 for the elem
254 1285 : n_qpoints = qrule.n_points();
255 : }
256 : else
257 : {
258 224 : side = input_side;
259 224 : n_qpoints = qrule_face.n_points();
260 : }
261 :
262 1509 : auto parent_props = initProps(tid, &elem, side, n_qpoints);
263 :
264 : // Copy from the child stateful properties
265 10317 : for (const auto qp : index_range(coarsening_map))
266 : {
267 8808 : const std::pair<unsigned int, QpMap> & qp_pair = coarsening_map[qp];
268 8808 : unsigned int child = qp_pair.first;
269 :
270 : mooseAssert(child < coarsened_element_children.size(),
271 : "Coarsened element children vector not initialized");
272 8808 : const Elem * child_elem = coarsened_element_children[child];
273 8808 : const QpMap & qp_map = qp_pair.second;
274 :
275 30816 : for (const auto state : stateIndexRange())
276 : {
277 22008 : const auto & child_props = props(child_elem, side, state);
278 44016 : for (const auto i : index_range(_stateful_prop_id_to_prop_id))
279 22008 : (*parent_props[state])[i].qpCopy(qp, child_props[i], qp_map._to);
280 : }
281 : }
282 1509 : }
283 :
284 : void
285 272552 : MaterialPropertyStorage::initStatefulProps(const THREAD_ID tid,
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 272552 : 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 272552 : RestartableMapType::mapped_type * restartable_entry = nullptr;
300 272552 : if (_restartable_map.size())
301 14734 : if (auto it = _restartable_map.find(std::make_pair(&elem, side)); it != _restartable_map.end())
302 14734 : 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 272552 : std::vector<MaterialBase *> stateful_mats;
309 : // The stateful IDs that we need to copy back
310 272552 : 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 965322 : for (const auto & mat : mats)
321 : {
322 : // For keeping track of this material in stateful_mats
323 692770 : bool stateful = false;
324 :
325 : // Check each material that was declared
326 1579424 : for (const auto id : mat->getSuppliedPropIDs())
327 : {
328 886654 : const auto & record = getPropRecord(id);
329 886654 : if (record.stateful())
330 : {
331 473346 : const auto stateful_id = record.stateful_id;
332 473346 : 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 473346 : bool restarting = false;
342 : // We have restartable data for this [elem, side]; see if we have it for this prop
343 473346 : if (restartable_entry)
344 : {
345 25276 : if (auto id_datum_pair_it = restartable_entry->find(stateful_id);
346 25276 : id_datum_pair_it != restartable_entry->end())
347 : {
348 25276 : restarting = true;
349 25276 : materials_to_restart[mat.get()].emplace_back(stateful_id, &id_datum_pair_it->second);
350 : }
351 : }
352 :
353 473346 : if (!restarting)
354 448070 : stateful_ids_to_copy.push_back(record.stateful_id);
355 : }
356 : }
357 :
358 692770 : if (stateful || mat->forceStatefulInit())
359 472459 : 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 272552 : 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 272552 : bool swapped = false;
371 :
372 : // Initialize properties
373 745011 : for (auto mat : stateful_mats)
374 : {
375 472459 : const auto materials_to_restart_find = materials_to_restart.find(mat);
376 472459 : const bool restart = materials_to_restart_find != materials_to_restart.end();
377 :
378 472459 : if (!restart || mat->forceStatefulInit())
379 : {
380 : // Need stateful _material_data in the material to store
381 447212 : if (!swapped)
382 : {
383 257392 : swap(tid, elem, side);
384 257392 : swapped = true;
385 : }
386 :
387 447212 : mat->initStatefulProperties(n_qpoints);
388 : }
389 :
390 472459 : if (restart)
391 : {
392 : // Need data in _storage
393 25247 : if (swapped)
394 : {
395 0 : swapBack(tid, elem, side);
396 0 : swapped = false;
397 : }
398 :
399 : // Load from the cached binary backup into place
400 50523 : for (auto & [stateful_id, datum_ptr] : materials_to_restart_find->second)
401 77799 : for (const auto state : index_range(*datum_ptr))
402 : {
403 52523 : (*datum_ptr)[state].seekg(0, std::ios::beg);
404 52523 : dataLoad((*datum_ptr)[state], (*props[state])[stateful_id], nullptr);
405 : }
406 : }
407 : }
408 :
409 : // Done with accessing from _material_data
410 272552 : if (swapped)
411 257392 : swapBack(tid, elem, side);
412 :
413 : // Copy to older states as needed, only for non-restarted data
414 720622 : for (const auto stateful_id : stateful_ids_to_copy)
415 940252 : for (const auto state : statefulIndexRange())
416 2563030 : for (const auto qp : make_range(n_qpoints))
417 2070848 : (*props[state])[stateful_id].qpCopy(qp, (*props[0])[stateful_id], qp);
418 272552 : }
419 :
420 : void
421 5360 : MaterialPropertyStorage::shift()
422 : {
423 : mooseAssert(hasStatefulProperties(), "Doesn't have stateful props");
424 :
425 : /**
426 : * Shift properties back in time and reuse older data for current (save reallocations etc.)
427 : * With current, old, and older this can be accomplished by two swaps:
428 : * older <-> old
429 : * old <-> current
430 : */
431 12700 : for (unsigned int state = maxState(); state != 0; state--)
432 7340 : std::swap(setProps(state), setProps(state - 1));
433 5360 : }
434 :
435 : void
436 0 : MaterialPropertyStorage::copy(const THREAD_ID tid,
437 : const Elem & elem_to,
438 : const Elem & elem_from,
439 : unsigned int side,
440 : unsigned int n_qpoints)
441 : {
442 0 : copy(tid, &elem_to, &elem_from, side, n_qpoints);
443 0 : }
444 :
445 : void
446 8 : MaterialPropertyStorage::copy(const THREAD_ID tid,
447 : const Elem * elem_to,
448 : const Elem * elem_from,
449 : unsigned int side,
450 : unsigned int n_qpoints)
451 : {
452 8 : auto to_props = initProps(tid, elem_to, side, n_qpoints);
453 :
454 24 : for (const auto state : stateIndexRange())
455 : {
456 16 : const auto & from_props = props(elem_from, side, state);
457 32 : for (const auto i : index_range(_stateful_prop_id_to_prop_id))
458 80 : for (const auto qp : make_range(n_qpoints))
459 64 : (*to_props[state])[i].qpCopy(qp, from_props[i], qp);
460 : }
461 8 : }
462 :
463 : void
464 5734583 : MaterialPropertyStorage::swap(const THREAD_ID tid, const Elem & elem, unsigned int side)
465 : {
466 5734583 : Threads::spin_mutex::scoped_lock lock(this->_spin_mtx);
467 :
468 18920197 : for (const auto state : stateIndexRange())
469 26371228 : shallowSwapData(_stateful_prop_id_to_prop_id,
470 13185614 : _material_data[tid].props(state),
471 : // Would be nice to make this setProps()
472 : initAndSetProps(&elem, side, state));
473 5734583 : }
474 :
475 : void
476 5734568 : MaterialPropertyStorage::swapBack(const THREAD_ID tid, const Elem & elem, unsigned int side)
477 : {
478 5734568 : Threads::spin_mutex::scoped_lock lock(this->_spin_mtx);
479 :
480 18920137 : for (const auto state : stateIndexRange())
481 13185569 : shallowSwapDataBack(_stateful_prop_id_to_prop_id,
482 : setProps(&elem, side, state),
483 13185569 : _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 18920137 : for (const auto state : stateIndexRange())
488 13185569 : if (props(&elem, side, state).empty())
489 0 : setProps(state)[&elem].erase(side);
490 5734568 : }
491 :
492 : unsigned int
493 132417 : 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 132417 : if (state > MaterialData::max_state)
499 0 : mooseError("Material property state of ",
500 : state,
501 : " is not supported. Max state supported: ",
502 : MaterialData::max_state);
503 :
504 : // Increment state as needed
505 132417 : if (maxState() < state)
506 3204 : _max_state = state;
507 :
508 : // Register the property
509 132417 : const auto prop_id = _registry.addOrGetID(prop_name, {});
510 :
511 : // Instantiate the record if needed
512 132417 : if (_prop_records.size() < _registry.size())
513 38629 : _prop_records.resize(_registry.size());
514 132417 : if (!_prop_records[prop_id])
515 55361 : _prop_records[prop_id] = PropRecord();
516 :
517 : // Fill the record
518 132417 : auto & record = *_prop_records[prop_id];
519 132417 : record.type = type.name();
520 132417 : if (declarer)
521 65287 : record.declarers.emplace(declarer->typeAndName());
522 132417 : record.state = std::max(state, record.state);
523 :
524 : // Keep track of stateful props by quick access
525 145305 : if (state > 0 && std::find(_stateful_prop_id_to_prop_id.begin(),
526 : _stateful_prop_id_to_prop_id.end(),
527 145305 : prop_id) == _stateful_prop_id_to_prop_id.end())
528 : {
529 3405 : _stateful_prop_id_to_prop_id.push_back(prop_id);
530 3405 : record.stateful_id = _stateful_prop_id_to_prop_id.size() - 1;
531 : }
532 :
533 132417 : return prop_id;
534 : }
535 :
536 : std::vector<MaterialProperties *>
537 293945 : MaterialPropertyStorage::initProps(const THREAD_ID tid,
538 : const Elem * elem,
539 : unsigned int side,
540 : unsigned int n_qpoints)
541 : {
542 293945 : std::vector<MaterialProperties *> props(numStates());
543 928290 : for (const auto state : stateIndexRange())
544 634345 : props[state] = &this->initProps(tid, state, elem, side, n_qpoints);
545 293945 : return props;
546 0 : }
547 :
548 : MaterialProperties &
549 641080 : MaterialPropertyStorage::initProps(const THREAD_ID tid,
550 : const unsigned int state,
551 : const Elem * elem,
552 : unsigned int side,
553 : unsigned int n_qpoints)
554 : {
555 641080 : auto & material_data = _material_data[tid];
556 641080 : material_data.resize(n_qpoints);
557 :
558 641080 : auto & mat_props = initAndSetProps(elem, side, state);
559 :
560 : // In some special cases, material_data might be larger than n_qpoints
561 641080 : if (material_data.isOnlyResizeIfSmaller())
562 0 : n_qpoints = material_data.nQPoints();
563 :
564 641080 : const auto n_props = _stateful_prop_id_to_prop_id.size();
565 641080 : if (mat_props.size() < n_props)
566 363835 : mat_props.resize(n_props, {});
567 :
568 : // init properties (allocate memory. etc)
569 1700924 : for (const auto i : index_range(_stateful_prop_id_to_prop_id))
570 1059844 : if (!mat_props.hasValue(i))
571 : {
572 580867 : const auto prop_id = _stateful_prop_id_to_prop_id[i];
573 580867 : 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 641080 : return mat_props;
578 : }
579 :
580 : void
581 237600 : dataStore(std::ostream & stream, MaterialPropertyStorage & storage, void * context)
582 : {
583 : // Store the material property ID -> name map for mapping back
584 237600 : const auto & registry = storage.getMaterialPropertyRegistry();
585 237600 : std::vector<std::string> ids_to_names(registry.idsToNamesBegin(), registry.idsToNamesEnd());
586 237600 : dataStore(stream, ids_to_names, nullptr);
587 :
588 : // Store the stateful ID -> property ID map for mapping back
589 237600 : dataStore(stream, storage._stateful_prop_id_to_prop_id, nullptr);
590 :
591 : // Store the prop -> record map
592 237600 : dataStore(stream, storage._prop_records, nullptr);
593 :
594 : // Store the number of states
595 237600 : auto num_states = storage.numStates();
596 237600 : dataStore(stream, num_states, nullptr);
597 :
598 : // Store every property
599 476320 : for (const auto state : storage.stateIndexRange())
600 : {
601 238720 : std::size_t num_elems = storage.setProps(state).size();
602 238720 : dataStore(stream, num_elems, nullptr);
603 :
604 303165 : for (auto & elem_side_map_pair : storage.setProps(state))
605 : {
606 64445 : const Elem * elem = elem_side_map_pair.first;
607 : mooseAssert(elem, "Null element");
608 64445 : dataStore(stream, elem, context);
609 :
610 64445 : auto & side_map = elem_side_map_pair.second;
611 64445 : std::size_t num_sides = side_map.size();
612 64445 : dataStore(stream, num_sides, nullptr);
613 :
614 129005 : for (auto & [side, props] : side_map)
615 : {
616 64560 : dataStore(stream, side, nullptr);
617 :
618 64560 : std::size_t num_props = props.size();
619 64560 : dataStore(stream, num_props, nullptr);
620 : mooseAssert(num_props > 0, "No properties");
621 :
622 64560 : std::size_t n_q_points = 0;
623 150956 : for (const auto & entry : props)
624 86396 : if (entry.size() > n_q_points)
625 64560 : n_q_points = entry.size();
626 64560 : 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 150956 : for (auto & entry : props)
633 : {
634 86396 : std::stringstream out;
635 86396 : dataStore(out, entry, nullptr);
636 86396 : dataStore(stream, out, nullptr);
637 86396 : }
638 : }
639 : }
640 : }
641 237600 : }
642 :
643 : void
644 76193 : dataLoad(std::istream & stream, MaterialPropertyStorage & storage, void * context)
645 : {
646 76193 : storage._restartable_map.clear();
647 :
648 76193 : const auto & registry = storage.getMaterialPropertyRegistry();
649 :
650 76193 : std::vector<std::string> from_prop_ids_to_names;
651 76193 : dataLoad(stream, from_prop_ids_to_names, nullptr);
652 :
653 76193 : decltype(storage._stateful_prop_id_to_prop_id) from_stateful_prop_id_to_prop_id;
654 76193 : dataLoad(stream, from_stateful_prop_id_to_prop_id, nullptr);
655 :
656 76193 : decltype(storage._prop_records) from_prop_records;
657 76193 : dataLoad(stream, from_prop_records, nullptr);
658 :
659 : decltype(storage.numStates()) num_states;
660 76193 : dataLoad(stream, num_states, nullptr);
661 :
662 : {
663 : // Build maps of material object -> properties and property -> material objects
664 152386 : const auto build_maps = [](const auto & prop_records, const auto & ids_to_names)
665 : {
666 152386 : std::map<std::string, std::set<std::string>> object_to_props, prop_to_objects;
667 166250 : for (const auto i : index_range(prop_records))
668 13864 : if (prop_records[i] && prop_records[i]->stateful())
669 : {
670 770 : const auto & prop = ids_to_names[i];
671 1594 : for (const auto & declarer : (*prop_records[i]).declarers)
672 : {
673 824 : object_to_props[declarer].insert(prop);
674 824 : prop_to_objects[prop].insert(declarer);
675 : }
676 : }
677 :
678 304772 : return std::make_pair(std::move(object_to_props), std::move(prop_to_objects));
679 152386 : };
680 : // Maps for the current stateful properties
681 : const std::vector<std::string> prop_ids_to_names(registry.idsToNamesBegin(),
682 76193 : registry.idsToNamesEnd());
683 76193 : const auto [object_to_props, prop_to_objects] =
684 76193 : build_maps(storage._prop_records, prop_ids_to_names);
685 : // Maps for the stored stateful properties
686 76193 : const auto [from_object_to_props, from_prop_to_objects] =
687 76193 : build_maps(from_prop_records, from_prop_ids_to_names);
688 :
689 : // Enforce our stateful requirements
690 76562 : for (const auto & [object, props] : object_to_props)
691 : {
692 379 : 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 379 : if (find_from_object != from_object_to_props.end())
699 : {
700 349 : const auto & from_props = find_from_object->second;
701 349 : if (props != from_props)
702 : {
703 10 : 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 10 : "material object from the checkpoint.\n\n";
707 10 : error << "Checkpointed stateful properties:\n";
708 28 : for (const auto & prop : from_props)
709 18 : error << " - " << prop << "\n";
710 10 : error << "\nCurrent stateful properties:\n";
711 28 : for (const auto & prop : props)
712 18 : error << " - " << prop << "\n";
713 10 : mooseError(error.str());
714 0 : }
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 30 : else if (storage._recovering)
719 : {
720 0 : 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 76542 : for (const auto & [from_prop, from_objects] : from_prop_to_objects)
733 365 : if (const auto find_objects = prop_to_objects.find(from_prop);
734 365 : find_objects != prop_to_objects.end())
735 703 : for (const auto & object : find_objects->second)
736 368 : if (!from_objects.count(object))
737 6 : 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 76177 : }
746 :
747 76177 : std::vector<std::optional<unsigned int>> to_stateful_ids(from_stateful_prop_id_to_prop_id.size());
748 :
749 76177 : auto & to_prop_records = storage._prop_records;
750 :
751 : // Mark everything as not restored in the event that we call this again
752 83053 : for (auto & record_ptr : to_prop_records)
753 6876 : if (record_ptr)
754 6726 : record_ptr->restored = false;
755 :
756 : // Fill the mapping from previous ID to current stateful ID
757 76522 : for (const auto from_stateful_id : index_range(from_stateful_prop_id_to_prop_id))
758 : {
759 355 : 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 355 : 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 355 : const auto & name = from_prop_ids_to_names[from_prop_id];
768 :
769 355 : if (const auto query_to_prop_id = registry.queryID(name))
770 : {
771 331 : 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 331 : auto & to_record = *to_prop_records[to_prop_id];
776 :
777 331 : if (to_record.stateful())
778 : {
779 331 : if (from_record.type != to_record.type)
780 4 : 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 327 : if (from_record.state != to_record.state)
787 6 : mooseError("The number of states for the restarted stateful material property '",
788 : name,
789 : "' do not match.\n\n",
790 : "Checkpointed states: ",
791 6 : from_record.state,
792 : "\nCurrent states: ",
793 6 : to_record.state);
794 :
795 321 : const auto to_stateful_id = storage.getPropRecord(to_prop_id).stateful_id;
796 : mooseAssert(to_stateful_id != invalid_uint, "Not stateful");
797 :
798 321 : to_stateful_ids[from_stateful_id] = to_stateful_id;
799 :
800 : // Mark that we're going to restore this property
801 321 : to_record.restored = true;
802 : mooseAssert(storage.isRestoredProperty(name), "Restored mismatch");
803 :
804 321 : 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 152693 : for (const auto state : make_range(num_states))
812 : {
813 : std::size_t num_elems;
814 76526 : dataLoad(stream, num_elems, nullptr);
815 :
816 107270 : for (std::size_t i_elem = 0; i_elem < num_elems; ++i_elem)
817 : {
818 : const Elem * elem;
819 30744 : dataLoad(stream, elem, context);
820 : mooseAssert(elem, "Null element");
821 :
822 : std::size_t num_sides;
823 30744 : dataLoad(stream, num_sides, nullptr);
824 :
825 61583 : for (std::size_t i_side = 0; i_side < num_sides; ++i_side)
826 : {
827 : unsigned int side;
828 30839 : dataLoad(stream, side, nullptr);
829 :
830 : std::size_t num_props;
831 30839 : dataLoad(stream, num_props, nullptr);
832 : mooseAssert(num_props <= to_stateful_ids.size(), "Missized map");
833 :
834 : std::size_t num_q_points;
835 30839 : dataLoad(stream, num_q_points, nullptr);
836 :
837 : // Avoid multiple map lookups for entries pertaining to [elem, side]
838 30839 : MaterialPropertyStorage::RestartableMapType::mapped_type * restart_entry = nullptr;
839 30839 : MaterialProperties * in_place_entry = nullptr;
840 :
841 : // Load each stateful property
842 83314 : 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 52475 : std::stringstream data;
847 52475 : dataLoad(stream, data, nullptr);
848 52475 : data.seekg(0, std::ios::beg);
849 :
850 : // We have a property to load into
851 52475 : if (const auto to_stateful_id_ptr = to_stateful_ids[from_stateful_id])
852 : {
853 52475 : const auto to_stateful_id = *to_stateful_id_ptr;
854 :
855 : // Load the data directly into _storage
856 52475 : if (storage._restart_in_place)
857 : {
858 0 : if (!in_place_entry)
859 0 : in_place_entry = &storage.setProps(elem, side, state);
860 :
861 0 : 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 52475 : if (!restart_entry)
868 30839 : restart_entry = &storage._restartable_map[std::make_pair(elem, side)];
869 :
870 52475 : auto & mat_entry = (*restart_entry)[to_stateful_id];
871 52475 : if (state >= mat_entry.size())
872 52475 : mat_entry.resize(state + 1);
873 :
874 52475 : mat_entry[state] = std::move(data);
875 : }
876 : }
877 52475 : }
878 : }
879 : }
880 : }
881 76167 : }
882 :
883 : void
884 6586 : dataStore(std::ostream & stream, MaterialPropertyStorage::PropRecord & record, void *)
885 : {
886 6586 : dataStore(stream, record.declarers, nullptr);
887 6586 : dataStore(stream, record.type, nullptr);
888 6586 : dataStore(stream, record.stateful_id, nullptr);
889 6586 : dataStore(stream, record.state, nullptr);
890 6586 : }
891 :
892 : void
893 6814 : dataLoad(std::istream & stream, MaterialPropertyStorage::PropRecord & record, void *)
894 : {
895 6814 : dataLoad(stream, record.declarers, nullptr);
896 6814 : dataLoad(stream, record.type, nullptr);
897 6814 : dataLoad(stream, record.stateful_id, nullptr);
898 6814 : dataLoad(stream, record.state, nullptr);
899 6814 : }
|