LCOV - code coverage report
Current view: top level - src/materials - MaterialPropertyStorage.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 419b9d Lines: 357 377 94.7 %
Date: 2025-08-08 20:01:16 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      187494 : MaterialPropertyStorage::MaterialPropertyStorage(MaterialPropertyRegistry & registry)
      24      187494 :   : _max_state(0),
      25      187494 :     _spin_mtx(libMesh::Threads::spin_mtx),
      26      187494 :     _registry(registry),
      27      187494 :     _restart_in_place(false),
      28      187494 :     _recovering(false)
      29             : {
      30      187494 :   _material_data.reserve(libMesh::n_threads());
      31      393354 :   for (const auto tid : make_range(libMesh::n_threads()))
      32      205860 :     _material_data.emplace_back(*this, tid);
      33      187494 : }
      34             : 
      35             : void
      36    13185598 : MaterialPropertyStorage::shallowSwapData(const std::vector<unsigned int> & stateful_prop_ids,
      37             :                                          MaterialProperties & data,
      38             :                                          MaterialProperties & data_from)
      39             : {
      40    32281958 :   for (const auto i : make_range(std::min(stateful_prop_ids.size(), data_from.size())))
      41    38192657 :     if (stateful_prop_ids[i] < data.size() && data.hasValue(stateful_prop_ids[i]) &&
      42    19096297 :         data_from.hasValue(i))
      43             :     {
      44    19096297 :       auto & prop = data[stateful_prop_ids[i]];
      45    19096297 :       auto & prop_from = data_from[i];
      46    19096297 :       prop.swap(prop_from);
      47             :     }
      48    13185598 : }
      49             : 
      50             : void
      51    13185571 : MaterialPropertyStorage::shallowSwapDataBack(const std::vector<unsigned int> & stateful_prop_ids,
      52             :                                              MaterialProperties & data,
      53             :                                              MaterialProperties & data_from)
      54             : {
      55    32281904 :   for (const auto i : make_range(std::min(stateful_prop_ids.size(), data.size())))
      56    38192666 :     if (stateful_prop_ids[i] < data_from.size() && data.hasValue(i) &&
      57    19096333 :         data_from.hasValue(stateful_prop_ids[i]))
      58             :     {
      59    19096270 :       auto & prop = data[i];
      60    19096270 :       auto & prop_from = data_from[stateful_prop_ids[i]];
      61    19096270 :       prop.swap(prop_from);
      62             :     }
      63    13185571 : }
      64             : 
      65             : std::optional<std::string>
      66        2191 : MaterialPropertyStorage::queryStatefulPropName(const unsigned int id) const
      67             : {
      68        2191 :   if (_prop_records.size() > id && _prop_records[id] && _prop_records[id]->stateful())
      69         916 :     return _registry.getName(id);
      70        1275 :   return {};
      71             : }
      72             : 
      73             : void
      74       35804 : MaterialPropertyStorage::eraseProperty(const Elem * elem)
      75             : {
      76      125415 :   for (const auto state : stateIndexRange())
      77       89611 :     setProps(state).erase(elem);
      78       35804 : }
      79             : 
      80             : void
      81      179211 : MaterialPropertyStorage::setRestartInPlace()
      82             : {
      83      179211 :   _restart_in_place = true;
      84      179211 :   _restartable_map.clear();
      85      179211 : }
      86             : 
      87             : const MaterialPropertyStorage::PropRecord &
      88     2546138 : MaterialPropertyStorage::getPropRecord(const unsigned int id) const
      89             : {
      90             :   mooseAssert(_prop_records.size() > id, "Invalid ID");
      91     2546138 :   auto & prop_record_ptr = _prop_records[id];
      92             :   mooseAssert(prop_record_ptr, "Not initialized");
      93     2546138 :   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         900 : 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         900 :   unsigned int n_qpoints = 0;
     116             : 
     117             :   // If we passed in -1 for these then we really need to store properties at 0
     118         900 :   unsigned int side = input_side == -1 ? 0 : input_side;
     119             : 
     120         900 :   if (input_side == -1) // Not doing side projection (ie, doing volume projection)
     121         900 :     n_qpoints = qrule.n_points();
     122             :   else
     123           0 :     n_qpoints = qrule_face.n_points();
     124             : 
     125         900 :   _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         900 :   auto current_p_level_props = initProps(tid, &elem, side, n_qpoints);
     132             : 
     133        2997 :   for (const auto state : stateIndexRange())
     134        4194 :     for (const auto i : index_range(_stateful_prop_id_to_prop_id))
     135             :     {
     136        2097 :       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        2097 :       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        2097 :       previous_p_level_prop->swap(current_p_level_prop);
     144        2097 :       current_p_level_prop.resize(n_qpoints);
     145       51885 :       for (const auto qp : index_range(p_refinement_map))
     146       49788 :         current_p_level_prop.qpCopy(qp, *previous_p_level_prop, p_refinement_map[qp]._to);
     147        2097 :     }
     148         900 : }
     149             : 
     150             : void
     151        3770 : 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        3770 :   unsigned int n_qpoints = 0;
     166             : 
     167             :   // If we passed in -1 for these then we really need to store properties at 0
     168        3770 :   unsigned int parent_side = input_parent_side == -1 ? 0 : input_parent_side;
     169        3770 :   unsigned int child_side = input_child_side == -1 ? 0 : input_child_side;
     170             : 
     171        3770 :   if (input_child_side == -1) // Not doing side projection (ie, doing volume projection)
     172        3398 :     n_qpoints = qrule.n_points();
     173             :   else
     174         372 :     n_qpoints = qrule_face.n_points();
     175             : 
     176        3770 :   _material_data[tid].resize(n_qpoints);
     177             : 
     178        3770 :   unsigned int n_children = elem.n_children();
     179             : 
     180        3770 :   std::vector<unsigned int> children;
     181             : 
     182        3770 :   if (input_child != -1) // Passed in a child explicitly
     183           0 :     children.push_back(input_child);
     184             :   else
     185             :   {
     186        3770 :     children.resize(n_children);
     187       23634 :     for (unsigned int child = 0; child < n_children; child++)
     188       19864 :       children[child] = child;
     189             :   }
     190             : 
     191       23634 :   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       19864 :     if (input_child == -1 && input_child_side != -1 && !elem.is_child_on_side(child, parent_side))
     196        1288 :       continue;
     197             : 
     198       19120 :     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       19120 :     if (child_elem->processor_id() != pid)
     203         544 :       continue;
     204             : 
     205             :     mooseAssert(child < refinement_map.size(), "Refinement_map vector not initialized");
     206       18576 :     const std::vector<QpMap> & child_map = refinement_map[child];
     207             : 
     208       18576 :     auto child_props = initProps(tid, child_elem, child_side, n_qpoints);
     209             : 
     210       64944 :     for (const auto state : stateIndexRange())
     211             :     {
     212       46368 :       const auto & parent_props = parent_material_props.props(&elem, parent_side, state);
     213       92736 :       for (const auto i : index_range(_stateful_prop_id_to_prop_id))
     214      355392 :         for (const auto qp : index_range(refinement_map[child]))
     215      309024 :           (*child_props[state])[i].qpCopy(qp, parent_props[i], child_map[qp]._to);
     216             :     }
     217       18576 :   }
     218        3770 : }
     219             : 
     220             : void
     221        1429 : 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        1429 :   bool doing_a_side = input_side != -1;
     233             : 
     234        1429 :   unsigned int n_qpoints = 0;
     235             : 
     236        1429 :   if (!doing_a_side)
     237             :   {
     238        1285 :     side = 0; // Use 0 for the elem
     239        1285 :     n_qpoints = qrule.n_points();
     240             :   }
     241             :   else
     242             :   {
     243         144 :     side = input_side;
     244         144 :     n_qpoints = qrule_face.n_points();
     245             :   }
     246             : 
     247        1429 :   auto parent_props = initProps(tid, &elem, side, n_qpoints);
     248             : 
     249             :   // Copy from the child stateful properties
     250        9997 :   for (const auto qp : index_range(coarsening_map))
     251             :   {
     252        8568 :     const std::pair<unsigned int, QpMap> & qp_pair = coarsening_map[qp];
     253        8568 :     unsigned int child = qp_pair.first;
     254             : 
     255             :     mooseAssert(child < coarsened_element_children.size(),
     256             :                 "Coarsened element children vector not initialized");
     257        8568 :     const Elem * child_elem = coarsened_element_children[child];
     258        8568 :     const QpMap & qp_map = qp_pair.second;
     259             : 
     260       30096 :     for (const auto state : stateIndexRange())
     261             :     {
     262       21528 :       const auto & child_props = props(child_elem, side, state);
     263       43056 :       for (const auto i : index_range(_stateful_prop_id_to_prop_id))
     264       21528 :         (*parent_props[state])[i].qpCopy(qp, child_props[i], qp_map._to);
     265             :     }
     266             :   }
     267        1429 : }
     268             : 
     269             : void
     270      271100 : 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      271100 :       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      271100 :   RestartableMapType::mapped_type * restartable_entry = nullptr;
     285      271100 :   if (_restartable_map.size())
     286       14668 :     if (auto it = _restartable_map.find(std::make_pair(&elem, side)); it != _restartable_map.end())
     287       14668 :       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      271100 :   std::vector<MaterialBase *> stateful_mats;
     294             :   // The stateful IDs that we need to copy back
     295      271100 :   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      960974 :   for (const auto & mat : mats)
     306             :   {
     307             :     // For keeping track of this material in stateful_mats
     308      689874 :     bool stateful = false;
     309             : 
     310             :     // Check each material that was declared
     311     1573632 :     for (const auto id : mat->getSuppliedPropIDs())
     312             :     {
     313      883758 :       const auto & record = getPropRecord(id);
     314      883758 :       if (record.stateful())
     315             :       {
     316      471965 :         const auto stateful_id = record.stateful_id;
     317      471965 :         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      471965 :         bool restarting = false;
     327             :         // We have restartable data for this [elem, side]; see if we have it for this prop
     328      471965 :         if (restartable_entry)
     329             :         {
     330       25211 :           if (auto id_datum_pair_it = restartable_entry->find(stateful_id);
     331       25211 :               id_datum_pair_it != restartable_entry->end())
     332             :           {
     333       25211 :             restarting = true;
     334       25211 :             materials_to_restart[mat.get()].emplace_back(stateful_id, &id_datum_pair_it->second);
     335             :           }
     336             :         }
     337             : 
     338      471965 :         if (!restarting)
     339      446754 :           stateful_ids_to_copy.push_back(record.stateful_id);
     340             :       }
     341             :     }
     342             : 
     343      689874 :     if (stateful || mat->forceStatefulInit())
     344      471078 :       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      271100 :   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      271100 :   bool swapped = false;
     356             : 
     357             :   // Initialize properties
     358      742178 :   for (auto mat : stateful_mats)
     359             :   {
     360      471078 :     const auto materials_to_restart_find = materials_to_restart.find(mat);
     361      471078 :     const bool restart = materials_to_restart_find != materials_to_restart.end();
     362             : 
     363      471078 :     if (!restart || mat->forceStatefulInit())
     364             :     {
     365             :       // Need stateful _material_data in the material to store
     366      445896 :       if (!swapped)
     367             :       {
     368      256076 :         swap(tid, elem, side);
     369      256076 :         swapped = true;
     370             :       }
     371             : 
     372      445896 :       mat->initStatefulProperties(n_qpoints);
     373             :     }
     374             : 
     375      471078 :     if (restart)
     376             :     {
     377             :       // Need data in _storage
     378       25182 :       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       50393 :       for (auto & [stateful_id, datum_ptr] : materials_to_restart_find->second)
     386       77604 :         for (const auto state : index_range(*datum_ptr))
     387             :         {
     388       52393 :           (*datum_ptr)[state].seekg(0, std::ios::beg);
     389       52393 :           dataLoad((*datum_ptr)[state], (*props[state])[stateful_id], nullptr);
     390             :         }
     391             :     }
     392             :   }
     393             : 
     394             :   // Done with accessing from _material_data
     395      271100 :   if (swapped)
     396      256076 :     swapBack(tid, elem, side);
     397             : 
     398             :   // Copy to older states as needed, only for non-restarted data
     399      717854 :   for (const auto stateful_id : stateful_ids_to_copy)
     400      937320 :     for (const auto state : statefulIndexRange())
     401     2558772 :       for (const auto qp : make_range(n_qpoints))
     402     2068206 :         (*props[state])[stateful_id].qpCopy(qp, (*props[0])[stateful_id], qp);
     403      271100 : }
     404             : 
     405             : void
     406        5360 : 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       12700 :   for (unsigned int state = maxState(); state != 0; state--)
     417        7340 :     std::swap(setProps(state), setProps(state - 1));
     418        5360 : }
     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     5734728 : MaterialPropertyStorage::swap(const THREAD_ID tid, const Elem & elem, unsigned int side)
     450             : {
     451     5734728 :   Threads::spin_mutex::scoped_lock lock(this->_spin_mtx);
     452             : 
     453    18920326 :   for (const auto state : stateIndexRange())
     454    26371196 :     shallowSwapData(_stateful_prop_id_to_prop_id,
     455    13185598 :                     _material_data[tid].props(state),
     456             :                     // Would be nice to make this setProps()
     457             :                     initAndSetProps(&elem, side, state));
     458     5734728 : }
     459             : 
     460             : void
     461     5734719 : MaterialPropertyStorage::swapBack(const THREAD_ID tid, const Elem & elem, unsigned int side)
     462             : {
     463     5734719 :   Threads::spin_mutex::scoped_lock lock(this->_spin_mtx);
     464             : 
     465    18920290 :   for (const auto state : stateIndexRange())
     466    13185571 :     shallowSwapDataBack(_stateful_prop_id_to_prop_id,
     467             :                         setProps(&elem, side, state),
     468    13185571 :                         _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    18920290 :   for (const auto state : stateIndexRange())
     473    13185571 :     if (props(&elem, side, state).empty())
     474           0 :       setProps(state)[&elem].erase(side);
     475     5734719 : }
     476             : 
     477             : unsigned int
     478      126571 : 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      126571 :   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      126571 :   if (maxState() < state)
     491        2544 :     _max_state = state;
     492             : 
     493             :   // Register the property
     494      126571 :   const auto prop_id = _registry.addOrGetID(prop_name, {});
     495             : 
     496             :   // Instantiate the record if needed
     497      126571 :   if (_prop_records.size() < _registry.size())
     498       36571 :     _prop_records.resize(_registry.size());
     499      126571 :   if (!_prop_records[prop_id])
     500       53135 :     _prop_records[prop_id] = PropRecord();
     501             : 
     502             :   // Fill the record
     503      126571 :   auto & record = *_prop_records[prop_id];
     504      126571 :   record.type = type.name();
     505      126571 :   if (declarer)
     506       62479 :     record.declarers.emplace(declarer->typeAndName());
     507      126571 :   record.state = std::max(state, record.state);
     508             : 
     509             :   // Keep track of stateful props by quick access
     510      135867 :   if (state > 0 && std::find(_stateful_prop_id_to_prop_id.begin(),
     511             :                              _stateful_prop_id_to_prop_id.end(),
     512      135867 :                              prop_id) == _stateful_prop_id_to_prop_id.end())
     513             :   {
     514        2940 :     _stateful_prop_id_to_prop_id.push_back(prop_id);
     515        2940 :     record.stateful_id = _stateful_prop_id_to_prop_id.size() - 1;
     516             :   }
     517             : 
     518      126571 :   return prop_id;
     519             : }
     520             : 
     521             : std::vector<MaterialProperties *>
     522      292013 : MaterialPropertyStorage::initProps(const THREAD_ID tid,
     523             :                                    const Elem * elem,
     524             :                                    unsigned int side,
     525             :                                    unsigned int n_qpoints)
     526             : {
     527      292013 :   std::vector<MaterialProperties *> props(numStates());
     528      922194 :   for (const auto state : stateIndexRange())
     529      630181 :     props[state] = &this->initProps(tid, state, elem, side, n_qpoints);
     530      292013 :   return props;
     531           0 : }
     532             : 
     533             : MaterialProperties &
     534      636752 : 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      636752 :   auto & material_data = _material_data[tid];
     541      636752 :   material_data.resize(n_qpoints);
     542             : 
     543      636752 :   auto & mat_props = initAndSetProps(elem, side, state);
     544             : 
     545             :   // In some special cases, material_data might be larger than n_qpoints
     546      636752 :   if (material_data.isOnlyResizeIfSmaller())
     547           0 :     n_qpoints = material_data.nQPoints();
     548             : 
     549      636752 :   const auto n_props = _stateful_prop_id_to_prop_id.size();
     550      636752 :   if (mat_props.size() < n_props)
     551      361049 :     mat_props.resize(n_props, {});
     552             : 
     553             :   // init properties (allocate memory. etc)
     554     1692268 :   for (const auto i : index_range(_stateful_prop_id_to_prop_id))
     555     1055516 :     if (!mat_props.hasValue(i))
     556             :     {
     557      578081 :       const auto prop_id = _stateful_prop_id_to_prop_id[i];
     558      578081 :       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      636752 :   return mat_props;
     563             : }
     564             : 
     565             : void
     566      135642 : dataStore(std::ostream & stream, MaterialPropertyStorage & storage, void * context)
     567             : {
     568             :   // Store the material property ID -> name map for mapping back
     569      135642 :   const auto & registry = storage.getMaterialPropertyRegistry();
     570      135642 :   std::vector<std::string> ids_to_names(registry.idsToNamesBegin(), registry.idsToNamesEnd());
     571      135642 :   dataStore(stream, ids_to_names, nullptr);
     572             : 
     573             :   // Store the stateful ID -> property ID map for mapping back
     574      135642 :   dataStore(stream, storage._stateful_prop_id_to_prop_id, nullptr);
     575             : 
     576             :   // Store the prop -> record map
     577      135642 :   dataStore(stream, storage._prop_records, nullptr);
     578             : 
     579             :   // Store the number of states
     580      135642 :   auto num_states = storage.numStates();
     581      135642 :   dataStore(stream, num_states, nullptr);
     582             : 
     583             :   // Store every property
     584      272154 :   for (const auto state : storage.stateIndexRange())
     585             :   {
     586      136512 :     std::size_t num_elems = storage.setProps(state).size();
     587      136512 :     dataStore(stream, num_elems, nullptr);
     588             : 
     589      200845 :     for (auto & elem_side_map_pair : storage.setProps(state))
     590             :     {
     591       64333 :       const Elem * elem = elem_side_map_pair.first;
     592             :       mooseAssert(elem, "Null element");
     593       64333 :       dataStore(stream, elem, context);
     594             : 
     595       64333 :       auto & side_map = elem_side_map_pair.second;
     596       64333 :       std::size_t num_sides = side_map.size();
     597       64333 :       dataStore(stream, num_sides, nullptr);
     598             : 
     599      128775 :       for (auto & [side, props] : side_map)
     600             :       {
     601       64442 :         dataStore(stream, side, nullptr);
     602             : 
     603       64442 :         std::size_t num_props = props.size();
     604       64442 :         dataStore(stream, num_props, nullptr);
     605             :         mooseAssert(num_props > 0, "No properties");
     606             : 
     607       64442 :         std::size_t n_q_points = 0;
     608      150720 :         for (const auto & entry : props)
     609       86278 :           if (entry.size() > n_q_points)
     610       64442 :             n_q_points = entry.size();
     611       64442 :         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      150720 :         for (auto & entry : props)
     618             :         {
     619       86278 :           std::stringstream out;
     620       86278 :           dataStore(out, entry, nullptr);
     621       86278 :           dataStore(stream, out, nullptr);
     622       86278 :         }
     623             :       }
     624             :     }
     625             :   }
     626      135642 : }
     627             : 
     628             : void
     629       42683 : dataLoad(std::istream & stream, MaterialPropertyStorage & storage, void * context)
     630             : {
     631       42683 :   storage._restartable_map.clear();
     632             : 
     633       42683 :   const auto & registry = storage.getMaterialPropertyRegistry();
     634             : 
     635       42683 :   std::vector<std::string> from_prop_ids_to_names;
     636       42683 :   dataLoad(stream, from_prop_ids_to_names, nullptr);
     637             : 
     638       42683 :   decltype(storage._stateful_prop_id_to_prop_id) from_stateful_prop_id_to_prop_id;
     639       42683 :   dataLoad(stream, from_stateful_prop_id_to_prop_id, nullptr);
     640             : 
     641       42683 :   decltype(storage._prop_records) from_prop_records;
     642       42683 :   dataLoad(stream, from_prop_records, nullptr);
     643             : 
     644             :   decltype(storage.numStates()) num_states;
     645       42683 :   dataLoad(stream, num_states, nullptr);
     646             : 
     647             :   {
     648             :     // Build maps of material object -> properties and property -> material objects
     649       85366 :     const auto build_maps = [](const auto & prop_records, const auto & ids_to_names)
     650             :     {
     651       85366 :       std::map<std::string, std::set<std::string>> object_to_props, prop_to_objects;
     652       98696 :       for (const auto i : index_range(prop_records))
     653       13330 :         if (prop_records[i] && prop_records[i]->stateful())
     654             :         {
     655         514 :           const auto & prop = ids_to_names[i];
     656        1078 :           for (const auto & declarer : (*prop_records[i]).declarers)
     657             :           {
     658         564 :             object_to_props[declarer].insert(prop);
     659         564 :             prop_to_objects[prop].insert(declarer);
     660             :           }
     661             :         }
     662             : 
     663      170732 :       return std::make_pair(std::move(object_to_props), std::move(prop_to_objects));
     664       85366 :     };
     665             :     // Maps for the current stateful properties
     666             :     const std::vector<std::string> prop_ids_to_names(registry.idsToNamesBegin(),
     667       42683 :                                                      registry.idsToNamesEnd());
     668       42683 :     const auto [object_to_props, prop_to_objects] =
     669       42683 :         build_maps(storage._prop_records, prop_ids_to_names);
     670             :     // Maps for the stored stateful properties
     671       42683 :     const auto [from_object_to_props, from_prop_to_objects] =
     672       42683 :         build_maps(from_prop_records, from_prop_ids_to_names);
     673             : 
     674             :     // Enforce our stateful requirements
     675       42924 :     for (const auto & [object, props] : object_to_props)
     676             :     {
     677         249 :       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         249 :       if (find_from_object != from_object_to_props.end())
     684             :       {
     685         245 :         const auto & from_props = find_from_object->second;
     686         245 :         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       42910 :     for (const auto & [from_prop, from_objects] : from_prop_to_objects)
     718         239 :       if (const auto find_objects = prop_to_objects.find(from_prop);
     719         239 :           find_objects != prop_to_objects.end())
     720         499 :         for (const auto & object : find_objects->second)
     721         264 :           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       42671 :   }
     731             : 
     732       42671 :   std::vector<std::optional<unsigned int>> to_stateful_ids(from_stateful_prop_id_to_prop_id.size());
     733             : 
     734       42671 :   auto & to_prop_records = storage._prop_records;
     735             : 
     736             :   // Mark everything as not restored in the event that we call this again
     737       49284 :   for (auto & record_ptr : to_prop_records)
     738        6613 :     if (record_ptr)
     739        6463 :       record_ptr->restored = false;
     740             : 
     741             :   // Fill the mapping from previous ID to current stateful ID
     742       42894 :   for (const auto from_stateful_id : index_range(from_stateful_prop_id_to_prop_id))
     743             :   {
     744         231 :     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         231 :     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         231 :     const auto & name = from_prop_ids_to_names[from_prop_id];
     753             : 
     754         231 :     if (const auto query_to_prop_id = registry.queryID(name))
     755             :     {
     756         231 :       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         231 :       auto & to_record = *to_prop_records[to_prop_id];
     761             : 
     762         231 :       if (to_record.stateful())
     763             :       {
     764         231 :         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         227 :         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         223 :         const auto to_stateful_id = storage.getPropRecord(to_prop_id).stateful_id;
     781             :         mooseAssert(to_stateful_id != invalid_uint, "Not stateful");
     782             : 
     783         223 :         to_stateful_ids[from_stateful_id] = to_stateful_id;
     784             : 
     785             :         // Mark that we're going to restore this property
     786         223 :         to_record.restored = true;
     787             :         mooseAssert(storage.isRestoredProperty(name), "Restored mismatch");
     788             : 
     789         223 :         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       85533 :   for (const auto state : make_range(num_states))
     797             :   {
     798             :     std::size_t num_elems;
     799       42870 :     dataLoad(stream, num_elems, nullptr);
     800             : 
     801       73514 :     for (std::size_t i_elem = 0; i_elem < num_elems; ++i_elem)
     802             :     {
     803             :       const Elem * elem;
     804       30644 :       dataLoad(stream, elem, context);
     805             :       mooseAssert(elem, "Null element");
     806             : 
     807             :       std::size_t num_sides;
     808       30644 :       dataLoad(stream, num_sides, nullptr);
     809             : 
     810       61385 :       for (std::size_t i_side = 0; i_side < num_sides; ++i_side)
     811             :       {
     812             :         unsigned int side;
     813       30741 :         dataLoad(stream, side, nullptr);
     814             : 
     815             :         std::size_t num_props;
     816       30741 :         dataLoad(stream, num_props, nullptr);
     817             :         mooseAssert(num_props <= to_stateful_ids.size(), "Missized map");
     818             : 
     819             :         std::size_t num_q_points;
     820       30741 :         dataLoad(stream, num_q_points, nullptr);
     821             : 
     822             :         // Avoid multiple map lookups for entries pertaining to [elem, side]
     823       30741 :         MaterialPropertyStorage::RestartableMapType::mapped_type * restart_entry = nullptr;
     824       30741 :         MaterialProperties * in_place_entry = nullptr;
     825             : 
     826             :         // Load each stateful property
     827       83118 :         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       52377 :           std::stringstream data;
     832       52377 :           dataLoad(stream, data, nullptr);
     833       52377 :           data.seekg(0, std::ios::beg);
     834             : 
     835             :           // We have a property to load into
     836       52377 :           if (const auto to_stateful_id_ptr = to_stateful_ids[from_stateful_id])
     837             :           {
     838       52377 :             const auto to_stateful_id = *to_stateful_id_ptr;
     839             : 
     840             :             // Load the data directly into _storage
     841       52377 :             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       52377 :               if (!restart_entry)
     853       30741 :                 restart_entry = &storage._restartable_map[std::make_pair(elem, side)];
     854             : 
     855       52377 :               auto & mat_entry = (*restart_entry)[to_stateful_id];
     856       52377 :               if (state >= mat_entry.size())
     857       52377 :                 mat_entry.resize(state + 1);
     858             : 
     859       52377 :               mat_entry[state] = std::move(data);
     860             :             }
     861             :           }
     862       52377 :         }
     863             :       }
     864             :     }
     865             :   }
     866       42663 : }
     867             : 
     868             : void
     869        6181 : dataStore(std::ostream & stream, MaterialPropertyStorage::PropRecord & record, void *)
     870             : {
     871        6181 :   dataStore(stream, record.declarers, nullptr);
     872        6181 :   dataStore(stream, record.type, nullptr);
     873        6181 :   dataStore(stream, record.stateful_id, nullptr);
     874        6181 :   dataStore(stream, record.state, nullptr);
     875        6181 : }
     876             : 
     877             : void
     878        6547 : dataLoad(std::istream & stream, MaterialPropertyStorage::PropRecord & record, void *)
     879             : {
     880        6547 :   dataLoad(stream, record.declarers, nullptr);
     881        6547 :   dataLoad(stream, record.type, nullptr);
     882        6547 :   dataLoad(stream, record.stateful_id, nullptr);
     883        6547 :   dataLoad(stream, record.state, nullptr);
     884        6547 : }

Generated by: LCOV version 1.14