LCOV - code coverage report
Current view: top level - src/mesh - checkpoint_io.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4583 (689827) with base 9e5b0b Lines: 709 805 88.1 %
Date: 2026-10-16 15:05:09 Functions: 37 63 58.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
       3             : 
       4             : // This library is free software; you can redistribute it and/or
       5             : // modify it under the terms of the GNU Lesser General Public
       6             : // License as published by the Free Software Foundation; either
       7             : // version 2.1 of the License, or (at your option) any later version.
       8             : 
       9             : // This library is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             : // Lesser General Public License for more details.
      13             : 
      14             : // You should have received a copy of the GNU Lesser General Public
      15             : // License along with this library; if not, write to the Free Software
      16             : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      17             : 
      18             : // Local includes
      19             : #include "libmesh/checkpoint_io.h"
      20             : #include "libmesh/boundary_info.h"
      21             : #include "libmesh/cell_c0polyhedron.h"
      22             : #include "libmesh/distributed_mesh.h"
      23             : #include "libmesh/elem.h"
      24             : #include "libmesh/enum_to_string.h"
      25             : #include "libmesh/enum_xdr_mode.h"
      26             : #include "libmesh/face_c0polygon.h"
      27             : #include "libmesh/libmesh_logging.h"
      28             : #include "libmesh/mesh_base.h"
      29             : #include "libmesh/mesh_communication.h"
      30             : #include "libmesh/mesh_tools.h"
      31             : #include "libmesh/node.h"
      32             : #include "libmesh/parallel.h"
      33             : #include "libmesh/partitioner.h"
      34             : #include "libmesh/periodic_boundaries.h"
      35             : #include "libmesh/periodic_boundary.h"
      36             : #include "libmesh/metis_partitioner.h"
      37             : #include "libmesh/remote_elem.h"
      38             : #include "libmesh/xdr_io.h"
      39             : #include "libmesh/xdr_cxx.h"
      40             : #include "libmesh/utility.h"
      41             : #include "libmesh/int_range.h"
      42             : 
      43             : // C++ includes
      44             : #include <algorithm>
      45             : #include <iostream>
      46             : #include <iomanip>
      47             : #include <cstdio>
      48             : #include <vector>
      49             : #include <string>
      50             : #include <cstring>
      51             : #include <filesystem>
      52             : #include <fstream>
      53             : #include <sstream> // for ostringstream
      54             : #include <unordered_map>
      55             : #include <unordered_set>
      56             : #ifdef LIBMESH_HAVE_DIRECT_H
      57             : #include <direct.h> // rmdir() on Windows
      58             : #endif
      59             : #ifdef LIBMESH_HAVE_UNISTD_H
      60             : #include <unistd.h>  // rmdir() on Unix
      61             : #endif
      62             : 
      63             : namespace
      64             : {
      65             : // chunking computes the number of chunks and first-chunk-offset when splitting a mesh
      66             : // into nsplits pieces using size procs for the given MPI rank.  The number of chunks and offset
      67             : // are stored in nchunks and first_chunk respectively.
      68           0 : void chunking(libMesh::processor_id_type size, libMesh::processor_id_type rank, libMesh::processor_id_type nsplits,
      69             :               libMesh::processor_id_type & nchunks, libMesh::processor_id_type & first_chunk)
      70             : {
      71           0 :   if (nsplits % size == 0) // the chunks divide evenly over the processors
      72             :     {
      73           0 :       nchunks = nsplits / size;
      74           0 :       first_chunk = libMesh::cast_int<libMesh::processor_id_type>(nchunks * rank);
      75           0 :       return;
      76             :     }
      77             : 
      78           0 :   libMesh::processor_id_type nextra = nsplits % size;
      79           0 :   if (rank < nextra) // leftover chunks cause an extra chunk to be added to this processor
      80             :     {
      81           0 :       nchunks = libMesh::cast_int<libMesh::processor_id_type>(nsplits / size + 1);
      82           0 :       first_chunk = libMesh::cast_int<libMesh::processor_id_type>(nchunks * rank);
      83             :     }
      84             :   else // no extra chunks, but first chunk is offset by extras on earlier ranks
      85             :     {
      86           0 :       nchunks = nsplits / size;
      87             :       // account for the case where nchunks is zero where we want max int
      88           0 :       first_chunk = libMesh::cast_int<libMesh::processor_id_type>
      89           0 :         (std::max((int)((nchunks + 1) * (nsplits % size) + nchunks * (rank - nsplits % size)),
      90           0 :          (1 - (int)nchunks) * std::numeric_limits<int>::max()));
      91             :     }
      92             : }
      93             : 
      94        3344 : std::string_view extension(std::string_view s)
      95             : {
      96        3204 :   auto pos = s.rfind(".");
      97        3344 :   if (pos == std::string::npos)
      98           0 :     return "";
      99        3344 :   return s.substr(pos, s.size() - pos);
     100             : }
     101             : 
     102        4340 : std::string split_dir(const std::string & input_name, libMesh::processor_id_type n_procs)
     103             : {
     104        8504 :   return input_name + "/" + std::to_string(n_procs);
     105             : }
     106             : 
     107             : 
     108        2112 : std::string header_file(const std::string & input_name, libMesh::processor_id_type n_procs)
     109             : {
     110        8120 :   return (split_dir(input_name, n_procs) + "/header").append(extension(input_name));
     111             : }
     112             : 
     113             : std::string
     114        1232 : split_file(const std::string & input_name,
     115             :            libMesh::processor_id_type n_procs,
     116             :            libMesh::processor_id_type proc_id)
     117             : {
     118        4696 :   return (split_dir(input_name, n_procs) + "/split-" + std::to_string(n_procs) + "-" +
     119        4812 :          std::to_string(proc_id)).append(extension(input_name));
     120             : }
     121             : 
     122             : // Report, as a comma-separated list, the processor counts for which a split
     123             : // already exists as a numbered subdirectory of input_name, to help a user
     124             : // pick out why the split they asked for could not be found.
     125          12 : std::string existing_splits(const std::string & input_name)
     126             : {
     127           1 :   std::error_code ec;
     128          23 :   if (!std::filesystem::is_directory(input_name, ec))
     129           0 :     return "";
     130             : 
     131           3 :   std::vector<std::string> splits;
     132          59 :   for (const auto & entry : std::filesystem::directory_iterator(input_name, ec))
     133             :   {
     134          24 :     const auto name = entry.path().filename().string();
     135          24 :     if (!name.empty() && name.find_first_not_of("0123456789") == std::string::npos &&
     136           1 :         entry.is_directory(ec))
     137          12 :       splits.push_back(name);
     138             :   }
     139          12 :   std::sort(splits.begin(), splits.end());
     140             : 
     141           2 :   std::string list;
     142          24 :   for (const auto & split : splits)
     143          23 :     list += (list.empty() ? "" : ", ") + split;
     144          11 :   return list;
     145          10 : }
     146             : 
     147         912 : void make_dir(const std::string & input_name, libMesh::processor_id_type n_procs)
     148             : {
     149         912 :   auto ret = libMesh::Utility::mkdir(input_name.c_str());
     150             :   // error only if we failed to create dir - don't care if it was already there
     151         912 :   libmesh_error_msg_if
     152             :     (ret != 0 && ret != -1,
     153             :      "Failed to create mesh split directory '" << input_name << "': " << std::strerror(ret));
     154             : 
     155         941 :   auto dir_name = split_dir(input_name, n_procs);
     156         912 :   ret = libMesh::Utility::mkdir(dir_name.c_str());
     157         912 :   if (ret == -1)
     158             :     libmesh_warning("In CheckpointIO::write, directory '"
     159             :                     << dir_name << "' already exists, overwriting contents.");
     160             :   else
     161         118 :     libmesh_error_msg_if
     162             :       (ret != 0, "Failed to create mesh split directory '" << dir_name << "': " << std::strerror(ret));
     163         912 : }
     164             : 
     165             : } // namespace
     166             : 
     167             : namespace libMesh
     168             : {
     169             : 
     170           0 : std::unique_ptr<CheckpointIO> split_mesh(MeshBase & mesh, processor_id_type nsplits)
     171             : {
     172             :   // There is currently an issue with DofObjects not being properly
     173             :   // reset if the mesh is not first repartitioned onto 1 processor
     174             :   // *before* being repartitioned onto the desired number of
     175             :   // processors. So, this is a workaround, but not a particularly
     176             :   // onerous one.
     177           0 :   mesh.partition(1);
     178           0 :   mesh.partition(nsplits);
     179             : 
     180           0 :   processor_id_type my_num_chunks = 0;
     181           0 :   processor_id_type my_first_chunk = 0;
     182           0 :   chunking(mesh.comm().size(), mesh.comm().rank(), nsplits, my_num_chunks, my_first_chunk);
     183             : 
     184           0 :   auto cpr = std::make_unique<CheckpointIO>(mesh);
     185           0 :   cpr->current_processor_ids().clear();
     186           0 :   for (processor_id_type i = my_first_chunk; i < my_first_chunk + my_num_chunks; i++)
     187           0 :     cpr->current_processor_ids().push_back(i);
     188           0 :   cpr->current_n_processors() = nsplits;
     189           0 :   cpr->parallel() = true;
     190           0 :   return cpr;
     191           0 : }
     192             : 
     193             : 
     194             : // ------------------------------------------------------------
     195             : // CheckpointIO members
     196        1682 : CheckpointIO::CheckpointIO (MeshBase & mesh, const bool binary_in) :
     197             :   MeshInput<MeshBase> (mesh,/* is_parallel_format = */ true),
     198             :   MeshOutput<MeshBase>(mesh,/* is_parallel_format = */ true),
     199             :   ParallelObject      (mesh),
     200        1574 :   _binary             (binary_in),
     201        1574 :   _parallel           (false),
     202           0 :   _version            ("checkpoint-1.7"),
     203        1790 :   _my_processor_ids   (1, processor_id()),
     204        3310 :   _my_n_processors    (mesh.is_replicated() ? 1 : n_processors())
     205             : {
     206        1682 : }
     207             : 
     208         142 : CheckpointIO::CheckpointIO (const MeshBase & mesh, const bool binary_in) :
     209             :   MeshInput<MeshBase> (), // write-only
     210             :   MeshOutput<MeshBase>(mesh,/* is_parallel_format = */ true),
     211             :   ParallelObject      (mesh),
     212         134 :   _binary             (binary_in),
     213         134 :   _parallel           (false),
     214           0 :   _version            ("checkpoint-1.7"),
     215         150 :   _my_processor_ids   (1, processor_id()),
     216         280 :   _my_n_processors    (mesh.is_replicated() ? 1 : n_processors())
     217             : {
     218         142 : }
     219             : 
     220        1708 : CheckpointIO::~CheckpointIO () = default;
     221             : 
     222         912 : processor_id_type CheckpointIO::select_split_config(const std::string & input_name, header_id_type & data_size)
     223             : {
     224          58 :   std::string header_name;
     225             : 
     226             :   // We'll read a header file from processor 0 and broadcast.
     227         941 :   if (this->processor_id() == 0)
     228             :     {
     229         391 :       header_name = header_file(input_name, _my_n_processors);
     230             : 
     231             :       {
     232             :         // look for header+splits with nprocs equal to _my_n_processors
     233         238 :         std::ifstream in (header_name.c_str());
     234         204 :         if (!in.good())
     235             :           {
     236             :             // otherwise fall back to a serial/single-split mesh
     237          12 :             auto orig_header_name = header_name;
     238          33 :             header_name = header_file(input_name, 1);
     239          14 :             std::ifstream in2 (header_name.c_str());
     240          13 :             const auto splits = existing_splits(input_name);
     241         110 :             libmesh_error_msg_if(!in2.good(),
     242             :                                  "ERROR: Neither one of the following files can be located:\n\t'"
     243             :                                  << orig_header_name << "' nor\n\t'" << input_name << "'\n"
     244             :                                  << "If you are running a parallel job, double check that you've "
     245             :                                  << "created a split for " << _my_n_processors << " ranks.\n"
     246             :                                  << "Note: One of paths above may refer to a valid directory on your "
     247             :                                  << "system, however we are attempting to read a valid header file.\n"
     248             :                                  << (splits.empty()
     249             :                                          ? "No processor-count splits were found in '" + input_name + "'."
     250             :                                          : "Splits that do exist in '" + input_name +
     251             :                                                "': " + splits + "."));
     252          10 :           }
     253         170 :       }
     254             : 
     255         507 :       Xdr io (header_name, this->binary() ? DECODE : READ);
     256             : 
     257             :       // read the version, but don't care about it
     258          16 :       std::string input_version;
     259         192 :       io.data(input_version);
     260             : 
     261             :       // read the data type
     262         192 :       io.data (data_size);
     263         160 :     }
     264             : 
     265         900 :   this->comm().broadcast(data_size);
     266         900 :   this->comm().broadcast(header_name);
     267             : 
     268             :   // How many per-processor files are here?
     269             :   largest_id_type input_n_procs;
     270             : 
     271         900 :   switch (data_size) {
     272           0 :   case 2:
     273           0 :     input_n_procs = this->read_header<uint16_t>(header_name);
     274           0 :     break;
     275           0 :   case 4:
     276           0 :     input_n_procs = this->read_header<uint32_t>(header_name);
     277           0 :     break;
     278         900 :   case 8:
     279         900 :     input_n_procs = this->read_header<uint64_t>(header_name);
     280          28 :     break;
     281           0 :   default:
     282           0 :     libmesh_error();
     283             :   }
     284             : 
     285          56 :   if (!input_n_procs)
     286          12 :     input_n_procs = 1;
     287         928 :   return cast_int<processor_id_type>(input_n_procs);
     288             : }
     289             : 
     290          84 : void CheckpointIO::cleanup(const std::string & input_name, processor_id_type n_procs)
     291             : {
     292          91 :   auto header = header_file(input_name, n_procs);
     293          84 :   auto ret = std::remove(header.c_str());
     294             :   if (ret != 0)
     295             :     libmesh_warning("Failed to clean up checkpoint header '" << header << "': " << std::strerror(ret));
     296             : 
     297         292 :   for (processor_id_type i = 0; i < n_procs; i++)
     298             :     {
     299         216 :       auto split = split_file(input_name, n_procs, i);
     300         208 :       ret = std::remove(split.c_str());
     301             :       if (ret != 0)
     302             :         libmesh_warning("Failed to clean up checkpoint split file '" << split << "': " << std::strerror(ret));
     303             :     }
     304             : 
     305          91 :   auto dir = split_dir(input_name, n_procs);
     306          84 :   ret = rmdir(dir.c_str());
     307             :   if (ret != 0)
     308             :     libmesh_warning("Failed to clean up checkpoint split dir '" << dir << "': " << std::strerror(ret));
     309             : 
     310             :   // We expect that this may fail if there are other split configurations still present in this
     311             :   // directory - so don't bother to check/warn for failure.
     312          84 :   rmdir(input_name.c_str());
     313          84 : }
     314             : 
     315             : 
     316        2444 : bool CheckpointIO::version_at_least_1_5() const
     317             : {
     318             :   return
     319        4755 :     (this->version().find("1.5") != std::string::npos) ||
     320        4888 :     (this->version().find("1.6") != std::string::npos) ||
     321        2577 :     (this->version().find("1.7") != std::string::npos);
     322             : }
     323             : 
     324             : 
     325        1024 : bool CheckpointIO::version_at_least_1_6() const
     326             : {
     327             :   return
     328        2048 :     (this->version().find("1.6") != std::string::npos) ||
     329        1074 :     (this->version().find("1.7") != std::string::npos);
     330             : }
     331             : 
     332             : 
     333        1296 : bool CheckpointIO::version_at_least_1_7() const
     334             : {
     335        1296 :   return (this->version().find("1.7") != std::string::npos);
     336             : }
     337             : 
     338             : 
     339         912 : void CheckpointIO::write (const std::string & name)
     340             : {
     341          87 :   LOG_SCOPE("write()", "CheckpointIO");
     342             : 
     343             :   // convenient reference to our mesh
     344          58 :   const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
     345             : 
     346             :   // FIXME: For backwards compatibility, we'll assume for now that we
     347             :   // only want to write distributed meshes in parallel.  Later we can
     348             :   // do a gather_to_zero() and support that case too.
     349         912 :   _parallel = _parallel || !mesh.is_serial();
     350             : 
     351          29 :   processor_id_type use_n_procs = 1;
     352         912 :   if (_parallel)
     353         868 :     use_n_procs = _my_n_processors;
     354             : 
     355         941 :   std::string header_file_name = header_file(name, use_n_procs);
     356         912 :   make_dir(name, use_n_procs);
     357             : 
     358             :   // We'll write a header file from processor 0 to make it easier to do unambiguous
     359             :   // restarts later:
     360         941 :   if (this->processor_id() == 0)
     361             :     {
     362         532 :       Xdr io (header_file_name, this->binary() ? ENCODE : WRITE);
     363             : 
     364             :       // write the version
     365         204 :       io.data(_version, "# version");
     366             : 
     367             :       // write what kind of data type we're using
     368         204 :       header_id_type data_size = sizeof(largest_id_type);
     369         204 :       io.data(data_size, "# integer size");
     370             : 
     371             :       // Write out the max mesh dimension for backwards compatibility
     372             :       // with code that sets it independently of element dimensions
     373             :       {
     374         204 :         uint16_t mesh_dimension = cast_int<uint16_t>(mesh.mesh_dimension());
     375         204 :         io.data(mesh_dimension, "# dimensions");
     376             :       }
     377             : 
     378             :       // Write out whether or not this is serial output
     379             :       {
     380         204 :         uint16_t parallel = _parallel;
     381         204 :         io.data(parallel, "# parallel");
     382             :       }
     383             : 
     384             :       // If we're writing out a parallel mesh then we need to write the number of processors
     385             :       // so we can check it upon reading the file
     386         204 :       if (_parallel)
     387             :         {
     388         172 :           largest_id_type n_procs = _my_n_processors;
     389         172 :           io.data(n_procs, "# n_procs");
     390             :         }
     391             : 
     392             :       // write subdomain names
     393         204 :       this->write_subdomain_names(io);
     394             : 
     395             :       // write boundary id names
     396          17 :       const BoundaryInfo & boundary_info = mesh.get_boundary_info();
     397         204 :       write_bc_names(io, boundary_info, true);  // sideset names
     398         204 :       write_bc_names(io, boundary_info, false); // nodeset names
     399             : 
     400             :       // write extra integer names
     401         204 :       const bool write_extra_integers = this->version_at_least_1_5();
     402             : 
     403         204 :       if (write_extra_integers)
     404             :         {
     405         204 :           largest_id_type n_node_integers = mesh.n_node_integers();
     406         204 :           io.data(n_node_integers, "# n_extra_integers per node");
     407             : 
     408          51 :           std::vector<std::string> node_integer_names;
     409         312 :           for (unsigned int i=0; i != n_node_integers; ++i)
     410         108 :             node_integer_names.push_back(mesh.get_node_integer_name(i));
     411             : 
     412         204 :           io.data(node_integer_names);
     413             : 
     414         204 :           largest_id_type n_elem_integers = mesh.n_elem_integers();
     415         204 :           io.data(n_elem_integers, "# n_extra_integers per elem");
     416             : 
     417          34 :           std::vector<std::string> elem_integer_names;
     418         258 :           for (unsigned int i=0; i != n_elem_integers; ++i)
     419          54 :             elem_integer_names.push_back(mesh.get_elem_integer_name(i));
     420             : 
     421         204 :           io.data(elem_integer_names);
     422         170 :         }
     423             : 
     424         204 :       if (this->version_at_least_1_7())
     425         204 :         this->write_disjoint_neighbor_boundary_pairs(io);
     426         170 :     }
     427             : 
     428             :   // If this is a serial mesh written to a serial file then we're only
     429             :   // going to write local data from processor 0.  If this is a mesh being
     430             :   // written in parallel then we're going to write from every
     431             :   // processor.
     432          58 :   std::vector<processor_id_type> ids_to_write;
     433             : 
     434             :   // We're going to sort elements by pid in one pass, to avoid sending
     435             :   // predicated iterators through the whole mesh N_p times.
     436             :   //
     437             :   // The data type here needs to be a non-const-pointer to whatever
     438             :   // our element_iterator is a const-pointer to, for compatibility
     439             :   // later.
     440             :   typedef std::remove_const<MeshBase::const_element_iterator::value_type>::type nc_v_t;
     441          58 :   std::unordered_map<processor_id_type, std::vector<nc_v_t>> elements_on_pid;
     442             : 
     443         912 :   if (_parallel)
     444             :     {
     445         868 :       ids_to_write = _my_processor_ids;
     446        1360 :       for (processor_id_type p : ids_to_write)
     447          18 :         elements_on_pid[p].clear();
     448          17 :       auto eop_end = elements_on_pid.end();
     449       13863 :       for (auto & elem : mesh.element_ptr_range())
     450             :         {
     451        6300 :           const processor_id_type p = elem->processor_id();
     452        6528 :           if (auto eop_it = elements_on_pid.find(p);
     453         228 :               eop_it != eop_end)
     454        1752 :             eop_it->second.push_back(elem);
     455         834 :         }
     456             :     }
     457          44 :   else if (mesh.is_serial())
     458             :     {
     459          56 :       if (mesh.processor_id() == 0)
     460             :         {
     461             :           // placeholder
     462          32 :           ids_to_write.push_back(0);
     463             :         }
     464             :     }
     465             :   else
     466             :     {
     467           0 :       libmesh_error_msg("Cannot write serial checkpoint from distributed mesh");
     468             :     }
     469             : 
     470             :   // Call build_side_list() and build_node_list() just *once* to avoid
     471             :   // redundant expensive sorts during mesh splitting.
     472          29 :   const BoundaryInfo & boundary_info = mesh.get_boundary_info();
     473             :   std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>>
     474         941 :     bc_triples = boundary_info.build_side_list();
     475             :   std::vector<std::tuple<dof_id_type, boundary_id_type>>
     476         941 :     bc_tuples = boundary_info.build_node_list();
     477             : 
     478        1436 :   for (const auto & my_pid : ids_to_write)
     479             :     {
     480         550 :       auto file_name = split_file(name, use_n_procs, my_pid);
     481        1346 :       Xdr io (file_name, this->binary() ? ENCODE : WRITE);
     482             : 
     483          52 :       std::set<const Elem *, CompareElemIdsByLevel> elements;
     484             : 
     485             :       // For serial files or for already-distributed meshs, we write
     486             :       // everything we can see.
     487         524 :       if (!_parallel || !mesh.is_serial())
     488         800 :         elements.insert(mesh.elements_begin(), mesh.elements_end());
     489             :       // For parallel files written from serial meshes we write what
     490             :       // we'd be required to keep if we were to be deleting remote
     491             :       // elements.  This allows us to write proper parallel files even
     492             :       // from a ReplicateMesh.
     493             :       //
     494             :       // WARNING: If we have a DistributedMesh which used
     495             :       // "add_extra_ghost_elem" rather than ghosting functors to
     496             :       // preserve elements and which is *also* currently serialized
     497             :       // then we're not preserving those elements here.  As a quick
     498             :       // workaround user code should delete_remote_elements() before
     499             :       // writing the checkpoint; as a long term workaround user code
     500             :       // should use ghosting functors instead of extra_ghost_elem
     501             :       // lists.
     502             :       else
     503             :         {
     504         348 :           for (processor_id_type p : {my_pid, DofObject::invalid_processor_id})
     505             :             {
     506         232 :               if (const auto elements_vec_it = elements_on_pid.find(p);
     507          20 :                   elements_vec_it != elements_on_pid.end())
     508             :                 {
     509          10 :                   auto & p_elements = elements_vec_it->second;
     510             : 
     511             :                   // Be compatible with both deprecated and
     512             :                   // corrected MeshBase iterator types
     513             :                   typedef MeshBase::const_element_iterator::value_type v_t;
     514             : 
     515         116 :                   v_t * elempp = p_elements.data();
     516         116 :                   v_t * elemend = elempp + p_elements.size();
     517             : 
     518             :                   const MeshBase::const_element_iterator
     519             :                     pid_elements_begin = MeshBase::const_element_iterator
     520         126 :                       (elempp, elemend, Predicates::NotNull<v_t *>()),
     521             :                     pid_elements_end = MeshBase::const_element_iterator
     522         126 :                       (elemend, elemend, Predicates::NotNull<v_t *>()),
     523             :                     active_pid_elements_begin = MeshBase::const_element_iterator
     524         126 :                       (elempp, elemend, Predicates::Active<v_t *>()),
     525             :                     active_pid_elements_end = MeshBase::const_element_iterator
     526         222 :                       (elemend, elemend, Predicates::Active<v_t *>());
     527             : 
     528             :                   query_ghosting_functors
     529         222 :                     (mesh, p, active_pid_elements_begin,
     530             :                      active_pid_elements_end, elements);
     531         222 :                   connect_children(mesh, pid_elements_begin,
     532             :                                    pid_elements_end, elements);
     533             :                 }
     534             :             }
     535             :         }
     536             : 
     537          52 :       connected_node_set_type connected_nodes;
     538         524 :       connect_element_dependencies(mesh, elements, connected_nodes);
     539             : 
     540             :       // write the nodal locations
     541         524 :       this->write_nodes (io, connected_nodes);
     542             : 
     543             :       // write connectivity
     544         524 :       this->write_connectivity (io, elements);
     545             : 
     546             :       // write remote_elem connectivity
     547         524 :       this->write_remote_elem (io, elements);
     548             : 
     549             :       // write the boundary condition information
     550         524 :       this->write_bcs (io, elements, bc_triples);
     551             : 
     552             :       // write the nodeset information
     553         524 :       this->write_nodesets (io, connected_nodes, bc_tuples);
     554             : 
     555             :       // close it up
     556         524 :       io.close();
     557         472 :     }
     558             : 
     559             :   // this->comm().barrier();
     560         912 : }
     561             : 
     562         204 : void CheckpointIO::write_subdomain_names(Xdr & io) const
     563             : {
     564             :   {
     565          34 :     const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
     566             : 
     567          17 :     const std::map<subdomain_id_type, std::string> & subdomain_map = mesh.get_subdomain_name_map();
     568             : 
     569         221 :     std::vector<largest_id_type> subdomain_ids;   subdomain_ids.reserve(subdomain_map.size());
     570         238 :     std::vector<std::string>  subdomain_names; subdomain_names.reserve(subdomain_map.size());
     571             : 
     572             :     // We need to loop over the map and make sure that there aren't any invalid entries.  Since we
     573             :     // return writable references in mesh_base, it's possible for the user to leave some entity names
     574             :     // blank.  We can't write those to the XDA file.
     575         204 :     largest_id_type n_subdomain_names = 0;
     576         204 :     for (const auto & [id, name] : subdomain_map)
     577           0 :       if (!name.empty())
     578             :         {
     579           0 :           n_subdomain_names++;
     580           0 :           subdomain_ids.push_back(id);
     581           0 :           subdomain_names.push_back(name);
     582             :         }
     583             : 
     584         204 :     io.data(n_subdomain_names, "# subdomain id to name map");
     585             :     // Write out the ids and names in two vectors
     586         204 :     if (n_subdomain_names)
     587             :       {
     588           0 :         io.data(subdomain_ids);
     589           0 :         io.data(subdomain_names);
     590             :       }
     591         170 :   }
     592         204 : }
     593             : 
     594             : 
     595             : 
     596         524 : void CheckpointIO::write_nodes (Xdr & io,
     597             :                                 const connected_node_set_type & nodeset) const
     598             : {
     599         524 :   largest_id_type n_nodes_here = nodeset.size();
     600             : 
     601         524 :   io.data(n_nodes_here, "# n_nodes on proc");
     602             : 
     603         524 :   const bool write_extra_integers = this->version_at_least_1_5();
     604             :   const unsigned int n_extra_integers =
     605         524 :     write_extra_integers ? MeshOutput<MeshBase>::mesh().n_node_integers() : 0;
     606             : 
     607             :   // Will hold the node id and pid and extra integers
     608         550 :   std::vector<largest_id_type> id_pid(2 + n_extra_integers);
     609             : 
     610             :   // For the coordinates
     611         550 :   std::vector<Real> coords(LIBMESH_DIM);
     612             : 
     613        6814 :   for (const auto & node : nodeset)
     614             :     {
     615        6290 :       id_pid[0] = node->id();
     616        6290 :       id_pid[1] = node->processor_id();
     617             : 
     618         428 :       libmesh_assert_equal_to(n_extra_integers, node->n_extra_integers());
     619       13182 :       for (unsigned int i=0; i != n_extra_integers; ++i)
     620        7348 :         id_pid[2+i] = node->get_extra_integer(i);
     621             : 
     622        6290 :       io.data_stream(id_pid.data(), 2 + n_extra_integers, 2 + n_extra_integers);
     623             : 
     624             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
     625        6290 :       largest_id_type unique_id = node->unique_id();
     626             : 
     627        6290 :       io.data(unique_id, "# unique id");
     628             : #endif
     629             : 
     630        6290 :       coords[0] = (*node)(0);
     631             : 
     632             : #if LIBMESH_DIM > 1
     633        6290 :       coords[1] = (*node)(1);
     634             : #endif
     635             : 
     636             : #if LIBMESH_DIM > 2
     637        6290 :       coords[2] = (*node)(2);
     638             : #endif
     639             : 
     640        6290 :       io.data_stream(coords.data(), LIBMESH_DIM, 3);
     641             :     }
     642         524 : }
     643             : 
     644             : 
     645             : 
     646         524 : void CheckpointIO::write_connectivity (Xdr & io,
     647             :                                        const std::set<const Elem *, CompareElemIdsByLevel> & elements) const
     648             : {
     649          26 :   libmesh_assert (io.writing());
     650             : 
     651         524 :   const bool write_extra_integers = this->version_at_least_1_5();
     652         524 :   const bool write_runtime_topology = this->version_at_least_1_6();
     653             :   const unsigned int n_extra_integers =
     654         524 :     write_extra_integers ? MeshOutput<MeshBase>::mesh().n_elem_integers() : 0;
     655             : 
     656             :   // Put these out here to reduce memory churn
     657             :   // id type pid subdomain_id parent_id extra_integer_0 ...
     658         550 :   std::vector<largest_id_type> elem_data(6 + n_extra_integers);
     659          52 :   std::vector<largest_id_type> conn_data;
     660          52 :   std::vector<largest_id_type> runtime_topology;
     661             : 
     662         524 :   largest_id_type n_elems_here = elements.size();
     663             : 
     664         524 :   io.data(n_elems_here, "# number of elements");
     665             : 
     666        3488 :   for (const auto & elem : elements)
     667             :     {
     668        2964 :       unsigned int n_nodes = elem->n_nodes();
     669             : 
     670        2964 :       elem_data[0] = elem->id();
     671        2964 :       elem_data[1] = elem->type();
     672        2964 :       elem_data[2] = elem->processor_id();
     673        2964 :       elem_data[3] = elem->subdomain_id();
     674             : 
     675             : #ifdef LIBMESH_ENABLE_AMR
     676        3185 :       if (elem->parent() != nullptr)
     677             :         {
     678           0 :           elem_data[4] = elem->parent()->id();
     679           0 :           elem_data[5] = elem->parent()->which_child_am_i(elem);
     680             :         }
     681             :       else
     682             : #endif
     683             :         {
     684        2964 :           elem_data[4] = static_cast<largest_id_type>(-1);
     685        2964 :           elem_data[5] = static_cast<largest_id_type>(-1);
     686             :         }
     687             : 
     688        4059 :       for (unsigned int i=0; i != n_extra_integers; ++i)
     689        1173 :         elem_data[6+i] = elem->get_extra_integer(i);
     690             : 
     691        2964 :       conn_data.resize(n_nodes);
     692             : 
     693       16612 :       for (unsigned int i=0; i<n_nodes; i++)
     694       15510 :         conn_data[i] = elem->node_id(i);
     695             : 
     696        3185 :       io.data_stream(elem_data.data(),
     697             :                      cast_int<unsigned int>(elem_data.size()),
     698             :                      cast_int<unsigned int>(elem_data.size()));
     699             : 
     700             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
     701        2964 :       largest_id_type unique_id = elem->unique_id();
     702             : 
     703        2964 :       io.data(unique_id, "# unique id");
     704             : #endif
     705             : 
     706             : #ifdef LIBMESH_ENABLE_AMR
     707        3185 :       uint16_t p_level = cast_int<uint16_t>(elem->p_level());
     708        2964 :       io.data(p_level, "# p_level");
     709             : 
     710        2964 :       uint16_t rflag = elem->refinement_flag();
     711        2964 :       io.data(rflag, "# rflag");
     712             : 
     713        2964 :       uint16_t pflag = elem->p_refinement_flag();
     714        2964 :       io.data(pflag, "# pflag");
     715             : #endif
     716             : 
     717        2964 :       if (elem->runtime_topology())
     718             :         {
     719          48 :           libmesh_error_msg_if
     720             :             (!write_runtime_topology,
     721             :              "Checkpoint format 1.6 or newer is required to write " <<
     722             :              Utility::enum_to_string(elem->type()) << " elements.");
     723             : 
     724           4 :           runtime_topology.clear();
     725          48 :           runtime_topology.push_back(n_nodes);
     726          48 :           runtime_topology.push_back(elem->n_sides());
     727         364 :           for (auto s : elem->side_index_range())
     728             :             {
     729         338 :               const auto side_nodes = elem->nodes_on_side(s);
     730         338 :               runtime_topology.push_back(side_nodes.size());
     731        1416 :               for (const auto n : side_nodes)
     732        1104 :                 runtime_topology.push_back(n);
     733             :             }
     734             : 
     735          48 :           io.data(runtime_topology, "# runtime topology");
     736             :         }
     737             : 
     738        3185 :       io.data_stream(conn_data.data(),
     739             :                      cast_int<unsigned int>(conn_data.size()),
     740             :                      cast_int<unsigned int>(conn_data.size()));
     741             :     }
     742         524 : }
     743             : 
     744             : 
     745         524 : void CheckpointIO::write_remote_elem (Xdr & io,
     746             :                                       const std::set<const Elem *, CompareElemIdsByLevel> & elements) const
     747             : {
     748          26 :   libmesh_assert (io.writing());
     749             : 
     750             :   // Find the remote_elem neighbor and child links
     751          52 :   std::vector<largest_id_type> elem_ids, parent_ids;
     752          52 :   std::vector<uint16_t> elem_sides, child_numbers;
     753             : 
     754        3488 :   for (const auto & elem : elements)
     755             :     {
     756       15769 :       for (auto n : elem->side_index_range())
     757             :         {
     758       12584 :           const Elem * neigh = elem->neighbor_ptr(n);
     759       13452 :           if (neigh == remote_elem ||
     760         868 :               (neigh && !elements.count(neigh)))
     761             :             {
     762         708 :               elem_ids.push_back(elem->id());
     763         708 :               elem_sides.push_back(n);
     764             :             }
     765             :         }
     766             : 
     767             : #ifdef LIBMESH_ENABLE_AMR
     768        2964 :       if (elem->has_children())
     769             :         {
     770           0 :           for (unsigned short c = 0,
     771           0 :                nc = cast_int<unsigned short>(elem->n_children());
     772           0 :                c != nc; ++c)
     773             :             {
     774           0 :               const Elem * child = elem->child_ptr(c);
     775           0 :               if (child == remote_elem ||
     776           0 :                   (child && !elements.count(child)))
     777             :                 {
     778           0 :                   parent_ids.push_back(elem->id());
     779           0 :                   child_numbers.push_back(c);
     780             :                 }
     781             :             }
     782             :         }
     783             : #endif
     784             :     }
     785             : 
     786         524 :   io.data(elem_ids, "# remote neighbor elem_ids");
     787         524 :   io.data(elem_sides, "# remote neighbor elem_sides");
     788         524 :   io.data(parent_ids, "# remote child parent_ids");
     789         524 :   io.data(child_numbers, "# remote child_numbers");
     790         524 : }
     791             : 
     792             : 
     793             : 
     794         524 : void CheckpointIO::write_bcs (Xdr & io,
     795             :                               const std::set<const Elem *, CompareElemIdsByLevel> & elements,
     796             :                               const std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>> & bc_triples) const
     797             : {
     798          26 :   libmesh_assert (io.writing());
     799             : 
     800             :   // Build a list of (elem, side, bc) tuples.
     801          52 :   std::size_t bc_size = bc_triples.size();
     802             : 
     803          52 :   std::vector<largest_id_type> element_id_list;
     804          52 :   std::vector<uint16_t> side_list;
     805          52 :   std::vector<largest_id_type> bc_id_list;
     806             : 
     807         524 :   element_id_list.reserve(bc_size);
     808         524 :   side_list.reserve(bc_size);
     809         524 :   bc_id_list.reserve(bc_size);
     810             : 
     811          26 :   std::unordered_set<dof_id_type> elems;
     812        3488 :   for (auto & e : elements)
     813        2964 :     elems.insert(e->id());
     814             : 
     815        4488 :   for (const auto & t : bc_triples)
     816         508 :     if (elems.count(std::get<0>(t)))
     817             :       {
     818        3436 :         element_id_list.push_back(std::get<0>(t));
     819        3436 :         side_list.push_back(std::get<1>(t));
     820        3436 :         bc_id_list.push_back(std::get<2>(t));
     821             :       }
     822             : 
     823             : 
     824         524 :   io.data(element_id_list, "# element ids for bcs");
     825         524 :   io.data(side_list, "# sides of elements for bcs");
     826         524 :   io.data(bc_id_list, "# bc ids");
     827         524 : }
     828             : 
     829             : 
     830             : 
     831         524 : void CheckpointIO::write_nodesets (Xdr & io,
     832             :                                    const connected_node_set_type & nodeset,
     833             :                                    const std::vector<std::tuple<dof_id_type, boundary_id_type>> & bc_tuples) const
     834             : {
     835          26 :   libmesh_assert (io.writing());
     836             : 
     837             :   // convenient reference to our mesh
     838          52 :   const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
     839             : 
     840             :   // Build a list of (node, bc) tuples
     841          52 :   std::size_t nodeset_size = bc_tuples.size();
     842             : 
     843          52 :   std::vector<largest_id_type> node_id_list;
     844          26 :   std::vector<largest_id_type> bc_id_list;
     845             : 
     846         524 :   node_id_list.reserve(nodeset_size);
     847         524 :   bc_id_list.reserve(nodeset_size);
     848             : 
     849        6712 :   for (const auto & t : bc_tuples)
     850        6244 :     if (nodeset.count(mesh.node_ptr(std::get<0>(t))))
     851             :       {
     852        5572 :         node_id_list.push_back(std::get<0>(t));
     853        5572 :         bc_id_list.push_back(std::get<1>(t));
     854             :       }
     855             : 
     856         524 :   io.data(node_id_list, "# node id list");
     857         524 :   io.data(bc_id_list, "# nodeset bc id list");
     858         524 : }
     859             : 
     860             : 
     861             : 
     862         408 : void CheckpointIO::write_bc_names (Xdr & io, const BoundaryInfo & info, bool is_sideset) const
     863             : {
     864         408 :   const std::map<boundary_id_type, std::string> & boundary_map = is_sideset ?
     865          34 :     info.get_sideset_name_map() : info.get_nodeset_name_map();
     866             : 
     867         442 :   std::vector<largest_id_type> boundary_ids;   boundary_ids.reserve(boundary_map.size());
     868         476 :   std::vector<std::string>  boundary_names; boundary_names.reserve(boundary_map.size());
     869             : 
     870             :   // We need to loop over the map and make sure that there aren't any invalid entries.  Since we
     871             :   // return writable references in boundary_info, it's possible for the user to leave some entity names
     872             :   // blank.  We can't write those to the XDA file.
     873         408 :   largest_id_type n_boundary_names = 0;
     874        1464 :   for (const auto & [id, name] : boundary_map)
     875        1056 :     if (!name.empty())
     876             :       {
     877        1056 :         n_boundary_names++;
     878        1056 :         boundary_ids.push_back(id);
     879        1056 :         boundary_names.push_back(name);
     880             :       }
     881             : 
     882         408 :   if (is_sideset)
     883         204 :     io.data(n_boundary_names, "# sideset id to name map");
     884             :   else
     885         204 :     io.data(n_boundary_names, "# nodeset id to name map");
     886             :   // Write out the ids and names in two vectors
     887         408 :   if (n_boundary_names)
     888             :     {
     889         264 :       io.data(boundary_ids);
     890         264 :       io.data(boundary_names);
     891             :     }
     892         748 : }
     893             : 
     894         204 : void CheckpointIO::write_disjoint_neighbor_boundary_pairs (Xdr & io) const
     895             : {
     896          34 :   std::vector<largest_id_type> pair_ids;
     897          34 :   std::vector<Real> translations;
     898             : 
     899             : #ifdef LIBMESH_ENABLE_PERIODIC
     900          34 :   const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
     901             : 
     902             :   // MeshBase stores each pair once per direction; write each pair once,
     903             :   // from its smaller boundary id, and re-register both directions on read.
     904         204 :   if (const PeriodicBoundaries * db = mesh.get_disjoint_neighbor_boundary_pairs())
     905          72 :     for (const auto & [id, pb] : *db)
     906             :       {
     907          52 :         if (id > pb->pairedboundary)
     908          24 :           continue;
     909             : 
     910             :         // MeshBase only registers translations, which we can recover
     911             :         // as the image of the origin.
     912          24 :         libmesh_error_msg_if(!dynamic_cast<const PeriodicBoundary *>(pb.get()) ||
     913             :                              pb->has_transformation_matrix(),
     914             :                              "CheckpointIO can only write translational disjoint neighbor "
     915             :                              "boundary pairs");
     916             : 
     917          24 :         pair_ids.push_back(id);
     918          24 :         pair_ids.push_back(pb->pairedboundary);
     919             : 
     920          26 :         const Point translation = pb->get_corresponding_pos(Point(0));
     921          96 :         for (auto d : make_range(LIBMESH_DIM))
     922          78 :           translations.push_back(translation(d));
     923             :       }
     924             : #endif
     925             : 
     926         221 :   largest_id_type n_pairs = pair_ids.size() / 2;
     927         204 :   io.data(n_pairs, "# disjoint neighbor boundary pairs");
     928         204 :   if (n_pairs)
     929             :     {
     930          24 :       io.data(pair_ids);
     931             :       // data_stream, unlike the generic vector path, handles extended
     932             :       // precision Real
     933          26 :       io.data_stream(translations.data(),
     934             :                      cast_int<unsigned int>(translations.size()),
     935             :                      LIBMESH_DIM);
     936             :     }
     937         204 : }
     938             : 
     939         912 : void CheckpointIO::read (const std::string & input_name)
     940             : {
     941          87 :   LOG_SCOPE("read()","CheckpointIO");
     942             : 
     943         912 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
     944             : 
     945          29 :   libmesh_assert(!mesh.n_elem());
     946             : 
     947             :   header_id_type data_size;
     948         912 :   processor_id_type input_n_procs = select_split_config(input_name, data_size);
     949         928 :   auto header_name = header_file(input_name, input_n_procs);
     950          28 :   bool input_parallel = input_n_procs > 0;
     951             : 
     952             :   // If this is a serial read then we're going to only read the mesh
     953             :   // on processor 0, then broadcast it
     954         900 :   if ((input_parallel && !mesh.is_replicated()) || mesh.processor_id() == 0)
     955             :     {
     956             :       // If we're trying to read a parallel checkpoint file on a
     957             :       // replicated mesh, we'll read every file on processor 0 so we
     958             :       // can broadcast it later.  If we're on a distributed mesh then
     959             :       // we'll read every id to it's own processor and we'll "wrap
     960             :       // around" with any ids that exceed our processor count.
     961             :       const processor_id_type begin_proc_id =
     962         652 :         (input_parallel && !mesh.is_replicated()) ?
     963          16 :         mesh.processor_id() : 0;
     964             :       const processor_id_type stride =
     965         652 :         (input_parallel && !mesh.is_replicated()) ?
     966          16 :         mesh.n_processors() : 1;
     967             : 
     968        1152 :       for (processor_id_type proc_id = begin_proc_id; proc_id < input_n_procs;
     969         500 :            proc_id = cast_int<processor_id_type>(proc_id + stride))
     970             :         {
     971         524 :           auto file_name = split_file(input_name, input_n_procs, proc_id);
     972             : 
     973             :           {
     974         548 :             std::ifstream in (file_name.c_str());
     975             : 
     976         500 :             libmesh_error_msg_if(!in.good(), "ERROR: cannot locate specified file:\n\t" << file_name);
     977         452 :           }
     978             : 
     979             :           // Do we expect all our files' remote_elem entries to really
     980             :           // be remote?  Only if we're not reading multiple input
     981             :           // files on the same processor.
     982             :           const bool expect_all_remote =
     983        1016 :             (input_n_procs <= mesh.n_processors() &&
     984         492 :              !mesh.is_replicated());
     985             : 
     986         798 :           Xdr io (file_name, this->binary() ? DECODE : READ);
     987             : 
     988         500 :           switch (data_size) {
     989           0 :           case 2:
     990           0 :             this->read_subfile<uint16_t>(io, expect_all_remote);
     991           0 :             break;
     992           0 :           case 4:
     993           0 :             this->read_subfile<uint32_t>(io, expect_all_remote);
     994           0 :             break;
     995         500 :           case 8:
     996         500 :             this->read_subfile<uint64_t>(io, expect_all_remote);
     997          24 :             break;
     998           0 :           default:
     999           0 :             libmesh_error();
    1000             :           }
    1001             : 
    1002         500 :           io.close();
    1003         452 :         }
    1004             :     }
    1005             : 
    1006             :   // If the mesh was only read on processor 0 then we need to broadcast it
    1007         900 :   if (mesh.is_replicated())
    1008         328 :     MeshCommunication().broadcast(mesh);
    1009             :   // If the mesh is really distributed then we need to make sure it
    1010             :   // knows that
    1011         580 :   else if (mesh.n_processors() > 1)
    1012         528 :     mesh.set_distributed();
    1013             : 
    1014             :   // If the mesh isn't getting even critical partitioning then we
    1015             :   // should update cached data from the partitioning we just read in
    1016         900 :   if (mesh.skip_partitioning())
    1017             :     {
    1018           0 :       mesh.recalculate_n_partitions();
    1019           0 :       mesh.update_post_partitioning();
    1020             :     }
    1021         900 : }
    1022             : 
    1023             : 
    1024             : 
    1025             : template <typename file_id_type>
    1026         900 : file_id_type CheckpointIO::read_header (const std::string & name)
    1027             : {
    1028         900 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
    1029             : 
    1030             :   // Hack for codes which don't look at all elem dimensions
    1031             :   uint16_t mesh_dimension;
    1032             : 
    1033             :   // Will this be a parallel input file?  With how many processors?  Stay tuned!
    1034             :   uint16_t input_parallel;
    1035             :   file_id_type input_n_procs;
    1036             : 
    1037          56 :   std::string input_version;
    1038          84 :   std::vector<std::string> node_integer_names, elem_integer_names;
    1039          56 :   std::vector<file_id_type> disjoint_pair_ids;
    1040          28 :   std::vector<Real> disjoint_translations;
    1041             : 
    1042             :   // We'll write a header file from processor 0 and broadcast.
    1043         928 :   if (this->processor_id() == 0)
    1044             :     {
    1045         496 :       Xdr io (name, this->binary() ? DECODE : READ);
    1046             : 
    1047             :       // read the version
    1048         192 :       io.data(input_version);
    1049             : 
    1050             :       // read the data type, don't care about it this time
    1051             :       header_id_type data_size;
    1052         192 :       io.data (data_size);
    1053             : 
    1054             :       // read the dimension
    1055         192 :       io.data (mesh_dimension);
    1056             : 
    1057             :       // Read whether or not this is a parallel file
    1058         192 :       io.data(input_parallel);
    1059             : 
    1060             :       // With how many processors?
    1061         192 :       if (input_parallel)
    1062         160 :         io.data(input_n_procs);
    1063             : 
    1064             :       // read subdomain names
    1065         192 :       this->read_subdomain_names<file_id_type>(io);
    1066             : 
    1067             :       // read boundary names
    1068          16 :       BoundaryInfo & boundary_info = mesh.get_boundary_info();
    1069             : 
    1070         192 :       this->read_bc_names<file_id_type>(io, boundary_info, true);  // sideset names
    1071         192 :       this->read_bc_names<file_id_type>(io, boundary_info, false); // nodeset names
    1072             : 
    1073             :       // read extra integer names?
    1074          16 :       std::swap(input_version, this->version());
    1075         192 :       const bool read_extra_integers = this->version_at_least_1_5();
    1076          16 :       std::swap(input_version, this->version());
    1077             : 
    1078         192 :       if (read_extra_integers)
    1079          32 :         this->read_integers_names<file_id_type>
    1080         160 :           (io, node_integer_names, elem_integer_names);
    1081             : 
    1082          16 :       std::swap(input_version, this->version());
    1083         192 :       const bool read_disjoint_pairs = this->version_at_least_1_7();
    1084          16 :       std::swap(input_version, this->version());
    1085             : 
    1086         192 :       if (read_disjoint_pairs)
    1087          32 :         this->read_disjoint_neighbor_boundary_pairs<file_id_type>
    1088         160 :           (io, disjoint_pair_ids, disjoint_translations);
    1089         160 :     }
    1090             : 
    1091             :   // broadcast data from processor 0, set values everywhere
    1092         900 :   this->comm().broadcast(input_version);
    1093          28 :   this->version() = input_version;
    1094             : 
    1095         900 :   this->comm().broadcast(mesh_dimension);
    1096        1772 :   mesh.set_mesh_dimension(cast_int<unsigned char>(mesh_dimension));
    1097             : 
    1098         900 :   this->comm().broadcast(input_parallel);
    1099             : 
    1100         900 :   if (input_parallel)
    1101         856 :     this->comm().broadcast(input_n_procs);
    1102             :   else
    1103          44 :     input_n_procs = 1;
    1104             : 
    1105             :   std::map<subdomain_id_type, std::string> & subdomain_map =
    1106          28 :     mesh.set_subdomain_name_map();
    1107         900 :   this->comm().broadcast(subdomain_map);
    1108             : 
    1109          28 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
    1110         900 :   this->comm().broadcast(boundary_info.set_sideset_name_map());
    1111         900 :   this->comm().broadcast(boundary_info.set_nodeset_name_map());
    1112             : 
    1113         900 :   this->comm().broadcast(node_integer_names);
    1114         900 :   this->comm().broadcast(elem_integer_names);
    1115             : 
    1116        1488 :   for (auto & int_name : node_integer_names)
    1117        1152 :     mesh.add_node_integer(int_name);
    1118             : 
    1119        1194 :   for (auto & int_name : elem_integer_names)
    1120         576 :     mesh.add_elem_integer(int_name);
    1121             : 
    1122         900 :   this->comm().broadcast(disjoint_pair_ids);
    1123         900 :   this->comm().broadcast(disjoint_translations);
    1124             : 
    1125             : #ifdef LIBMESH_ENABLE_PERIODIC
    1126             :   // The file is the complete record of the pairs, so it replaces any
    1127             :   // pairs left on a reused mesh rather than merging with them
    1128         900 :   if (this->version_at_least_1_7())
    1129         900 :     if (PeriodicBoundaries * db = mesh.get_disjoint_neighbor_boundary_pairs())
    1130           4 :       db->clear();
    1131             : 
    1132        1070 :   for (auto i : make_range(disjoint_pair_ids.size() / 2))
    1133             :     {
    1134           4 :       RealVectorValue translation;
    1135         568 :       for (auto d : make_range(LIBMESH_DIM))
    1136         438 :         translation(d) = disjoint_translations[LIBMESH_DIM * i + d];
    1137          16 :       mesh.add_disjoint_neighbor_boundary_pairs
    1138         142 :         (cast_int<boundary_id_type>(disjoint_pair_ids[2 * i]),
    1139         146 :          cast_int<boundary_id_type>(disjoint_pair_ids[2 * i + 1]),
    1140             :          translation);
    1141             :     }
    1142             : #else
    1143             :   libmesh_error_msg_if(!disjoint_pair_ids.empty(),
    1144             :                        "Reading disjoint neighbor boundary pairs requires --enable-periodic");
    1145             : #endif
    1146             : 
    1147        1800 :   return input_parallel ? input_n_procs : 0;
    1148         844 : }
    1149             : 
    1150             : 
    1151             : 
    1152             : template <typename file_id_type>
    1153         500 : void CheckpointIO::read_subfile (Xdr & io, bool expect_all_remote)
    1154             : {
    1155             :   // read the nodal locations
    1156         500 :   this->read_nodes<file_id_type> (io);
    1157             : 
    1158             :   // read connectivity
    1159         500 :   this->read_connectivity<file_id_type> (io);
    1160             : 
    1161             :   // read remote_elem connectivity
    1162         500 :   this->read_remote_elem<file_id_type> (io, expect_all_remote);
    1163             : 
    1164             :   // read the boundary conditions
    1165         500 :   this->read_bcs<file_id_type> (io);
    1166             : 
    1167             :   // read the nodesets
    1168         500 :   this->read_nodesets<file_id_type> (io);
    1169         500 : }
    1170             : 
    1171             : 
    1172             : 
    1173             : template <typename file_id_type>
    1174         192 : void CheckpointIO::read_subdomain_names(Xdr & io)
    1175             : {
    1176         192 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
    1177             : 
    1178             :   std::map<subdomain_id_type, std::string> & subdomain_map =
    1179          16 :     mesh.set_subdomain_name_map();
    1180             : 
    1181          32 :   std::vector<file_id_type> subdomain_ids;
    1182         192 :   subdomain_ids.reserve(subdomain_map.size());
    1183             : 
    1184          48 :   std::vector<std::string>  subdomain_names;
    1185         192 :   subdomain_names.reserve(subdomain_map.size());
    1186             : 
    1187         192 :   file_id_type n_subdomain_names = 0;
    1188         192 :   io.data(n_subdomain_names, "# subdomain id to name map");
    1189             : 
    1190         192 :   if (n_subdomain_names)
    1191             :     {
    1192           0 :       io.data(subdomain_ids);
    1193           0 :       io.data(subdomain_names);
    1194             : 
    1195           0 :       for (auto i : index_range(subdomain_ids))
    1196           0 :         subdomain_map[cast_int<subdomain_id_type>(subdomain_ids[i])] =
    1197           0 :           subdomain_names[i];
    1198             :     }
    1199         352 : }
    1200             : 
    1201             : 
    1202             : 
    1203             : template <typename file_id_type>
    1204         500 : void CheckpointIO::read_nodes (Xdr & io)
    1205             : {
    1206             :   // convenient reference to our mesh
    1207         500 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
    1208             : 
    1209             :   file_id_type n_nodes_here;
    1210         500 :   io.data(n_nodes_here, "# n_nodes on proc");
    1211             : 
    1212         500 :   const bool read_extra_integers = this->version_at_least_1_5();
    1213             : 
    1214          48 :   const unsigned int n_extra_integers =
    1215         476 :     read_extra_integers ? mesh.n_node_integers() : 0;
    1216             : 
    1217             :   // Will hold the node id and pid and extra integers
    1218         524 :   std::vector<file_id_type> id_pid(2 + n_extra_integers);
    1219             : 
    1220             :   // For the coordinates
    1221         524 :   std::vector<Real> coords(LIBMESH_DIM);
    1222             : 
    1223        6574 :   for (unsigned int i=0; i<n_nodes_here; i++)
    1224             :     {
    1225        6074 :       io.data_stream(id_pid.data(), 2 + n_extra_integers, 2 + n_extra_integers);
    1226             : 
    1227             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    1228        6074 :       file_id_type unique_id = 0;
    1229        6074 :       io.data(unique_id, "# unique id");
    1230             : #endif
    1231             : 
    1232        6074 :       io.data_stream(coords.data(), LIBMESH_DIM, LIBMESH_DIM);
    1233             : 
    1234         410 :       Point p;
    1235        6074 :       p(0) = coords[0];
    1236             : 
    1237             : #if LIBMESH_DIM > 1
    1238        6074 :       p(1) = coords[1];
    1239             : #endif
    1240             : 
    1241             : #if LIBMESH_DIM > 2
    1242        6074 :       p(2) = coords[2];
    1243             : #endif
    1244             : 
    1245        6074 :       const dof_id_type id = cast_int<dof_id_type>(id_pid[0]);
    1246             : 
    1247             :       // "Wrap around" if we see more processors than we're using.
    1248         410 :       processor_id_type pid =
    1249        6484 :         cast_int<processor_id_type>(id_pid[1] % mesh.n_processors());
    1250             : 
    1251             :       // If we already have this node (e.g. from another file, when
    1252             :       // reading multiple distributed CheckpointIO files into a
    1253             :       // ReplicatedMesh) then we don't want to add it again (because
    1254             :       // ReplicatedMesh can't handle that) but we do want to assert
    1255             :       // consistency between what we're reading and what we have.
    1256        6074 :       const Node * old_node = mesh.query_node_ptr(id);
    1257             : 
    1258        6074 :       if (old_node)
    1259             :         {
    1260          60 :           libmesh_assert_equal_to(pid, old_node->processor_id());
    1261             : 
    1262          60 :           libmesh_assert_equal_to(n_extra_integers, old_node->n_extra_integers());
    1263             : #ifndef NDEBUG
    1264          60 :           for (unsigned int ei=0; ei != n_extra_integers; ++ei)
    1265             :             {
    1266           0 :               const dof_id_type extra_int = cast_int<dof_id_type>(id_pid[2+ei]);
    1267           0 :               libmesh_assert_equal_to(extra_int, old_node->get_extra_integer(ei));
    1268             :             }
    1269             : #endif
    1270             : 
    1271             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    1272          60 :           libmesh_assert_equal_to(unique_id, old_node->unique_id());
    1273             : #endif
    1274             :         }
    1275             :       else
    1276             :         {
    1277             :           Node * node =
    1278        5408 :             mesh.add_point(p, id, pid);
    1279             : 
    1280             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    1281        5408 :           node->set_unique_id(unique_id);
    1282             : #endif
    1283             : 
    1284         350 :           libmesh_assert_equal_to(n_extra_integers, node->n_extra_integers());
    1285             : 
    1286       12300 :           for (unsigned int ei=0; ei != n_extra_integers; ++ei)
    1287             :             {
    1288        7120 :               const dof_id_type extra_int = cast_int<dof_id_type>(id_pid[2+ei]);
    1289        6892 :               node->set_extra_integer(ei, extra_int);
    1290             :             }
    1291             :         }
    1292             :     }
    1293         500 : }
    1294             : 
    1295             : 
    1296             : 
    1297             : template <typename file_id_type>
    1298         500 : void CheckpointIO::read_connectivity (Xdr & io)
    1299             : {
    1300             :   // convenient reference to our mesh
    1301         500 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
    1302             : 
    1303         500 :   const bool read_extra_integers = this->version_at_least_1_5();
    1304         500 :   const bool read_runtime_topology = this->version_at_least_1_6();
    1305             : 
    1306          48 :   const unsigned int n_extra_integers =
    1307         476 :     read_extra_integers ? mesh.n_elem_integers() : 0;
    1308             : 
    1309             :   file_id_type n_elems_here;
    1310         500 :   io.data(n_elems_here);
    1311             : 
    1312             :   // Keep track of the highest dimensional element we've added to the mesh
    1313         500 :   unsigned int highest_elem_dim = mesh.mesh_dimension();
    1314             : 
    1315             :   // RHS: Originally we used invalid_processor_id as a "no parent" tag
    1316             :   // number, because I'm an idiot.  Let's try to support broken files
    1317             :   // as much as possible.
    1318          24 :   bool file_is_broken = false;
    1319             : 
    1320        5810 :   for (unsigned int i=0; i<n_elems_here; i++)
    1321             :     {
    1322             :       // id type pid subdomain_id parent_id
    1323        3081 :       std::vector<file_id_type> elem_data(6 + n_extra_integers);
    1324         639 :       io.data_stream
    1325        2442 :         (elem_data.data(), cast_int<unsigned int>(elem_data.size()),
    1326             :          cast_int<unsigned int>(elem_data.size()));
    1327             : 
    1328             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    1329        2868 :       file_id_type unique_id = 0;
    1330        2868 :       io.data(unique_id, "# unique id");
    1331             : #endif
    1332             : 
    1333             : #ifdef LIBMESH_ENABLE_AMR
    1334        2868 :       uint16_t p_level = 0;
    1335        2868 :       io.data(p_level, "# p_level");
    1336             : 
    1337             :       uint16_t rflag, pflag;
    1338        2868 :       io.data(rflag, "# rflag");
    1339        2868 :       io.data(pflag, "# pflag");
    1340             : #endif
    1341             : 
    1342        2868 :       const ElemType elem_type =
    1343        2868 :         static_cast<ElemType>(elem_data[1]);
    1344         213 :       const bool is_c0polygon = (elem_type == C0POLYGON);
    1345         213 :       const bool is_c0polyhedron = (elem_type == C0POLYHEDRON);
    1346             : 
    1347        2868 :       unsigned int n_nodes = Elem::type_to_n_nodes_map[elem_data[1]];
    1348             :       // Runtime-topology types have no fixed node count in this map.
    1349         213 :       const bool has_runtime_topology = (n_nodes == invalid_uint);
    1350         639 :       std::vector<std::vector<unsigned int>> nodes_on_sides;
    1351             : 
    1352        2868 :       if (has_runtime_topology)
    1353             :         {
    1354          48 :           libmesh_error_msg_if
    1355             :             (!read_runtime_topology,
    1356             :              "Checkpoint format 1.6 or newer is required to read " <<
    1357             :              Utility::enum_to_string(elem_type) << " elements.");
    1358             : 
    1359           8 :           std::vector<file_id_type> runtime_topology;
    1360          48 :           io.data(runtime_topology, "# runtime topology");
    1361          52 :           libmesh_error_msg_if(runtime_topology.size() < 2,
    1362             :                                "Invalid runtime element topology.");
    1363             : 
    1364           4 :           std::size_t topology_index = 0;
    1365          44 :           n_nodes =
    1366          48 :             cast_int<unsigned int>(runtime_topology[topology_index++]);
    1367             :           const unsigned int n_sides =
    1368          48 :             cast_int<unsigned int>(runtime_topology[topology_index++]);
    1369          48 :           nodes_on_sides.resize(n_sides);
    1370             : 
    1371         360 :           for (auto s : index_range(nodes_on_sides))
    1372             :             {
    1373         338 :               libmesh_error_msg_if
    1374             :                 (topology_index == runtime_topology.size(),
    1375             :                  "Incomplete runtime element checkpoint topology.");
    1376             : 
    1377             :               const unsigned int n_side_nodes =
    1378         312 :                 cast_int<unsigned int>(runtime_topology[topology_index++]);
    1379         312 :               libmesh_error_msg_if
    1380             :                 (n_side_nodes > runtime_topology.size() - topology_index,
    1381             :                  "Invalid runtime element side checkpoint topology.");
    1382             : 
    1383          52 :               auto & side_nodes = nodes_on_sides[s];
    1384         312 :               side_nodes.resize(n_side_nodes);
    1385        1416 :               for (auto n : index_range(side_nodes))
    1386             :                 {
    1387             :                   const unsigned int local_node =
    1388        1196 :                     cast_int<unsigned int>(runtime_topology[topology_index++]);
    1389        1104 :                   libmesh_error_msg_if
    1390             :                     (local_node >= n_nodes,
    1391             :                      "Runtime element side checkpoint topology references "
    1392             :                      "an invalid local node.");
    1393        1104 :                   side_nodes[n] = local_node;
    1394             :                 }
    1395             :             }
    1396             : 
    1397          52 :           libmesh_error_msg_if
    1398             :             (topology_index != runtime_topology.size(),
    1399             :              "Extra data in runtime element checkpoint topology.");
    1400             : 
    1401          48 :           if (is_c0polygon)
    1402             :             {
    1403          24 :               libmesh_error_msg_if
    1404             :                 (n_nodes < 3 || n_sides != n_nodes,
    1405             :                  "Invalid C0POLYGON checkpoint topology.");
    1406         144 :               for (const auto & side_nodes : nodes_on_sides)
    1407         130 :                 libmesh_error_msg_if
    1408             :                   (side_nodes.size() != 2,
    1409             :                    "Invalid C0POLYGON side checkpoint topology.");
    1410             :             }
    1411          24 :           else if (is_c0polyhedron)
    1412             :             {
    1413          24 :               libmesh_error_msg_if(n_sides < 4,
    1414             :                                    "Invalid C0POLYHEDRON checkpoint topology.");
    1415         216 :               for (const auto & side_nodes : nodes_on_sides)
    1416         208 :                 libmesh_error_msg_if
    1417             :                   (side_nodes.size() < 3,
    1418             :                    "Invalid C0POLYHEDRON side checkpoint topology.");
    1419             :             }
    1420             :         }
    1421             : 
    1422             :       // Snag the node ids this element was connected to
    1423        3081 :       std::vector<file_id_type> conn_data(n_nodes);
    1424         639 :       io.data_stream
    1425        2442 :         (conn_data.data(), cast_int<unsigned int>(conn_data.size()),
    1426             :          cast_int<unsigned int>(conn_data.size()));
    1427             : 
    1428             :       const dof_id_type id                 =
    1429        2868 :         cast_int<dof_id_type>      (elem_data[0]);
    1430         213 :       const processor_id_type proc_id      =
    1431             :         cast_int<processor_id_type>
    1432        3081 :         (elem_data[2] % mesh.n_processors());
    1433         213 :       const subdomain_id_type subdomain_id =
    1434        2868 :         restrict_int<subdomain_id_type>(elem_data[3]);
    1435             : 
    1436             :       // Old broken files used processsor_id_type(-1)...
    1437             :       // But we *know* our first element will be level 0
    1438        2868 :       if (i == 0 && elem_data[4] == 65535)
    1439           0 :         file_is_broken = true;
    1440             : 
    1441             :       // On a broken file we can't tell whether a parent of 65535 is a
    1442             :       // null parent or an actual parent of 65535.  Assuming the
    1443             :       // former will cause less breakage.
    1444        2655 :       Elem * parent =
    1445         213 :         (elem_data[4] == static_cast<largest_id_type>(-1) ||
    1446        2868 :          (file_is_broken && elem_data[4] == 65535)) ?
    1447           0 :         nullptr : mesh.elem_ptr(cast_int<dof_id_type>(elem_data[4]));
    1448             : 
    1449         213 :       const unsigned short int child_num   =
    1450         213 :         (elem_data[5] == static_cast<largest_id_type>(-1) ||
    1451        2868 :          (file_is_broken && elem_data[5] == 65535)) ?
    1452             :         static_cast<unsigned short>(-1) :
    1453           0 :         cast_int<unsigned short>(elem_data[5]);
    1454             : 
    1455         213 :       if (!parent)
    1456         213 :         libmesh_assert_equal_to
    1457             :           (child_num, static_cast<unsigned short>(-1));
    1458             : 
    1459        2868 :       Elem * old_elem = mesh.query_elem_ptr(id);
    1460             : 
    1461             :       // If we already have this element (e.g. from another file,
    1462             :       // when reading multiple distributed CheckpointIO files into
    1463             :       // a ReplicatedMesh) then we don't want to add it again
    1464             :       // (because ReplicatedMesh can't handle that) but we do want
    1465             :       // to assert consistency between what we're reading and what
    1466             :       // we have.
    1467        2868 :       if (old_elem)
    1468             :         {
    1469          32 :           libmesh_assert_equal_to(elem_type, old_elem->type());
    1470          32 :           libmesh_assert_equal_to(proc_id, old_elem->processor_id());
    1471          32 :           libmesh_assert_equal_to(subdomain_id, old_elem->subdomain_id());
    1472          32 :           if (parent)
    1473           0 :             libmesh_assert_equal_to(parent, old_elem->parent());
    1474             :           else
    1475          32 :             libmesh_assert(!old_elem->parent());
    1476             : 
    1477          32 :           libmesh_assert_equal_to(n_extra_integers, old_elem->n_extra_integers());
    1478             : #ifndef NDEBUG
    1479          32 :           for (unsigned int ei=0; ei != n_extra_integers; ++ei)
    1480             :             {
    1481           0 :               const dof_id_type extra_int = cast_int<dof_id_type>(elem_data[6+ei]);
    1482           0 :               libmesh_assert_equal_to(extra_int, old_elem->get_extra_integer(ei));
    1483             :             }
    1484             : #endif
    1485             : 
    1486          32 :           libmesh_assert_equal_to(old_elem->n_nodes(), conn_data.size());
    1487             : 
    1488         128 :           for (unsigned int n=0,
    1489          32 :                n_conn = cast_int<unsigned int>(conn_data.size());
    1490         160 :                n != n_conn; n++)
    1491         128 :             libmesh_assert_equal_to
    1492             :               (old_elem->node_id(n),
    1493             :                cast_int<dof_id_type>(conn_data[n]));
    1494             : 
    1495          32 :           if (has_runtime_topology)
    1496             :             {
    1497           0 :               libmesh_assert_equal_to(old_elem->n_sides(),
    1498             :                                       nodes_on_sides.size());
    1499             : #ifndef NDEBUG
    1500           0 :               for (auto s : index_range(nodes_on_sides))
    1501           0 :                 libmesh_assert(old_elem->nodes_on_side(s) ==
    1502             :                                nodes_on_sides[s]);
    1503             : #endif
    1504             :             }
    1505             :         }
    1506             :       else
    1507             :         {
    1508             :           // Create the element
    1509        2331 :           std::unique_ptr<Elem> elem;
    1510        2512 :           std::unique_ptr<Node> generated_mid_elem_node;
    1511             : 
    1512        2512 :           if (is_c0polygon)
    1513          26 :             elem = std::make_unique<C0Polygon>(n_nodes, parent);
    1514        2488 :           else if (is_c0polyhedron)
    1515             :             {
    1516          30 :               std::vector<std::shared_ptr<Polygon>> sides(nodes_on_sides.size());
    1517         216 :               for (auto s : index_range(nodes_on_sides))
    1518             :                 {
    1519          32 :                   const auto & side_node_indices = nodes_on_sides[s];
    1520          16 :                   auto side =
    1521             :                     std::make_shared<C0Polygon>
    1522         208 :                       (cast_int<unsigned int>(side_node_indices.size()));
    1523        1056 :                   for (auto n : index_range(side_node_indices))
    1524         288 :                     side->set_node
    1525         720 :                       (n, mesh.node_ptr(cast_int<dof_id_type>
    1526         936 :                        (conn_data[side_node_indices[n]])));
    1527          32 :                   sides[s] = std::move(side);
    1528             :                 }
    1529             : 
    1530          24 :               elem = std::make_unique<C0Polyhedron>
    1531             :                 (sides, generated_mid_elem_node, parent);
    1532             : 
    1533          24 :               libmesh_error_msg_if
    1534             :                 (elem->n_nodes() != conn_data.size(),
    1535             :                  "C0POLYHEDRON checkpoint topology is incompatible with "
    1536             :                  "this libMesh configuration.");
    1537             : 
    1538         312 :               for (auto n : make_range(elem->n_vertices()))
    1539         360 :                 libmesh_error_msg_if
    1540             :                   (elem->node_id(n) !=
    1541             :                    cast_int<dof_id_type>(conn_data[n]),
    1542             :                    "C0POLYHEDRON checkpoint topology has inconsistent "
    1543             :                    "local node ordering.");
    1544          20 :             }
    1545             :           else
    1546        4751 :             elem = Elem::build(elem_type, parent);
    1547             : 
    1548        2512 :           if (has_runtime_topology)
    1549             :             {
    1550          48 :               libmesh_error_msg_if
    1551             :                 (!elem->runtime_topology() ||
    1552             :                  elem->n_nodes() != conn_data.size() ||
    1553             :                  elem->n_sides() != nodes_on_sides.size(),
    1554             :                  Utility::enum_to_string(elem_type) <<
    1555             :                  " checkpoint topology is incompatible with this "
    1556             :                  "libMesh configuration.");
    1557             : 
    1558         360 :               for (auto s : index_range(nodes_on_sides))
    1559         624 :                 libmesh_error_msg_if
    1560             :                   (elem->nodes_on_side(s) != nodes_on_sides[s],
    1561             :                    Utility::enum_to_string(elem_type) <<
    1562             :                    " checkpoint topology has inconsistent side ordering.");
    1563             :             }
    1564             : 
    1565             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    1566        2512 :           elem->set_unique_id(unique_id);
    1567             : #endif
    1568             : 
    1569        2512 :           if (elem->dim() > highest_elem_dim)
    1570           0 :             highest_elem_dim = elem->dim();
    1571             : 
    1572        2512 :           elem->set_id()       = id;
    1573        2512 :           elem->processor_id() = proc_id;
    1574        2512 :           elem->subdomain_id() = subdomain_id;
    1575             : 
    1576             : #ifdef LIBMESH_ENABLE_AMR
    1577        2512 :           elem->hack_p_level(p_level);
    1578             : 
    1579        2512 :           elem->set_refinement_flag  (cast_int<Elem::RefinementState>(rflag));
    1580        2512 :           elem->set_p_refinement_flag(cast_int<Elem::RefinementState>(pflag));
    1581             : 
    1582             :           // Set parent connections
    1583        2512 :           if (parent)
    1584             :             {
    1585             :               // We must specify a child_num, because we will have
    1586             :               // skipped adding any preceding remote_elem children
    1587           0 :               parent->add_child(elem.get(), child_num);
    1588             :             }
    1589             : #else
    1590             :           libmesh_ignore(child_num);
    1591             : #endif
    1592             : 
    1593         181 :           libmesh_assert(elem->n_nodes() == conn_data.size());
    1594             : 
    1595             :           // Connect all the nodes to this element
    1596       11840 :           for (unsigned int n=0,
    1597         362 :                n_conn = cast_int<unsigned int>(conn_data.size());
    1598       14352 :                n != n_conn; n++)
    1599       13382 :             elem->set_node(n,
    1600       11840 :               mesh.node_ptr(cast_int<dof_id_type>(conn_data[n])));
    1601             : 
    1602        2874 :           Elem * added_elem = mesh.add_elem(std::move(elem));
    1603             : 
    1604         181 :           libmesh_assert_equal_to(n_extra_integers, added_elem->n_extra_integers());
    1605        3607 :           for (unsigned int ei=0; ei != n_extra_integers; ++ei)
    1606             :             {
    1607        1134 :               const dof_id_type extra_int = cast_int<dof_id_type>(elem_data[6+ei]);
    1608        1095 :               added_elem->set_extra_integer(ei, extra_int);
    1609             :             }
    1610        2150 :         }
    1611             :     }
    1612             : 
    1613         500 :   mesh.set_mesh_dimension(cast_int<unsigned char>(highest_elem_dim));
    1614         500 : }
    1615             : 
    1616             : 
    1617             : template <typename file_id_type>
    1618         500 : void CheckpointIO::read_remote_elem (Xdr & io, bool libmesh_dbg_var(expect_all_remote))
    1619             : {
    1620             :   // convenient reference to our mesh
    1621         500 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
    1622             : 
    1623             :   // Find the remote_elem neighbor links
    1624          48 :   std::vector<file_id_type> elem_ids;
    1625          48 :   std::vector<uint16_t> elem_sides;
    1626             : 
    1627         500 :   io.data(elem_ids, "# remote neighbor elem_ids");
    1628         500 :   io.data(elem_sides, "# remote neighbor elem_sides");
    1629             : 
    1630          24 :   libmesh_assert_equal_to(elem_ids.size(), elem_sides.size());
    1631             : 
    1632        1208 :   for (auto i : index_range(elem_ids))
    1633             :     {
    1634         836 :       Elem & elem = mesh.elem_ref(cast_int<dof_id_type>(elem_ids[i]));
    1635         836 :       if (!elem.neighbor_ptr(elem_sides[i]))
    1636         708 :         elem.set_neighbor(elem_sides[i],
    1637             :                           const_cast<RemoteElem *>(remote_elem));
    1638             :       else
    1639           0 :         libmesh_assert(!expect_all_remote);
    1640             :     }
    1641             : 
    1642             :   // Find the remote_elem children links
    1643          48 :   std::vector<file_id_type> parent_ids;
    1644          48 :   std::vector<uint16_t> child_numbers;
    1645             : 
    1646         500 :   io.data(parent_ids, "# remote child parent_ids");
    1647         500 :   io.data(child_numbers, "# remote child_numbers");
    1648             : 
    1649             : #ifdef LIBMESH_ENABLE_AMR
    1650         500 :   for (auto i : index_range(parent_ids))
    1651             :     {
    1652           0 :       Elem & elem = mesh.elem_ref(cast_int<dof_id_type>(parent_ids[i]));
    1653             : 
    1654             :       // We'd like to assert that no child pointer already exists to
    1655             :       // be overwritten by remote_elem, but Elem doesn't actually have
    1656             :       // an API that will return a child pointer without asserting
    1657             :       // that it isn't nullptr.
    1658           0 :       const Elem * child = elem.raw_child_ptr(child_numbers[i]);
    1659             : 
    1660           0 :       if (!child)
    1661           0 :         elem.add_child(const_cast<RemoteElem *>(remote_elem),
    1662           0 :                        child_numbers[i]);
    1663             :       else
    1664           0 :         libmesh_assert(!expect_all_remote);
    1665             :     }
    1666             : #endif
    1667         500 : }
    1668             : 
    1669             : 
    1670             : 
    1671             : template <typename file_id_type>
    1672         500 : void CheckpointIO::read_bcs (Xdr & io)
    1673             : {
    1674             :   // convenient reference to our mesh
    1675         500 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
    1676             : 
    1677             :   // and our boundary info object
    1678          24 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
    1679             : 
    1680          48 :   std::vector<file_id_type> element_id_list;
    1681          48 :   std::vector<uint16_t> side_list;
    1682          48 :   std::vector<file_id_type> bc_id_list;
    1683             : 
    1684         500 :   io.data(element_id_list, "# element ids for bcs");
    1685         500 :   io.data(side_list, "# sides of elements for bcs");
    1686         500 :   io.data(bc_id_list, "# bc ids");
    1687             : 
    1688        3744 :   for (auto i : index_range(element_id_list))
    1689         570 :     boundary_info.add_side
    1690        3434 :       (cast_int<dof_id_type>(element_id_list[i]), side_list[i],
    1691        3434 :        cast_int<boundary_id_type>(bc_id_list[i]));
    1692         500 : }
    1693             : 
    1694             : 
    1695             : 
    1696             : template <typename file_id_type>
    1697         500 : void CheckpointIO::read_nodesets (Xdr & io)
    1698             : {
    1699             :   // convenient reference to our mesh
    1700         500 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
    1701             : 
    1702             :   // and our boundary info object
    1703          24 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
    1704             : 
    1705          48 :   std::vector<file_id_type> node_id_list;
    1706          48 :   std::vector<file_id_type> bc_id_list;
    1707             : 
    1708         500 :   io.data(node_id_list, "# node id list");
    1709         500 :   io.data(bc_id_list, "# nodeset bc id list");
    1710             : 
    1711        5784 :   for (auto i : index_range(node_id_list))
    1712         792 :     boundary_info.add_node
    1713        5284 :       (cast_int<dof_id_type>(node_id_list[i]),
    1714        5548 :        cast_int<boundary_id_type>(bc_id_list[i]));
    1715         500 : }
    1716             : 
    1717             : 
    1718             : 
    1719             : template <typename file_id_type>
    1720         384 : void CheckpointIO::read_bc_names(Xdr & io, BoundaryInfo & info, bool is_sideset)
    1721             : {
    1722         384 :   std::map<boundary_id_type, std::string> & boundary_map = is_sideset ?
    1723          32 :     info.set_sideset_name_map() : info.set_nodeset_name_map();
    1724             : 
    1725          64 :   std::vector<file_id_type> boundary_ids;
    1726          96 :   std::vector<std::string>  boundary_names;
    1727             : 
    1728         384 :   file_id_type n_boundary_names = 0;
    1729             : 
    1730         384 :   if (is_sideset)
    1731         192 :     io.data(n_boundary_names, "# sideset id to name map");
    1732             :   else
    1733         192 :     io.data(n_boundary_names, "# nodeset id to name map");
    1734             : 
    1735         384 :   if (n_boundary_names)
    1736             :     {
    1737         240 :       io.data(boundary_ids);
    1738         240 :       io.data(boundary_names);
    1739             :     }
    1740             : 
    1741             :   // Add them back into the map
    1742        1344 :   for (auto i : index_range(boundary_ids))
    1743        2000 :     boundary_map[cast_int<boundary_id_type>(boundary_ids[i])] =
    1744         160 :       boundary_names[i];
    1745         704 : }
    1746             : 
    1747             : 
    1748             : template <typename file_id_type>
    1749         192 : void CheckpointIO::read_disjoint_neighbor_boundary_pairs
    1750             :   (Xdr & io,
    1751             :    std::vector<file_id_type> & pair_ids,
    1752             :    std::vector<Real> & translations)
    1753             : {
    1754         192 :   file_id_type n_pairs = 0;
    1755         192 :   io.data(n_pairs, "# disjoint neighbor boundary pairs");
    1756         192 :   if (n_pairs)
    1757             :     {
    1758          24 :       io.data(pair_ids);
    1759          26 :       libmesh_error_msg_if(pair_ids.size() != 2 * n_pairs,
    1760             :                            "Expected " << 2 * n_pairs << " disjoint neighbor boundary ids, found "
    1761             :                            << pair_ids.size());
    1762             : 
    1763          24 :       translations.resize(LIBMESH_DIM * n_pairs);
    1764          26 :       io.data_stream(translations.data(),
    1765             :                      cast_int<unsigned int>(translations.size()),
    1766             :                      LIBMESH_DIM);
    1767             :     }
    1768         192 : }
    1769             : 
    1770             : 
    1771             : 
    1772             : template <typename file_id_type>
    1773         192 : void CheckpointIO::read_integers_names
    1774             :   (Xdr & io,
    1775             :    std::vector<std::string> & node_integer_names,
    1776             :    std::vector<std::string> & elem_integer_names)
    1777             : {
    1778             :   file_id_type n_node_integers, n_elem_integers;
    1779             : 
    1780         192 :   io.data(n_node_integers, "# n_extra_integers per node");
    1781         192 :   io.data(node_integer_names);
    1782         192 :   io.data(n_elem_integers, "# n_extra_integers per elem");
    1783         192 :   io.data(elem_integer_names);
    1784         192 : }
    1785             : 
    1786             : 
    1787           0 : unsigned int CheckpointIO::n_active_levels_in(MeshBase::const_element_iterator begin,
    1788             :                                               MeshBase::const_element_iterator end) const
    1789             : {
    1790           0 :   unsigned int max_level = 0;
    1791             : 
    1792           0 :   for (const auto & elem : as_range(begin, end))
    1793           0 :     max_level = std::max(elem->level(), max_level);
    1794             : 
    1795           0 :   return max_level + 1;
    1796             : }
    1797             : 
    1798             : } // namespace libMesh

Generated by: LCOV version 1.14