LCOV - code coverage report
Current view: top level - src/systems - system_io.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4509 (b8210d) with base 165cb7 Lines: 570 775 73.5 %
Date: 2026-08-01 00:41:51 Functions: 36 48 75.0 %
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             : 
      19             : #include "libmesh/libmesh_common.h"
      20             : #include "libmesh/parallel.h"
      21             : 
      22             : 
      23             : // Local Include
      24             : #include "libmesh/libmesh_version.h"
      25             : #include "libmesh/system.h"
      26             : #include "libmesh/mesh_base.h"
      27             : #include "libmesh/elem.h"
      28             : #include "libmesh/xdr_cxx.h"
      29             : #include "libmesh/numeric_vector.h"
      30             : #include "libmesh/dof_map.h"
      31             : 
      32             : 
      33             : // C++ Includes
      34             : #include <memory>
      35             : #include <numeric> // for std::partial_sum
      36             : #include <set>
      37             : 
      38             : 
      39             : // Anonymous namespace for implementation details.
      40             : namespace {
      41             : 
      42             : using libMesh::DofObject;
      43             : using libMesh::Number;
      44             : using libMesh::cast_int;
      45             : 
      46             : // Comments:
      47             : // ---------
      48             : // - The max_io_blksize governs how many nodes or elements will be
      49             : // treated as a single block when performing parallel IO on large
      50             : // systems.
      51             : // - This parameter only loosely affects the size of the actual IO
      52             : // buffer as this depends on the number of components a given
      53             : // variable has for the nodes/elements in the block.
      54             : // - When reading/writing each processor uses an ID map which is
      55             : // 3*io_blksize*sizeof(dof_id_type) bytes long, so with unsigned int
      56             : // and // io_blksize=256000 we would expect that buffer alone to be
      57             : // ~3Mb.
      58             : // - In general, an increase in max_io_blksize should increase the
      59             : // efficiency of large parallel read/writes by reducing the number
      60             : // of MPI messages at the expense of memory.
      61             : // - If the library exhausts memory during IO you might reduce this
      62             : // parameter.
      63             : 
      64             : const std::size_t max_io_blksize = 256000;
      65             : 
      66             : /**
      67             :  *
      68             :  */
      69             : template <typename InValType>
      70             : class ThreadedIO
      71             : {
      72             : private:
      73             :   libMesh::Xdr & _io;
      74             :   std::vector<InValType> & _data;
      75             : 
      76             : public:
      77       45988 :   ThreadedIO (libMesh::Xdr & io, std::vector<InValType> & data) :
      78       37624 :     _io(io),
      79       43604 :     _data(data)
      80        4182 :   {}
      81             : 
      82       41806 :   void operator()()
      83             :   {
      84       41806 :     if (_data.empty()) return;
      85       20945 :     _io.data_stream (_data.data(), cast_int<unsigned int>(_data.size()));
      86             :   }
      87             : };
      88             : }
      89             : 
      90             : 
      91             : namespace libMesh
      92             : {
      93             : 
      94             : 
      95             : // ------------------------------------------------------------
      96             : // System class implementation
      97       11283 : void System::read_header (Xdr & io,
      98             :                           std::string_view version,
      99             :                           const bool read_header_in,
     100             :                           const bool read_additional_data,
     101             :                           const bool read_legacy_format)
     102             : {
     103             :   // This method implements the input of a
     104             :   // System object, embedded in the output of
     105             :   // an EquationSystems<T_sys>.  This warrants some
     106             :   // documentation.  The output file essentially
     107             :   // consists of 5 sections:
     108             :   //
     109             :   // for this system
     110             :   //
     111             :   //   5.) The number of variables in the system (unsigned int)
     112             :   //
     113             :   //   for each variable in the system
     114             :   //
     115             :   //     6.) The name of the variable (string)
     116             :   //
     117             :   //     6.1.) Variable subdomains
     118             :   //
     119             :   //     7.) Combined in an FEType:
     120             :   //         - The approximation order(s) of the variable
     121             :   //           (Order Enum, cast to int/s)
     122             :   //         - The finite element family/ies of the variable
     123             :   //           (FEFamily Enum, cast to int/s)
     124             :   //
     125             :   //   end variable loop
     126             :   //
     127             :   //   8.) The number of additional vectors (unsigned int),
     128             :   //
     129             :   //     for each additional vector in the system object
     130             :   //
     131             :   //     9.) the name of the additional vector  (string)
     132             :   //
     133             :   // end system
     134         324 :   libmesh_assert (io.reading());
     135             : 
     136             :   // Possibly clear data structures and start from scratch.
     137       11283 :   if (read_header_in)
     138         919 :     this->clear ();
     139             : 
     140             :   // Figure out if we need to read infinite element information.
     141             :   // This will be true if the version string contains " with infinite elements"
     142             :   const bool read_ifem_info =
     143       11315 :     Utility::contains(version, " with infinite elements") ||
     144       11315 :     libMesh::on_command_line ("--read-ifem-systems");
     145             : 
     146             : 
     147             :   {
     148             :     // 5.)
     149             :     // Read the number of variables in the system
     150       11283 :     unsigned int nv=0;
     151       11607 :     if (this->processor_id() == 0)
     152        1782 :       io.data (nv);
     153       11283 :     this->comm().broadcast(nv);
     154             : 
     155       11283 :     _written_var_indices.clear();
     156       11283 :     _written_var_indices.resize(nv, 0);
     157             : 
     158       22992 :     for (unsigned int var=0; var<nv; var++)
     159             :       {
     160             :         // 6.)
     161             :         // Read the name of the var-th variable
     162         672 :         std::string var_name;
     163       12045 :         if (this->processor_id() == 0)
     164        1854 :           io.data (var_name);
     165       11709 :         this->comm().broadcast(var_name);
     166             : 
     167             :         // 6.1.)
     168         672 :         std::set<subdomain_id_type> domains;
     169       11709 :         if (io.version() >= LIBMESH_VERSION_ID(0,7,2))
     170             :           {
     171         672 :             std::vector<subdomain_id_type> domain_array;
     172       12045 :             if (this->processor_id() == 0)
     173        1854 :               io.data (domain_array);
     174       11709 :             for (const auto & id : domain_array)
     175           0 :               domains.insert(id);
     176             :           }
     177       11709 :         this->comm().broadcast(domains);
     178             : 
     179             :         // 7.)
     180             :         // Read the approximation order(s) of the var-th variable
     181       11709 :         int order=0;
     182       12045 :         if (this->processor_id() == 0)
     183        1854 :           io.data (order);
     184       11709 :         this->comm().broadcast(order);
     185             : 
     186             : 
     187             :         // do the same for infinite element radial_order
     188       11709 :         int rad_order=0;
     189       11709 :         if (read_ifem_info)
     190             :           {
     191         974 :             if (this->processor_id() == 0)
     192         334 :               io.data(rad_order);
     193         654 :             this->comm().broadcast(rad_order);
     194             :           }
     195             : 
     196             :         // Read the finite element type of the var-th variable
     197       11709 :         int fam=0;
     198       12045 :         if (this->processor_id() == 0)
     199        1854 :           io.data (fam);
     200       11709 :         this->comm().broadcast(fam);
     201         336 :         FEType type;
     202       11709 :         type.order  = static_cast<Order>(order);
     203       11709 :         type.family = static_cast<FEFamily>(fam);
     204             : 
     205             :         // Check for incompatibilities.  The shape function indexing was
     206             :         // changed for the monomial and xyz finite element families to
     207             :         // simplify extension to arbitrary p.  The consequence is that
     208             :         // old restart files will not be read correctly.  This is expected
     209             :         // to be an unlikely occurrence, but catch it anyway.
     210       11709 :         if (read_legacy_format)
     211           0 :           if ((type.family == MONOMIAL || type.family == XYZ) &&
     212           0 :               ((type.order.get_order() > 2 && this->get_mesh().mesh_dimension() == 2) ||
     213           0 :                (type.order.get_order() > 1 && this->get_mesh().mesh_dimension() == 3)))
     214             :             {
     215           0 :               libmesh_here();
     216           0 :               libMesh::out << "*****************************************************************\n"
     217           0 :                            << "* WARNING: reading a potentially incompatible restart file!!!   *\n"
     218           0 :                            << "*  contact libmesh-users@lists.sourceforge.net for more details *\n"
     219           0 :                            << "*****************************************************************"
     220           0 :                            << std::endl;
     221             :             }
     222             : 
     223             :         // Read additional information for infinite elements
     224       11709 :         int radial_fam=0;
     225       11709 :         int i_map=0;
     226       11709 :         if (read_ifem_info)
     227             :           {
     228         974 :             if (this->processor_id() == 0)
     229         334 :               io.data (radial_fam);
     230         654 :             this->comm().broadcast(radial_fam);
     231         974 :             if (this->processor_id() == 0)
     232         334 :               io.data (i_map);
     233         654 :             this->comm().broadcast(i_map);
     234             :           }
     235             : 
     236             : #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
     237             : 
     238         686 :         type.radial_order  = static_cast<Order>(rad_order);
     239         686 :         type.radial_family = static_cast<FEFamily>(radial_fam);
     240         686 :         type.inf_map       = static_cast<InfMapType>(i_map);
     241             : 
     242             : #endif
     243             : 
     244       11709 :         if (read_header_in)
     245             :           {
     246        1061 :             if (domains.empty())
     247        1061 :               _written_var_indices[var] = this->add_variable (var_name, type);
     248             :             else
     249           0 :               _written_var_indices[var] = this->add_variable (var_name, type, &domains);
     250             :           }
     251             :         else
     252       10648 :           _written_var_indices[var] = this->variable_number(var_name);
     253             :       }
     254             :   }
     255             : 
     256             :   // 8.)
     257             :   // Read the number of additional vectors.
     258       11283 :   unsigned int nvecs=0;
     259       11607 :   if (this->processor_id() == 0)
     260        1782 :     io.data (nvecs);
     261       11283 :   this->comm().broadcast(nvecs);
     262             : 
     263             :   // If nvecs > 0, this means that write_additional_data
     264             :   // was true when this file was written.  We will need to
     265             :   // make use of this fact later.
     266       11283 :   this->_additional_data_written = nvecs;
     267             : 
     268       83383 :   for (unsigned int vec=0; vec<nvecs; vec++)
     269             :     {
     270             :       // 9.)
     271             :       // Read the name of the vec-th additional vector
     272        4120 :       std::string vec_name;
     273       74160 :       if (this->processor_id() == 0)
     274       11330 :         io.data (vec_name);
     275       72100 :       this->comm().broadcast(vec_name);
     276       72100 :       if (io.version() >= LIBMESH_VERSION_ID(1,7,0))
     277             :         {
     278       72100 :           int vec_projection = 0;
     279       74160 :           if (this->processor_id() == 0)
     280       11330 :             io.data (vec_projection);
     281       72100 :           this->comm().broadcast(vec_projection);
     282             :           int vec_type;
     283       74160 :           if (this->processor_id() == 0)
     284       11330 :             io.data (vec_type);
     285       72100 :           this->comm().broadcast(vec_type);
     286             : 
     287       72100 :           if (read_additional_data)
     288       74160 :             this->add_vector(vec_name, bool(vec_projection), ParallelType(vec_type));
     289             :         }
     290           0 :       else if (read_additional_data)
     291             :           // Systems now can handle adding post-initialization vectors
     292             :           //  libmesh_assert(this->_can_add_vectors);
     293             :           // Some systems may have added their own vectors already
     294             :           //  libmesh_assert_equal_to (this->_vectors.count(vec_name), 0);
     295           0 :         this->add_vector(vec_name);
     296             :     }
     297       11283 : }
     298             : 
     299             : 
     300             : 
     301             : template <typename InValType>
     302           0 : void System::read_parallel_data (Xdr & io,
     303             :                                  const bool read_additional_data)
     304             : {
     305             :   /**
     306             :    * This method implements the output of the vectors
     307             :    * contained in this System object, embedded in the
     308             :    * output of an EquationSystems<T_sys>.
     309             :    *
     310             :    *   9.) The global solution vector, re-ordered to be node-major
     311             :    *       (More on this later.)
     312             :    *
     313             :    *      for each additional vector in the object
     314             :    *
     315             :    *      10.) The global additional vector, re-ordered to be
     316             :    *           node-major (More on this later.)
     317             :    *
     318             :    * Note that the actual IO is handled through the Xdr class
     319             :    * (to be renamed later?) which provides a uniform interface to
     320             :    * both the XDR (eXternal Data Representation) interface and standard
     321             :    * ASCII output.  Thus this one section of code will read XDR or ASCII
     322             :    * files with no changes.
     323             :    */
     324             :   // PerfLog pl("IO Performance",false);
     325             :   // pl.push("read_parallel_data");
     326           0 :   [[maybe_unused]] dof_id_type total_read_size = 0;
     327             : 
     328           0 :   libmesh_assert (io.reading());
     329           0 :   libmesh_assert (io.is_open());
     330             : 
     331             :   // build the ordered nodes and element maps.
     332             :   // when writing/reading parallel files we need to iterate
     333             :   // over our nodes/elements in order of increasing global id().
     334             :   // however, this is not guaranteed to be ordering we obtain
     335             :   // by using the node_iterators/element_iterators directly.
     336             :   // so build a set, sorted by id(), that provides the ordering.
     337             :   // further, for memory economy build the set but then transfer
     338             :   // its contents to vectors, which will be sorted.
     339           0 :   std::vector<const DofObject *> ordered_nodes, ordered_elements;
     340             :   {
     341             :     std::set<const DofObject *, CompareDofObjectsByID>
     342           0 :       ordered_nodes_set (this->get_mesh().local_nodes_begin(),
     343           0 :                          this->get_mesh().local_nodes_end());
     344             : 
     345           0 :     ordered_nodes.insert(ordered_nodes.end(),
     346             :                          ordered_nodes_set.begin(),
     347             :                          ordered_nodes_set.end());
     348             :   }
     349             :   {
     350             :     std::set<const DofObject *, CompareDofObjectsByID>
     351           0 :       ordered_elements_set (this->get_mesh().local_elements_begin(),
     352           0 :                             this->get_mesh().local_elements_end());
     353             : 
     354           0 :     ordered_elements.insert(ordered_elements.end(),
     355             :                             ordered_elements_set.begin(),
     356             :                             ordered_elements_set.end());
     357             :   }
     358             : 
     359             :   //  std::vector<Number> io_buffer;
     360           0 :   std::vector<InValType> io_buffer;
     361             : 
     362             :   // 9.)
     363             :   //
     364             :   // Actually read the solution components
     365             :   // for the ith system to disk
     366           0 :   io.data(io_buffer);
     367             : 
     368           0 :   total_read_size += cast_int<dof_id_type>(io_buffer.size());
     369             : 
     370           0 :   const unsigned int sys_num = this->number();
     371           0 :   const unsigned int nv      = cast_int<unsigned int>
     372           0 :     (this->_written_var_indices.size());
     373           0 :   libmesh_assert_less_equal (nv, this->n_vars());
     374             : 
     375           0 :   dof_id_type cnt=0;
     376             : 
     377             :   // Loop over each non-SCALAR variable and each node, and read out the value.
     378           0 :   for (unsigned int data_var=0; data_var<nv; data_var++)
     379             :     {
     380           0 :       const unsigned int var = _written_var_indices[data_var];
     381           0 :       if (this->variable(var).type().family != SCALAR)
     382             :         {
     383             :           // First read the node DOF values
     384           0 :           for (const auto & node : ordered_nodes)
     385           0 :             for (auto comp : make_range(node->n_comp(sys_num,var)))
     386             :               {
     387           0 :                 libmesh_assert_not_equal_to (node->dof_number(sys_num, var, comp),
     388             :                                              DofObject::invalid_id);
     389           0 :                 libmesh_assert_less (cnt, io_buffer.size());
     390           0 :                 this->solution->set(node->dof_number(sys_num, var, comp), io_buffer[cnt++]);
     391             :               }
     392             : 
     393             :           // Then read the element DOF values
     394           0 :           for (const auto & elem : ordered_elements)
     395           0 :             for (auto comp : make_range(elem->n_comp(sys_num,var)))
     396             :               {
     397           0 :                 libmesh_assert_not_equal_to (elem->dof_number(sys_num, var, comp),
     398             :                                              DofObject::invalid_id);
     399           0 :                 libmesh_assert_less (cnt, io_buffer.size());
     400           0 :                 this->solution->set(elem->dof_number(sys_num, var, comp), io_buffer[cnt++]);
     401             :               }
     402             :         }
     403             :     }
     404             : 
     405             :   // Finally, read the SCALAR variables on the last processor
     406           0 :   for (unsigned int data_var=0; data_var<nv; data_var++)
     407             :     {
     408           0 :       const unsigned int var = _written_var_indices[data_var];
     409           0 :       if (this->variable(var).type().family == SCALAR)
     410             :         {
     411           0 :           if (this->processor_id() == (this->n_processors()-1))
     412             :             {
     413           0 :               const DofMap & dof_map = this->get_dof_map();
     414           0 :               std::vector<dof_id_type> SCALAR_dofs;
     415           0 :               dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
     416             : 
     417           0 :               for (auto dof : SCALAR_dofs)
     418           0 :                 this->solution->set(dof, io_buffer[cnt++]);
     419             :             }
     420             :         }
     421             :     }
     422             : 
     423             :   // And we're done setting solution entries
     424           0 :   this->solution->close();
     425             : 
     426             :   // For each additional vector, simply go through the list.
     427             :   // ONLY attempt to do this IF additional data was actually
     428             :   // written to the file for this system (controlled by the
     429             :   // _additional_data_written flag).
     430           0 :   if (this->_additional_data_written)
     431             :     {
     432           0 :       const std::size_t nvecs = this->_vectors.size();
     433             : 
     434             :       // If the number of additional vectors written is non-zero, and
     435             :       // the number of additional vectors we have is non-zero, and
     436             :       // they don't match, then something is wrong and we can't be
     437             :       // sure we're reading data into the correct places.
     438           0 :       if (read_additional_data && nvecs &&
     439           0 :           nvecs != this->_additional_data_written)
     440           0 :         libmesh_error_msg
     441             :           ("Additional vectors in file do not match system");
     442             : 
     443           0 :       auto pos = _vectors.begin();
     444             : 
     445           0 :       for (std::size_t i = 0; i != this->_additional_data_written; ++i)
     446             :         {
     447           0 :           cnt=0;
     448           0 :           io_buffer.clear();
     449             : 
     450             :           // 10.)
     451             :           //
     452             :           // Actually read the additional vector components
     453             :           // for the ith system from disk
     454           0 :           io.data(io_buffer);
     455             : 
     456           0 :           total_read_size += cast_int<dof_id_type>(io_buffer.size());
     457             : 
     458             :           // If read_additional_data==true and we have additional vectors,
     459             :           // then we will keep this vector data; otherwise we are going to
     460             :           // throw it away.
     461           0 :           if (read_additional_data && nvecs)
     462             :             {
     463             :               // Loop over each non-SCALAR variable and each node, and read out the value.
     464           0 :               for (unsigned int data_var=0; data_var<nv; data_var++)
     465             :                 {
     466           0 :                   const unsigned int var = _written_var_indices[data_var];
     467           0 :                   if (this->variable(var).type().family != SCALAR)
     468             :                     {
     469             :                       // First read the node DOF values
     470           0 :                       for (const auto & node : ordered_nodes)
     471           0 :                         for (auto comp : make_range(node->n_comp(sys_num,var)))
     472             :                           {
     473           0 :                             libmesh_assert_not_equal_to (node->dof_number(sys_num, var, comp),
     474             :                                                          DofObject::invalid_id);
     475           0 :                             libmesh_assert_less (cnt, io_buffer.size());
     476           0 :                             pos->second->set(node->dof_number(sys_num, var, comp), io_buffer[cnt++]);
     477             :                           }
     478             : 
     479             :                       // Then read the element DOF values
     480           0 :                       for (const auto & elem : ordered_elements)
     481           0 :                         for (auto comp : make_range(elem->n_comp(sys_num,var)))
     482             :                           {
     483           0 :                             libmesh_assert_not_equal_to (elem->dof_number(sys_num, var, comp),
     484             :                                                          DofObject::invalid_id);
     485           0 :                             libmesh_assert_less (cnt, io_buffer.size());
     486           0 :                             pos->second->set(elem->dof_number(sys_num, var, comp), io_buffer[cnt++]);
     487             :                           }
     488             :                     }
     489             :                 }
     490             : 
     491             :               // Finally, read the SCALAR variables on the last processor
     492           0 :               for (unsigned int data_var=0; data_var<nv; data_var++)
     493             :                 {
     494           0 :                   const unsigned int var = _written_var_indices[data_var];
     495           0 :                   if (this->variable(var).type().family == SCALAR)
     496             :                     {
     497           0 :                       if (this->processor_id() == (this->n_processors()-1))
     498             :                         {
     499           0 :                           const DofMap & dof_map = this->get_dof_map();
     500           0 :                           std::vector<dof_id_type> SCALAR_dofs;
     501           0 :                           dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
     502             : 
     503           0 :                           for (auto dof : SCALAR_dofs)
     504           0 :                             pos->second->set(dof, io_buffer[cnt++]);
     505             :                         }
     506             :                     }
     507             :                 }
     508             : 
     509             :               // And we're done setting entries for this variable
     510           0 :               pos->second->close();
     511             :             }
     512             : 
     513             :           // If we've got vectors then we need to be iterating through
     514             :           // those too
     515           0 :           if (pos != this->_vectors.end())
     516           0 :             ++pos;
     517             :         }
     518             :     }
     519             : 
     520             :   // const Real
     521             :   //   dt   = pl.get_elapsed_time(),
     522             :   //   rate = total_read_size*sizeof(Number)/dt;
     523             : 
     524             :   // libMesh::err << "Read " << total_read_size << " \"Number\" values\n"
     525             :   //     << " Elapsed time = " << dt << '\n'
     526             :   //     << " Rate = " << rate/1.e6 << "(MB/sec)\n\n";
     527             : 
     528             :   // pl.pop("read_parallel_data");
     529           0 : }
     530             : 
     531             : 
     532             : template <typename InValType>
     533       10999 : void System::read_serialized_data (Xdr & io,
     534             :                                    const bool read_additional_data)
     535             : {
     536             :   // This method implements the input of the vectors
     537             :   // contained in this System object, embedded in the
     538             :   // output of an EquationSystems<T_sys>.
     539             :   //
     540             :   //   10.) The global solution vector, re-ordered to be node-major
     541             :   //       (More on this later.)
     542             :   //
     543             :   //      for each additional vector in the object
     544             :   //
     545             :   //      11.) The global additional vector, re-ordered to be
     546             :   //          node-major (More on this later.)
     547         316 :   parallel_object_only();
     548         632 :   std::string comment;
     549             : 
     550             :   // PerfLog pl("IO Performance",false);
     551             :   // pl.push("read_serialized_data");
     552             :   // std::size_t total_read_size = 0;
     553             : 
     554             :   // 10.)
     555             :   // Read the global solution vector
     556             :   {
     557             :     // total_read_size +=
     558       10999 :     this->read_serialized_vector<InValType>(io, this->solution.get());
     559             : 
     560             :     // get the comment
     561       11315 :     if (this->processor_id() == 0)
     562        1734 :       io.comment (comment);
     563             :   }
     564             : 
     565             :   // 11.)
     566             :   // Only read additional vectors if data is available, and only use
     567             :   // that data to fill our vectors if the user requested it.
     568       10999 :   if (this->_additional_data_written)
     569             :     {
     570         304 :       const std::size_t nvecs = this->_vectors.size();
     571             : 
     572             :       // If the number of additional vectors written is non-zero, and
     573             :       // the number of additional vectors we have is non-zero, and
     574             :       // they don't match, then we can't read additional vectors
     575             :       // and be sure we're reading data into the correct places.
     576       10640 :       if (read_additional_data && nvecs &&
     577       10640 :           nvecs != this->_additional_data_written)
     578           0 :         libmesh_error_msg
     579             :           ("Additional vectors in file do not match system");
     580             : 
     581         304 :       auto pos = _vectors.begin();
     582             : 
     583       82740 :       for (std::size_t i = 0; i != this->_additional_data_written; ++i)
     584             :         {
     585             :           // Read data, but only put it into a vector if we've been
     586             :           // asked to and if we have a corresponding vector to read.
     587             : 
     588             :           // total_read_size +=
     589        8240 :           this->read_serialized_vector<InValType>
     590      138020 :             (io, (read_additional_data && nvecs) ? pos->second.get() : nullptr);
     591             : 
     592             :           // get the comment
     593       74160 :           if (this->processor_id() == 0)
     594       11330 :             io.comment (comment);
     595             : 
     596             : 
     597             :           // If we've got vectors then we need to be iterating through
     598             :           // those too
     599       72100 :           if (pos != this->_vectors.end())
     600        2060 :             ++pos;
     601             :         }
     602             :     }
     603             : 
     604             :   // const Real
     605             :   //   dt   = pl.get_elapsed_time(),
     606             :   //   rate = total_read_size*sizeof(Number)/dt;
     607             : 
     608             :   // libMesh::out << "Read " << total_read_size << " \"Number\" values\n"
     609             :   //     << " Elapsed time = " << dt << '\n'
     610             :   //     << " Rate = " << rate/1.e6 << "(MB/sec)\n\n";
     611             : 
     612             :   // pl.pop("read_serialized_data");
     613       10999 : }
     614             : 
     615             : 
     616             : 
     617             : template <typename iterator_type, typename InValType>
     618      166766 : std::size_t System::read_serialized_blocked_dof_objects (const dof_id_type n_objs,
     619             :                                                          const iterator_type begin,
     620             :                                                          const iterator_type end,
     621             :                                                          const InValType ,
     622             :                                                          Xdr & io,
     623             :                                                          const std::vector<NumericVector<Number> *> & vecs,
     624             :                                                          const unsigned int var_to_read) const
     625             : {
     626             :   //-------------------------------------------------------
     627             :   // General order: (IO format 0.7.4 & greater)
     628             :   //
     629             :   // for (objects ...)
     630             :   //   for (vecs ....)
     631             :   //     for (vars ....)
     632             :   //       for (comps ...)
     633             :   //
     634             :   // where objects are nodes or elements, sorted to be
     635             :   // partition independent,
     636             :   // vecs are one or more *identically distributed* solution
     637             :   // coefficient vectors, vars are one or more variables
     638             :   // to write, and comps are all the components for said
     639             :   // vars on the object.
     640             : 
     641             :   // variables to read.  Unless specified otherwise, defaults to _written_var_indices.
     642      171534 :   std::vector<unsigned int> vars_to_read (_written_var_indices);
     643             : 
     644      166766 :   if (var_to_read != libMesh::invalid_uint)
     645           0 :     vars_to_read.assign({var_to_read});
     646             : 
     647             :   const unsigned int
     648        9536 :     sys_num    = this->number(),
     649        9536 :     num_vecs   = cast_int<unsigned int>(vecs.size());
     650             :   const dof_id_type
     651      171550 :     io_blksize = cast_int<dof_id_type>(std::min(max_io_blksize, static_cast<std::size_t>(n_objs))),
     652      166766 :     num_blks   = cast_int<unsigned int>(std::ceil(static_cast<double>(n_objs)/
     653      157230 :                                                   static_cast<double>(io_blksize)));
     654             : 
     655        4768 :   libmesh_assert_less_equal (_written_var_indices.size(), this->n_vars());
     656             : 
     657        4768 :   std::size_t n_read_values=0;
     658             : 
     659      176302 :   std::vector<std::vector<dof_id_type>> xfer_ids(num_blks);  // The global IDs and # of components for the local objects in all blocks
     660      176302 :   std::vector<std::vector<Number>>      recv_vals(num_blks); // The raw values for the local objects in all blocks
     661             :   std::vector<Parallel::Request>
     662      181070 :     id_requests(num_blks), val_requests(num_blks);
     663             :   std::vector<Parallel::MessageTag>
     664      181070 :     id_tags(num_blks), val_tags(num_blks);
     665             : 
     666             :   // ------------------------------------------------------
     667             :   // First pass - count the number of objects in each block
     668             :   // traverse all the objects and figure out which block they
     669             :   // will ultimately live in.
     670             :   std::vector<std::size_t>
     671      171534 :     xfer_ids_size  (num_blks,0),
     672      171534 :     recv_vals_size (num_blks,0);
     673             : 
     674             : 
     675    32632620 :   for (iterator_type it=begin; it!=end; ++it)
     676             :     {
     677             :       const dof_id_type
     678    32465854 :         id    = (*it)->id(),
     679    32465854 :         block = id/io_blksize;
     680             : 
     681     2951215 :       libmesh_assert_less (block, num_blks);
     682             : 
     683    35417069 :       xfer_ids_size[block] += 2; // for each object, we send its id, as well as the total number of components for all variables
     684             : 
     685     2951215 :       dof_id_type n_comp_tot=0;
     686    65180504 :       for (const auto & var : vars_to_read)
     687    32714650 :         n_comp_tot += (*it)->n_comp(sys_num, var); // for each variable, we will receive the nonzero components
     688             : 
     689    35417069 :       recv_vals_size[block] += n_comp_tot*num_vecs;
     690             :     }
     691             : 
     692             :   // knowing the recv_vals_size[block] for each processor allows
     693             :   // us to sum them and find the global size for each block.
     694      171534 :   std::vector<std::size_t> tot_vals_size(recv_vals_size);
     695      166766 :   this->comm().sum (tot_vals_size);
     696             : 
     697             : 
     698             :   //------------------------------------------
     699             :   // Collect the ids & number of values needed
     700             :   // for all local objects, binning them into
     701             :   // 'blocks' that will be sent to processor 0
     702      333532 :   for (dof_id_type blk=0; blk<num_blks; blk++)
     703             :     {
     704             :       // Each processor should build up its transfer buffers for its
     705             :       // local objects in [first_object,last_object).
     706             :       const dof_id_type
     707      166766 :         first_object = blk*io_blksize,
     708      166766 :         last_object  = std::min(cast_int<dof_id_type>((blk+1)*io_blksize), n_objs);
     709             : 
     710             :       // convenience
     711        9552 :       std::vector<dof_id_type> & ids (xfer_ids[blk]);
     712        9536 :       std::vector<Number> & vals (recv_vals[blk]);
     713             : 
     714             :       // we now know the number of values we will store for each block,
     715             :       // so we can do efficient preallocation
     716      171534 :       ids.clear(); /**/ ids.reserve (xfer_ids_size[blk]);
     717      171534 :       vals.resize(recv_vals_size[blk]);
     718             : 
     719             : #ifdef DEBUG
     720        9536 :       std::unordered_set<dof_id_type> seen_ids;
     721             : #endif
     722             : 
     723      171534 :       if (recv_vals_size[blk] != 0) // only if there are nonzero values to receive
     724    26031865 :         for (iterator_type it=begin; it!=end; ++it)
     725             :           {
     726    14287886 :             dof_id_type id = (*it)->id();
     727             : #ifdef DEBUG
     728             :             // Any renumbering tricks should not have given us any
     729             :             // duplicate ids.
     730     1298479 :             libmesh_assert(!seen_ids.count(id));
     731     1298479 :             seen_ids.insert(id);
     732             : #endif
     733             : 
     734    14287886 :             if ((id >= first_object) && // object in [first_object,last_object)
     735     1298479 :                 (id <   last_object))
     736             :               {
     737    14287886 :                 ids.push_back(id);
     738             : 
     739     1298479 :                 unsigned int n_comp_tot=0;
     740             : 
     741    28871020 :                 for (const auto & var : vars_to_read)
     742    14583134 :                   n_comp_tot += (*it)->n_comp(sys_num, var);
     743             : 
     744    14287886 :                 ids.push_back (n_comp_tot*num_vecs);
     745             :               }
     746             :           }
     747             : 
     748             : #ifdef LIBMESH_HAVE_MPI
     749      166766 :       id_tags[blk]  = this->comm().get_unique_tag(100*num_blks + blk);
     750      166766 :       val_tags[blk] = this->comm().get_unique_tag(200*num_blks + blk);
     751             : 
     752             :       // nonblocking send the data for this block
     753      171534 :       this->comm().send (0, ids,  id_requests[blk],  id_tags[blk]);
     754             : 
     755             :       // Go ahead and post the receive too
     756      171534 :       this->comm().receive (0, vals, val_requests[blk], val_tags[blk]);
     757             : #endif
     758             :     }
     759             : 
     760             :   //---------------------------------------------------
     761             :   // Here processor 0 will read and distribute the data.
     762             :   // We have to do this block-wise to ensure that we
     763             :   // do not exhaust memory on processor 0.
     764             : 
     765             :   // give these variables scope outside the block to avoid reallocation
     766      181070 :   std::vector<std::vector<dof_id_type>> recv_ids       (this->n_processors());
     767      181070 :   std::vector<std::vector<Number>>      send_vals      (this->n_processors());
     768      181070 :   std::vector<Parallel::Request>         reply_requests (this->n_processors());
     769        9536 :   std::vector<unsigned int>              obj_val_offsets;          // map to traverse entry-wise rather than processor-wise
     770        9536 :   std::vector<Number>                    input_vals;               // The input buffer for the current block
     771        4768 :   std::vector<InValType>                 input_vals_tmp;               // The input buffer for the current block
     772             : 
     773      333532 :   for (dof_id_type blk=0; blk<num_blks; blk++)
     774             :     {
     775             :       // Each processor should build up its transfer buffers for its
     776             :       // local objects in [first_object,last_object).
     777             :       const dof_id_type
     778      166766 :         first_object  = blk*io_blksize,
     779      166766 :         last_object   = std::min(cast_int<dof_id_type>((blk+1)*io_blksize), n_objs),
     780      166766 :         n_objects_blk = last_object - first_object;
     781             : 
     782             :       // Processor 0 has a special job.  It needs to gather the requested indices
     783             :       // in [first_object,last_object) from all processors, read the data from
     784             :       // disk, and reply
     785      171534 :       if (this->processor_id() == 0)
     786             :         {
     787             :           // we know the input buffer size for this block and can begin reading it now
     788       28608 :           input_vals.resize(tot_vals_size[blk]);
     789       28608 :           input_vals_tmp.resize(tot_vals_size[blk]);
     790             : 
     791             :           // a ThreadedIO object to perform asynchronous file IO
     792        2384 :           ThreadedIO<InValType> threaded_io(io, input_vals_tmp);
     793       28608 :           Threads::Thread async_io(threaded_io);
     794             : 
     795             :           // offset array. this will define where each object's values
     796             :           // map into the actual input_vals buffer.  this must get
     797             :           // 0-initialized because 0-component objects are not actually sent
     798       26224 :           obj_val_offsets.resize (n_objects_blk); /**/ std::fill (obj_val_offsets.begin(), obj_val_offsets.end(), 0);
     799       28608 :           recv_vals_size.resize(this->n_processors()); // reuse this to count how many values are going to each processor
     800             : 
     801             : #ifndef NDEBUG
     802        2384 :           std::size_t n_vals_blk = 0;
     803             : #endif
     804             : 
     805             :           // loop over all processors and process their index request
     806      192990 :           for (processor_id_type comm_step=0, tnp=this->n_processors(); comm_step != tnp; ++comm_step)
     807             :             {
     808             : #ifdef LIBMESH_HAVE_MPI
     809             :               // blocking receive indices for this block, imposing no particular order on processor
     810      171534 :               Parallel::Status id_status (this->comm().probe (Parallel::any_source, id_tags[blk]));
     811      166766 :               std::vector<dof_id_type> & ids (recv_ids[id_status.source()]);
     812        9536 :               std::size_t & n_vals_proc (recv_vals_size[id_status.source()]);
     813      171534 :               this->comm().receive (id_status.source(), ids, id_tags[blk]);
     814             : #else
     815             :               // straight copy without MPI
     816             :               std::vector<dof_id_type> & ids (recv_ids[0]);
     817             :               std::size_t & n_vals_proc (recv_vals_size[0]);
     818             :               ids = xfer_ids[blk];
     819             : #endif
     820             : 
     821      166766 :               n_vals_proc = 0;
     822             : 
     823             :               // note its possible we didn't receive values for objects in
     824             :               // this block if they have no components allocated.
     825    14454652 :               for (std::size_t idx=0, sz=ids.size(); idx<sz; idx+=2)
     826             :                 {
     827             :                   const dof_id_type
     828    14287886 :                     local_idx          = ids[idx+0]-first_object,
     829    14287886 :                     n_vals_tot_allvecs = ids[idx+1];
     830             : 
     831     1298479 :                   libmesh_assert_less (local_idx, n_objects_blk);
     832             : 
     833    14287886 :                   obj_val_offsets[local_idx] = n_vals_tot_allvecs;
     834    14287886 :                   n_vals_proc += n_vals_tot_allvecs;
     835             :                 }
     836             : 
     837             : #ifndef NDEBUG
     838        4768 :               n_vals_blk += n_vals_proc;
     839             : #endif
     840             :             }
     841             : 
     842             :           // We need the offsets into the input_vals vector for each object.
     843             :           // fortunately, this is simply the partial sum of the total number
     844             :           // of components for each object
     845       23840 :           std::partial_sum(obj_val_offsets.begin(), obj_val_offsets.end(),
     846             :                            obj_val_offsets.begin());
     847             : 
     848        2384 :           libmesh_assert_equal_to (n_vals_blk, obj_val_offsets.back());
     849        2384 :           libmesh_assert_equal_to (n_vals_blk, tot_vals_size[blk]);
     850             : 
     851             :           // Wait for read completion
     852       26224 :           async_io.join();
     853             :           // now copy the values back to the main vector for transfer
     854    15661729 :           for (auto i_val : index_range(input_vals))
     855    18458375 :             input_vals[i_val] = input_vals_tmp[i_val];
     856             : 
     857       26224 :           n_read_values += input_vals.size();
     858             : 
     859             :           // pack data replies for each processor
     860      192990 :           for (auto proc : make_range(this->n_processors()))
     861             :             {
     862      166766 :               const std::vector<dof_id_type> & ids (recv_ids[proc]);
     863        9536 :               std::vector<Number> & vals (send_vals[proc]);
     864        9536 :               const std::size_t & n_vals_proc (recv_vals_size[proc]);
     865             : 
     866      166766 :               vals.clear(); /**/ vals.reserve(n_vals_proc);
     867             : 
     868    14454652 :               for (std::size_t idx=0, sz=ids.size(); idx<sz; idx+=2)
     869             :                 {
     870             :                   const dof_id_type
     871    14287886 :                     local_idx          = ids[idx+0]-first_object,
     872    15586365 :                     n_vals_tot_allvecs = ids[idx+1];
     873             : 
     874     1298479 :                   std::vector<Number>::const_iterator in_vals(input_vals.begin());
     875    14287886 :                   if (local_idx != 0)
     876    15572036 :                     std::advance (in_vals, obj_val_offsets[local_idx-1]);
     877             : 
     878    29923391 :                   for (unsigned int val=0; val<n_vals_tot_allvecs; val++, ++in_vals)
     879             :                     {
     880     1411435 :                       libmesh_assert (in_vals != input_vals.end());
     881             :                       //libMesh::out << "*in_vals=" << *in_vals << '\n';
     882    15635505 :                       vals.push_back(*in_vals);
     883             :                     }
     884             :                 }
     885             : 
     886             : #ifdef LIBMESH_HAVE_MPI
     887             :               // send the relevant values to this processor
     888      171534 :               this->comm().send (proc, vals, reply_requests[proc], val_tags[blk]);
     889             : #else
     890             :               recv_vals[blk] = vals;
     891             : #endif
     892             :             }
     893             :         } // end processor 0 read/reply
     894             : 
     895             :       // all processors complete the (already posted) read for this block
     896             :       {
     897        9552 :         Parallel::wait (val_requests[blk]);
     898             : 
     899        9536 :         const std::vector<Number> & vals (recv_vals[blk]);
     900        9536 :         std::vector<Number>::const_iterator val_it(vals.begin());
     901             : 
     902      166766 :         if (!recv_vals[blk].empty()) // nonzero values to receive
     903    26031865 :           for (iterator_type it=begin; it!=end; ++it)
     904    27277293 :             if (((*it)->id() >= first_object) && // object in [first_object,last_object)
     905    14287886 :                 ((*it)->id() <   last_object))
     906             :               // unpack & set the values
     907    30370184 :               for (auto & vec : vecs)
     908    34573284 :                 for (const auto & var : vars_to_read)
     909             :                   {
     910    18490986 :                     const unsigned int n_comp = (*it)->n_comp(sys_num, var);
     911             : 
     912    34126491 :                     for (unsigned int comp=0; comp<n_comp; comp++, ++val_it)
     913             :                       {
     914    15635505 :                         const dof_id_type dof_index = (*it)->dof_number (sys_num, var, comp);
     915     1411435 :                         libmesh_assert (val_it != vals.end());
     916    15635505 :                         if (vec)
     917             :                           {
     918     1411435 :                             libmesh_assert_greater_equal (dof_index, vec->first_local_index());
     919     1411435 :                             libmesh_assert_less (dof_index, vec->last_local_index());
     920             :                             //libMesh::out << "dof_index, *val_it = \t" << dof_index << ", " << *val_it << '\n';
     921    15635505 :                             vec->set (dof_index, *val_it);
     922             :                           }
     923             :                       }
     924             :                   }
     925             :       }
     926             : 
     927             :       // processor 0 needs to make sure all replies have been handed off
     928      171534 :       if (this->processor_id () == 0)
     929       26224 :         Parallel::wait(reply_requests);
     930             :     }
     931             : 
     932      166766 :   Parallel::wait(id_requests);
     933             : 
     934      171534 :   return n_read_values;
     935      314460 : }
     936             : 
     937             : 
     938             : 
     939         840 : unsigned int System::read_SCALAR_dofs (const unsigned int var,
     940             :                                        Xdr & io,
     941             :                                        NumericVector<Number> * vec) const
     942             : {
     943          24 :   unsigned int n_assigned_vals = 0; // the number of values assigned, this will be returned.
     944             : 
     945             :   // Processor 0 will read the block from the buffer stream and send it to the last processor
     946         840 :   const unsigned int n_SCALAR_dofs = this->variable(var).type().order.get_order();
     947         840 :   std::vector<Number> input_buffer(n_SCALAR_dofs);
     948         864 :   if (this->processor_id() == 0)
     949         132 :     io.data_stream(input_buffer.data(), n_SCALAR_dofs);
     950             : 
     951             : #ifdef LIBMESH_HAVE_MPI
     952         864 :   if (this->n_processors() > 1)
     953             :     {
     954         876 :       const Parallel::MessageTag val_tag = this->comm().get_unique_tag();
     955             : 
     956             :       // Post the receive on the last processor
     957         852 :       if (this->processor_id() == (this->n_processors()-1))
     958         120 :         this->comm().receive(0, input_buffer, val_tag);
     959             : 
     960             :       // Send the data to processor 0
     961         852 :       if (this->processor_id() == 0)
     962         120 :         this->comm().send(this->n_processors()-1, input_buffer, val_tag);
     963         780 :     }
     964             : #endif
     965             : 
     966             :   // Finally, set the SCALAR values
     967         864 :   if (this->processor_id() == (this->n_processors()-1))
     968             :     {
     969          12 :       const DofMap & dof_map = this->get_dof_map();
     970          24 :       std::vector<dof_id_type> SCALAR_dofs;
     971         132 :       dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
     972             : 
     973         264 :       for (auto i : index_range(SCALAR_dofs))
     974             :         {
     975         132 :           if (vec)
     976         156 :             vec->set (SCALAR_dofs[i], input_buffer[i]);
     977         132 :           ++n_assigned_vals;
     978             :         }
     979             :     }
     980             : 
     981         864 :   return n_assigned_vals;
     982             : }
     983             : 
     984             : 
     985             : template <typename InValType>
     986       83099 : numeric_index_type System::read_serialized_vector (Xdr & io,
     987             :                                                    NumericVector<Number> * vec)
     988             : {
     989        2376 :   parallel_object_only();
     990             : 
     991             : #ifndef NDEBUG
     992             :   // In parallel we better be reading a parallel vector -- if not
     993             :   // we will not set all of its components below!!
     994        2376 :   if (this->n_processors() > 1 && vec)
     995             :     {
     996        2376 :       libmesh_assert (vec->type() == PARALLEL ||
     997             :                       vec->type() == GHOSTED);
     998             :     }
     999             : #endif
    1000             : 
    1001        2376 :   libmesh_assert (io.reading());
    1002             : 
    1003             :   // vector length
    1004       83099 :   unsigned int vector_length=0; // FIXME?  size_t would break binary compatibility...
    1005             : #ifndef NDEBUG
    1006        2376 :   std::size_t n_assigned_vals=0;
    1007             : #endif
    1008             : 
    1009             :   // Get the buffer size
    1010       85475 :   if (this->processor_id() == 0)
    1011       13064 :     io.data(vector_length, "# vector length");
    1012       83099 :   this->comm().broadcast(vector_length);
    1013             : 
    1014        2376 :   const unsigned int nv = cast_int<unsigned int>
    1015        4752 :     (this->_written_var_indices.size());
    1016             :   const dof_id_type
    1017       83099 :     n_nodes = this->get_mesh().n_nodes(),
    1018       83099 :     n_elem  = this->get_mesh().n_elem();
    1019             : 
    1020        2376 :   libmesh_assert_less_equal (nv, this->n_vars());
    1021             : 
    1022             :   // for newer versions, read variables node/elem major
    1023       83099 :   if (io.version() >= LIBMESH_VERSION_ID(0,7,4))
    1024             :     {
    1025             :       //---------------------------------
    1026             :       // Collect the values for all nodes
    1027             : #ifndef NDEBUG
    1028        4752 :       n_assigned_vals +=
    1029             : #endif
    1030      163822 :         this->read_serialized_blocked_dof_objects (n_nodes,
    1031       83099 :                                                    this->get_mesh().local_nodes_begin(),
    1032       83099 :                                                    this->get_mesh().local_nodes_end(),
    1033             :                                                    InValType(),
    1034             :                                                    io,
    1035       78347 :                                                    std::vector<NumericVector<Number> *> (1,vec));
    1036             : 
    1037             : 
    1038             :       //------------------------------------
    1039             :       // Collect the values for all elements
    1040             : #ifndef NDEBUG
    1041        4752 :       n_assigned_vals +=
    1042             : #endif
    1043      166198 :         this->read_serialized_blocked_dof_objects (n_elem,
    1044       83099 :                                                    this->get_mesh().local_elements_begin(),
    1045       83099 :                                                    this->get_mesh().local_elements_end(),
    1046             :                                                    InValType(),
    1047             :                                                    io,
    1048      156694 :                                                    std::vector<NumericVector<Number> *> (1,vec));
    1049             :     }
    1050             : 
    1051             :   // for older versions, read variables var-major
    1052             :   else
    1053             :     {
    1054             :       // Loop over each variable in the system, and then each node/element in the mesh.
    1055           0 :       for (unsigned int data_var=0; data_var<nv; data_var++)
    1056             :         {
    1057           0 :           const unsigned int var = _written_var_indices[data_var];
    1058           0 :           if (this->variable(var).type().family != SCALAR)
    1059             :             {
    1060             :               //---------------------------------
    1061             :               // Collect the values for all nodes
    1062             : #ifndef NDEBUG
    1063           0 :               n_assigned_vals +=
    1064             : #endif
    1065           0 :                 this->read_serialized_blocked_dof_objects (n_nodes,
    1066           0 :                                                            this->get_mesh().local_nodes_begin(),
    1067           0 :                                                            this->get_mesh().local_nodes_end(),
    1068             :                                                            InValType(),
    1069             :                                                            io,
    1070           0 :                                                            std::vector<NumericVector<Number> *> (1,vec),
    1071             :                                                            var);
    1072             : 
    1073             : 
    1074             :               //------------------------------------
    1075             :               // Collect the values for all elements
    1076             : #ifndef NDEBUG
    1077           0 :               n_assigned_vals +=
    1078             : #endif
    1079           0 :                 this->read_serialized_blocked_dof_objects (n_elem,
    1080           0 :                                                            this->get_mesh().local_elements_begin(),
    1081           0 :                                                            this->get_mesh().local_elements_end(),
    1082             :                                                            InValType(),
    1083             :                                                            io,
    1084           0 :                                                            std::vector<NumericVector<Number> *> (1,vec),
    1085             :                                                            var);
    1086             :             } // end variable loop
    1087             :         }
    1088             :     }
    1089             : 
    1090             :   //-------------------------------------------
    1091             :   // Finally loop over all the SCALAR variables
    1092      167180 :   for (unsigned int data_var=0; data_var<nv; data_var++)
    1093             :     {
    1094       84081 :       const unsigned int var = _written_var_indices[data_var];
    1095       84081 :       if (this->variable(var).type().family == SCALAR)
    1096             :         {
    1097             : #ifndef NDEBUG
    1098          24 :           n_assigned_vals +=
    1099             : #endif
    1100         840 :             this->read_SCALAR_dofs (var, io, vec);
    1101             :         }
    1102             :     }
    1103             : 
    1104       83099 :   if (vec)
    1105       83099 :     vec->close();
    1106             : 
    1107             : #ifndef NDEBUG
    1108        2376 :   this->comm().sum (n_assigned_vals);
    1109        2376 :   libmesh_assert_equal_to (n_assigned_vals, vector_length);
    1110             : #endif
    1111             : 
    1112       83099 :   return vector_length;
    1113             : }
    1114             : 
    1115             : 
    1116             : 
    1117        1390 : void System::write_header (Xdr & io,
    1118             :                            std::string_view /* version is currently unused */,
    1119             :                            const bool write_additional_data) const
    1120             : {
    1121             :   /**
    1122             :    * This method implements the output of a
    1123             :    * System object, embedded in the output of
    1124             :    * an EquationSystems<T_sys>.  This warrants some
    1125             :    * documentation.  The output of this part
    1126             :    * consists of 5 sections:
    1127             :    *
    1128             :    * for this system
    1129             :    *
    1130             :    *   5.) The number of variables in the system (unsigned int)
    1131             :    *
    1132             :    *   for each variable in the system
    1133             :    *
    1134             :    *     6.) The name of the variable (string)
    1135             :    *
    1136             :    *     6.1.) subdomain where the variable lives
    1137             :    *
    1138             :    *     7.) Combined in an FEType:
    1139             :    *         - The approximation order(s) of the variable
    1140             :    *           (Order Enum, cast to int/s)
    1141             :    *         - The finite element family/ies of the variable
    1142             :    *           (FEFamily Enum, cast to int/s)
    1143             :    *
    1144             :    *   end variable loop
    1145             :    *
    1146             :    *   8.) The number of additional vectors (unsigned int),
    1147             :    *
    1148             :    *     for each additional vector in the system object
    1149             :    *
    1150             :    *     9.) the name of the additional vector  (string)
    1151             :    *
    1152             :    * end system
    1153             :    */
    1154         127 :   libmesh_assert (io.writing());
    1155             : 
    1156             : 
    1157             :   // Only write the header information
    1158             :   // if we are processor 0.
    1159        1517 :   if (this->get_mesh().processor_id() != 0)
    1160           0 :     return;
    1161             : 
    1162         254 :   std::string comment;
    1163             : 
    1164             :   // 5.)
    1165             :   // Write the number of variables in the system
    1166             : 
    1167             :   {
    1168             :     // set up the comment
    1169         127 :     comment = "# No. of Variables in System \"";
    1170         127 :     comment += this->name();
    1171         127 :     comment += "\"";
    1172             : 
    1173        1390 :     unsigned int nv = this->n_vars();
    1174        1390 :     io.data (nv, comment);
    1175             :   }
    1176             : 
    1177             : 
    1178        2852 :   for (auto var : make_range(this->n_vars()))
    1179             :     {
    1180             :       // 6.)
    1181             :       // Write the name of the var-th variable
    1182             :       {
    1183             :         // set up the comment
    1184         133 :         comment  = "#   Name, Variable No. ";
    1185        2658 :         comment += std::to_string(var);
    1186         133 :         comment += ", System \"";
    1187         133 :         comment += this->name();
    1188         133 :         comment += "\"";
    1189             : 
    1190        1595 :         std::string var_name = this->variable_name(var);
    1191        1462 :         io.data (var_name, comment);
    1192             :       }
    1193             : 
    1194             :       // 6.1.) Variable subdomains
    1195             :       {
    1196             :         // set up the comment
    1197         133 :         comment  = "#     Subdomains, Variable \"";
    1198        1462 :         comment += this->variable_name(var);
    1199         133 :         comment += "\", System \"";
    1200         133 :         comment += this->name();
    1201         133 :         comment += "\"";
    1202             : 
    1203        1462 :         const std::set<subdomain_id_type> & domains = this->variable(var).active_subdomains();
    1204         266 :         std::vector<subdomain_id_type> domain_array;
    1205        1329 :         domain_array.assign(domains.begin(), domains.end());
    1206        1462 :         io.data (domain_array, comment);
    1207             :       }
    1208             : 
    1209             :       // 7.)
    1210             :       // Write the approximation order of the var-th variable
    1211             :       // in this system
    1212             :       {
    1213             :         // set up the comment
    1214         133 :         comment = "#     Approximation Order, Variable \"";
    1215        1462 :         comment += this->variable_name(var);
    1216         133 :         comment += "\", System \"";
    1217         133 :         comment += this->name();
    1218         133 :         comment += "\"";
    1219             : 
    1220        1462 :         int order = static_cast<int>(this->variable_type(var).order);
    1221        1462 :         io.data (order, comment);
    1222             :       }
    1223             : 
    1224             : 
    1225             : #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
    1226             : 
    1227             :       // do the same for radial_order
    1228             :       {
    1229         133 :         comment = "#     Radial Approximation Order, Variable \"";
    1230         282 :         comment += this->variable_name(var);
    1231         133 :         comment += "\", System \"";
    1232         133 :         comment += this->name();
    1233         133 :         comment += "\"";
    1234             : 
    1235         282 :         int rad_order = static_cast<int>(this->variable_type(var).radial_order);
    1236         282 :         io.data (rad_order, comment);
    1237             :       }
    1238             : 
    1239             : #endif
    1240             : 
    1241             :       // Write the Finite Element type of the var-th variable
    1242             :       // in this System
    1243             :       {
    1244             :         // set up the comment
    1245         133 :         comment = "#     FE Family, Variable \"";
    1246        1462 :         comment += this->variable_name(var);
    1247         133 :         comment += "\", System \"";
    1248         133 :         comment += this->name();
    1249         133 :         comment += "\"";
    1250             : 
    1251        1462 :         const FEType & type = this->variable_type(var);
    1252        1462 :         int fam = static_cast<int>(type.family);
    1253        1462 :         io.data (fam, comment);
    1254             : 
    1255             : #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
    1256             : 
    1257         133 :         comment = "#     Radial FE Family, Variable \"";
    1258         282 :         comment += this->variable_name(var);
    1259         133 :         comment += "\", System \"";
    1260         133 :         comment += this->name();
    1261         133 :         comment += "\"";
    1262             : 
    1263         282 :         int radial_fam = static_cast<int>(type.radial_family);
    1264         282 :         io.data (radial_fam, comment);
    1265             : 
    1266         133 :         comment = "#     Infinite Mapping Type, Variable \"";
    1267         282 :         comment += this->variable_name(var);
    1268         133 :         comment += "\", System \"";
    1269         133 :         comment += this->name();
    1270         133 :         comment += "\"";
    1271             : 
    1272         282 :         int i_map = static_cast<int>(type.inf_map);
    1273         282 :         io.data (i_map, comment);
    1274             : #endif
    1275             :       }
    1276             :     } // end of the variable loop
    1277             : 
    1278             :   // 8.)
    1279             :   // Write the number of additional vectors in the System.
    1280             :   // If write_additional_data==false, then write zero for
    1281             :   // the number of additional vectors.
    1282             :   {
    1283             :     {
    1284             :       // set up the comment
    1285         127 :       comment = "# No. of Additional Vectors, System \"";
    1286         127 :       comment += this->name();
    1287         127 :       comment += "\"";
    1288             : 
    1289        2550 :       unsigned int nvecs = write_additional_data ? this->n_vectors () : 0;
    1290        1390 :       io.data (nvecs, comment);
    1291             :     }
    1292             : 
    1293        1390 :     if (write_additional_data)
    1294             :       {
    1295         116 :         unsigned int cnt=0;
    1296        9768 :         for (const auto & [vec_name, vec] : _vectors)
    1297             :           {
    1298             :             // 9.)
    1299             :             // write the name of the cnt-th additional vector
    1300        9264 :             const std::string dth_vector = std::to_string(cnt++)+"th vector";
    1301        9264 :             comment =  "# Name of " + dth_vector;
    1302        8492 :             std::string nonconst_vec_name = vec_name; // Stupid XDR API
    1303             : 
    1304        8492 :             io.data (nonconst_vec_name, comment);
    1305        8492 :             int vec_projection = _vector_projections.at(vec_name);
    1306       16212 :             comment = "# Whether to do projections for " + dth_vector;
    1307        8492 :             io.data (vec_projection, comment);
    1308        8492 :             int vec_type = vec->type();
    1309       16212 :             comment = "# Parallel type of " + dth_vector;
    1310        8492 :             io.data (vec_type, comment);
    1311             :           }
    1312             :       }
    1313             :   }
    1314             : }
    1315             : 
    1316             : 
    1317             : 
    1318           0 : void System::write_parallel_data (Xdr & io,
    1319             :                                   const bool write_additional_data) const
    1320             : {
    1321             :   /**
    1322             :    * This method implements the output of the vectors
    1323             :    * contained in this System object, embedded in the
    1324             :    * output of an EquationSystems<T_sys>.
    1325             :    *
    1326             :    *   9.) The global solution vector, re-ordered to be node-major
    1327             :    *       (More on this later.)
    1328             :    *
    1329             :    *      for each additional vector in the object
    1330             :    *
    1331             :    *      10.) The global additional vector, re-ordered to be
    1332             :    *           node-major (More on this later.)
    1333             :    *
    1334             :    * Note that the actual IO is handled through the Xdr class
    1335             :    * (to be renamed later?) which provides a uniform interface to
    1336             :    * both the XDR (eXternal Data Representation) interface and standard
    1337             :    * ASCII output.  Thus this one section of code will read XDR or ASCII
    1338             :    * files with no changes.
    1339             :    */
    1340             :   // PerfLog pl("IO Performance",false);
    1341             :   // pl.push("write_parallel_data");
    1342             :   // std::size_t total_written_size = 0;
    1343             : 
    1344           0 :   std::string comment;
    1345             : 
    1346           0 :   libmesh_assert (io.writing());
    1347             : 
    1348           0 :   std::vector<Number> io_buffer; io_buffer.reserve(this->solution->local_size());
    1349             : 
    1350             :   // build the ordered nodes and element maps.
    1351             :   // when writing/reading parallel files we need to iterate
    1352             :   // over our nodes/elements in order of increasing global id().
    1353             :   // however, this is not guaranteed to be ordering we obtain
    1354             :   // by using the node_iterators/element_iterators directly.
    1355             :   // so build a set, sorted by id(), that provides the ordering.
    1356             :   // further, for memory economy build the set but then transfer
    1357             :   // its contents to vectors, which will be sorted.
    1358           0 :   std::vector<const DofObject *> ordered_nodes, ordered_elements;
    1359             :   {
    1360             :     std::set<const DofObject *, CompareDofObjectsByID>
    1361           0 :       ordered_nodes_set (this->get_mesh().local_nodes_begin(),
    1362           0 :                          this->get_mesh().local_nodes_end());
    1363             : 
    1364           0 :     ordered_nodes.insert(ordered_nodes.end(),
    1365             :                          ordered_nodes_set.begin(),
    1366           0 :                          ordered_nodes_set.end());
    1367             :   }
    1368             :   {
    1369             :     std::set<const DofObject *, CompareDofObjectsByID>
    1370           0 :       ordered_elements_set (this->get_mesh().local_elements_begin(),
    1371           0 :                             this->get_mesh().local_elements_end());
    1372             : 
    1373           0 :     ordered_elements.insert(ordered_elements.end(),
    1374             :                             ordered_elements_set.begin(),
    1375           0 :                             ordered_elements_set.end());
    1376             :   }
    1377             : 
    1378           0 :   const unsigned int sys_num = this->number();
    1379           0 :   const unsigned int nv      = this->n_vars();
    1380             : 
    1381             :   // Loop over each non-SCALAR variable and each node, and write out the value.
    1382           0 :   for (unsigned int var=0; var<nv; var++)
    1383           0 :     if (this->variable(var).type().family != SCALAR)
    1384             :       {
    1385             :         // First write the node DOF values
    1386           0 :         for (const auto & node : ordered_nodes)
    1387           0 :           for (auto comp : make_range(node->n_comp(sys_num,var)))
    1388             :             {
    1389           0 :               libmesh_assert_not_equal_to (node->dof_number(sys_num, var, comp),
    1390             :                                            DofObject::invalid_id);
    1391             : 
    1392           0 :               io_buffer.push_back((*this->solution)(node->dof_number(sys_num, var, comp)));
    1393             :             }
    1394             : 
    1395             :         // Then write the element DOF values
    1396           0 :         for (const auto & elem : ordered_elements)
    1397           0 :           for (auto comp : make_range(elem->n_comp(sys_num,var)))
    1398             :             {
    1399           0 :               libmesh_assert_not_equal_to (elem->dof_number(sys_num, var, comp),
    1400             :                                            DofObject::invalid_id);
    1401             : 
    1402           0 :               io_buffer.push_back((*this->solution)(elem->dof_number(sys_num, var, comp)));
    1403             :             }
    1404             :       }
    1405             : 
    1406             :   // Finally, write the SCALAR data on the last processor
    1407           0 :   for (auto var : make_range(this->n_vars()))
    1408           0 :     if (this->variable(var).type().family == SCALAR)
    1409             :       {
    1410           0 :         if (this->processor_id() == (this->n_processors()-1))
    1411             :           {
    1412           0 :             const DofMap & dof_map = this->get_dof_map();
    1413           0 :             std::vector<dof_id_type> SCALAR_dofs;
    1414           0 :             dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
    1415             : 
    1416           0 :             for (auto dof : SCALAR_dofs)
    1417           0 :               io_buffer.push_back((*this->solution)(dof));
    1418             :           }
    1419             :       }
    1420             : 
    1421             :   // 9.)
    1422             :   //
    1423             :   // Actually write the reordered solution vector
    1424             :   // for the ith system to disk
    1425             : 
    1426             :   // set up the comment
    1427             :   {
    1428           0 :     comment = "# System \"";
    1429           0 :     comment += this->name();
    1430           0 :     comment += "\" Solution Vector";
    1431             :   }
    1432             : 
    1433           0 :   io.data (io_buffer, comment);
    1434             : 
    1435             :   // total_written_size += io_buffer.size();
    1436             : 
    1437             :   // Only write additional vectors if wanted
    1438           0 :   if (write_additional_data)
    1439             :     {
    1440           0 :       for (auto & [vec_name, vec] : _vectors)
    1441             :         {
    1442           0 :           io_buffer.clear();
    1443           0 :           io_buffer.reserve(vec->local_size());
    1444             : 
    1445             :           // Loop over each non-SCALAR variable and each node, and write out the value.
    1446           0 :           for (unsigned int var=0; var<nv; var++)
    1447           0 :             if (this->variable(var).type().family != SCALAR)
    1448             :               {
    1449             :                 // First write the node DOF values
    1450           0 :                 for (const auto & node : ordered_nodes)
    1451           0 :                   for (auto comp : make_range(node->n_comp(sys_num,var)))
    1452             :                     {
    1453           0 :                       libmesh_assert_not_equal_to (node->dof_number(sys_num, var, comp),
    1454             :                                                    DofObject::invalid_id);
    1455             : 
    1456           0 :                       io_buffer.push_back((*vec)(node->dof_number(sys_num, var, comp)));
    1457             :                     }
    1458             : 
    1459             :                 // Then write the element DOF values
    1460           0 :                 for (const auto & elem : ordered_elements)
    1461           0 :                   for (auto comp : make_range(elem->n_comp(sys_num,var)))
    1462             :                     {
    1463           0 :                       libmesh_assert_not_equal_to (elem->dof_number(sys_num, var, comp),
    1464             :                                                    DofObject::invalid_id);
    1465             : 
    1466           0 :                       io_buffer.push_back((*vec)(elem->dof_number(sys_num, var, comp)));
    1467             :                     }
    1468             :               }
    1469             : 
    1470             :           // Finally, write the SCALAR data on the last processor
    1471           0 :           for (auto var : make_range(this->n_vars()))
    1472           0 :             if (this->variable(var).type().family == SCALAR)
    1473             :               {
    1474           0 :                 if (this->processor_id() == (this->n_processors()-1))
    1475             :                   {
    1476           0 :                     const DofMap & dof_map = this->get_dof_map();
    1477           0 :                     std::vector<dof_id_type> SCALAR_dofs;
    1478           0 :                     dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
    1479             : 
    1480           0 :                     for (auto dof : SCALAR_dofs)
    1481           0 :                       io_buffer.push_back((*vec)(dof));
    1482             :                   }
    1483             :               }
    1484             : 
    1485             :           // 10.)
    1486             :           //
    1487             :           // Actually write the reordered additional vector
    1488             :           // for this system to disk
    1489             : 
    1490             :           // set up the comment
    1491             :           {
    1492           0 :             comment = "# System \"";
    1493           0 :             comment += this->name();
    1494           0 :             comment += "\" Additional Vector \"";
    1495           0 :             comment += vec_name;
    1496           0 :             comment += "\"";
    1497             :           }
    1498             : 
    1499           0 :           io.data (io_buffer, comment);
    1500             : 
    1501             :           // total_written_size += io_buffer.size();
    1502             :         }
    1503             :     }
    1504             : 
    1505             :   // const Real
    1506             :   //   dt   = pl.get_elapsed_time(),
    1507             :   //   rate = total_written_size*sizeof(Number)/dt;
    1508             : 
    1509             :   // libMesh::err << "Write " << total_written_size << " \"Number\" values\n"
    1510             :   //     << " Elapsed time = " << dt << '\n'
    1511             :   //     << " Rate = " << rate/1.e6 << "(MB/sec)\n\n";
    1512             : 
    1513             :   // pl.pop("write_parallel_data");
    1514           0 : }
    1515             : 
    1516             : 
    1517             : 
    1518        8485 : void System::write_serialized_data (Xdr & io,
    1519             :                                     const bool write_additional_data) const
    1520             : {
    1521             :   /**
    1522             :    * This method implements the output of the vectors
    1523             :    * contained in this System object, embedded in the
    1524             :    * output of an EquationSystems<T_sys>.
    1525             :    *
    1526             :    *   9.) The global solution vector, re-ordered to be node-major
    1527             :    *       (More on this later.)
    1528             :    *
    1529             :    *      for each additional vector in the object
    1530             :    *
    1531             :    *      10.) The global additional vector, re-ordered to be
    1532             :    *          node-major (More on this later.)
    1533             :    */
    1534         246 :   parallel_object_only();
    1535         492 :   std::string comment;
    1536             : 
    1537             :   // PerfLog pl("IO Performance",false);
    1538             :   // pl.push("write_serialized_data");
    1539             :   // std::size_t total_written_size = 0;
    1540             : 
    1541             :   // total_written_size +=
    1542        8485 :   this->write_serialized_vector(io, *this->solution);
    1543             : 
    1544             :   // set up the comment
    1545        8731 :   if (this->processor_id() == 0)
    1546             :     {
    1547         123 :       comment = "# System \"";
    1548         123 :       comment += this->name();
    1549         123 :       comment += "\" Solution Vector";
    1550             : 
    1551        1342 :       io.comment (comment);
    1552             :     }
    1553             : 
    1554             :   // Only write additional vectors if wanted
    1555        8485 :   if (write_additional_data)
    1556             :     {
    1557       62160 :       for (auto & pair : this->_vectors)
    1558             :         {
    1559             :           // total_written_size +=
    1560       54040 :           this->write_serialized_vector(io, *pair.second);
    1561             : 
    1562             :           // set up the comment
    1563       55584 :           if (this->processor_id() == 0)
    1564             :             {
    1565         772 :               comment = "# System \"";
    1566         772 :               comment += this->name();
    1567         772 :               comment += "\" Additional Vector \"";
    1568         772 :               comment += pair.first;
    1569         772 :               comment += "\"";
    1570        8492 :               io.comment (comment);
    1571             :             }
    1572             :         }
    1573             :     }
    1574             : 
    1575             :   // const Real
    1576             :   //   dt   = pl.get_elapsed_time(),
    1577             :   //   rate = total_written_size*sizeof(Number)/dt;
    1578             : 
    1579             :   // libMesh::out << "Write " << total_written_size << " \"Number\" values\n"
    1580             :   //     << " Elapsed time = " << dt << '\n'
    1581             :   //     << " Rate = " << rate/1.e6 << "(MB/sec)\n\n";
    1582             : 
    1583             :   // pl.pop("write_serialized_data");
    1584             : 
    1585             : 
    1586             : 
    1587             : 
    1588             :   // // test the new method
    1589             :   // {
    1590             :   //   std::vector<std::string> names;
    1591             :   //   std::vector<NumericVector<Number> *> vectors_to_write;
    1592             : 
    1593             :   //   names.push_back("Solution Vector");
    1594             :   //   vectors_to_write.push_back(this->solution.get());
    1595             : 
    1596             :   //   // Only write additional vectors if wanted
    1597             :   //   if (write_additional_data)
    1598             :   //     {
    1599             :   // std::map<std::string, NumericVector<Number> *>::const_iterator
    1600             :   //   pos = _vectors.begin();
    1601             : 
    1602             :   // for (; pos != this->_vectors.end(); ++pos)
    1603             :   //   {
    1604             :   //     names.push_back("Additional Vector " + pos->first);
    1605             :   //     vectors_to_write.push_back(pos->second);
    1606             :   //   }
    1607             :   //     }
    1608             : 
    1609             :   //   total_written_size =
    1610             :   //     this->write_serialized_vectors (io, names, vectors_to_write);
    1611             : 
    1612             :   //   const Real
    1613             :   //     dt2   = pl.get_elapsed_time(),
    1614             :   //     rate2 = total_written_size*sizeof(Number)/(dt2-dt);
    1615             : 
    1616             :   //   libMesh::out << "Write (new) " << total_written_size << " \"Number\" values\n"
    1617             :   //       << " Elapsed time = " << (dt2-dt) << '\n'
    1618             :   //       << " Rate = " << rate2/1.e6 << "(MB/sec)\n\n";
    1619             : 
    1620             :   // }
    1621        8485 : }
    1622             : 
    1623             : 
    1624             : 
    1625             : template <typename iterator_type>
    1626      125618 : std::size_t System::write_serialized_blocked_dof_objects (const std::vector<const NumericVector<Number> *> & vecs,
    1627             :                                                           const dof_id_type n_objs,
    1628             :                                                           const iterator_type begin,
    1629             :                                                           const iterator_type end,
    1630             :                                                           Xdr & io,
    1631             :                                                           const unsigned int var_to_write) const
    1632             : {
    1633        3596 :   parallel_object_only();
    1634             : 
    1635             :   //-------------------------------------------------------
    1636             :   // General order: (IO format 0.7.4 & greater)
    1637             :   //
    1638             :   // for (objects ...)
    1639             :   //   for (vecs ....)
    1640             :   //     for (vars ....)
    1641             :   //       for (comps ...)
    1642             :   //
    1643             :   // where objects are nodes or elements, sorted to be
    1644             :   // partition independent,
    1645             :   // vecs are one or more *identically distributed* solution
    1646             :   // coefficient vectors, vars are one or more variables
    1647             :   // to write, and comps are all the components for said
    1648             :   // vars on the object.
    1649             : 
    1650             :   // We will write all variables unless requested otherwise.
    1651      129214 :   std::vector<unsigned int> vars_to_write(1, var_to_write);
    1652             : 
    1653      125618 :   if (var_to_write == libMesh::invalid_uint)
    1654             :     {
    1655      125618 :       vars_to_write.clear(); /**/ vars_to_write.reserve(this->n_vars());
    1656      252088 :       for (auto var : make_range(this->n_vars()))
    1657      126470 :         vars_to_write.push_back(var);
    1658             :     }
    1659             : 
    1660             :   const dof_id_type io_blksize = cast_int<dof_id_type>
    1661      247640 :     (std::min(max_io_blksize, static_cast<std::size_t>(n_objs)));
    1662             : 
    1663             :   const unsigned int
    1664        7192 :     sys_num  = this->number(),
    1665        7192 :     num_vecs = cast_int<unsigned int>(vecs.size()),
    1666      125618 :     num_blks = cast_int<unsigned int>(std::ceil(static_cast<double>(n_objs)/
    1667      118426 :                                                 static_cast<double>(io_blksize)));
    1668             : 
    1669             :   // libMesh::out << "io_blksize = "    << io_blksize
    1670             :   //     << ", num_objects = " << n_objs
    1671             :   //     << ", num_blks = "    << num_blks
    1672             :   //     << std::endl;
    1673             : 
    1674      125618 :   std::size_t written_length=0;                                  // The numer of values written.  This will be returned
    1675      132810 :   std::vector<std::vector<dof_id_type>> xfer_ids(num_blks);      // The global IDs and # of components for the local objects in all blocks
    1676      132810 :   std::vector<std::vector<Number>>      send_vals(num_blks);     // The raw values for the local objects in all blocks
    1677             :   std::vector<Parallel::Request>
    1678      136406 :     id_requests(num_blks), val_requests(num_blks);               // send request handle for each block
    1679             :   std::vector<Parallel::MessageTag>
    1680      136406 :     id_tags(num_blks), val_tags(num_blks);                       // tag number for each block
    1681             : 
    1682             :   // ------------------------------------------------------
    1683             :   // First pass - count the number of objects in each block
    1684             :   // traverse all the objects and figure out which block they
    1685             :   // will ultimately live in.
    1686             :   std::vector<unsigned int>
    1687      129214 :     xfer_ids_size  (num_blks,0),
    1688      125618 :     send_vals_size (num_blks,0);
    1689             : 
    1690    33656292 :   for (iterator_type it=begin; it!=end; ++it)
    1691             :     {
    1692             :       const dof_id_type
    1693    33530674 :         id    = (*it)->id(),
    1694    33530674 :         block = id/io_blksize;
    1695             : 
    1696     3049452 :       libmesh_assert_less (block, num_blks);
    1697             : 
    1698    36580126 :       xfer_ids_size[block] += 2; // for each object, we store its id, as well as the total number of components for all variables
    1699             : 
    1700     3049452 :       unsigned int n_comp_tot=0;
    1701             : 
    1702    67228172 :       for (const auto & var : vars_to_write)
    1703    33697498 :         n_comp_tot += (*it)->n_comp(sys_num, var); // for each variable, we will store the nonzero components
    1704             : 
    1705    36580126 :       send_vals_size[block] += n_comp_tot*num_vecs;
    1706             :     }
    1707             : 
    1708             :   //-----------------------------------------
    1709             :   // Collect the values for all local objects,
    1710             :   // binning them into 'blocks' that will be
    1711             :   // sent to processor 0
    1712      251236 :   for (unsigned int blk=0; blk<num_blks; blk++)
    1713             :     {
    1714             :       // libMesh::out << "Writing object block " << blk << std::endl;
    1715             : 
    1716             :       // Each processor should build up its transfer buffers for its
    1717             :       // local objects in [first_object,last_object).
    1718             :       const dof_id_type
    1719      125618 :         first_object = blk*io_blksize,
    1720      125618 :         last_object  = std::min(cast_int<dof_id_type>((blk+1)*io_blksize), n_objs);
    1721             : 
    1722             :       // convenience
    1723        7212 :       std::vector<dof_id_type> & ids  (xfer_ids[blk]);
    1724        7192 :       std::vector<Number>      & vals (send_vals[blk]);
    1725             : 
    1726             :       // we now know the number of values we will store for each block,
    1727             :       // so we can do efficient preallocation
    1728      129214 :       ids.clear();  /**/ ids.reserve  (xfer_ids_size[blk]);
    1729      129214 :       vals.clear(); /**/ vals.reserve (send_vals_size[blk]);
    1730             : 
    1731      129214 :       if (send_vals_size[blk] != 0) // only send if we have nonzero components to write
    1732    26796153 :         for (iterator_type it=begin; it!=end; ++it)
    1733    28091956 :           if (((*it)->id() >= first_object) && // object in [first_object,last_object)
    1734    14714978 :               ((*it)->id() <   last_object))
    1735             :             {
    1736    14714978 :               ids.push_back((*it)->id());
    1737             : 
    1738             :               // count the total number of nonzeros transferred for this object
    1739             :               {
    1740     1338000 :                 unsigned int n_comp_tot=0;
    1741             : 
    1742    29582380 :                 for (const auto & var : vars_to_write)
    1743    14867402 :                   n_comp_tot += (*it)->n_comp(sys_num, var);
    1744             : 
    1745    14714978 :                 ids.push_back (n_comp_tot*num_vecs); // even if 0 - processor 0 has no way of knowing otherwise...
    1746             :               }
    1747             : 
    1748             :               // pack the values to send
    1749    31224368 :               for (const auto & vec : vecs)
    1750    35284644 :                 for (const auto & var : vars_to_write)
    1751             :                   {
    1752    18775254 :                     const unsigned int n_comp = (*it)->n_comp(sys_num, var);
    1753             : 
    1754    34875939 :                     for (unsigned int comp=0; comp<n_comp; comp++)
    1755             :                       {
    1756     1454980 :                         libmesh_assert_greater_equal ((*it)->dof_number(sys_num, var, comp), vec->first_local_index());
    1757     1454980 :                         libmesh_assert_less ((*it)->dof_number(sys_num, var, comp), vec->last_local_index());
    1758    16216204 :                         vals.push_back((*vec)((*it)->dof_number(sys_num, var, comp)));
    1759             :                       }
    1760             :                   }
    1761             :             }
    1762             : 
    1763             : #ifdef LIBMESH_HAVE_MPI
    1764      125618 :       id_tags[blk]  = this->comm().get_unique_tag(100*num_blks + blk);
    1765      125618 :       val_tags[blk] = this->comm().get_unique_tag(200*num_blks + blk);
    1766             : 
    1767             :       // nonblocking send the data for this block
    1768      129214 :       this->comm().send (0, ids,  id_requests[blk],  id_tags[blk]);
    1769      129214 :       this->comm().send (0, vals, val_requests[blk], val_tags[blk]);
    1770             : #endif
    1771             :     }
    1772             : 
    1773             : 
    1774      129214 :   if (this->processor_id() == 0)
    1775             :     {
    1776       23360 :       std::vector<std::vector<dof_id_type>> recv_ids  (this->n_processors());
    1777       25158 :       std::vector<std::vector<Number>>      recv_vals (this->n_processors());
    1778        3596 :       std::vector<unsigned int> obj_val_offsets;          // map to traverse entry-wise rather than processor-wise
    1779        3596 :       std::vector<Number>       output_vals;              // The output buffer for the current block
    1780             : 
    1781             :       // a ThreadedIO object to perform asynchronous file IO
    1782        1798 :       ThreadedIO<Number> threaded_io(io, output_vals);
    1783       17966 :       std::unique_ptr<Threads::Thread> async_io;
    1784             : 
    1785       39528 :       for (unsigned int blk=0; blk<num_blks; blk++)
    1786             :         {
    1787             :           // Each processor should build up its transfer buffers for its
    1788             :           // local objects in [first_object,last_object).
    1789             :           const dof_id_type
    1790       19764 :             first_object  = cast_int<dof_id_type>(blk*io_blksize),
    1791       19764 :             last_object   = std::min(cast_int<dof_id_type>((blk+1)*io_blksize), n_objs),
    1792       19764 :             n_objects_blk = last_object - first_object;
    1793             : 
    1794             :           // offset array. this will define where each object's values
    1795             :           // map into the actual output_vals buffer.  this must get
    1796             :           // 0-initialized because 0-component objects are not actually sent
    1797       19764 :           obj_val_offsets.resize (n_objects_blk); /**/ std::fill (obj_val_offsets.begin(), obj_val_offsets.end(), 0);
    1798             : 
    1799        1798 :           std::size_t n_val_recvd_blk=0;
    1800             : 
    1801             :           // receive this block of data from all processors.
    1802      145382 :           for (processor_id_type comm_step=0, tnp=this->n_processors(); comm_step != tnp; ++comm_step)
    1803             :             {
    1804             : #ifdef LIBMESH_HAVE_MPI
    1805             :               // blocking receive indices for this block, imposing no particular order on processor
    1806      129214 :               Parallel::Status id_status (this->comm().probe (Parallel::any_source, id_tags[blk]));
    1807      125618 :               std::vector<dof_id_type> & ids (recv_ids[id_status.source()]);
    1808      129214 :               this->comm().receive (id_status.source(), ids, id_tags[blk]);
    1809             : #else
    1810             :               std::vector<dof_id_type> & ids (recv_ids[0]);
    1811             :               ids = xfer_ids[blk];
    1812             : #endif
    1813             : 
    1814             :               // note its possible we didn't receive values for objects in
    1815             :               // this block if they have no components allocated.
    1816    14840596 :               for (std::size_t idx=0, sz=ids.size(); idx<sz; idx+=2)
    1817             :                 {
    1818             :                   const dof_id_type
    1819    14714978 :                     local_idx          = ids[idx+0]-first_object,
    1820    14714978 :                     n_vals_tot_allvecs = ids[idx+1];
    1821             : 
    1822     1338000 :                   libmesh_assert_less (local_idx, n_objects_blk);
    1823     1338000 :                   libmesh_assert_less (local_idx, obj_val_offsets.size());
    1824             : 
    1825    16052978 :                   obj_val_offsets[local_idx] = n_vals_tot_allvecs;
    1826             :                 }
    1827             : 
    1828             : #ifdef LIBMESH_HAVE_MPI
    1829             :               // blocking receive values for this block, imposing no particular order on processor
    1830      129214 :               Parallel::Status val_status  (this->comm().probe (Parallel::any_source, val_tags[blk]));
    1831      125618 :               std::vector<Number> & vals    (recv_vals[val_status.source()]);
    1832      129214 :               this->comm().receive (val_status.source(), vals, val_tags[blk]);
    1833             : #else
    1834             :               // straight copy without MPI
    1835             :               std::vector<Number> & vals (recv_vals[0]);
    1836             :               vals = send_vals[blk];
    1837             : #endif
    1838             : 
    1839      129214 :               n_val_recvd_blk += vals.size();
    1840             :             }
    1841             : 
    1842             :           // We need the offsets into the output_vals vector for each object.
    1843             :           // fortunately, this is simply the partial sum of the total number
    1844             :           // of components for each object
    1845       17966 :           std::partial_sum(obj_val_offsets.begin(), obj_val_offsets.end(),
    1846             :                            obj_val_offsets.begin());
    1847             : 
    1848             :           // wait on any previous asynchronous IO - this *must* complete before
    1849             :           // we start messing with the output_vals buffer!
    1850       19764 :           if (async_io.get()) async_io->join();
    1851             : 
    1852             :           // this is the actual output buffer that will be written to disk.
    1853             :           // at ths point we finally know wha size it will be.
    1854       19764 :           output_vals.resize(n_val_recvd_blk);
    1855             : 
    1856             :           // pack data from all processors into output values
    1857      145382 :           for (auto proc : make_range(this->n_processors()))
    1858             :             {
    1859      125618 :               const std::vector<dof_id_type> & ids (recv_ids [proc]);
    1860        7192 :               const std::vector<Number>      & vals(recv_vals[proc]);
    1861        7192 :               std::vector<Number>::const_iterator proc_vals(vals.begin());
    1862             : 
    1863    14840596 :               for (std::size_t idx=0, sz=ids.size(); idx<sz; idx+=2)
    1864             :                 {
    1865             :                   const dof_id_type
    1866    14714978 :                     local_idx          = ids[idx+0]-first_object,
    1867    16052978 :                     n_vals_tot_allvecs = ids[idx+1];
    1868             : 
    1869             :                   // put this object's data into the proper location
    1870             :                   // in  the output buffer
    1871    13376978 :                   std::vector<Number>::iterator out_vals(output_vals.begin());
    1872    14714978 :                   if (local_idx != 0)
    1873    16042172 :                     std::advance (out_vals, obj_val_offsets[local_idx-1]);
    1874             : 
    1875    30815663 :                   for (unsigned int val=0; val<n_vals_tot_allvecs; val++, ++out_vals, ++proc_vals)
    1876             :                     {
    1877     1454980 :                       libmesh_assert (out_vals  != output_vals.end());
    1878     1454980 :                       libmesh_assert (proc_vals != vals.end());
    1879    16100685 :                       *out_vals = *proc_vals;
    1880             :                     }
    1881             :                 }
    1882             :             }
    1883             : 
    1884             :           // output_vals buffer is now filled for this block.
    1885             :           // write it to disk
    1886       35932 :           async_io = std::make_unique<Threads::Thread>(threaded_io);
    1887       21562 :           written_length += output_vals.size();
    1888             :         }
    1889             : 
    1890             :       // wait on any previous asynchronous IO - this *must* complete before
    1891             :       // our stuff goes out of scope
    1892       19764 :       async_io->join();
    1893       32336 :     }
    1894             : 
    1895      125618 :   Parallel::wait(id_requests);
    1896      125618 :   Parallel::wait(val_requests);
    1897             : 
    1898             :   // we need some synchronization here.  Because this method
    1899             :   // can be called for a range of nodes, then a range of elements,
    1900             :   // we need some mechanism to prevent processors from racing past
    1901             :   // to the next range and overtaking ongoing communication.  one
    1902             :   // approach would be to figure out unique tags for each range,
    1903             :   // but for now we just impose a barrier here.  And might as
    1904             :   // well have it do some useful work.
    1905      125618 :   this->comm().broadcast(written_length);
    1906             : 
    1907      251236 :   return written_length;
    1908      118426 : }
    1909             : 
    1910             : 
    1911             : 
    1912           0 : unsigned int System::write_SCALAR_dofs (const NumericVector<Number> & vec,
    1913             :                                         const unsigned int var,
    1914             :                                         Xdr & io) const
    1915             : {
    1916           0 :   unsigned int written_length=0;
    1917           0 :   std::vector<Number> vals; // The raw values for the local objects in the current block
    1918             :   // Collect the SCALARs for the current variable
    1919           0 :   if (this->processor_id() == (this->n_processors()-1))
    1920             :     {
    1921           0 :       const DofMap & dof_map = this->get_dof_map();
    1922           0 :       std::vector<dof_id_type> SCALAR_dofs;
    1923           0 :       dof_map.SCALAR_dof_indices(SCALAR_dofs, var);
    1924             :       const unsigned int n_scalar_dofs = cast_int<unsigned int>
    1925           0 :         (SCALAR_dofs.size());
    1926             : 
    1927           0 :       for (unsigned int i=0; i<n_scalar_dofs; i++)
    1928             :         {
    1929           0 :           vals.push_back( vec(SCALAR_dofs[i]) );
    1930             :         }
    1931             :     }
    1932             : 
    1933             : #ifdef LIBMESH_HAVE_MPI
    1934           0 :   if (this->n_processors() > 1)
    1935             :     {
    1936             :       const Parallel::MessageTag val_tag =
    1937           0 :         this->comm().get_unique_tag(1);
    1938             : 
    1939             :       // Post the receive on processor 0
    1940           0 :       if (this->processor_id() == 0)
    1941             :         {
    1942           0 :           this->comm().receive(this->n_processors()-1, vals, val_tag);
    1943             :         }
    1944             : 
    1945             :       // Send the data to processor 0
    1946           0 :       if (this->processor_id() == (this->n_processors()-1))
    1947             :         {
    1948           0 :           this->comm().send(0, vals, val_tag);
    1949             :         }
    1950           0 :     }
    1951             : #endif
    1952             : 
    1953             :   // -------------------------------------------------------
    1954             :   // Write the output on processor 0.
    1955           0 :   if (this->processor_id() == 0)
    1956             :     {
    1957             :       const unsigned int vals_size =
    1958           0 :         cast_int<unsigned int>(vals.size());
    1959           0 :       io.data_stream (vals.data(), vals_size);
    1960           0 :       written_length += vals_size;
    1961             :     }
    1962             : 
    1963           0 :   return written_length;
    1964             : }
    1965             : 
    1966             : 
    1967             : 
    1968       62525 : dof_id_type System::write_serialized_vector (Xdr & io,
    1969             :                                              const NumericVector<Number> & vec) const
    1970             : {
    1971        1790 :   parallel_object_only();
    1972             : 
    1973        1790 :   libmesh_assert (io.writing());
    1974             : 
    1975       62525 :   dof_id_type vec_length = vec.size();
    1976       65210 :   if (this->processor_id() == 0) io.data (vec_length, "# vector length");
    1977             : 
    1978        1790 :   dof_id_type written_length = 0;
    1979             : 
    1980             :   //---------------------------------
    1981             :   // Collect the values for all nodes
    1982        1790 :   written_length += cast_int<dof_id_type>
    1983      123260 :     (this->write_serialized_blocked_dof_objects (std::vector<const NumericVector<Number> *>(1,&vec),
    1984       62525 :                                                  this->get_mesh().n_nodes(),
    1985      125050 :                                                  this->get_mesh().local_nodes_begin(),
    1986      123260 :                                                  this->get_mesh().local_nodes_end(),
    1987             :                                                  io));
    1988             : 
    1989             :   //------------------------------------
    1990             :   // Collect the values for all elements
    1991       62525 :   written_length += cast_int<dof_id_type>
    1992      123260 :     (this->write_serialized_blocked_dof_objects (std::vector<const NumericVector<Number> *>(1,&vec),
    1993       62525 :                                                  this->get_mesh().n_elem(),
    1994      125050 :                                                  this->get_mesh().local_elements_begin(),
    1995       64315 :                                                  this->get_mesh().local_elements_end(),
    1996             :                                                  io));
    1997             : 
    1998             :   //-------------------------------------------
    1999             :   // Finally loop over all the SCALAR variables
    2000      125192 :   for (auto var : make_range(this->n_vars()))
    2001       62667 :     if (this->variable(var).type().family == SCALAR)
    2002             :       {
    2003           0 :         written_length +=
    2004           0 :           this->write_SCALAR_dofs (vec, var, io);
    2005             :       }
    2006             : 
    2007        1790 :   if (this->processor_id() == 0)
    2008         895 :     libmesh_assert_equal_to (written_length, vec_length);
    2009             : 
    2010       62525 :   return written_length;
    2011             : }
    2012             : 
    2013             : 
    2014             : template <typename InValType>
    2015         284 : std::size_t System::read_serialized_vectors (Xdr & io,
    2016             :                                              const std::vector<NumericVector<Number> *> & vectors) const
    2017             : {
    2018           8 :   parallel_object_only();
    2019             : 
    2020             :   // Error checking
    2021             :   // #ifndef NDEBUG
    2022             :   //   // In parallel we better be reading a parallel vector -- if not
    2023             :   //   // we will not set all of its components below!!
    2024             :   //   if (this->n_processors() > 1)
    2025             :   //     {
    2026             :   //       libmesh_assert (vec.type() == PARALLEL ||
    2027             :   //       vec.type() == GHOSTED);
    2028             :   //     }
    2029             :   // #endif
    2030             : 
    2031           8 :   libmesh_assert (io.reading());
    2032             : 
    2033         292 :   if (this->processor_id() == 0)
    2034             :     {
    2035             :       // sizes
    2036          48 :       unsigned int num_vecs=0;
    2037          48 :       dof_id_type vector_length=0;
    2038             : 
    2039             :       // Get the number of vectors
    2040          48 :       io.data(num_vecs);
    2041             :       // Get the buffer size
    2042          48 :       io.data(vector_length);
    2043             : 
    2044          52 :       libmesh_error_msg_if
    2045             :         (num_vecs != vectors.size(),
    2046             :          "Xdr file header declares " << num_vecs << " vectors, but we were asked to read " << vectors.size());
    2047             : 
    2048          48 :       if (num_vecs != 0)
    2049             :         {
    2050          48 :           libmesh_error_msg_if (vectors[0] == nullptr, "vectors[0] should not be null");
    2051          48 :           libmesh_error_msg_if (vectors[0]->size() != vector_length, "Inconsistent vector sizes");
    2052             :         }
    2053             :     }
    2054             : 
    2055             :   // no need to actually communicate these.
    2056             :   // this->comm().broadcast(num_vecs);
    2057             :   // this->comm().broadcast(vector_length);
    2058             : 
    2059             :   // Cache these - they are not free!
    2060             :   const dof_id_type
    2061         284 :     n_nodes = this->get_mesh().n_nodes(),
    2062         284 :     n_elem  = this->get_mesh().n_elem();
    2063             : 
    2064           8 :   std::size_t read_length = 0;
    2065             : 
    2066             :   //---------------------------------
    2067             :   // Collect the values for all nodes
    2068          24 :   read_length +=
    2069         536 :     this->read_serialized_blocked_dof_objects (n_nodes,
    2070         284 :                                                this->get_mesh().local_nodes_begin(),
    2071         284 :                                                this->get_mesh().local_nodes_end(),
    2072             :                                                InValType(),
    2073             :                                                io,
    2074             :                                                vectors);
    2075             : 
    2076             :   //------------------------------------
    2077             :   // Collect the values for all elements
    2078         284 :   read_length +=
    2079         536 :     this->read_serialized_blocked_dof_objects (n_elem,
    2080         284 :                                                this->get_mesh().local_elements_begin(),
    2081         284 :                                                this->get_mesh().local_elements_end(),
    2082             :                                                InValType(),
    2083             :                                                io,
    2084             :                                                vectors);
    2085             : 
    2086             :   //-------------------------------------------
    2087             :   // Finally loop over all the SCALAR variables
    2088        5240 :   for (NumericVector<Number> * vec : vectors)
    2089       14172 :     for (auto var : make_range(this->n_vars()))
    2090        9216 :       if (this->variable(var).type().family == SCALAR)
    2091             :         {
    2092           0 :           libmesh_assert_not_equal_to (vec, 0);
    2093             : 
    2094           0 :           read_length +=
    2095           0 :             this->read_SCALAR_dofs (var, io, vec);
    2096             :         }
    2097             : 
    2098             :   //---------------------------------------
    2099             :   // last step - must close all the vectors
    2100        5240 :   for (NumericVector<Number> * vec : vectors)
    2101             :     {
    2102         140 :       libmesh_assert_not_equal_to (vec, 0);
    2103        4956 :       vec->close();
    2104             :     }
    2105             : 
    2106         284 :   return read_length;
    2107             : }
    2108             : 
    2109             : 
    2110             : 
    2111         284 : std::size_t System::write_serialized_vectors (Xdr & io,
    2112             :                                               const std::vector<const NumericVector<Number> *> & vectors) const
    2113             : {
    2114           8 :   parallel_object_only();
    2115             : 
    2116           8 :   libmesh_assert (io.writing());
    2117             : 
    2118             :   // Cache these - they are not free!
    2119             :   const dof_id_type
    2120         284 :     n_nodes       = this->get_mesh().n_nodes(),
    2121         284 :     n_elem        = this->get_mesh().n_elem();
    2122             : 
    2123           8 :   std::size_t written_length = 0;
    2124             : 
    2125         292 :   if (this->processor_id() == 0)
    2126             :     {
    2127             :       unsigned int
    2128          48 :         n_vec    = cast_int<unsigned int>(vectors.size());
    2129             :       dof_id_type
    2130          48 :         vec_size = vectors.empty() ? 0 : vectors[0]->size();
    2131             :       // Set the number of vectors
    2132          48 :       io.data(n_vec, "# number of vectors");
    2133             :       // Set the buffer size
    2134          48 :       io.data(vec_size, "# vector length");
    2135             :     }
    2136             : 
    2137             :   //---------------------------------
    2138             :   // Collect the values for all nodes
    2139           8 :   written_length +=
    2140         284 :     this->write_serialized_blocked_dof_objects (vectors,
    2141             :                                                 n_nodes,
    2142         568 :                                                 this->get_mesh().local_nodes_begin(),
    2143         560 :                                                 this->get_mesh().local_nodes_end(),
    2144             :                                                 io);
    2145             : 
    2146             :   //------------------------------------
    2147             :   // Collect the values for all elements
    2148         284 :   written_length +=
    2149         284 :     this->write_serialized_blocked_dof_objects (vectors,
    2150             :                                                 n_elem,
    2151         568 :                                                 this->get_mesh().local_elements_begin(),
    2152         560 :                                                 this->get_mesh().local_elements_end(),
    2153             :                                                 io);
    2154             : 
    2155             :   //-------------------------------------------
    2156             :   // Finally loop over all the SCALAR variables
    2157        5240 :   for (const NumericVector<Number> * vec : vectors)
    2158       14172 :     for (auto var : make_range(this->n_vars()))
    2159        9216 :       if (this->variable(var).type().family == SCALAR)
    2160             :         {
    2161           0 :           libmesh_assert_not_equal_to (vec, 0);
    2162             : 
    2163           0 :           written_length +=
    2164           0 :             this->write_SCALAR_dofs (*vec, var, io);
    2165             :         }
    2166             : 
    2167         284 :   return written_length;
    2168             : }
    2169             : 
    2170             : 
    2171             : 
    2172             : 
    2173             : template LIBMESH_EXPORT void System::read_parallel_data<Number> (Xdr & io, const bool read_additional_data);
    2174             : template LIBMESH_EXPORT void System::read_serialized_data<Number> (Xdr & io, const bool read_additional_data);
    2175             : template LIBMESH_EXPORT numeric_index_type System::read_serialized_vector<Number> (Xdr & io, NumericVector<Number> * vec);
    2176             : template LIBMESH_EXPORT std::size_t System::read_serialized_vectors<Number> (Xdr & io, const std::vector<NumericVector<Number> *> & vectors) const;
    2177             : #ifdef LIBMESH_USE_COMPLEX_NUMBERS
    2178             : template LIBMESH_EXPORT void System::read_parallel_data<Real> (Xdr & io, const bool read_additional_data);
    2179             : template LIBMESH_EXPORT void System::read_serialized_data<Real> (Xdr & io, const bool read_additional_data);
    2180             : template LIBMESH_EXPORT numeric_index_type System::read_serialized_vector<Real> (Xdr & io, NumericVector<Number> * vec);
    2181             : template LIBMESH_EXPORT std::size_t System::read_serialized_vectors<Real> (Xdr & io, const std::vector<NumericVector<Number> *> & vectors) const;
    2182             : #endif
    2183             : 
    2184             : } // namespace libMesh

Generated by: LCOV version 1.14