LCOV - code coverage report
Current view: top level - src/mesh - checkpoint_io.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 548 655 83.7 %
Date: 2025-08-19 19:27:09 Functions: 31 56 55.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2025 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/distributed_mesh.h"
      22             : #include "libmesh/elem.h"
      23             : #include "libmesh/enum_xdr_mode.h"
      24             : #include "libmesh/libmesh_logging.h"
      25             : #include "libmesh/mesh_base.h"
      26             : #include "libmesh/mesh_communication.h"
      27             : #include "libmesh/mesh_tools.h"
      28             : #include "libmesh/node.h"
      29             : #include "libmesh/parallel.h"
      30             : #include "libmesh/partitioner.h"
      31             : #include "libmesh/metis_partitioner.h"
      32             : #include "libmesh/remote_elem.h"
      33             : #include "libmesh/xdr_io.h"
      34             : #include "libmesh/xdr_cxx.h"
      35             : #include "libmesh/utility.h"
      36             : #include "libmesh/int_range.h"
      37             : 
      38             : // C++ includes
      39             : #include <iostream>
      40             : #include <iomanip>
      41             : #include <cstdio>
      42             : #include <vector>
      43             : #include <string>
      44             : #include <cstring>
      45             : #include <fstream>
      46             : #include <sstream> // for ostringstream
      47             : #include <unordered_map>
      48             : #include <unordered_set>
      49             : #ifdef LIBMESH_HAVE_DIRECT_H
      50             : #include <direct.h> // rmdir() on Windows
      51             : #endif
      52             : #ifdef LIBMESH_HAVE_UNISTD_H
      53             : #include <unistd.h>  // rmdir() on Unix
      54             : #endif
      55             : 
      56             : namespace
      57             : {
      58             : // chunking computes the number of chunks and first-chunk-offset when splitting a mesh
      59             : // into nsplits pieces using size procs for the given MPI rank.  The number of chunks and offset
      60             : // are stored in nchunks and first_chunk respectively.
      61           0 : void chunking(libMesh::processor_id_type size, libMesh::processor_id_type rank, libMesh::processor_id_type nsplits,
      62             :               libMesh::processor_id_type & nchunks, libMesh::processor_id_type & first_chunk)
      63             : {
      64           0 :   if (nsplits % size == 0) // the chunks divide evenly over the processors
      65             :     {
      66           0 :       nchunks = nsplits / size;
      67           0 :       first_chunk = libMesh::cast_int<libMesh::processor_id_type>(nchunks * rank);
      68           0 :       return;
      69             :     }
      70             : 
      71           0 :   libMesh::processor_id_type nextra = nsplits % size;
      72           0 :   if (rank < nextra) // leftover chunks cause an extra chunk to be added to this processor
      73             :     {
      74           0 :       nchunks = libMesh::cast_int<libMesh::processor_id_type>(nsplits / size + 1);
      75           0 :       first_chunk = libMesh::cast_int<libMesh::processor_id_type>(nchunks * rank);
      76             :     }
      77             :   else // no extra chunks, but first chunk is offset by extras on earlier ranks
      78             :     {
      79           0 :       nchunks = nsplits / size;
      80             :       // account for the case where nchunks is zero where we want max int
      81           0 :       first_chunk = libMesh::cast_int<libMesh::processor_id_type>
      82           0 :         (std::max((int)((nchunks + 1) * (nsplits % size) + nchunks * (rank - nsplits % size)),
      83           0 :          (1 - (int)nchunks) * std::numeric_limits<int>::max()));
      84             :     }
      85             : }
      86             : 
      87        2172 : std::string_view extension(std::string_view s)
      88             : {
      89        2086 :   auto pos = s.rfind(".");
      90        2172 :   if (pos == std::string::npos)
      91           0 :     return "";
      92        2172 :   return s.substr(pos, s.size() - pos);
      93             : }
      94             : 
      95        2882 : std::string split_dir(const std::string & input_name, libMesh::processor_id_type n_procs)
      96             : {
      97        5658 :   return input_name + "/" + std::to_string(n_procs);
      98             : }
      99             : 
     100             : 
     101        1540 : std::string header_file(const std::string & input_name, libMesh::processor_id_type n_procs)
     102             : {
     103        5960 :   return (split_dir(input_name, n_procs) + "/header").append(extension(input_name));
     104             : }
     105             : 
     106             : std::string
     107         632 : split_file(const std::string & input_name,
     108             :            libMesh::processor_id_type n_procs,
     109             :            libMesh::processor_id_type proc_id)
     110             : {
     111        2384 :   return (split_dir(input_name, n_procs) + "/split-" + std::to_string(n_procs) + "-" +
     112        2456 :          std::to_string(proc_id)).append(extension(input_name));
     113             : }
     114             : 
     115         710 : void make_dir(const std::string & input_name, libMesh::processor_id_type n_procs)
     116             : {
     117         710 :   auto ret = libMesh::Utility::mkdir(input_name.c_str());
     118             :   // error only if we failed to create dir - don't care if it was already there
     119         710 :   libmesh_error_msg_if
     120             :     (ret != 0 && ret != -1,
     121             :      "Failed to create mesh split directory '" << input_name << "': " << std::strerror(ret));
     122             : 
     123         730 :   auto dir_name = split_dir(input_name, n_procs);
     124         710 :   ret = libMesh::Utility::mkdir(dir_name.c_str());
     125         710 :   if (ret == -1)
     126             :     libmesh_warning("In CheckpointIO::write, directory '"
     127             :                     << dir_name << "' already exists, overwriting contents.");
     128             :   else
     129          34 :     libmesh_error_msg_if
     130             :       (ret != 0, "Failed to create mesh split directory '" << dir_name << "': " << std::strerror(ret));
     131         710 : }
     132             : 
     133             : } // namespace
     134             : 
     135             : namespace libMesh
     136             : {
     137             : 
     138           0 : std::unique_ptr<CheckpointIO> split_mesh(MeshBase & mesh, processor_id_type nsplits)
     139             : {
     140             :   // There is currently an issue with DofObjects not being properly
     141             :   // reset if the mesh is not first repartitioned onto 1 processor
     142             :   // *before* being repartitioned onto the desired number of
     143             :   // processors. So, this is a workaround, but not a particularly
     144             :   // onerous one.
     145           0 :   mesh.partition(1);
     146           0 :   mesh.partition(nsplits);
     147             : 
     148           0 :   processor_id_type my_num_chunks = 0;
     149           0 :   processor_id_type my_first_chunk = 0;
     150           0 :   chunking(mesh.comm().size(), mesh.comm().rank(), nsplits, my_num_chunks, my_first_chunk);
     151             : 
     152           0 :   auto cpr = std::make_unique<CheckpointIO>(mesh);
     153           0 :   cpr->current_processor_ids().clear();
     154           0 :   for (processor_id_type i = my_first_chunk; i < my_first_chunk + my_num_chunks; i++)
     155           0 :     cpr->current_processor_ids().push_back(i);
     156           0 :   cpr->current_n_processors() = nsplits;
     157           0 :   cpr->parallel() = true;
     158           0 :   return cpr;
     159           0 : }
     160             : 
     161             : 
     162             : // ------------------------------------------------------------
     163             : // CheckpointIO members
     164        1278 : CheckpointIO::CheckpointIO (MeshBase & mesh, const bool binary_in) :
     165             :   MeshInput<MeshBase> (mesh,/* is_parallel_format = */ true),
     166             :   MeshOutput<MeshBase>(mesh,/* is_parallel_format = */ true),
     167             :   ParallelObject      (mesh),
     168        1206 :   _binary             (binary_in),
     169        1206 :   _parallel           (false),
     170           0 :   _version            ("checkpoint-1.5"),
     171        1350 :   _my_processor_ids   (1, processor_id()),
     172        2520 :   _my_n_processors    (mesh.is_replicated() ? 1 : n_processors())
     173             : {
     174        1278 : }
     175             : 
     176         142 : CheckpointIO::CheckpointIO (const MeshBase & mesh, const bool binary_in) :
     177             :   MeshInput<MeshBase> (), // write-only
     178             :   MeshOutput<MeshBase>(mesh,/* is_parallel_format = */ true),
     179             :   ParallelObject      (mesh),
     180         134 :   _binary             (binary_in),
     181         134 :   _parallel           (false),
     182           0 :   _version            ("checkpoint-1.5"),
     183         150 :   _my_processor_ids   (1, processor_id()),
     184         280 :   _my_n_processors    (mesh.is_replicated() ? 1 : n_processors())
     185             : {
     186         142 : }
     187             : 
     188        1340 : CheckpointIO::~CheckpointIO () = default;
     189             : 
     190         710 : processor_id_type CheckpointIO::select_split_config(const std::string & input_name, header_id_type & data_size)
     191             : {
     192          40 :   std::string header_name;
     193             : 
     194             :   // We'll read a header file from processor 0 and broadcast.
     195         730 :   if (this->processor_id() == 0)
     196             :     {
     197         230 :       header_name = header_file(input_name, _my_n_processors);
     198             : 
     199             :       {
     200             :         // look for header+splits with nprocs equal to _my_n_processors
     201         140 :         std::ifstream in (header_name.c_str());
     202         120 :         if (!in.good())
     203             :           {
     204             :             // otherwise fall back to a serial/single-split mesh
     205           0 :             auto orig_header_name = header_name;
     206           0 :             header_name = header_file(input_name, 1);
     207           0 :             std::ifstream in2 (header_name.c_str());
     208           0 :             libmesh_error_msg_if(!in2.good(),
     209             :                                  "ERROR: Neither one of the following files can be located:\n\t'"
     210             :                                  << orig_header_name << "' nor\n\t'" << input_name << "'\n"
     211             :                                  << "If you are running a parallel job, double check that you've "
     212             :                                  << "created a split for " << _my_n_processors << " ranks.\n"
     213             :                                  << "Note: One of paths above may refer to a valid directory on your "
     214             :                                  << "system, however we are attempting to read a valid header file.");
     215           0 :           }
     216         100 :       }
     217             : 
     218         310 :       Xdr io (header_name, this->binary() ? DECODE : READ);
     219             : 
     220             :       // read the version, but don't care about it
     221          10 :       std::string input_version;
     222         120 :       io.data(input_version);
     223             : 
     224             :       // read the data type
     225         120 :       io.data (data_size);
     226         100 :     }
     227             : 
     228         710 :   this->comm().broadcast(data_size);
     229         710 :   this->comm().broadcast(header_name);
     230             : 
     231             :   // How many per-processor files are here?
     232             :   largest_id_type input_n_procs;
     233             : 
     234         710 :   switch (data_size) {
     235           0 :   case 2:
     236           0 :     input_n_procs = this->read_header<uint16_t>(header_name);
     237           0 :     break;
     238           0 :   case 4:
     239           0 :     input_n_procs = this->read_header<uint32_t>(header_name);
     240           0 :     break;
     241         710 :   case 8:
     242         710 :     input_n_procs = this->read_header<uint64_t>(header_name);
     243          20 :     break;
     244           0 :   default:
     245           0 :     libmesh_error();
     246             :   }
     247             : 
     248          40 :   if (!input_n_procs)
     249           4 :     input_n_procs = 1;
     250         730 :   return cast_int<processor_id_type>(input_n_procs);
     251             : }
     252             : 
     253           0 : void CheckpointIO::cleanup(const std::string & input_name, processor_id_type n_procs)
     254             : {
     255           0 :   auto header = header_file(input_name, n_procs);
     256           0 :   auto ret = std::remove(header.c_str());
     257             :   if (ret != 0)
     258             :     libmesh_warning("Failed to clean up checkpoint header '" << header << "': " << std::strerror(ret));
     259             : 
     260           0 :   for (processor_id_type i = 0; i < n_procs; i++)
     261             :     {
     262           0 :       auto split = split_file(input_name, n_procs, i);
     263           0 :       ret = std::remove(split.c_str());
     264             :       if (ret != 0)
     265             :         libmesh_warning("Failed to clean up checkpoint split file '" << split << "': " << std::strerror(ret));
     266             :     }
     267             : 
     268           0 :   auto dir = split_dir(input_name, n_procs);
     269           0 :   ret = rmdir(dir.c_str());
     270             :   if (ret != 0)
     271             :     libmesh_warning("Failed to clean up checkpoint split dir '" << dir << "': " << std::strerror(ret));
     272             : 
     273             :   // We expect that this may fail if there are other split configurations still present in this
     274             :   // directory - so don't bother to check/warn for failure.
     275           0 :   rmdir(input_name.c_str());
     276           0 : }
     277             : 
     278             : 
     279        1504 : bool CheckpointIO::version_at_least_1_5() const
     280             : {
     281        1504 :   return (this->version().find("1.5") != std::string::npos);
     282             : }
     283             : 
     284             : 
     285         710 : void CheckpointIO::write (const std::string & name)
     286             : {
     287          40 :   LOG_SCOPE("write()", "CheckpointIO");
     288             : 
     289             :   // convenient reference to our mesh
     290          40 :   const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
     291             : 
     292             :   // FIXME: For backwards compatibility, we'll assume for now that we
     293             :   // only want to write distributed meshes in parallel.  Later we can
     294             :   // do a gather_to_zero() and support that case too.
     295         710 :   _parallel = _parallel || !mesh.is_serial();
     296             : 
     297          20 :   processor_id_type use_n_procs = 1;
     298         710 :   if (_parallel)
     299         696 :     use_n_procs = _my_n_processors;
     300             : 
     301         730 :   std::string header_file_name = header_file(name, use_n_procs);
     302         710 :   make_dir(name, use_n_procs);
     303             : 
     304             :   // We'll write a header file from processor 0 to make it easier to do unambiguous
     305             :   // restarts later:
     306         730 :   if (this->processor_id() == 0)
     307             :     {
     308         310 :       Xdr io (header_file_name, this->binary() ? ENCODE : WRITE);
     309             : 
     310             :       // write the version
     311         120 :       io.data(_version, "# version");
     312             : 
     313             :       // write what kind of data type we're using
     314         120 :       header_id_type data_size = sizeof(largest_id_type);
     315         120 :       io.data(data_size, "# integer size");
     316             : 
     317             :       // Write out the max mesh dimension for backwards compatibility
     318             :       // with code that sets it independently of element dimensions
     319             :       {
     320         120 :         uint16_t mesh_dimension = cast_int<uint16_t>(mesh.mesh_dimension());
     321         120 :         io.data(mesh_dimension, "# dimensions");
     322             :       }
     323             : 
     324             :       // Write out whether or not this is serial output
     325             :       {
     326         120 :         uint16_t parallel = _parallel;
     327         120 :         io.data(parallel, "# parallel");
     328             :       }
     329             : 
     330             :       // If we're writing out a parallel mesh then we need to write the number of processors
     331             :       // so we can check it upon reading the file
     332         120 :       if (_parallel)
     333             :         {
     334         112 :           largest_id_type n_procs = _my_n_processors;
     335         112 :           io.data(n_procs, "# n_procs");
     336             :         }
     337             : 
     338             :       // write subdomain names
     339         120 :       this->write_subdomain_names(io);
     340             : 
     341             :       // write boundary id names
     342          10 :       const BoundaryInfo & boundary_info = mesh.get_boundary_info();
     343         120 :       write_bc_names(io, boundary_info, true);  // sideset names
     344         120 :       write_bc_names(io, boundary_info, false); // nodeset names
     345             : 
     346             :       // write extra integer names
     347         120 :       const bool write_extra_integers = this->version_at_least_1_5();
     348             : 
     349         120 :       if (write_extra_integers)
     350             :         {
     351         120 :           largest_id_type n_node_integers = mesh.n_node_integers();
     352         120 :           io.data(n_node_integers, "# n_extra_integers per node");
     353             : 
     354          30 :           std::vector<std::string> node_integer_names;
     355         228 :           for (unsigned int i=0; i != n_node_integers; ++i)
     356         108 :             node_integer_names.push_back(mesh.get_node_integer_name(i));
     357             : 
     358         120 :           io.data(node_integer_names);
     359             : 
     360         120 :           largest_id_type n_elem_integers = mesh.n_elem_integers();
     361         120 :           io.data(n_elem_integers, "# n_extra_integers per elem");
     362             : 
     363          20 :           std::vector<std::string> elem_integer_names;
     364         174 :           for (unsigned int i=0; i != n_elem_integers; ++i)
     365          54 :             elem_integer_names.push_back(mesh.get_elem_integer_name(i));
     366             : 
     367         120 :           io.data(elem_integer_names);
     368         100 :         }
     369             : 
     370             : 
     371         100 :     }
     372             : 
     373             :   // If this is a serial mesh written to a serial file then we're only
     374             :   // going to write local data from processor 0.  If this is a mesh being
     375             :   // written in parallel then we're going to write from every
     376             :   // processor.
     377          40 :   std::vector<processor_id_type> ids_to_write;
     378             : 
     379             :   // We're going to sort elements by pid in one pass, to avoid sending
     380             :   // predicated iterators through the whole mesh N_p times.
     381             :   //
     382             :   // The data type here needs to be a non-const-pointer to whatever
     383             :   // our element_iterator is a const-pointer to, for compatibility
     384             :   // later.
     385             :   typedef std::remove_const<MeshBase::const_element_iterator::value_type>::type nc_v_t;
     386          40 :   std::unordered_map<processor_id_type, std::vector<nc_v_t>> elements_on_pid;
     387             : 
     388         710 :   if (_parallel)
     389             :     {
     390         696 :       ids_to_write = _my_processor_ids;
     391        1004 :       for (processor_id_type p : ids_to_write)
     392          16 :         elements_on_pid[p].clear();
     393          16 :       auto eop_end = elements_on_pid.end();
     394       13248 :       for (auto & elem : mesh.element_ptr_range())
     395             :         {
     396        6160 :           const processor_id_type p = elem->processor_id();
     397        6384 :           if (auto eop_it = elements_on_pid.find(p);
     398         224 :               eop_it != eop_end)
     399        1640 :             eop_it->second.push_back(elem);
     400         664 :         }
     401             :     }
     402          14 :   else if (mesh.is_serial())
     403             :     {
     404          18 :       if (mesh.processor_id() == 0)
     405             :         {
     406             :           // placeholder
     407           8 :           ids_to_write.push_back(0);
     408             :         }
     409             :     }
     410             :   else
     411             :     {
     412           0 :       libmesh_error_msg("Cannot write serial checkpoint from distributed mesh");
     413             :     }
     414             : 
     415             :   // Call build_side_list() and build_node_list() just *once* to avoid
     416             :   // redundant expensive sorts during mesh splitting.
     417          20 :   const BoundaryInfo & boundary_info = mesh.get_boundary_info();
     418             :   std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>>
     419         730 :     bc_triples = boundary_info.build_side_list();
     420             :   std::vector<std::tuple<dof_id_type, boundary_id_type>>
     421         730 :     bc_tuples = boundary_info.build_node_list();
     422             : 
     423        1026 :   for (const auto & my_pid : ids_to_write)
     424             :     {
     425         334 :       auto file_name = split_file(name, use_n_procs, my_pid);
     426         808 :       Xdr io (file_name, this->binary() ? ENCODE : WRITE);
     427             : 
     428          36 :       std::set<const Elem *, CompareElemIdsByLevel> elements;
     429             : 
     430             :       // For serial files or for already-distributed meshs, we write
     431             :       // everything we can see.
     432         316 :       if (!_parallel || !mesh.is_serial())
     433         438 :         elements.insert(mesh.elements_begin(), mesh.elements_end());
     434             :       // For parallel files written from serial meshes we write what
     435             :       // we'd be required to keep if we were to be deleting remote
     436             :       // elements.  This allows us to write proper parallel files even
     437             :       // from a ReplicateMesh.
     438             :       //
     439             :       // WARNING: If we have a DistributedMesh which used
     440             :       // "add_extra_ghost_elem" rather than ghosting functors to
     441             :       // preserve elements and which is *also* currently serialized
     442             :       // then we're not preserving those elements here.  As a quick
     443             :       // workaround user code should delete_remote_elements() before
     444             :       // writing the checkpoint; as a long term workaround user code
     445             :       // should use ghosting functors instead of extra_ghost_elem
     446             :       // lists.
     447             :       else
     448             :         {
     449         276 :           for (processor_id_type p : {my_pid, DofObject::invalid_processor_id})
     450             :             {
     451         184 :               if (const auto elements_vec_it = elements_on_pid.find(p);
     452          16 :                   elements_vec_it != elements_on_pid.end())
     453             :                 {
     454           8 :                   auto & p_elements = elements_vec_it->second;
     455             : 
     456             :                   // Be compatible with both deprecated and
     457             :                   // corrected MeshBase iterator types
     458             :                   typedef MeshBase::const_element_iterator::value_type v_t;
     459             : 
     460          92 :                   v_t * elempp = p_elements.data();
     461          92 :                   v_t * elemend = elempp + p_elements.size();
     462             : 
     463             :                   const MeshBase::const_element_iterator
     464             :                     pid_elements_begin = MeshBase::const_element_iterator
     465         100 :                       (elempp, elemend, Predicates::NotNull<v_t *>()),
     466             :                     pid_elements_end = MeshBase::const_element_iterator
     467         100 :                       (elemend, elemend, Predicates::NotNull<v_t *>()),
     468             :                     active_pid_elements_begin = MeshBase::const_element_iterator
     469         100 :                       (elempp, elemend, Predicates::Active<v_t *>()),
     470             :                     active_pid_elements_end = MeshBase::const_element_iterator
     471         176 :                       (elemend, elemend, Predicates::Active<v_t *>());
     472             : 
     473             :                   query_ghosting_functors
     474         176 :                     (mesh, p, active_pid_elements_begin,
     475             :                      active_pid_elements_end, elements);
     476         176 :                   connect_children(mesh, pid_elements_begin,
     477             :                                    pid_elements_end, elements);
     478             :                 }
     479             :             }
     480             :         }
     481             : 
     482          36 :       connected_node_set_type connected_nodes;
     483         316 :       connect_element_dependencies(mesh, elements, connected_nodes);
     484             : 
     485             :       // write the nodal locations
     486         316 :       this->write_nodes (io, connected_nodes);
     487             : 
     488             :       // write connectivity
     489         316 :       this->write_connectivity (io, elements);
     490             : 
     491             :       // write remote_elem connectivity
     492         316 :       this->write_remote_elem (io, elements);
     493             : 
     494             :       // write the boundary condition information
     495         316 :       this->write_bcs (io, elements, bc_triples);
     496             : 
     497             :       // write the nodeset information
     498         316 :       this->write_nodesets (io, connected_nodes, bc_tuples);
     499             : 
     500             :       // close it up
     501         316 :       io.close();
     502         280 :     }
     503             : 
     504             :   // this->comm().barrier();
     505         710 : }
     506             : 
     507         120 : void CheckpointIO::write_subdomain_names(Xdr & io) const
     508             : {
     509             :   {
     510          20 :     const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
     511             : 
     512          10 :     const std::map<subdomain_id_type, std::string> & subdomain_map = mesh.get_subdomain_name_map();
     513             : 
     514         130 :     std::vector<largest_id_type> subdomain_ids;   subdomain_ids.reserve(subdomain_map.size());
     515         140 :     std::vector<std::string>  subdomain_names; subdomain_names.reserve(subdomain_map.size());
     516             : 
     517             :     // We need to loop over the map and make sure that there aren't any invalid entries.  Since we
     518             :     // return writable references in mesh_base, it's possible for the user to leave some entity names
     519             :     // blank.  We can't write those to the XDA file.
     520         120 :     largest_id_type n_subdomain_names = 0;
     521         120 :     for (const auto & [id, name] : subdomain_map)
     522           0 :       if (!name.empty())
     523             :         {
     524           0 :           n_subdomain_names++;
     525           0 :           subdomain_ids.push_back(id);
     526           0 :           subdomain_names.push_back(name);
     527             :         }
     528             : 
     529         120 :     io.data(n_subdomain_names, "# subdomain id to name map");
     530             :     // Write out the ids and names in two vectors
     531         120 :     if (n_subdomain_names)
     532             :       {
     533           0 :         io.data(subdomain_ids);
     534           0 :         io.data(subdomain_names);
     535             :       }
     536         100 :   }
     537         120 : }
     538             : 
     539             : 
     540             : 
     541         316 : void CheckpointIO::write_nodes (Xdr & io,
     542             :                                 const connected_node_set_type & nodeset) const
     543             : {
     544         316 :   largest_id_type n_nodes_here = nodeset.size();
     545             : 
     546         316 :   io.data(n_nodes_here, "# n_nodes on proc");
     547             : 
     548         316 :   const bool write_extra_integers = this->version_at_least_1_5();
     549             :   const unsigned int n_extra_integers =
     550         316 :     write_extra_integers ? MeshOutput<MeshBase>::mesh().n_node_integers() : 0;
     551             : 
     552             :   // Will hold the node id and pid and extra integers
     553         334 :   std::vector<largest_id_type> id_pid(2 + n_extra_integers);
     554             : 
     555             :   // For the coordinates
     556         334 :   std::vector<Real> coords(LIBMESH_DIM);
     557             : 
     558        5654 :   for (const auto & node : nodeset)
     559             :     {
     560        5338 :       id_pid[0] = node->id();
     561        5338 :       id_pid[1] = node->processor_id();
     562             : 
     563         358 :       libmesh_assert_equal_to(n_extra_integers, node->n_extra_integers());
     564       12230 :       for (unsigned int i=0; i != n_extra_integers; ++i)
     565        7348 :         id_pid[2+i] = node->get_extra_integer(i);
     566             : 
     567        5338 :       io.data_stream(id_pid.data(), 2 + n_extra_integers, 2 + n_extra_integers);
     568             : 
     569             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
     570        5338 :       largest_id_type unique_id = node->unique_id();
     571             : 
     572        5338 :       io.data(unique_id, "# unique id");
     573             : #endif
     574             : 
     575        5338 :       coords[0] = (*node)(0);
     576             : 
     577             : #if LIBMESH_DIM > 1
     578        5338 :       coords[1] = (*node)(1);
     579             : #endif
     580             : 
     581             : #if LIBMESH_DIM > 2
     582        5338 :       coords[2] = (*node)(2);
     583             : #endif
     584             : 
     585        5338 :       io.data_stream(coords.data(), LIBMESH_DIM, 3);
     586             :     }
     587         316 : }
     588             : 
     589             : 
     590             : 
     591         316 : void CheckpointIO::write_connectivity (Xdr & io,
     592             :                                        const std::set<const Elem *, CompareElemIdsByLevel> & elements) const
     593             : {
     594          18 :   libmesh_assert (io.writing());
     595             : 
     596         316 :   const bool write_extra_integers = this->version_at_least_1_5();
     597             :   const unsigned int n_extra_integers =
     598         316 :     write_extra_integers ? MeshOutput<MeshBase>::mesh().n_elem_integers() : 0;
     599             : 
     600             :   // Put these out here to reduce memory churn
     601             :   // id type pid subdomain_id parent_id extra_integer_0 ...
     602         334 :   std::vector<largest_id_type> elem_data(6 + n_extra_integers);
     603          36 :   std::vector<largest_id_type> conn_data;
     604             : 
     605         316 :   largest_id_type n_elems_here = elements.size();
     606             : 
     607         316 :   io.data(n_elems_here, "# number of elements");
     608             : 
     609        3060 :   for (const auto & elem : elements)
     610             :     {
     611        2744 :       unsigned int n_nodes = elem->n_nodes();
     612             : 
     613        2744 :       elem_data[0] = elem->id();
     614        2744 :       elem_data[1] = elem->type();
     615        2744 :       elem_data[2] = elem->processor_id();
     616        2744 :       elem_data[3] = elem->subdomain_id();
     617             : 
     618             : #ifdef LIBMESH_ENABLE_AMR
     619        2949 :       if (elem->parent() != nullptr)
     620             :         {
     621           0 :           elem_data[4] = elem->parent()->id();
     622           0 :           elem_data[5] = elem->parent()->which_child_am_i(elem);
     623             :         }
     624             :       else
     625             : #endif
     626             :         {
     627        2744 :           elem_data[4] = static_cast<largest_id_type>(-1);
     628        2744 :           elem_data[5] = static_cast<largest_id_type>(-1);
     629             :         }
     630             : 
     631        3839 :       for (unsigned int i=0; i != n_extra_integers; ++i)
     632        1173 :         elem_data[6+i] = elem->get_extra_integer(i);
     633             : 
     634        2744 :       conn_data.resize(n_nodes);
     635             : 
     636       15272 :       for (unsigned int i=0; i<n_nodes; i++)
     637       14222 :         conn_data[i] = elem->node_id(i);
     638             : 
     639        2949 :       io.data_stream(elem_data.data(),
     640             :                      cast_int<unsigned int>(elem_data.size()),
     641             :                      cast_int<unsigned int>(elem_data.size()));
     642             : 
     643             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
     644        2744 :       largest_id_type unique_id = elem->unique_id();
     645             : 
     646        2744 :       io.data(unique_id, "# unique id");
     647             : #endif
     648             : 
     649             : #ifdef LIBMESH_ENABLE_AMR
     650        2949 :       uint16_t p_level = cast_int<uint16_t>(elem->p_level());
     651        2744 :       io.data(p_level, "# p_level");
     652             : 
     653        2744 :       uint16_t rflag = elem->refinement_flag();
     654        2744 :       io.data(rflag, "# rflag");
     655             : 
     656        2744 :       uint16_t pflag = elem->p_refinement_flag();
     657        2744 :       io.data(pflag, "# pflag");
     658             : #endif
     659        2949 :       io.data_stream(conn_data.data(),
     660             :                      cast_int<unsigned int>(conn_data.size()),
     661             :                      cast_int<unsigned int>(conn_data.size()));
     662             :     }
     663         316 : }
     664             : 
     665             : 
     666         316 : void CheckpointIO::write_remote_elem (Xdr & io,
     667             :                                       const std::set<const Elem *, CompareElemIdsByLevel> & elements) const
     668             : {
     669          18 :   libmesh_assert (io.writing());
     670             : 
     671             :   // Find the remote_elem neighbor and child links
     672          36 :   std::vector<largest_id_type> elem_ids, parent_ids;
     673          36 :   std::vector<uint16_t> elem_sides, child_numbers;
     674             : 
     675        3060 :   for (const auto & elem : elements)
     676             :     {
     677       14533 :       for (auto n : elem->side_index_range())
     678             :         {
     679       11584 :           const Elem * neigh = elem->neighbor_ptr(n);
     680       12378 :           if (neigh == remote_elem ||
     681         794 :               (neigh && !elements.count(neigh)))
     682             :             {
     683         708 :               elem_ids.push_back(elem->id());
     684         708 :               elem_sides.push_back(n);
     685             :             }
     686             :         }
     687             : 
     688             : #ifdef LIBMESH_ENABLE_AMR
     689        2744 :       if (elem->has_children())
     690             :         {
     691           0 :           for (unsigned short c = 0,
     692           0 :                nc = cast_int<unsigned short>(elem->n_children());
     693           0 :                c != nc; ++c)
     694             :             {
     695           0 :               const Elem * child = elem->child_ptr(c);
     696           0 :               if (child == remote_elem ||
     697           0 :                   (child && !elements.count(child)))
     698             :                 {
     699           0 :                   parent_ids.push_back(elem->id());
     700           0 :                   child_numbers.push_back(c);
     701             :                 }
     702             :             }
     703             :         }
     704             : #endif
     705             :     }
     706             : 
     707         316 :   io.data(elem_ids, "# remote neighbor elem_ids");
     708         316 :   io.data(elem_sides, "# remote neighbor elem_sides");
     709         316 :   io.data(parent_ids, "# remote child parent_ids");
     710         316 :   io.data(child_numbers, "# remote child_numbers");
     711         316 : }
     712             : 
     713             : 
     714             : 
     715         316 : void CheckpointIO::write_bcs (Xdr & io,
     716             :                               const std::set<const Elem *, CompareElemIdsByLevel> & elements,
     717             :                               const std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>> & bc_triples) const
     718             : {
     719          18 :   libmesh_assert (io.writing());
     720             : 
     721             :   // Build a list of (elem, side, bc) tuples.
     722          36 :   std::size_t bc_size = bc_triples.size();
     723             : 
     724          36 :   std::vector<largest_id_type> element_id_list;
     725          36 :   std::vector<uint16_t> side_list;
     726          36 :   std::vector<largest_id_type> bc_id_list;
     727             : 
     728         316 :   element_id_list.reserve(bc_size);
     729         316 :   side_list.reserve(bc_size);
     730         316 :   bc_id_list.reserve(bc_size);
     731             : 
     732          18 :   std::unordered_set<dof_id_type> elems;
     733        3060 :   for (auto & e : elements)
     734        2744 :     elems.insert(e->id());
     735             : 
     736        4012 :   for (const auto & t : bc_triples)
     737         468 :     if (elems.count(std::get<0>(t)))
     738             :       {
     739        3168 :         element_id_list.push_back(std::get<0>(t));
     740        3168 :         side_list.push_back(std::get<1>(t));
     741        3168 :         bc_id_list.push_back(std::get<2>(t));
     742             :       }
     743             : 
     744             : 
     745         316 :   io.data(element_id_list, "# element ids for bcs");
     746         316 :   io.data(side_list, "# sides of elements for bcs");
     747         316 :   io.data(bc_id_list, "# bc ids");
     748         316 : }
     749             : 
     750             : 
     751             : 
     752         316 : void CheckpointIO::write_nodesets (Xdr & io,
     753             :                                    const connected_node_set_type & nodeset,
     754             :                                    const std::vector<std::tuple<dof_id_type, boundary_id_type>> & bc_tuples) const
     755             : {
     756          18 :   libmesh_assert (io.writing());
     757             : 
     758             :   // convenient reference to our mesh
     759          36 :   const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
     760             : 
     761             :   // Build a list of (node, bc) tuples
     762          36 :   std::size_t nodeset_size = bc_tuples.size();
     763             : 
     764          36 :   std::vector<largest_id_type> node_id_list;
     765          18 :   std::vector<largest_id_type> bc_id_list;
     766             : 
     767         316 :   node_id_list.reserve(nodeset_size);
     768         316 :   bc_id_list.reserve(nodeset_size);
     769             : 
     770        6216 :   for (const auto & t : bc_tuples)
     771        5956 :     if (nodeset.count(mesh.node_ptr(std::get<0>(t))))
     772             :       {
     773        5284 :         node_id_list.push_back(std::get<0>(t));
     774        5284 :         bc_id_list.push_back(std::get<1>(t));
     775             :       }
     776             : 
     777         316 :   io.data(node_id_list, "# node id list");
     778         316 :   io.data(bc_id_list, "# nodeset bc id list");
     779         316 : }
     780             : 
     781             : 
     782             : 
     783         240 : void CheckpointIO::write_bc_names (Xdr & io, const BoundaryInfo & info, bool is_sideset) const
     784             : {
     785         240 :   const std::map<boundary_id_type, std::string> & boundary_map = is_sideset ?
     786          20 :     info.get_sideset_name_map() : info.get_nodeset_name_map();
     787             : 
     788         260 :   std::vector<largest_id_type> boundary_ids;   boundary_ids.reserve(boundary_map.size());
     789         280 :   std::vector<std::string>  boundary_names; boundary_names.reserve(boundary_map.size());
     790             : 
     791             :   // We need to loop over the map and make sure that there aren't any invalid entries.  Since we
     792             :   // return writable references in boundary_info, it's possible for the user to leave some entity names
     793             :   // blank.  We can't write those to the XDA file.
     794         240 :   largest_id_type n_boundary_names = 0;
     795        1200 :   for (const auto & [id, name] : boundary_map)
     796         960 :     if (!name.empty())
     797             :       {
     798         960 :         n_boundary_names++;
     799         960 :         boundary_ids.push_back(id);
     800         960 :         boundary_names.push_back(name);
     801             :       }
     802             : 
     803         240 :   if (is_sideset)
     804         120 :     io.data(n_boundary_names, "# sideset id to name map");
     805             :   else
     806         120 :     io.data(n_boundary_names, "# nodeset id to name map");
     807             :   // Write out the ids and names in two vectors
     808         240 :   if (n_boundary_names)
     809             :     {
     810         240 :       io.data(boundary_ids);
     811         240 :       io.data(boundary_names);
     812             :     }
     813         440 : }
     814             : 
     815         710 : void CheckpointIO::read (const std::string & input_name)
     816             : {
     817          40 :   LOG_SCOPE("read()","CheckpointIO");
     818             : 
     819         710 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
     820             : 
     821          20 :   libmesh_assert(!mesh.n_elem());
     822             : 
     823             :   header_id_type data_size;
     824         710 :   processor_id_type input_n_procs = select_split_config(input_name, data_size);
     825         730 :   auto header_name = header_file(input_name, input_n_procs);
     826          20 :   bool input_parallel = input_n_procs > 0;
     827             : 
     828             :   // If this is a serial read then we're going to only read the mesh
     829             :   // on processor 0, then broadcast it
     830         710 :   if ((input_parallel && !mesh.is_replicated()) || mesh.processor_id() == 0)
     831             :     {
     832             :       // If we're trying to read a parallel checkpoint file on a
     833             :       // replicated mesh, we'll read every file on processor 0 so we
     834             :       // can broadcast it later.  If we're on a distributed mesh then
     835             :       // we'll read every id to it's own processor and we'll "wrap
     836             :       // around" with any ids that exceed our processor count.
     837             :       const processor_id_type begin_proc_id =
     838         468 :         (input_parallel && !mesh.is_replicated()) ?
     839          16 :         mesh.processor_id() : 0;
     840             :       const processor_id_type stride =
     841         468 :         (input_parallel && !mesh.is_replicated()) ?
     842          16 :         mesh.n_processors() : 1;
     843             : 
     844         784 :       for (processor_id_type proc_id = begin_proc_id; proc_id < input_n_procs;
     845         316 :            proc_id = cast_int<processor_id_type>(proc_id + stride))
     846             :         {
     847         334 :           auto file_name = split_file(input_name, input_n_procs, proc_id);
     848             : 
     849             :           {
     850         352 :             std::ifstream in (file_name.c_str());
     851             : 
     852         316 :             libmesh_error_msg_if(!in.good(), "ERROR: cannot locate specified file:\n\t" << file_name);
     853         280 :           }
     854             : 
     855             :           // Do we expect all our files' remote_elem entries to really
     856             :           // be remote?  Only if we're not reading multiple input
     857             :           // files on the same processor.
     858             :           const bool expect_all_remote =
     859         642 :             (input_n_procs <= mesh.n_processors() &&
     860         308 :              !mesh.is_replicated());
     861             : 
     862         510 :           Xdr io (file_name, this->binary() ? DECODE : READ);
     863             : 
     864         316 :           switch (data_size) {
     865           0 :           case 2:
     866           0 :             this->read_subfile<uint16_t>(io, expect_all_remote);
     867           0 :             break;
     868           0 :           case 4:
     869           0 :             this->read_subfile<uint32_t>(io, expect_all_remote);
     870           0 :             break;
     871         316 :           case 8:
     872         316 :             this->read_subfile<uint64_t>(io, expect_all_remote);
     873          18 :             break;
     874           0 :           default:
     875           0 :             libmesh_error();
     876             :           }
     877             : 
     878         316 :           io.close();
     879         280 :         }
     880             :     }
     881             : 
     882             :   // If the mesh was only read on processor 0 then we need to broadcast it
     883         710 :   if (mesh.is_replicated())
     884         298 :     MeshCommunication().broadcast(mesh);
     885             :   // If the mesh is really distributed then we need to make sure it
     886             :   // knows that
     887         420 :   else if (mesh.n_processors() > 1)
     888         402 :     mesh.set_distributed();
     889             : 
     890             :   // If the mesh isn't getting even critical partitioning then we
     891             :   // should update cached data from the partitioning we just read in
     892         710 :   if (mesh.skip_partitioning())
     893             :     {
     894           0 :       mesh.recalculate_n_partitions();
     895           0 :       mesh.update_post_partitioning();
     896             :     }
     897         710 : }
     898             : 
     899             : 
     900             : 
     901             : template <typename file_id_type>
     902         710 : file_id_type CheckpointIO::read_header (const std::string & name)
     903             : {
     904         710 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
     905             : 
     906             :   // Hack for codes which don't look at all elem dimensions
     907             :   uint16_t mesh_dimension;
     908             : 
     909             :   // Will this be a parallel input file?  With how many processors?  Stay tuned!
     910             :   uint16_t input_parallel;
     911             :   file_id_type input_n_procs;
     912             : 
     913          60 :   std::vector<std::string> node_integer_names, elem_integer_names;
     914             : 
     915             :   // We'll write a header file from processor 0 and broadcast.
     916         730 :   if (this->processor_id() == 0)
     917             :     {
     918         310 :       Xdr io (name, this->binary() ? DECODE : READ);
     919             : 
     920             :       // read the version, but don't care about it
     921          20 :       std::string input_version;
     922         120 :       io.data(input_version);
     923             : 
     924             :       // read the data type, don't care about it this time
     925             :       header_id_type data_size;
     926         120 :       io.data (data_size);
     927             : 
     928             :       // read the dimension
     929         120 :       io.data (mesh_dimension);
     930             : 
     931             :       // Read whether or not this is a parallel file
     932         120 :       io.data(input_parallel);
     933             : 
     934             :       // With how many processors?
     935         120 :       if (input_parallel)
     936         112 :         io.data(input_n_procs);
     937             : 
     938             :       // read subdomain names
     939         120 :       this->read_subdomain_names<file_id_type>(io);
     940             : 
     941             :       // read boundary names
     942          10 :       BoundaryInfo & boundary_info = mesh.get_boundary_info();
     943             : 
     944         120 :       this->read_bc_names<file_id_type>(io, boundary_info, true);  // sideset names
     945         120 :       this->read_bc_names<file_id_type>(io, boundary_info, false); // nodeset names
     946             : 
     947             :       // read extra integer names?
     948          10 :       std::swap(input_version, this->version());
     949         120 :       const bool read_extra_integers = this->version_at_least_1_5();
     950          10 :       std::swap(input_version, this->version());
     951             : 
     952         120 :       if (read_extra_integers)
     953          20 :         this->read_integers_names<file_id_type>
     954         100 :           (io, node_integer_names, elem_integer_names);
     955         100 :     }
     956             : 
     957             :   // broadcast data from processor 0, set values everywhere
     958         710 :   this->comm().broadcast(mesh_dimension);
     959        1400 :   mesh.set_mesh_dimension(cast_int<unsigned char>(mesh_dimension));
     960             : 
     961         710 :   this->comm().broadcast(input_parallel);
     962             : 
     963         710 :   if (input_parallel)
     964         696 :     this->comm().broadcast(input_n_procs);
     965             :   else
     966          14 :     input_n_procs = 1;
     967             : 
     968             :   std::map<subdomain_id_type, std::string> & subdomain_map =
     969          20 :     mesh.set_subdomain_name_map();
     970         710 :   this->comm().broadcast(subdomain_map);
     971             : 
     972          20 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
     973         710 :   this->comm().broadcast(boundary_info.set_sideset_name_map());
     974         710 :   this->comm().broadcast(boundary_info.set_nodeset_name_map());
     975             : 
     976         710 :   this->comm().broadcast(node_integer_names);
     977         710 :   this->comm().broadcast(elem_integer_names);
     978             : 
     979        1298 :   for (auto & int_name : node_integer_names)
     980        1152 :     mesh.add_node_integer(int_name);
     981             : 
     982        1004 :   for (auto & int_name : elem_integer_names)
     983         576 :     mesh.add_elem_integer(int_name);
     984             : 
     985        1420 :   return input_parallel ? input_n_procs : 0;
     986         670 : }
     987             : 
     988             : 
     989             : 
     990             : template <typename file_id_type>
     991         316 : void CheckpointIO::read_subfile (Xdr & io, bool expect_all_remote)
     992             : {
     993             :   // read the nodal locations
     994         316 :   this->read_nodes<file_id_type> (io);
     995             : 
     996             :   // read connectivity
     997         316 :   this->read_connectivity<file_id_type> (io);
     998             : 
     999             :   // read remote_elem connectivity
    1000         316 :   this->read_remote_elem<file_id_type> (io, expect_all_remote);
    1001             : 
    1002             :   // read the boundary conditions
    1003         316 :   this->read_bcs<file_id_type> (io);
    1004             : 
    1005             :   // read the nodesets
    1006         316 :   this->read_nodesets<file_id_type> (io);
    1007         316 : }
    1008             : 
    1009             : 
    1010             : 
    1011             : template <typename file_id_type>
    1012         120 : void CheckpointIO::read_subdomain_names(Xdr & io)
    1013             : {
    1014         120 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
    1015             : 
    1016             :   std::map<subdomain_id_type, std::string> & subdomain_map =
    1017          10 :     mesh.set_subdomain_name_map();
    1018             : 
    1019          20 :   std::vector<file_id_type> subdomain_ids;
    1020         120 :   subdomain_ids.reserve(subdomain_map.size());
    1021             : 
    1022          30 :   std::vector<std::string>  subdomain_names;
    1023         120 :   subdomain_names.reserve(subdomain_map.size());
    1024             : 
    1025         120 :   file_id_type n_subdomain_names = 0;
    1026         120 :   io.data(n_subdomain_names, "# subdomain id to name map");
    1027             : 
    1028         120 :   if (n_subdomain_names)
    1029             :     {
    1030           0 :       io.data(subdomain_ids);
    1031           0 :       io.data(subdomain_names);
    1032             : 
    1033           0 :       for (auto i : index_range(subdomain_ids))
    1034           0 :         subdomain_map[cast_int<subdomain_id_type>(subdomain_ids[i])] =
    1035           0 :           subdomain_names[i];
    1036             :     }
    1037         220 : }
    1038             : 
    1039             : 
    1040             : 
    1041             : template <typename file_id_type>
    1042         316 : void CheckpointIO::read_nodes (Xdr & io)
    1043             : {
    1044             :   // convenient reference to our mesh
    1045         316 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
    1046             : 
    1047             :   file_id_type n_nodes_here;
    1048         316 :   io.data(n_nodes_here, "# n_nodes on proc");
    1049             : 
    1050         316 :   const bool read_extra_integers = this->version_at_least_1_5();
    1051             : 
    1052          36 :   const unsigned int n_extra_integers =
    1053         298 :     read_extra_integers ? mesh.n_node_integers() : 0;
    1054             : 
    1055             :   // Will hold the node id and pid and extra integers
    1056         334 :   std::vector<file_id_type> id_pid(2 + n_extra_integers);
    1057             : 
    1058             :   // For the coordinates
    1059         334 :   std::vector<Real> coords(LIBMESH_DIM);
    1060             : 
    1061        5654 :   for (unsigned int i=0; i<n_nodes_here; i++)
    1062             :     {
    1063        5338 :       io.data_stream(id_pid.data(), 2 + n_extra_integers, 2 + n_extra_integers);
    1064             : 
    1065             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    1066        5338 :       file_id_type unique_id = 0;
    1067        5338 :       io.data(unique_id, "# unique id");
    1068             : #endif
    1069             : 
    1070        5338 :       io.data_stream(coords.data(), LIBMESH_DIM, LIBMESH_DIM);
    1071             : 
    1072         358 :       Point p;
    1073        5338 :       p(0) = coords[0];
    1074             : 
    1075             : #if LIBMESH_DIM > 1
    1076        5338 :       p(1) = coords[1];
    1077             : #endif
    1078             : 
    1079             : #if LIBMESH_DIM > 2
    1080        5338 :       p(2) = coords[2];
    1081             : #endif
    1082             : 
    1083        5338 :       const dof_id_type id = cast_int<dof_id_type>(id_pid[0]);
    1084             : 
    1085             :       // "Wrap around" if we see more processors than we're using.
    1086         358 :       processor_id_type pid =
    1087        5696 :         cast_int<processor_id_type>(id_pid[1] % mesh.n_processors());
    1088             : 
    1089             :       // If we already have this node (e.g. from another file, when
    1090             :       // reading multiple distributed CheckpointIO files into a
    1091             :       // ReplicatedMesh) then we don't want to add it again (because
    1092             :       // ReplicatedMesh can't handle that) but we do want to assert
    1093             :       // consistency between what we're reading and what we have.
    1094        5338 :       const Node * old_node = mesh.query_node_ptr(id);
    1095             : 
    1096        5338 :       if (old_node)
    1097             :         {
    1098          60 :           libmesh_assert_equal_to(pid, old_node->processor_id());
    1099             : 
    1100          60 :           libmesh_assert_equal_to(n_extra_integers, old_node->n_extra_integers());
    1101             : #ifndef NDEBUG
    1102          60 :           for (unsigned int ei=0; ei != n_extra_integers; ++ei)
    1103             :             {
    1104           0 :               const dof_id_type extra_int = cast_int<dof_id_type>(id_pid[2+ei]);
    1105           0 :               libmesh_assert_equal_to(extra_int, old_node->get_extra_integer(ei));
    1106             :             }
    1107             : #endif
    1108             : 
    1109             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    1110          60 :           libmesh_assert_equal_to(unique_id, old_node->unique_id());
    1111             : #endif
    1112             :         }
    1113             :       else
    1114             :         {
    1115             :           Node * node =
    1116        4672 :             mesh.add_point(p, id, pid);
    1117             : 
    1118             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    1119        4672 :           node->set_unique_id(unique_id);
    1120             : #endif
    1121             : 
    1122         298 :           libmesh_assert_equal_to(n_extra_integers, node->n_extra_integers());
    1123             : 
    1124       11564 :           for (unsigned int ei=0; ei != n_extra_integers; ++ei)
    1125             :             {
    1126        7120 :               const dof_id_type extra_int = cast_int<dof_id_type>(id_pid[2+ei]);
    1127        6892 :               node->set_extra_integer(ei, extra_int);
    1128             :             }
    1129             :         }
    1130             :     }
    1131         316 : }
    1132             : 
    1133             : 
    1134             : 
    1135             : template <typename file_id_type>
    1136         316 : void CheckpointIO::read_connectivity (Xdr & io)
    1137             : {
    1138             :   // convenient reference to our mesh
    1139         316 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
    1140             : 
    1141         316 :   const bool read_extra_integers = this->version_at_least_1_5();
    1142             : 
    1143          36 :   const unsigned int n_extra_integers =
    1144         298 :     read_extra_integers ? mesh.n_elem_integers() : 0;
    1145             : 
    1146             :   file_id_type n_elems_here;
    1147         316 :   io.data(n_elems_here);
    1148             : 
    1149             :   // Keep track of the highest dimensional element we've added to the mesh
    1150          18 :   unsigned int highest_elem_dim = 1;
    1151             : 
    1152             :   // RHS: Originally we used invalid_processor_id as a "no parent" tag
    1153             :   // number, because I'm an idiot.  Let's try to support broken files
    1154             :   // as much as possible.
    1155          18 :   bool file_is_broken = false;
    1156             : 
    1157        3060 :   for (unsigned int i=0; i<n_elems_here; i++)
    1158             :     {
    1159             :       // id type pid subdomain_id parent_id
    1160        2949 :       std::vector<file_id_type> elem_data(6 + n_extra_integers);
    1161         615 :       io.data_stream
    1162        2334 :         (elem_data.data(), cast_int<unsigned int>(elem_data.size()),
    1163             :          cast_int<unsigned int>(elem_data.size()));
    1164             : 
    1165             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    1166        2744 :       file_id_type unique_id = 0;
    1167        2744 :       io.data(unique_id, "# unique id");
    1168             : #endif
    1169             : 
    1170             : #ifdef LIBMESH_ENABLE_AMR
    1171        2744 :       uint16_t p_level = 0;
    1172        2744 :       io.data(p_level, "# p_level");
    1173             : 
    1174             :       uint16_t rflag, pflag;
    1175        2744 :       io.data(rflag, "# rflag");
    1176        2744 :       io.data(pflag, "# pflag");
    1177             : #endif
    1178             : 
    1179        2744 :       unsigned int n_nodes = Elem::type_to_n_nodes_map[elem_data[1]];
    1180        2744 :       if (n_nodes == invalid_uint)
    1181           0 :         libmesh_not_implemented_msg("Support for Polygons/Polyhedra not yet implemented");
    1182             : 
    1183             :       // Snag the node ids this element was connected to
    1184        2949 :       std::vector<file_id_type> conn_data(n_nodes);
    1185         615 :       io.data_stream
    1186        2334 :         (conn_data.data(), cast_int<unsigned int>(conn_data.size()),
    1187             :          cast_int<unsigned int>(conn_data.size()));
    1188             : 
    1189             :       const dof_id_type id                 =
    1190        2744 :         cast_int<dof_id_type>      (elem_data[0]);
    1191        2744 :       const ElemType elem_type             =
    1192        2539 :         static_cast<ElemType>      (elem_data[1]);
    1193         205 :       const processor_id_type proc_id      =
    1194             :         cast_int<processor_id_type>
    1195        2949 :         (elem_data[2] % mesh.n_processors());
    1196         205 :       const subdomain_id_type subdomain_id =
    1197        2744 :         cast_int<subdomain_id_type>(elem_data[3]);
    1198             : 
    1199             :       // Old broken files used processsor_id_type(-1)...
    1200             :       // But we *know* our first element will be level 0
    1201        2744 :       if (i == 0 && elem_data[4] == 65535)
    1202           0 :         file_is_broken = true;
    1203             : 
    1204             :       // On a broken file we can't tell whether a parent of 65535 is a
    1205             :       // null parent or an actual parent of 65535.  Assuming the
    1206             :       // former will cause less breakage.
    1207         205 :       Elem * parent =
    1208         205 :         (elem_data[4] == static_cast<largest_id_type>(-1) ||
    1209        2744 :          (file_is_broken && elem_data[4] == 65535)) ?
    1210           0 :         nullptr : mesh.elem_ptr(cast_int<dof_id_type>(elem_data[4]));
    1211             : 
    1212         205 :       const unsigned short int child_num   =
    1213         205 :         (elem_data[5] == static_cast<largest_id_type>(-1) ||
    1214        2744 :          (file_is_broken && elem_data[5] == 65535)) ?
    1215             :         static_cast<unsigned short>(-1) :
    1216           0 :         cast_int<unsigned short>(elem_data[5]);
    1217             : 
    1218         205 :       if (!parent)
    1219         205 :         libmesh_assert_equal_to
    1220             :           (child_num, static_cast<unsigned short>(-1));
    1221             : 
    1222        2744 :       Elem * old_elem = mesh.query_elem_ptr(id);
    1223             : 
    1224             :       // If we already have this element (e.g. from another file,
    1225             :       // when reading multiple distributed CheckpointIO files into
    1226             :       // a ReplicatedMesh) then we don't want to add it again
    1227             :       // (because ReplicatedMesh can't handle that) but we do want
    1228             :       // to assert consistency between what we're reading and what
    1229             :       // we have.
    1230        2744 :       if (old_elem)
    1231             :         {
    1232          32 :           libmesh_assert_equal_to(elem_type, old_elem->type());
    1233          32 :           libmesh_assert_equal_to(proc_id, old_elem->processor_id());
    1234          32 :           libmesh_assert_equal_to(subdomain_id, old_elem->subdomain_id());
    1235          32 :           if (parent)
    1236           0 :             libmesh_assert_equal_to(parent, old_elem->parent());
    1237             :           else
    1238          32 :             libmesh_assert(!old_elem->parent());
    1239             : 
    1240          32 :           libmesh_assert_equal_to(n_extra_integers, old_elem->n_extra_integers());
    1241             : #ifndef NDEBUG
    1242          32 :           for (unsigned int ei=0; ei != n_extra_integers; ++ei)
    1243             :             {
    1244           0 :               const dof_id_type extra_int = cast_int<dof_id_type>(elem_data[6+ei]);
    1245           0 :               libmesh_assert_equal_to(extra_int, old_elem->get_extra_integer(ei));
    1246             :             }
    1247             : #endif
    1248             : 
    1249          32 :           libmesh_assert_equal_to(old_elem->n_nodes(), conn_data.size());
    1250             : 
    1251         128 :           for (unsigned int n=0,
    1252          32 :                n_conn = cast_int<unsigned int>(conn_data.size());
    1253         160 :                n != n_conn; n++)
    1254         128 :             libmesh_assert_equal_to
    1255             :               (old_elem->node_id(n),
    1256             :                cast_int<dof_id_type>(conn_data[n]));
    1257             :         }
    1258             :       else
    1259             :         {
    1260             :           // Create the element
    1261        2561 :           auto elem = Elem::build(elem_type, parent);
    1262             : 
    1263             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    1264        2388 :           elem->set_unique_id(unique_id);
    1265             : #endif
    1266             : 
    1267        2388 :           if (elem->dim() > highest_elem_dim)
    1268         232 :             highest_elem_dim = elem->dim();
    1269             : 
    1270        2388 :           elem->set_id()       = id;
    1271        2388 :           elem->processor_id() = proc_id;
    1272        2388 :           elem->subdomain_id() = subdomain_id;
    1273             : 
    1274             : #ifdef LIBMESH_ENABLE_AMR
    1275        2388 :           elem->hack_p_level(p_level);
    1276             : 
    1277        2388 :           elem->set_refinement_flag  (cast_int<Elem::RefinementState>(rflag));
    1278        2388 :           elem->set_p_refinement_flag(cast_int<Elem::RefinementState>(pflag));
    1279             : 
    1280             :           // Set parent connections
    1281        2388 :           if (parent)
    1282             :             {
    1283             :               // We must specify a child_num, because we will have
    1284             :               // skipped adding any preceding remote_elem children
    1285           0 :               parent->add_child(elem.get(), child_num);
    1286             :             }
    1287             : #else
    1288             :           libmesh_ignore(child_num);
    1289             : #endif
    1290             : 
    1291         173 :           libmesh_assert(elem->n_nodes() == conn_data.size());
    1292             : 
    1293             :           // Connect all the nodes to this element
    1294       11104 :           for (unsigned int n=0,
    1295         346 :                n_conn = cast_int<unsigned int>(conn_data.size());
    1296       13492 :                n != n_conn; n++)
    1297       12542 :             elem->set_node(n,
    1298       11104 :               mesh.node_ptr(cast_int<dof_id_type>(conn_data[n])));
    1299             : 
    1300        2734 :           Elem * added_elem = mesh.add_elem(std::move(elem));
    1301             : 
    1302         173 :           libmesh_assert_equal_to(n_extra_integers, added_elem->n_extra_integers());
    1303        3483 :           for (unsigned int ei=0; ei != n_extra_integers; ++ei)
    1304             :             {
    1305        1134 :               const dof_id_type extra_int = cast_int<dof_id_type>(elem_data[6+ei]);
    1306        1095 :               added_elem->set_extra_integer(ei, extra_int);
    1307             :             }
    1308        2042 :         }
    1309             :     }
    1310             : 
    1311         316 :   mesh.set_mesh_dimension(cast_int<unsigned char>(highest_elem_dim));
    1312         316 : }
    1313             : 
    1314             : 
    1315             : template <typename file_id_type>
    1316         316 : void CheckpointIO::read_remote_elem (Xdr & io, bool libmesh_dbg_var(expect_all_remote))
    1317             : {
    1318             :   // convenient reference to our mesh
    1319         316 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
    1320             : 
    1321             :   // Find the remote_elem neighbor links
    1322          36 :   std::vector<file_id_type> elem_ids;
    1323          36 :   std::vector<uint16_t> elem_sides;
    1324             : 
    1325         316 :   io.data(elem_ids, "# remote neighbor elem_ids");
    1326         316 :   io.data(elem_sides, "# remote neighbor elem_sides");
    1327             : 
    1328          18 :   libmesh_assert_equal_to(elem_ids.size(), elem_sides.size());
    1329             : 
    1330        1024 :   for (auto i : index_range(elem_ids))
    1331             :     {
    1332         836 :       Elem & elem = mesh.elem_ref(cast_int<dof_id_type>(elem_ids[i]));
    1333         836 :       if (!elem.neighbor_ptr(elem_sides[i]))
    1334         708 :         elem.set_neighbor(elem_sides[i],
    1335             :                           const_cast<RemoteElem *>(remote_elem));
    1336             :       else
    1337           0 :         libmesh_assert(!expect_all_remote);
    1338             :     }
    1339             : 
    1340             :   // Find the remote_elem children links
    1341          36 :   std::vector<file_id_type> parent_ids;
    1342          36 :   std::vector<uint16_t> child_numbers;
    1343             : 
    1344         316 :   io.data(parent_ids, "# remote child parent_ids");
    1345         316 :   io.data(child_numbers, "# remote child_numbers");
    1346             : 
    1347             : #ifdef LIBMESH_ENABLE_AMR
    1348         316 :   for (auto i : index_range(parent_ids))
    1349             :     {
    1350           0 :       Elem & elem = mesh.elem_ref(cast_int<dof_id_type>(parent_ids[i]));
    1351             : 
    1352             :       // We'd like to assert that no child pointer already exists to
    1353             :       // be overwritten by remote_elem, but Elem doesn't actually have
    1354             :       // an API that will return a child pointer without asserting
    1355             :       // that it isn't nullptr.
    1356           0 :       const Elem * child = elem.raw_child_ptr(child_numbers[i]);
    1357             : 
    1358           0 :       if (!child)
    1359           0 :         elem.add_child(const_cast<RemoteElem *>(remote_elem),
    1360           0 :                        child_numbers[i]);
    1361             :       else
    1362           0 :         libmesh_assert(!expect_all_remote);
    1363             :     }
    1364             : #endif
    1365         316 : }
    1366             : 
    1367             : 
    1368             : 
    1369             : template <typename file_id_type>
    1370         316 : void CheckpointIO::read_bcs (Xdr & io)
    1371             : {
    1372             :   // convenient reference to our mesh
    1373         316 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
    1374             : 
    1375             :   // and our boundary info object
    1376          18 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
    1377             : 
    1378          36 :   std::vector<file_id_type> element_id_list;
    1379          36 :   std::vector<uint16_t> side_list;
    1380          36 :   std::vector<file_id_type> bc_id_list;
    1381             : 
    1382         316 :   io.data(element_id_list, "# element ids for bcs");
    1383         316 :   io.data(side_list, "# sides of elements for bcs");
    1384         316 :   io.data(bc_id_list, "# bc ids");
    1385             : 
    1386        3484 :   for (auto i : index_range(element_id_list))
    1387         558 :     boundary_info.add_side
    1388        3354 :       (cast_int<dof_id_type>(element_id_list[i]), side_list[i],
    1389        3354 :        cast_int<boundary_id_type>(bc_id_list[i]));
    1390         316 : }
    1391             : 
    1392             : 
    1393             : 
    1394             : template <typename file_id_type>
    1395         316 : void CheckpointIO::read_nodesets (Xdr & io)
    1396             : {
    1397             :   // convenient reference to our mesh
    1398         316 :   MeshBase & mesh = MeshInput<MeshBase>::mesh();
    1399             : 
    1400             :   // and our boundary info object
    1401          18 :   BoundaryInfo & boundary_info = mesh.get_boundary_info();
    1402             : 
    1403          36 :   std::vector<file_id_type> node_id_list;
    1404          36 :   std::vector<file_id_type> bc_id_list;
    1405             : 
    1406         316 :   io.data(node_id_list, "# node id list");
    1407         316 :   io.data(bc_id_list, "# nodeset bc id list");
    1408             : 
    1409        5600 :   for (auto i : index_range(node_id_list))
    1410         792 :     boundary_info.add_node
    1411        5284 :       (cast_int<dof_id_type>(node_id_list[i]),
    1412        5548 :        cast_int<boundary_id_type>(bc_id_list[i]));
    1413         316 : }
    1414             : 
    1415             : 
    1416             : 
    1417             : template <typename file_id_type>
    1418         240 : void CheckpointIO::read_bc_names(Xdr & io, BoundaryInfo & info, bool is_sideset)
    1419             : {
    1420         240 :   std::map<boundary_id_type, std::string> & boundary_map = is_sideset ?
    1421          20 :     info.set_sideset_name_map() : info.set_nodeset_name_map();
    1422             : 
    1423          40 :   std::vector<file_id_type> boundary_ids;
    1424          60 :   std::vector<std::string>  boundary_names;
    1425             : 
    1426         240 :   file_id_type n_boundary_names = 0;
    1427             : 
    1428         240 :   if (is_sideset)
    1429         120 :     io.data(n_boundary_names, "# sideset id to name map");
    1430             :   else
    1431         120 :     io.data(n_boundary_names, "# nodeset id to name map");
    1432             : 
    1433         240 :   if (n_boundary_names)
    1434             :     {
    1435         240 :       io.data(boundary_ids);
    1436         240 :       io.data(boundary_names);
    1437             :     }
    1438             : 
    1439             :   // Add them back into the map
    1440        1200 :   for (auto i : index_range(boundary_ids))
    1441        2000 :     boundary_map[cast_int<boundary_id_type>(boundary_ids[i])] =
    1442         160 :       boundary_names[i];
    1443         440 : }
    1444             : 
    1445             : 
    1446             : template <typename file_id_type>
    1447         120 : void CheckpointIO::read_integers_names
    1448             :   (Xdr & io,
    1449             :    std::vector<std::string> & node_integer_names,
    1450             :    std::vector<std::string> & elem_integer_names)
    1451             : {
    1452             :   file_id_type n_node_integers, n_elem_integers;
    1453             : 
    1454         120 :   io.data(n_node_integers, "# n_extra_integers per node");
    1455         120 :   io.data(node_integer_names);
    1456         120 :   io.data(n_elem_integers, "# n_extra_integers per elem");
    1457         120 :   io.data(elem_integer_names);
    1458         120 : }
    1459             : 
    1460             : 
    1461           0 : unsigned int CheckpointIO::n_active_levels_in(MeshBase::const_element_iterator begin,
    1462             :                                               MeshBase::const_element_iterator end) const
    1463             : {
    1464           0 :   unsigned int max_level = 0;
    1465             : 
    1466           0 :   for (const auto & elem : as_range(begin, end))
    1467           0 :     max_level = std::max(elem->level(), max_level);
    1468             : 
    1469           0 :   return max_level + 1;
    1470             : }
    1471             : 
    1472             : } // namespace libMesh

Generated by: LCOV version 1.14