LCOV - code coverage report
Current view: top level - src/restart - RestartableEquationSystems.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 39a256 Lines: 209 230 90.9 %
Date: 2026-07-14 14:36:17 Functions: 19 21 90.5 %
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 "RestartableEquationSystems.h"
      11             : 
      12             : #include "DataIO.h"
      13             : 
      14             : #include "libmesh/dof_map.h"
      15             : #include "libmesh/dof_object.h"
      16             : #include "libmesh/elem.h"
      17             : #include "libmesh/node.h"
      18             : 
      19             : const std::string RestartableEquationSystems::SystemHeader::system_solution_name =
      20             :     "SYSTEM_SOLUTION";
      21             : 
      22       62453 : RestartableEquationSystems::RestartableEquationSystems(libMesh::MeshBase & mesh)
      23       62453 :   : _es(mesh), _load_all_vectors(true)
      24             : {
      25       62453 : }
      26             : 
      27             : RestartableEquationSystems::EquationSystemsHeader
      28       46660 : RestartableEquationSystems::buildHeader(
      29             :     const std::vector<const libMesh::DofObject *> & ordered_objects) const
      30             : {
      31       46660 :   EquationSystemsHeader es_header;
      32             : 
      33             :   // Systems
      34      141034 :   for (const auto sys_num : make_range(_es.n_systems()))
      35             :   {
      36       94374 :     const auto & sys = _es.get_system(sys_num);
      37             : 
      38       94374 :     SystemHeader sys_header;
      39       94374 :     sys_header.name = sys.name();
      40       94374 :     sys_header.type = sys.system_type();
      41             : 
      42             :     // Variables in the system
      43      188332 :     for (const auto var_num : make_range(sys.n_vars()))
      44             :     {
      45       93958 :       const auto & var = sys.variable(var_num);
      46             : 
      47       93958 :       VariableHeader var_header;
      48       93958 :       var_header.name = var.name();
      49       93958 :       var_header.type = var.type();
      50       93958 :       var_header.size = 0;
      51       93958 :       var_header.variable = &var;
      52             : 
      53             :       mooseAssert(_es.comm().verify("sys_" + sys.name() + "_var_" + var.name()),
      54             :                   "Out of order in parallel");
      55             :       mooseAssert(!sys_header.variables.count(var.name()), "Already inserted");
      56             : 
      57             :       // Non-SCALAR variable
      58       93958 :       if (var.type().family != SCALAR)
      59             :       {
      60    19408274 :         for (const auto & obj : ordered_objects)
      61    19315973 :           var_header.size += sizeof(Real) * obj->n_comp(sys.number(), var.number());
      62             :       }
      63             :       // SCALAR variable on the last rank
      64        1657 :       else if (_es.processor_id() == _es.n_processors() - 1)
      65             :       {
      66        1611 :         std::vector<dof_id_type> scalar_dofs;
      67        1611 :         sys.get_dof_map().SCALAR_dof_indices(scalar_dofs, var.number());
      68        1611 :         var_header.size += sizeof(Real) * scalar_dofs.size();
      69        1611 :       }
      70             : 
      71       93958 :       sys_header.variables.emplace(var.name(), var_header);
      72       93958 :     }
      73             : 
      74             :     // System vector
      75       94374 :     auto & sys_vec_header = sys_header.vectors[SystemHeader::system_solution_name];
      76       94374 :     sys_vec_header.name = SystemHeader::system_solution_name;
      77       94374 :     sys_vec_header.vector = sys.solution.get();
      78       94374 :     sys_vec_header.type = sys_vec_header.vector->type();
      79      409699 :     for (const auto vec_num : make_range(sys.n_vectors()))
      80             :     {
      81             :       mooseAssert(_es.comm().verify("sys_" + sys.name() + "_vec_" + sys.vector_name(vec_num)),
      82             :                   "Out of order in parallel");
      83      315325 :       const auto & name = sys.vector_name(vec_num);
      84             :       mooseAssert(!sys_header.vectors.count(name), "Already inserted");
      85      315325 :       auto & vec_header = sys_header.vectors[name];
      86      315325 :       vec_header.name = sys.vector_name(vec_num);
      87      315325 :       vec_header.projections = sys.vector_preservation(vec_header.name);
      88      315325 :       vec_header.vector = &sys.get_vector(vec_header.name);
      89      315325 :       vec_header.type = vec_header.vector->type();
      90             :     }
      91             : 
      92             :     // System in this EquationSystems
      93             :     mooseAssert(!es_header.systems.count(sys.name()), "Already inserted");
      94       94374 :     es_header.systems.emplace(sys.name(), sys_header);
      95       94374 :   }
      96             : 
      97             :   // Setup the positions in each vector for easy access later
      98       46660 :   std::size_t offset = 0;
      99      141034 :   for (auto & sys_name_header_pair : es_header.systems)
     100             :   {
     101       94374 :     auto & sys_header = sys_name_header_pair.second;
     102      504073 :     for (auto & vec_name_header_pair : sys_header.vectors)
     103             :     {
     104      409699 :       auto & vec_header = vec_name_header_pair.second;
     105      821143 :       for (const auto & var_name_header_pair : sys_header.variables)
     106             :       {
     107      411444 :         const auto & var_header = var_name_header_pair.second;
     108             :         mooseAssert(!vec_header.variable_offset.count(var_header.name), "Already inserted");
     109      411444 :         vec_header.variable_offset[var_header.name] = offset;
     110      411444 :         offset += var_header.size;
     111             :       }
     112             :     }
     113             :   }
     114             : 
     115       46660 :   es_header.data_size = offset;
     116             : 
     117       46660 :   return es_header;
     118           0 : }
     119             : 
     120             : std::vector<const libMesh::DofObject *>
     121       60150 : RestartableEquationSystems::orderDofObjects() const
     122             : {
     123       60150 :   std::vector<const libMesh::DofObject *> objects;
     124      120300 :   auto add = [&objects](const auto begin, const auto end)
     125             :   {
     126      120300 :     std::set<const libMesh::DofObject *, libMesh::CompareDofObjectsByID> ordered(begin, end);
     127      120300 :     objects.insert(objects.end(), ordered.begin(), ordered.end());
     128      180450 :   };
     129             : 
     130       60150 :   const auto & mesh = _es.get_mesh();
     131       60150 :   add(mesh.local_elements_begin(), mesh.local_elements_end());
     132       60150 :   add(mesh.local_nodes_begin(), mesh.local_nodes_end());
     133             : 
     134      120300 :   return objects;
     135           0 : }
     136             : 
     137             : void
     138       46660 : RestartableEquationSystems::store(std::ostream & stream) const
     139             : {
     140             :   // Order objects (elements and then nodes) by ID for storing
     141       46660 :   const auto ordered_objects = orderDofObjects();
     142             : 
     143             :   // Store the header (systems, variables, vectors)
     144       46660 :   EquationSystemsHeader es_header = buildHeader(ordered_objects);
     145       46660 :   dataStore(stream, es_header, nullptr);
     146             : 
     147             :   // Store the ordered objects so we can do a sanity check on if we're
     148             :   // loading the same thing
     149             :   {
     150       46660 :     std::vector<dof_id_type> ordered_objects_ids(ordered_objects.size());
     151    10158367 :     for (const auto i : index_range(ordered_objects))
     152    10111707 :       ordered_objects_ids[i] = ordered_objects[i]->id();
     153       46660 :     dataStore(stream, ordered_objects_ids, nullptr);
     154       46660 :   }
     155             : 
     156             : #ifndef NDEBUG
     157             :   const std::size_t data_initial_position = static_cast<std::size_t>(stream.tellp());
     158             : #endif
     159             : 
     160             :   // Store each system
     161      141034 :   for (const auto & sys_name_header_pair : es_header.systems)
     162             :   {
     163       94374 :     const auto & sys_header = sys_name_header_pair.second;
     164       94374 :     const auto & sys = _es.get_system(sys_header.name);
     165             : 
     166             :     // Store each vector and variable
     167      504073 :     for (const auto & vec_name_header_pair : sys_header.vectors)
     168             :     {
     169      409699 :       const auto & vec_header = vec_name_header_pair.second;
     170      409699 :       const auto & vec = *vec_header.vector;
     171      821143 :       for (const auto & var_name_header_pair : sys_header.variables)
     172             :       {
     173      411444 :         const auto & var_header = var_name_header_pair.second;
     174      411444 :         const auto & var = *var_header.variable;
     175             : 
     176             : #ifndef NDEBUG
     177             :         const std::size_t var_initial_position = stream.tellp();
     178             : #endif
     179             : 
     180             :         // Non-SCALAR variable
     181      411444 :         if (var.type().family != SCALAR)
     182             :         {
     183             :           // Store for each component of each element and node
     184    77677682 :           for (const auto & obj : ordered_objects)
     185   116751013 :             for (const auto comp : make_range(obj->n_comp(sys.number(), var.number())))
     186             :             {
     187    39476785 :               auto val = vec(obj->dof_number(sys.number(), var.number(), comp));
     188    39476785 :               dataStore(stream, val, nullptr);
     189             :             }
     190             :         }
     191             :         // SCALAR variable on the last rank
     192        7990 :         else if (_es.processor_id() == _es.n_processors() - 1)
     193             :         {
     194        7864 :           const auto & dof_map = sys.get_dof_map();
     195        7864 :           std::vector<dof_id_type> scalar_dofs;
     196        7864 :           dof_map.SCALAR_dof_indices(scalar_dofs, var.number());
     197       16965 :           for (const auto dof : scalar_dofs)
     198             :           {
     199        9101 :             auto val = vec(dof);
     200        9101 :             dataStore(stream, val, nullptr);
     201             :           }
     202        7864 :         }
     203             : 
     204             : #ifndef NDEBUG
     205             :         const std::size_t data_offset = var_initial_position - data_initial_position;
     206             :         mooseAssert(vec_header.variable_offset.at(var_header.name) == data_offset,
     207             :                     "Invalid offset");
     208             : 
     209             :         const std::size_t current_position = static_cast<std::size_t>(stream.tellp());
     210             :         const std::size_t var_size = current_position - var_initial_position;
     211             :         mooseAssert(var_size == sys_header.variables.at(var.name()).size, "Incorrect assumed size");
     212             : #endif
     213             :       }
     214             :     }
     215             :   }
     216             : 
     217             :   mooseAssert((data_initial_position + es_header.data_size) ==
     218             :                   static_cast<std::size_t>(stream.tellp()),
     219             :               "Incorrect assumed size");
     220       46660 : }
     221             : 
     222             : void
     223       13490 : RestartableEquationSystems::load(std::istream & stream)
     224             : {
     225             :   // Load the header (systems, variables, vectors)
     226             :   // We do this first so that the loader can make informed decisions
     227             :   // on what to put where based on everything that is available
     228       13490 :   _loaded_header.systems.clear();
     229       13490 :   dataLoad(stream, _loaded_header, nullptr);
     230             : 
     231             :   // Order objects (elements and then node) by ID for storing
     232       13490 :   _loaded_ordered_objects = orderDofObjects();
     233             : 
     234             :   // Clear previously loaded variables to ensure a clean state
     235       13490 :   _loaded_variables.clear();
     236             : 
     237             :   // Sanity check on if we're loading the same thing
     238             :   {
     239             :     const std::string mesh_mismatch_message =
     240             :         ". This means that equation-system data is being restored onto a different mesh topology. "
     241             :         "If this occurs while restoring a MultiApp sub-application that uses mesh adaptivity, the "
     242             :         "backup may not contain mesh checkpoint data for the restored topology, or the "
     243             :         "sub-application mesh may have changed after the backup was made. Use clone_parent_mesh = "
     244             :         "true when the sub-application should share the parent application's mesh topology, or "
     245       13490 :         "avoid changing the sub-application mesh between backup and restore.";
     246             : 
     247       13490 :     std::vector<dof_id_type> from_ordered_objects_ids;
     248       13490 :     dataLoad(stream, from_ordered_objects_ids, nullptr);
     249       13490 :     if (_loaded_ordered_objects.size() != from_ordered_objects_ids.size())
     250           0 :       mooseError("RestartableEquationSystems::load(): Number of previously stored elements/nodes (",
     251           0 :                  from_ordered_objects_ids.size(),
     252             :                  ") does not match the current number of elements/nodes (",
     253           0 :                  _loaded_ordered_objects.size(),
     254             :                  mesh_mismatch_message);
     255     3578428 :     for (const auto i : index_range(_loaded_ordered_objects))
     256     3564938 :       if (_loaded_ordered_objects[i]->id() != from_ordered_objects_ids[i])
     257           0 :         mooseError("RestartableEquationSystems::load(): Id of previously stored element/node (",
     258           0 :                    from_ordered_objects_ids[i],
     259             :                    ") does not match the current element/node id (",
     260           0 :                    _loaded_ordered_objects[i]->id(),
     261             :                    mesh_mismatch_message);
     262       13490 :   }
     263             : 
     264       13490 :   _loaded_stream_data_begin = static_cast<std::size_t>(stream.tellg());
     265             : 
     266             :   // Load everything that we have available at the moment
     267       40609 :   for (const auto & sys_name_header_pair : _loaded_header.systems)
     268             :   {
     269       27119 :     const auto & sys_header = sys_name_header_pair.second;
     270       27119 :     if (!_es.has_system(sys_header.name))
     271          66 :       continue;
     272       27053 :     auto & sys = _es.get_system(sys_header.name);
     273             : 
     274       27053 :     bool modified_sys = false;
     275             : 
     276      138446 :     for (const auto & vec_name_header_pair : sys_header.vectors)
     277             :     {
     278      111393 :       bool modified_vec = false;
     279             : 
     280      111393 :       const auto & vec_header = vec_name_header_pair.second;
     281      111393 :       const bool is_solution = vec_header.name == SystemHeader::system_solution_name;
     282             : 
     283      111393 :       if (!is_solution && !sys.have_vector(vec_header.name))
     284             :       {
     285         174 :         if (_load_all_vectors)
     286          97 :           sys.add_vector(vec_header.name, vec_header.projections, vec_header.type);
     287             :         else
     288          77 :           continue;
     289             :       }
     290             : 
     291      111316 :       auto & vec = is_solution ? *sys.solution : sys.get_vector(vec_header.name);
     292             : 
     293      221628 :       for (const auto & var_name_header_pair : sys_header.variables)
     294             :       {
     295      110312 :         const auto & var_header = var_name_header_pair.second;
     296      110312 :         if (!sys.has_variable(var_header.name))
     297          13 :           continue;
     298      110299 :         const auto & var = sys.variable(sys.variable_number(var_header.name));
     299      110299 :         if (var.type() != var_header.type)
     300           0 :           continue;
     301             : 
     302      110299 :         restore(sys_header, vec_header, var_header, sys, vec, var, stream);
     303      110299 :         modified_vec = true;
     304             :       }
     305             : 
     306      111316 :       if (modified_vec)
     307             :       {
     308       88813 :         vec.close();
     309       88813 :         modified_sys = true;
     310             :       }
     311             :     }
     312             : 
     313       27053 :     if (modified_sys)
     314       20475 :       sys.update();
     315             :   }
     316             : 
     317             :   // Move the stream to the end of our data so that we make RestartableDataReader happy
     318       13490 :   stream.seekg(_loaded_stream_data_begin + _loaded_header.data_size);
     319       13490 : }
     320             : 
     321             : bool
     322           0 : RestartableEquationSystems::isVariableRestored(const std::string & system_name,
     323             :                                                const std::string & vector_name,
     324             :                                                const std::string & variable_name) const
     325             : {
     326           0 :   std::tuple<std::string, std::string, std::string> key(system_name, vector_name, variable_name);
     327           0 :   return _loaded_variables.count(key);
     328           0 : }
     329             : 
     330             : void
     331      110299 : RestartableEquationSystems::restore(const SystemHeader & from_sys_header,
     332             :                                     const VectorHeader & from_vec_header,
     333             :                                     const VariableHeader & from_var_header,
     334             :                                     const libMesh::System & to_sys,
     335             :                                     libMesh::NumericVector<libMesh::Number> & to_vec,
     336             :                                     const libMesh::Variable & to_var,
     337             :                                     std::istream & stream)
     338             : {
     339             : #ifndef NDEBUG
     340             :   const auto sys_it = _loaded_header.systems.find(from_sys_header.name);
     341             :   mooseAssert(sys_it != _loaded_header.systems.end(), "System does not exist");
     342             :   const auto & sys_header = sys_it->second;
     343             :   mooseAssert(sys_header == from_sys_header, "Not my system");
     344             :   const auto vec_it = sys_header.vectors.find(from_vec_header.name);
     345             :   mooseAssert(vec_it != sys_header.vectors.end(), "Vector does not exist");
     346             :   const auto & vec_header = vec_it->second;
     347             :   mooseAssert(vec_header == from_vec_header, "Not my vector");
     348             :   const auto var_it = sys_header.variables.find(from_var_header.name);
     349             :   mooseAssert(var_it != sys_header.variables.end(), "Variable does not exist");
     350             :   const auto & var_header = var_it->second;
     351             :   mooseAssert(var_header == from_var_header, "Not my variable");
     352             :   mooseAssert(!isVariableRestored(from_sys_header.name, from_vec_header.name, from_var_header.name),
     353             :               "Variable already restored");
     354             : #endif
     355             : 
     356             :   const auto error =
     357           0 :       [&from_sys_header, &from_vec_header, &from_var_header, &to_sys, &to_var](auto... args)
     358             :   {
     359           0 :     mooseError("An error occured while restoring a system:\n",
     360             :                args...,
     361             :                "\n\nFrom system: ",
     362           0 :                from_sys_header.name,
     363             :                "\nFrom vector: ",
     364           0 :                from_vec_header.name,
     365             :                "\nFrom variable: ",
     366           0 :                from_var_header.name,
     367             :                "\nTo system: ",
     368           0 :                to_sys.name(),
     369             :                "\nTo variable: ",
     370           0 :                to_var.name());
     371      110299 :   };
     372             : 
     373      110299 :   if (from_var_header.type != to_var.type())
     374           0 :     error("Cannot restore to a variable of a different type");
     375             : 
     376      110299 :   const auto offset = from_vec_header.variable_offset.at(from_var_header.name);
     377      110299 :   stream.seekg(_loaded_stream_data_begin + offset);
     378             : 
     379             :   // Non-SCALAR variable
     380      110299 :   if (to_var.type().family != SCALAR)
     381             :   {
     382    25914880 :     for (const auto & obj : _loaded_ordered_objects)
     383    39170823 :       for (const auto comp : make_range(obj->n_comp(to_sys.number(), to_var.number())))
     384             :       {
     385             :         Real val;
     386    13365535 :         dataLoad(stream, val, nullptr);
     387    13365535 :         to_vec.set(obj->dof_number(to_sys.number(), to_var.number(), comp), val);
     388             :       }
     389             :   }
     390             :   // SCALAR variable on the last rank
     391         707 :   else if (_es.processor_id() == _es.n_processors() - 1)
     392             :   {
     393         704 :     const auto & dof_map = to_sys.get_dof_map();
     394         704 :     std::vector<dof_id_type> scalar_dofs;
     395         704 :     dof_map.SCALAR_dof_indices(scalar_dofs, to_var.number());
     396        1651 :     for (const auto dof : scalar_dofs)
     397             :     {
     398             :       Real val;
     399         947 :       dataLoad(stream, val, nullptr);
     400         947 :       to_vec.set(dof, val);
     401             :     }
     402         704 :   }
     403             : 
     404             :   // insert into the member variable
     405             :   std::tuple<std::string, std::string, std::string> _loaded_variable = {
     406      110299 :       from_sys_header.name, from_vec_header.name, from_var_header.name};
     407             : 
     408      110299 :   _loaded_variables.insert(_loaded_variable);
     409             : 
     410             :   mooseAssert(isVariableRestored(from_sys_header.name, from_vec_header.name, from_var_header.name),
     411             :               "Variable not marked as restored");
     412      110299 : }
     413             : 
     414             : void
     415       46660 : dataStore(std::ostream & stream, RestartableEquationSystems & res, void *)
     416             : {
     417       46660 :   res.store(stream);
     418       46660 : }
     419             : 
     420             : void
     421       13490 : dataLoad(std::istream & stream, RestartableEquationSystems & res, void *)
     422             : {
     423       13490 :   res.load(stream);
     424       13490 : }
     425             : 
     426             : void
     427       46660 : dataStore(std::ostream & stream, RestartableEquationSystems::EquationSystemsHeader & header, void *)
     428             : {
     429       46660 :   dataStore(stream, header.systems, nullptr);
     430       46660 :   dataStore(stream, header.data_size, nullptr);
     431       46660 : }
     432             : 
     433             : void
     434       13490 : dataLoad(std::istream & stream, RestartableEquationSystems::EquationSystemsHeader & header, void *)
     435             : {
     436       13490 :   dataLoad(stream, header.systems, nullptr);
     437       13490 :   dataLoad(stream, header.data_size, nullptr);
     438       13490 : }
     439             : 
     440             : void
     441       94374 : dataStore(std::ostream & stream, RestartableEquationSystems::SystemHeader & header, void *)
     442             : {
     443       94374 :   dataStore(stream, header.name, nullptr);
     444       94374 :   dataStore(stream, header.type, nullptr);
     445       94374 :   dataStore(stream, header.variables, nullptr);
     446       94374 :   dataStore(stream, header.vectors, nullptr);
     447       94374 : }
     448             : 
     449             : void
     450       27119 : dataLoad(std::istream & stream, RestartableEquationSystems::SystemHeader & header, void *)
     451             : {
     452       27119 :   dataLoad(stream, header.name, nullptr);
     453       27119 :   dataLoad(stream, header.type, nullptr);
     454       27119 :   dataLoad(stream, header.variables, nullptr);
     455       27119 :   dataLoad(stream, header.vectors, nullptr);
     456       27119 : }
     457             : 
     458             : void
     459       93958 : dataStore(std::ostream & stream, RestartableEquationSystems::VariableHeader & header, void *)
     460             : {
     461       93958 :   dataStore(stream, header.name, nullptr);
     462       93958 :   dataStore(stream, header.type, nullptr);
     463       93958 : }
     464             : void
     465       27638 : dataLoad(std::istream & stream, RestartableEquationSystems::VariableHeader & header, void *)
     466             : {
     467       27638 :   dataLoad(stream, header.name, nullptr);
     468       27638 :   dataLoad(stream, header.type, nullptr);
     469       27638 : }
     470             : 
     471             : void
     472      409699 : dataStore(std::ostream & stream, RestartableEquationSystems::VectorHeader & header, void *)
     473             : {
     474      409699 :   dataStore(stream, header.name, nullptr);
     475      409699 :   dataStore(stream, header.projections, nullptr);
     476      409699 :   dataStore(stream, header.type, nullptr);
     477      409699 :   dataStore(stream, header.variable_offset, nullptr);
     478      409699 : }
     479             : void
     480      111521 : dataLoad(std::istream & stream, RestartableEquationSystems::VectorHeader & header, void *)
     481             : {
     482      111521 :   dataLoad(stream, header.name, nullptr);
     483      111521 :   dataLoad(stream, header.projections, nullptr);
     484      111521 :   dataLoad(stream, header.type, nullptr);
     485      111521 :   dataLoad(stream, header.variable_offset, nullptr);
     486      111521 : }
     487             : 
     488             : void
     489          22 : to_json(nlohmann::json & json, const RestartableEquationSystems & res)
     490             : {
     491             : 
     492          22 :   nlohmann::json loaded_vars = nlohmann::json::array();
     493             : 
     494         220 :   for (const auto & [system, vector, variable] : res.getLoadedVariables())
     495             :   {
     496        1980 :     loaded_vars.push_back({{"system", system}, {"vector", vector}, {"variable", variable}});
     497             :   }
     498             : 
     499          22 :   json["loaded_variables"] = loaded_vars;
     500        1606 : }

Generated by: LCOV version 1.14