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

Generated by: LCOV version 1.14