LCOV - code coverage report
Current view: top level - src/materials - MaterialPropertyStorage.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 357 377 94.7 %
Date: 2025-07-17 01:28:37 Functions: 23 25 92.0 %
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.14