LCOV - code coverage report
Current view: top level - src/mesh - mesh_communication_global_indices.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4624 (b6265e) with base 1fb77f Lines: 287 353 81.3 %
Date: 2026-11-03 16:49:57 Functions: 32 45 71.1 %
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             : 
      20             : // Local includes
      21             : #include "libmesh/mesh_communication.h"
      22             : 
      23             : // libMesh includes
      24             : #include "libmesh/libmesh_config.h"
      25             : #include "libmesh/libmesh_common.h"
      26             : #include "libmesh/libmesh_logging.h"
      27             : #include "libmesh/mesh_base.h"
      28             : #include "libmesh/mesh_tools.h"
      29             : #include "libmesh/parallel_hilbert.h"
      30             : #include "libmesh/parallel_sort.h"
      31             : #include "libmesh/elem.h"
      32             : #include "libmesh/elem_range.h"
      33             : #include "libmesh/node_range.h"
      34             : 
      35             : // TIMPI includes
      36             : #include "timpi/parallel_implementation.h"
      37             : #include "timpi/parallel_sync.h"
      38             : 
      39             : // C/C++ includes
      40             : #ifdef LIBMESH_HAVE_LIBHILBERT
      41             : #  include "hilbert.h"
      42             : #endif
      43             : 
      44             : 
      45             : #if defined(LIBMESH_HAVE_LIBHILBERT) && defined(LIBMESH_HAVE_MPI)
      46             : 
      47             : namespace { // anonymous namespace for helper functions
      48             : 
      49             : using namespace libMesh;
      50             : 
      51             : 
      52             : // Trying to divide per mesh, not per point
      53       26100 : Point invert_bbox (const BoundingBox & bbox)
      54             : {
      55       13137 :   Point p;
      56             : 
      57       52374 :   p(0) = (bbox.first(0) == bbox.second(0)) ? 0. :
      58       39237 :           1/(bbox.second(0)-bbox.first(0));
      59             : 
      60             : #if LIBMESH_DIM > 1
      61       51530 :   p(1) = (bbox.first(1) == bbox.second(1)) ? 0. :
      62       36705 :           1/(bbox.second(1)-bbox.first(1));
      63             : #endif
      64             : 
      65             : #if LIBMESH_DIM > 2
      66       52245 :   p(2) = (bbox.first(2) == bbox.second(2)) ? 0. :
      67       19450 :           1/(bbox.second(2)-bbox.first(2));
      68             : #endif
      69             : 
      70       26100 :   return p;
      71             : }
      72             : 
      73             : 
      74             : 
      75             : // Utility function to map (x,y,z) in [bbox.min, bbox.max]^3 into
      76             : // [0,max_inttype]^3 for computing Hilbert keys
      77    18976192 : void get_hilbert_coords (const Point & p,
      78             :                          const BoundingBox & bbox,
      79             :                          const Point & bboxinv,
      80             :                          CFixBitVec icoords[3])
      81             : {
      82             :   static const Hilbert::inttype max_inttype = static_cast<Hilbert::inttype>(-1);
      83             : 
      84             :   const Real // put (x,y,z) in [0,1]^3 (don't divide by 0)
      85    18976192 :     x = (p(0)-bbox.first(0)) * bboxinv(0),
      86             : 
      87             : #if LIBMESH_DIM > 1
      88    18976192 :     y = (p(1)-bbox.first(1)) * bboxinv(1),
      89             : #else
      90             :     y = 0.,
      91             : #endif
      92             : 
      93             : #if LIBMESH_DIM > 2
      94    18976192 :     z = (p(2)-bbox.first(2)) * bboxinv(2);
      95             : #else
      96             :     z = 0.;
      97             : #endif
      98             : 
      99             :   // (icoords) in [0,max_inttype]^3
     100    18976192 :   icoords[0] = static_cast<Hilbert::inttype>(x*max_inttype);
     101    18976192 :   icoords[1] = static_cast<Hilbert::inttype>(y*max_inttype);
     102    18976192 :   icoords[2] = static_cast<Hilbert::inttype>(z*max_inttype);
     103    18976192 : }
     104             : 
     105             : 
     106             : 
     107             : Parallel::DofObjectKey
     108    12007594 : get_dofobject_key (const Elem & e,
     109             :                    const BoundingBox & bbox,
     110             :                    const Point & bboxinv)
     111             : {
     112             :   static const unsigned int sizeof_inttype = sizeof(Hilbert::inttype);
     113             : 
     114     4026716 :   Hilbert::HilbertIndices index;
     115    48030376 :   CFixBitVec icoords[3];
     116     4026716 :   Hilbert::BitVecType bv;
     117    12007594 :   get_hilbert_coords (e.vertex_average(), bbox, bboxinv, icoords);
     118     4026716 :   Hilbert::coordsToIndex (icoords, 8*sizeof_inttype, 3, bv);
     119    12007594 :   index = bv;
     120             : 
     121             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
     122    16034310 :   return std::make_pair(index, e.unique_id());
     123             : #else
     124             :   // A parent and a child can share a vertex average; their levels differ
     125             :   return std::make_pair(index, unique_id_type(e.level()));
     126             : #endif
     127             : }
     128             : 
     129             : 
     130             : 
     131             : // Compute the hilbert index
     132             : Parallel::DofObjectKey
     133     6968598 : get_dofobject_key (const Node & n,
     134             :                    const BoundingBox & bbox,
     135             :                    const Point & bboxinv)
     136             : {
     137             :   static const unsigned int sizeof_inttype = sizeof(Hilbert::inttype);
     138             : 
     139     2303645 :   Hilbert::HilbertIndices index;
     140    27874392 :   CFixBitVec icoords[3];
     141     2303645 :   Hilbert::BitVecType bv;
     142     6968598 :   get_hilbert_coords (n, bbox, bboxinv, icoords);
     143     2303645 :   Hilbert::coordsToIndex (icoords, 8*sizeof_inttype, 3, bv);
     144     6968598 :   index = bv;
     145             : 
     146             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
     147     9272243 :   return std::make_pair(index, n.unique_id());
     148             : #else
     149             :   return std::make_pair(index, unique_id_type(0));
     150             : #endif
     151             : }
     152             : 
     153             : // Helper class for threaded Hilbert key computation
     154             : class ComputeHilbertKeys
     155             : {
     156             : public:
     157        1140 :   ComputeHilbertKeys (const BoundingBox & bbox,
     158             :                       const Point & bboxinv,
     159        3444 :                       std::vector<Parallel::DofObjectKey> & keys) :
     160        1164 :     _bbox(bbox),
     161        1164 :     _bboxinv(bboxinv),
     162        3444 :     _keys(keys)
     163        1140 :   {}
     164             : 
     165             :   // computes the hilbert index for a node
     166        2334 :   void operator() (const ConstNodeRange & range) const
     167             :   {
     168        1548 :     std::size_t pos = range.first_idx();
     169     1405468 :     for (const auto & node : range)
     170             :       {
     171      460729 :         libmesh_assert(node);
     172      460729 :         libmesh_assert_less (pos, _keys.size());
     173     1403134 :         _keys[pos++] = get_dofobject_key (*node, _bbox, _bboxinv);
     174             :       }
     175        2334 :   }
     176             : 
     177             :   // computes the hilbert index for an element
     178        2334 :   void operator() (const ConstElemRange & range) const
     179             :   {
     180        1548 :     std::size_t pos = range.first_idx();
     181     1725708 :     for (const auto & elem : range)
     182             :       {
     183      571452 :         libmesh_assert(elem);
     184      571452 :         libmesh_assert_less (pos, _keys.size());
     185     1723374 :         _keys[pos++] = get_dofobject_key (*elem, _bbox, _bboxinv);
     186             :       }
     187        2334 :   }
     188             : 
     189             : private:
     190             :   const BoundingBox _bbox;
     191             :   const Point _bboxinv;
     192             :   std::vector<Parallel::DofObjectKey> & _keys;
     193             : };
     194             : 
     195             : }
     196             : #endif // defined(LIBMESH_HAVE_LIBHILBERT) && defined(LIBMESH_HAVE_MPI)
     197             : 
     198             : 
     199             : namespace libMesh
     200             : {
     201             : 
     202             : // ------------------------------------------------------------
     203             : // MeshCommunication class members
     204             : #if defined(LIBMESH_HAVE_LIBHILBERT) && defined(LIBMESH_HAVE_MPI)
     205        1722 : void MeshCommunication::assign_global_indices (MeshBase & mesh) const
     206             : {
     207        1710 :   LOG_SCOPE ("assign_global_indices()", "MeshCommunication");
     208             : 
     209             :   // This method determines partition-agnostic global indices
     210             :   // for nodes and elements.
     211             : 
     212             :   // Algorithm:
     213             :   // (1) compute the Hilbert key for each local node/element
     214             :   // (2) perform a parallel sort of the Hilbert key
     215             :   // (3) get the min/max value on each processor
     216             :   // (4) determine the position in the global ranking for
     217             :   //     each local object
     218             : 
     219        1140 :   const Parallel::Communicator & communicator (mesh.comm());
     220             : 
     221             : #ifdef DEBUG
     222             :   // This is going to be a mess if geometry is out of sync
     223         570 :   MeshTools::libmesh_assert_equal_points(mesh);
     224         570 :   MeshTools::libmesh_assert_equal_connectivity(mesh);
     225             : #endif
     226             : 
     227             :   // Global bounding box.  We choose the nodal bounding box for
     228             :   // backwards compatibility; the element bounding box may be looser
     229             :   // on curved elements.
     230             :   const BoundingBox bbox =
     231        1722 :     MeshTools::create_nodal_bounding_box (mesh);
     232             : 
     233        1152 :   const Point bboxinv = invert_bbox(bbox);
     234             : 
     235             :   //-------------------------------------------------------------
     236             :   // (1) compute Hilbert keys
     237             :   std::vector<Parallel::DofObjectKey>
     238        1140 :     node_keys, elem_keys;
     239             : 
     240             :   {
     241             :     // Nodes first
     242             :     {
     243        2862 :       ConstNodeRange nr (mesh.local_nodes_begin(),
     244        6888 :                          mesh.local_nodes_end());
     245        1722 :       node_keys.resize (nr.size());
     246        1722 :       Threads::parallel_for (nr, ComputeHilbertKeys (bbox, bboxinv, node_keys));
     247             : 
     248             :       // // It's O(N^2) to check that these keys don't duplicate before the
     249             :       // // sort...
     250             :       //
     251             :       // MeshBase::const_node_iterator nodei = mesh.local_nodes_begin();
     252             :       // for (std::size_t i = 0; i != node_keys.size(); ++i, ++nodei)
     253             :       //   {
     254             :       //     MeshBase::const_node_iterator nodej = mesh.local_nodes_begin();
     255             :       //     for (std::size_t j = 0; j != i; ++j, ++nodej)
     256             :       //       {
     257             :       //         if (node_keys[i] == node_keys[j])
     258             :       //           {
     259             :       //             CFixBitVec icoords[3], jcoords[3];
     260             :       //             get_hilbert_coords(**nodej, bbox, bboxinv, jcoords);
     261             :       //             libMesh::err << "node " << (*nodej)->id() << ", " << static_cast<Point &>(**nodej) << " has HilbertIndices " << node_keys[j] << std::endl;
     262             :       //             get_hilbert_coords(**nodei, bbox, bboxinv, icoords);
     263             :       //             libMesh::err << "node " << (*nodei)->id() << ", " << static_cast<Point &>(**nodei) << " has HilbertIndices " << node_keys[i] << std::endl;
     264             :       //             libmesh_error_msg("Error: nodes with duplicate Hilbert keys!");
     265             :       //           }
     266             :       //       }
     267             :       //   }
     268             :     }
     269             : 
     270             :     // Elements next
     271             :     {
     272        2862 :       ConstElemRange er (mesh.local_elements_begin(),
     273        6888 :                          mesh.local_elements_end());
     274        1722 :       elem_keys.resize (er.size());
     275        1722 :       Threads::parallel_for (er, ComputeHilbertKeys (bbox, bboxinv, elem_keys));
     276             : 
     277             :       // // For elements, the keys can be (and in the case of TRI, are
     278             :       // // expected to be) duplicates, but only if the elements are at
     279             :       // // different levels
     280             :       // MeshBase::const_element_iterator elemi = mesh.local_elements_begin();
     281             :       // for (std::size_t i = 0; i != elem_keys.size(); ++i, ++elemi)
     282             :       //   {
     283             :       //     MeshBase::const_element_iterator elemj = mesh.local_elements_begin();
     284             :       //     for (std::size_t j = 0; j != i; ++j, ++elemj)
     285             :       //       {
     286             :       //         if ((elem_keys[i] == elem_keys[j]) &&
     287             :       //             ((*elemi)->level() == (*elemj)->level()))
     288             :       //           {
     289             :       //             libMesh::err << "level " << (*elemj)->level()
     290             :       //                          << " elem\n" << (**elemj)
     291             :       //                          << " vertex average " << (*elemj)->vertex_average()
     292             :       //                          << " has HilbertIndices " << elem_keys[j]
     293             :       //                          << " or " << get_dofobject_key((**elemj), bbox, bboxinv)
     294             :       //                          << std::endl;
     295             :       //             libMesh::err << "level " << (*elemi)->level()
     296             :       //                          << " elem\n" << (**elemi)
     297             :       //                          << " vertex average " << (*elemi)->vertex_average()
     298             :       //                          << " has HilbertIndices " << elem_keys[i]
     299             :       //                          << " or " << get_dofobject_key((**elemi), bbox, bboxinv)
     300             :       //                          << std::endl;
     301             :       //             libmesh_error_msg("Error: level " << (*elemi)->level() << " elements with duplicate Hilbert keys!");
     302             :       //           }
     303             :       //       }
     304             :       //  }
     305             :     }
     306             :   } // done computing Hilbert keys
     307             : 
     308             : 
     309             : 
     310             :   //-------------------------------------------------------------
     311             :   // (2) parallel sort the Hilbert keys
     312             :   Parallel::Sort<Parallel::DofObjectKey> node_sorter (communicator,
     313        2862 :                                                       node_keys);
     314        1722 :   node_sorter.sort(); /* done with node_keys */ //node_keys.clear();
     315             : 
     316             :   const std::vector<Parallel::DofObjectKey> & my_node_bin =
     317        1722 :     node_sorter.bin();
     318             : 
     319             :   Parallel::Sort<Parallel::DofObjectKey> elem_sorter (communicator,
     320        2862 :                                                       elem_keys);
     321        1722 :   elem_sorter.sort(); /* done with elem_keys */ //elem_keys.clear();
     322             : 
     323             :   const std::vector<Parallel::DofObjectKey> & my_elem_bin =
     324        1722 :     elem_sorter.bin();
     325             : 
     326             : 
     327             : 
     328             :   //-------------------------------------------------------------
     329             :   // (3) get the max value on each processor
     330             :   std::vector<Parallel::DofObjectKey>
     331        2292 :     node_upper_bounds(communicator.size()),
     332        2292 :     elem_upper_bounds(communicator.size());
     333             : 
     334             :   { // limit scope of temporaries
     335        2292 :     std::vector<Parallel::DofObjectKey> recvbuf(2*communicator.size());
     336             :     std::vector<unsigned short int> /* do not use a vector of bools here since it is not always so! */
     337        2292 :       empty_nodes (communicator.size()),
     338        2292 :       empty_elem  (communicator.size());
     339        2292 :     std::vector<Parallel::DofObjectKey> my_max(2);
     340             : 
     341        2292 :     communicator.allgather (static_cast<unsigned short int>(my_node_bin.empty()), empty_nodes);
     342        1722 :     communicator.allgather (static_cast<unsigned short int>(my_elem_bin.empty()),  empty_elem);
     343             : 
     344        1722 :     if (!my_node_bin.empty()) my_max[0] = my_node_bin.back();
     345        1722 :     if (!my_elem_bin.empty()) my_max[1] = my_elem_bin.back();
     346             : 
     347        1722 :     communicator.allgather (my_max, /* identical_buffer_sizes = */ true);
     348             : 
     349             :     // Be careful here.  The *_upper_bounds will be used to find the processor
     350             :     // a given object belongs to.  So, if a processor contains no objects (possible!)
     351             :     // then copy the bound from the lower processor id.
     352        5148 :     for (auto p : make_range(communicator.size()))
     353             :       {
     354        4566 :         node_upper_bounds[p] = my_max[2*p+0];
     355        4566 :         elem_upper_bounds[p] = my_max[2*p+1];
     356             : 
     357        3426 :         if (p > 0) // default hilbert index value is the OK upper bound for processor 0.
     358             :           {
     359        2274 :             if (empty_nodes[p]) node_upper_bounds[p] = node_upper_bounds[p-1];
     360        2274 :             if (empty_elem[p])  elem_upper_bounds[p] = elem_upper_bounds[p-1];
     361             :           }
     362             :       }
     363             :   }
     364             : 
     365             : 
     366             : 
     367             :   //-------------------------------------------------------------
     368             :   // (4) determine the position in the global ranking for
     369             :   //     each local object
     370             :   {
     371             :     //----------------------------------------------
     372             :     // Nodes first -- all nodes, not just local ones
     373             :     {
     374             :       // Request sets to send to each processor
     375             :       std::map<processor_id_type, std::vector<Parallel::DofObjectKey>>
     376        1140 :         requested_ids;
     377             :       // Results to gather from each processor - kept in a map so we
     378             :       // do only one loop over nodes after all receives are done.
     379             :       std::map<dof_id_type, std::vector<dof_id_type>>
     380        1140 :         filled_request;
     381             : 
     382             :       // build up list of requests
     383     3725422 :       for (const auto & node : mesh.node_ptr_range())
     384             :         {
     385      921458 :           libmesh_assert(node);
     386             :           const Parallel::DofObjectKey hi =
     387     2782732 :             get_dofobject_key (*node, bbox, bboxinv);
     388             :           const processor_id_type pid =
     389             :             cast_int<processor_id_type>
     390     2782732 :             (std::distance (node_upper_bounds.begin(),
     391             :                             std::lower_bound(node_upper_bounds.begin(),
     392             :                                              node_upper_bounds.end(),
     393     2782732 :                                              hi)));
     394             : 
     395      921458 :           libmesh_assert_less (pid, communicator.size());
     396             : 
     397     2782732 :           requested_ids[pid].push_back(hi);
     398         582 :         }
     399             : 
     400             :       // The number of objects in my_node_bin on each processor
     401        2292 :       std::vector<dof_id_type> node_bin_sizes(communicator.size());
     402        2292 :       communicator.allgather (static_cast<dof_id_type>(my_node_bin.size()), node_bin_sizes);
     403             : 
     404             :       // The offset of my first global index
     405         570 :       dof_id_type my_offset = 0;
     406        2574 :       for (auto pid : make_range(communicator.rank()))
     407        1137 :         my_offset += node_bin_sizes[pid];
     408             : 
     409             :       auto gather_functor =
     410        1146 :         [
     411             : #ifndef NDEBUG
     412             :          & node_upper_bounds,
     413             :          & communicator,
     414             : #endif
     415             :          & my_node_bin,
     416             :          my_offset
     417             :         ]
     418             :         (processor_id_type,
     419             :          const std::vector<Parallel::DofObjectKey> & keys,
     420     9235218 :          std::vector<dof_id_type> & global_ids)
     421             :         {
     422             :           // Fill the requests
     423        2280 :           const std::size_t keys_size = keys.size();
     424        3426 :           global_ids.reserve(keys_size);
     425     2786158 :           for (std::size_t idx=0; idx != keys_size; idx++)
     426             :             {
     427     1842916 :               const Parallel::DofObjectKey & hi = keys[idx];
     428      921458 :               libmesh_assert_less_equal (hi, node_upper_bounds[communicator.rank()]);
     429             : 
     430             :               // find the requested index in my node bin
     431             :               std::vector<Parallel::DofObjectKey>::const_iterator pos =
     432     2782732 :                 std::lower_bound (my_node_bin.begin(), my_node_bin.end(), hi);
     433      921458 :               libmesh_assert (pos != my_node_bin.end());
     434      921458 :               libmesh_assert_equal_to (*pos, hi);
     435             : 
     436             :               // Finally, assign the global index based off the position of the index
     437             :               // in my array, properly offset.
     438     4625648 :               global_ids.push_back(cast_int<dof_id_type>(std::distance(my_node_bin.begin(), pos) + my_offset));
     439             :             }
     440        4578 :         };
     441             : 
     442             :       auto action_functor =
     443        1146 :         [&filled_request]
     444             :         (processor_id_type pid,
     445             :          const std::vector<Parallel::DofObjectKey> &,
     446        2286 :          const std::vector<dof_id_type> & global_ids)
     447             :         {
     448        3426 :           filled_request[pid] = global_ids;
     449        4008 :         };
     450             : 
     451             :       // Trade requests with other processors
     452         570 :       const dof_id_type * ex = nullptr;
     453             :       Parallel::pull_parallel_vector_data
     454        1722 :         (communicator, requested_ids, gather_functor, action_functor, ex);
     455             : 
     456             :       // We now have all the filled requests, so we can loop through our
     457             :       // nodes once and assign the global index to each one.
     458             :       {
     459             :         std::map<dof_id_type, std::vector<dof_id_type>::const_iterator>
     460        1140 :           next_obj_on_proc;
     461        5148 :         for (auto & p : filled_request)
     462        3426 :           next_obj_on_proc[p.first] = p.second.begin();
     463             : 
     464     2809142 :         for (auto & node : mesh.node_ptr_range())
     465             :           {
     466      921458 :             libmesh_assert(node);
     467             :             const Parallel::DofObjectKey hi =
     468     2782732 :               get_dofobject_key (*node, bbox, bboxinv);
     469             :             const processor_id_type pid =
     470             :               cast_int<processor_id_type>
     471     2782732 :               (std::distance (node_upper_bounds.begin(),
     472             :                               std::lower_bound(node_upper_bounds.begin(),
     473             :                                                node_upper_bounds.end(),
     474     1866452 :                                                hi)));
     475             : 
     476      921458 :             libmesh_assert_less (pid, communicator.size());
     477      921458 :             libmesh_assert (next_obj_on_proc[pid] != filled_request[pid].end());
     478             : 
     479     2782732 :             const dof_id_type global_index = *next_obj_on_proc[pid];
     480      921458 :             libmesh_assert_less (global_index, mesh.n_nodes());
     481     2782732 :             node->set_id() = global_index;
     482             : 
     483     2782732 :             ++next_obj_on_proc[pid];
     484         582 :           }
     485             :       }
     486             :     }
     487             : 
     488             :     //---------------------------------------------------
     489             :     // elements next -- all elements, not just local ones
     490             :     {
     491             :       // Request sets to send to each processor
     492             :       std::map<processor_id_type, std::vector<Parallel::DofObjectKey>>
     493        1140 :         requested_ids;
     494             :       // Results to gather from each processor - kept in a map so we
     495             :       // do only one loop over elements after all receives are done.
     496             :       std::map<dof_id_type, std::vector<dof_id_type>>
     497        1140 :         filled_request;
     498             : 
     499     4586878 :       for (const auto & elem : mesh.element_ptr_range())
     500             :         {
     501     1142904 :           libmesh_assert(elem);
     502             :           const Parallel::DofObjectKey hi =
     503     3434906 :             get_dofobject_key (*elem, bbox, bboxinv);
     504             :           const processor_id_type pid =
     505             :             cast_int<processor_id_type>
     506     3434906 :             (std::distance (elem_upper_bounds.begin(),
     507             :                             std::lower_bound(elem_upper_bounds.begin(),
     508             :                                              elem_upper_bounds.end(),
     509     3434906 :                                              hi)));
     510             : 
     511     1142904 :           libmesh_assert_less (pid, communicator.size());
     512             : 
     513     3434906 :           requested_ids[pid].push_back(hi);
     514         582 :         }
     515             : 
     516             :       // The number of objects in my_elem_bin on each processor
     517        2292 :       std::vector<dof_id_type> elem_bin_sizes(communicator.size());
     518        2292 :       communicator.allgather (static_cast<dof_id_type>(my_elem_bin.size()), elem_bin_sizes);
     519             : 
     520             :       // The offset of my first global index
     521         570 :       dof_id_type my_offset = 0;
     522        2574 :       for (auto pid : make_range(communicator.rank()))
     523        1137 :         my_offset += elem_bin_sizes[pid];
     524             : 
     525             :       auto gather_functor =
     526        1146 :         [
     527             : #ifndef NDEBUG
     528             :          & elem_upper_bounds,
     529             :          & communicator,
     530             : #endif
     531             :          & my_elem_bin,
     532             :          my_offset
     533             :         ]
     534             :         (processor_id_type,
     535             :          const std::vector<Parallel::DofObjectKey> & keys,
     536    11437514 :          std::vector<dof_id_type> & global_ids)
     537             :         {
     538             :           // Fill the requests
     539        2280 :           const std::size_t keys_size = keys.size();
     540        3426 :           global_ids.reserve(keys_size);
     541     3438332 :           for (std::size_t idx=0; idx != keys_size; idx++)
     542             :             {
     543     2285808 :               const Parallel::DofObjectKey & hi = keys[idx];
     544     1142904 :               libmesh_assert_less_equal (hi, elem_upper_bounds[communicator.rank()]);
     545             : 
     546             :               // find the requested index in my elem bin
     547             :               std::vector<Parallel::DofObjectKey>::const_iterator pos =
     548     3434906 :                 std::lower_bound (my_elem_bin.begin(), my_elem_bin.end(), hi);
     549     1142904 :               libmesh_assert (pos != my_elem_bin.end());
     550     1142904 :               libmesh_assert_equal_to (*pos, hi);
     551             : 
     552             :               // Finally, assign the global index based off the position of the index
     553             :               // in my array, properly offset.
     554     5720714 :               global_ids.push_back (cast_int<dof_id_type>(std::distance(my_elem_bin.begin(), pos) + my_offset));
     555             :             }
     556        4578 :         };
     557             : 
     558             :       auto action_functor =
     559        1146 :         [&filled_request]
     560             :         (processor_id_type pid,
     561             :          const std::vector<Parallel::DofObjectKey> &,
     562        2286 :          const std::vector<dof_id_type> & global_ids)
     563             :         {
     564        3426 :           filled_request[pid] = global_ids;
     565        4008 :         };
     566             : 
     567             :       // Trade requests with other processors
     568         570 :       const dof_id_type * ex = nullptr;
     569             :       Parallel::pull_parallel_vector_data
     570        1722 :         (communicator, requested_ids, gather_functor, action_functor, ex);
     571             : 
     572             :       // We now have all the filled requests, so we can loop through our
     573             :       // elements once and assign the global index to each one.
     574             :       {
     575             :         std::vector<std::vector<dof_id_type>::const_iterator>
     576        2292 :           next_obj_on_proc; next_obj_on_proc.reserve(communicator.size());
     577        5148 :         for (auto pid : make_range(communicator.size()))
     578        5712 :           next_obj_on_proc.push_back(filled_request[pid].begin());
     579             : 
     580     4586878 :         for (auto & elem : mesh.element_ptr_range())
     581             :           {
     582     1142904 :             libmesh_assert(elem);
     583             :             const Parallel::DofObjectKey hi =
     584     3434906 :               get_dofobject_key (*elem, bbox, bboxinv);
     585             :             const processor_id_type pid =
     586             :               cast_int<processor_id_type>
     587     3434906 :               (std::distance (elem_upper_bounds.begin(),
     588             :                               std::lower_bound(elem_upper_bounds.begin(),
     589             :                                                elem_upper_bounds.end(),
     590     1142904 :                                                hi)));
     591             : 
     592     1142904 :             libmesh_assert_less (pid, communicator.size());
     593     1142904 :             libmesh_assert (next_obj_on_proc[pid] != filled_request[pid].end());
     594             : 
     595     3434906 :             const dof_id_type global_index = *next_obj_on_proc[pid];
     596     1142904 :             libmesh_assert_less (global_index, mesh.n_elem());
     597     3434906 :             elem->set_id() = global_index;
     598             : 
     599     1142904 :             ++next_obj_on_proc[pid];
     600         582 :           }
     601             :       }
     602             :     }
     603             :   }
     604        2304 : }
     605             : #else // LIBMESH_HAVE_LIBHILBERT, LIBMESH_HAVE_MPI
     606             : void MeshCommunication::assign_global_indices (MeshBase &) const
     607             : {
     608             : }
     609             : #endif // LIBMESH_HAVE_LIBHILBERT, LIBMESH_HAVE_MPI
     610             : 
     611             : 
     612             : #if defined(LIBMESH_HAVE_LIBHILBERT) && defined(LIBMESH_HAVE_MPI)
     613           0 : void MeshCommunication::check_for_duplicate_global_indices (MeshBase & mesh) const
     614             : {
     615           0 :   LOG_SCOPE ("check_for_duplicate_global_indices()", "MeshCommunication");
     616             : 
     617             :   // Global bounding box.  We choose the nodal bounding box for
     618             :   // backwards compatibility; the element bounding box may be looser
     619             :   // on curved elements.
     620             :   const BoundingBox bbox =
     621           0 :     MeshTools::create_nodal_bounding_box (mesh);
     622             : 
     623           0 :   const Point bboxinv = invert_bbox(bbox);
     624             : 
     625             :   std::vector<Parallel::DofObjectKey>
     626           0 :     node_keys, elem_keys;
     627             : 
     628             :   {
     629             :     // Nodes first
     630             :     {
     631           0 :       ConstNodeRange nr (mesh.local_nodes_begin(),
     632           0 :                          mesh.local_nodes_end());
     633           0 :       node_keys.resize (nr.size());
     634           0 :       Threads::parallel_for (nr, ComputeHilbertKeys (bbox, bboxinv, node_keys));
     635             : 
     636             :       // It's O(N^2) to check that these keys don't duplicate before the
     637             :       // sort...
     638           0 :       MeshBase::const_node_iterator nodei = mesh.local_nodes_begin();
     639           0 :       for (std::size_t i = 0; i != node_keys.size(); ++i, ++nodei)
     640             :         {
     641           0 :           MeshBase::const_node_iterator nodej = mesh.local_nodes_begin();
     642           0 :           for (std::size_t j = 0; j != i; ++j, ++nodej)
     643             :             {
     644           0 :               if (node_keys[i] == node_keys[j])
     645             :                 {
     646           0 :                   CFixBitVec icoords[3], jcoords[3];
     647           0 :                   get_hilbert_coords(**nodej, bbox, bboxinv, jcoords);
     648             :                   libMesh::err <<
     649           0 :                     "node " << (*nodej)->id() << ", " <<
     650           0 :                     *(const Point *)(*nodej) << " has HilbertIndices " <<
     651           0 :                     node_keys[j] << std::endl;
     652           0 :                   get_hilbert_coords(**nodei, bbox, bboxinv, icoords);
     653             :                   libMesh::err <<
     654           0 :                     "node " << (*nodei)->id() << ", " <<
     655           0 :                     *(const Point *)(*nodei) << " has HilbertIndices " <<
     656           0 :                     node_keys[i] << std::endl;
     657           0 :                   libmesh_error_msg("Error: nodes with duplicate Hilbert keys!");
     658             :                 }
     659             :             }
     660             :         }
     661             :     }
     662             : 
     663             :     // Elements next
     664             :     {
     665           0 :       ConstElemRange er (mesh.local_elements_begin(),
     666           0 :                          mesh.local_elements_end());
     667           0 :       elem_keys.resize (er.size());
     668           0 :       Threads::parallel_for (er, ComputeHilbertKeys (bbox, bboxinv, elem_keys));
     669             : 
     670             :       // For elements, the keys can be (and in the case of TRI, are
     671             :       // expected to be) duplicates, but only if the elements are at
     672             :       // different levels
     673           0 :       MeshBase::const_element_iterator elemi = mesh.local_elements_begin();
     674           0 :       for (std::size_t i = 0; i != elem_keys.size(); ++i, ++elemi)
     675             :         {
     676           0 :           MeshBase::const_element_iterator elemj = mesh.local_elements_begin();
     677           0 :           for (std::size_t j = 0; j != i; ++j, ++elemj)
     678             :             {
     679           0 :               if ((elem_keys[i] == elem_keys[j]) &&
     680           0 :                   ((*elemi)->level() == (*elemj)->level()))
     681             :                 {
     682             :                   libMesh::err <<
     683           0 :                     "level " << (*elemj)->level() << " elem\n" <<
     684           0 :                     (**elemj) << " vertex average " <<
     685           0 :                     (*elemj)->vertex_average() << " has HilbertIndices " <<
     686           0 :                     elem_keys[j] << " or " <<
     687           0 :                     get_dofobject_key((**elemj), bbox, bboxinv) <<
     688           0 :                     std::endl;
     689             :                   libMesh::err <<
     690           0 :                     "level " << (*elemi)->level() << " elem\n" <<
     691           0 :                     (**elemi) << " vertex average " <<
     692           0 :                     (*elemi)->vertex_average() << " has HilbertIndices " <<
     693           0 :                     elem_keys[i] << " or " <<
     694           0 :                     get_dofobject_key((**elemi), bbox, bboxinv) <<
     695           0 :                     std::endl;
     696           0 :                   libmesh_error_msg("Error: level " << (*elemi)->level() << " elements with duplicate Hilbert keys!");
     697             :                 }
     698             :             }
     699             :         }
     700             :     }
     701             :   } // done checking Hilbert keys
     702           0 : }
     703             : #else // LIBMESH_HAVE_LIBHILBERT, LIBMESH_HAVE_MPI
     704             : void MeshCommunication::check_for_duplicate_global_indices (MeshBase &) const
     705             : {
     706             : }
     707             : #endif // LIBMESH_HAVE_LIBHILBERT, LIBMESH_HAVE_MPI
     708             : 
     709             : #if defined(LIBMESH_HAVE_LIBHILBERT) && defined(LIBMESH_HAVE_MPI)
     710             : template <typename ForwardIterator>
     711        2340 : void MeshCommunication::find_local_indices (const BoundingBox & bbox,
     712             :                                             const ForwardIterator & begin,
     713             :                                             const ForwardIterator & end,
     714             :                                             std::unordered_map<dof_id_type, dof_id_type> & index_map) const
     715             : {
     716        2340 :   LOG_SCOPE ("find_local_indices()", "MeshCommunication");
     717             : 
     718             :   // This method determines id-agnostic local indices
     719             :   // for nodes and elements by sorting Hilbert keys.
     720             : 
     721         780 :   index_map.clear();
     722             : 
     723        1560 :   const Point bboxinv = invert_bbox(bbox);
     724             : 
     725             :   //-------------------------------------------------------------
     726             :   // (1) compute Hilbert keys
     727             :   // These aren't trivial to compute, and we will need them again.
     728             :   // But the binsort will sort the input vector, trashing the order
     729             :   // that we'd like to rely on.  So, two vectors...
     730        1560 :   std::map<Parallel::DofObjectKey, dof_id_type> hilbert_keys;
     731             :   {
     732        2340 :     LOG_SCOPE("local_hilbert_indices", "MeshCommunication");
     733       73344 :     for (ForwardIterator it=begin; it!=end; ++it)
     734             :       {
     735       71004 :         const Parallel::DofObjectKey hi(get_dofobject_key ((**it), bbox, bboxinv));
     736       71004 :         hilbert_keys.emplace(hi, (*it)->id());
     737             :       }
     738             :   }
     739             : 
     740             :   {
     741         780 :     dof_id_type cnt = 0;
     742       73344 :     for (auto key_val : hilbert_keys)
     743       71004 :       index_map[key_val.second] = cnt++;
     744             :   }
     745        2340 : }
     746             : 
     747             : 
     748             : template <typename ForwardIterator>
     749       35175 : void MeshCommunication::find_global_indices (const Parallel::Communicator & communicator,
     750             :                                              const BoundingBox & bbox,
     751             :                                              const ForwardIterator & begin,
     752             :                                              const ForwardIterator & end,
     753             :                                              std::vector<dof_id_type> & index_map) const
     754             : {
     755       35361 :   LOG_SCOPE ("find_global_indices()", "MeshCommunication");
     756             : 
     757             :   // This method determines partition-agnostic global indices
     758             :   // for nodes and elements.
     759             : 
     760             :   // Algorithm:
     761             :   // (1) compute the Hilbert key for each local node/element
     762             :   // (2) perform a parallel sort of the Hilbert key
     763             :   // (3) get the min/max value on each processor
     764             :   // (4) determine the position in the global ranking for
     765             :   //     each local object
     766       11787 :   index_map.clear();
     767       35175 :   std::size_t n_objects = std::distance (begin, end);
     768       35175 :   index_map.reserve(n_objects);
     769             : 
     770       23388 :   const Point bboxinv = invert_bbox(bbox);
     771             : 
     772             :   //-------------------------------------------------------------
     773             :   // (1) compute Hilbert keys
     774             :   // These aren't trivial to compute, and we will need them again.
     775             :   // But the binsort will sort the input vector, trashing the order
     776             :   // that we'd like to rely on.  So, two vectors...
     777             :   std::vector<Parallel::DofObjectKey>
     778       23574 :     sorted_hilbert_keys,
     779       23574 :     hilbert_keys;
     780       35175 :   sorted_hilbert_keys.reserve(n_objects);
     781       35175 :   hilbert_keys.reserve(n_objects);
     782             :   {
     783       35361 :     LOG_SCOPE("compute_hilbert_indices()", "MeshCommunication");
     784       23574 :     const processor_id_type my_pid = communicator.rank();
     785     4430407 :     for (ForwardIterator it=begin; it!=end; ++it)
     786             :       {
     787     3343404 :         const Parallel::DofObjectKey hi(get_dofobject_key (**it, bbox, bboxinv));
     788     3343404 :         hilbert_keys.push_back(hi);
     789             : 
     790     3343404 :         const processor_id_type pid = (*it)->processor_id();
     791             : 
     792     3343404 :         if (pid == my_pid)
     793     1667816 :           sorted_hilbert_keys.push_back(hi);
     794             : 
     795             :         // someone needs to take care of unpartitioned objects!
     796     4489192 :         if ((my_pid == 0) &&
     797     2197616 :             (pid == DofObject::invalid_processor_id))
     798        9762 :           sorted_hilbert_keys.push_back(hi);
     799             :       }
     800             :   }
     801             : 
     802             :   //-------------------------------------------------------------
     803             :   // (2) parallel sort the Hilbert keys
     804       23574 :   START_LOG ("parallel_sort()", "MeshCommunication");
     805       58749 :   Parallel::Sort<Parallel::DofObjectKey> sorter (communicator,
     806             :                                                  sorted_hilbert_keys);
     807       35175 :   sorter.sort();
     808       23574 :   STOP_LOG ("parallel_sort()", "MeshCommunication");
     809       35175 :   const std::vector<Parallel::DofObjectKey> & my_bin = sorter.bin();
     810             : 
     811             :   // The number of objects in my_bin on each processor
     812       46962 :   std::vector<unsigned int> bin_sizes(communicator.size());
     813       46962 :   communicator.allgather (static_cast<unsigned int>(my_bin.size()), bin_sizes);
     814             : 
     815             :   // The offset of my first global index
     816       11787 :   unsigned int my_offset = 0;
     817       52758 :   for (auto pid : make_range(communicator.rank()))
     818       23476 :     my_offset += bin_sizes[pid];
     819             : 
     820             :   //-------------------------------------------------------------
     821             :   // (3) get the max value on each processor
     822             :   std::vector<Parallel::DofObjectKey>
     823       46962 :     upper_bounds(1);
     824             : 
     825       35175 :   if (!my_bin.empty())
     826       23574 :     upper_bounds[0] = my_bin.back();
     827             : 
     828       35175 :   communicator.allgather (upper_bounds, /* identical_buffer_sizes = */ true);
     829             : 
     830             :   // Be careful here.  The *_upper_bounds will be used to find the processor
     831             :   // a given object belongs to.  So, if a processor contains no objects (possible!)
     832             :   // then copy the bound from the lower processor id.
     833       70341 :   for (auto p : IntRange<processor_id_type>(1, communicator.size()))
     834       46952 :     if (!bin_sizes[p]) upper_bounds[p] = upper_bounds[p-1];
     835             : 
     836             : 
     837             :   //-------------------------------------------------------------
     838             :   // (4) determine the position in the global ranking for
     839             :   //     each local object
     840             :   {
     841             :     //----------------------------------------------
     842             :     // all objects, not just local ones
     843             : 
     844             :     // Request sets to send to each processor
     845             :     std::map<processor_id_type, std::vector<Parallel::DofObjectKey>>
     846       23574 :       requested_ids;
     847             :     // Results to gather from each processor
     848             :     std::map<processor_id_type, std::vector<dof_id_type>>
     849       23574 :       filled_request;
     850             : 
     851             :     // build up list of requests
     852       11787 :     std::vector<Parallel::DofObjectKey>::const_iterator hi =
     853       11787 :       hilbert_keys.begin();
     854             : 
     855     3378696 :     for (ForwardIterator it = begin; it != end; ++it)
     856             :       {
     857     1145788 :         libmesh_assert (hi != hilbert_keys.end());
     858             : 
     859             :         std::vector<Parallel::DofObjectKey>::iterator lb =
     860     3343404 :           std::lower_bound(upper_bounds.begin(), upper_bounds.end(),
     861     1145788 :                            *hi);
     862             : 
     863     3343404 :         const processor_id_type pid =
     864             :           cast_int<processor_id_type>
     865     1145788 :           (std::distance (upper_bounds.begin(), lb));
     866             : 
     867     1145788 :         libmesh_assert_less (pid, communicator.size());
     868             : 
     869     3343404 :         requested_ids[pid].push_back(*hi);
     870             : 
     871     1145788 :         ++hi;
     872             :         // go ahead and put pid in index_map, that way we
     873             :         // don't have to repeat the std::lower_bound()
     874     3343404 :         index_map.push_back(pid);
     875             :       }
     876             : 
     877     3519683 :   auto gather_functor =
     878     1075022 :     [
     879             : #ifndef NDEBUG
     880             :      & upper_bounds,
     881             :      & communicator,
     882             : #endif
     883             :      & bbox,
     884             :      & my_bin,
     885             :        my_offset
     886             :     ]
     887             :     (processor_id_type, const std::vector<Parallel::DofObjectKey> & keys,
     888       47144 :      std::vector<dof_id_type> & global_ids)
     889             :     {
     890             :       // Ignore unused lambda capture warnings in devel mode
     891       23572 :       libmesh_ignore(bbox);
     892             : 
     893             :       // Fill the requests
     894       47144 :       const std::size_t keys_size = keys.size();
     895       23572 :       global_ids.clear();
     896       70338 :       global_ids.reserve(keys_size);
     897     3413742 :       for (std::size_t idx=0; idx != keys_size; idx++)
     898             :         {
     899     2291576 :           const Parallel::DofObjectKey & hilbert_indices = keys[idx];
     900     1145788 :           libmesh_assert_less_equal (hilbert_indices, upper_bounds[communicator.rank()]);
     901             : 
     902             :           // find the requested index in my node bin
     903             :           std::vector<Parallel::DofObjectKey>::const_iterator pos =
     904     3343404 :             std::lower_bound (my_bin.begin(), my_bin.end(), hilbert_indices);
     905     1145788 :           libmesh_assert (pos != my_bin.end());
     906             : #ifdef DEBUG
     907             :           // If we could not find the requested Hilbert index in
     908             :           // my_bin, something went terribly wrong, possibly the
     909             :           // Mesh was displaced differently on different processors,
     910             :           // and therefore the Hilbert indices don't agree.
     911     1145788 :           if (*pos != hilbert_indices)
     912             :             {
     913             :               // The input will be hilbert_indices.  We convert it
     914             :               // to BitVecType using the operator= provided by the
     915             :               // BitVecType class. BitVecType is a CBigBitVec!
     916           0 :               Hilbert::BitVecType input;
     917           0 :               input = hilbert_indices.first;
     918             : 
     919             :               // Get output in a vector of CBigBitVec
     920           0 :               std::vector<CBigBitVec> output(3);
     921             : 
     922             :               // Call the indexToCoords function
     923           0 :               Hilbert::indexToCoords(output.data(), 8*sizeof(Hilbert::inttype), 3, input);
     924             : 
     925             :               // The entries in the output racks are integers in the
     926             :               // range [0, Hilbert::inttype::max] which can be
     927             :               // converted to floating point values in [0,1] and
     928             :               // finally to actual values using the bounding box.
     929           0 :               const Real max_int_as_real =
     930             :                 static_cast<Real>(std::numeric_limits<Hilbert::inttype>::max());
     931             : 
     932             :               // Get the points in [0,1]^3.  The zeroth rack of each entry in
     933             :               // 'output' maps to the normalized x, y, and z locations,
     934             :               // respectively.
     935           0 :               Point p_hat(static_cast<Real>(output[0].racks()[0]) / max_int_as_real,
     936           0 :                           static_cast<Real>(output[1].racks()[0]) / max_int_as_real,
     937           0 :                           static_cast<Real>(output[2].racks()[0]) / max_int_as_real);
     938             : 
     939             :               // Convert the points from [0,1]^3 to their actual (x,y,z) locations
     940             :               Real
     941           0 :                 xmin = bbox.first(0),
     942           0 :                 xmax = bbox.second(0),
     943           0 :                 ymin = bbox.first(1),
     944           0 :                 ymax = bbox.second(1),
     945           0 :                 zmin = bbox.first(2),
     946           0 :                 zmax = bbox.second(2);
     947             : 
     948             :               // Convert the points from [0,1]^3 to their actual (x,y,z) locations
     949           0 :               Point p(xmin + (xmax-xmin)*p_hat(0),
     950           0 :                       ymin + (ymax-ymin)*p_hat(1),
     951           0 :                       zmin + (zmax-zmin)*p_hat(2));
     952             : 
     953           0 :               libmesh_error_msg("Could not find hilbert indices: "
     954             :                                 << hilbert_indices
     955             :                                 << " corresponding to point " << p);
     956             :             }
     957             : #endif
     958             : 
     959             :           // Finally, assign the global index based off the position of the index
     960             :           // in my array, properly offset.
     961     5634980 :           global_ids.push_back (cast_int<dof_id_type>(std::distance(my_bin.begin(), pos) + my_offset));
     962             :         }
     963             :     };
     964             : 
     965       82319 :   auto action_functor =
     966       46388 :     [&filled_request]
     967             :     (processor_id_type pid,
     968             :      const std::vector<Parallel::DofObjectKey> &,
     969       23572 :      const std::vector<dof_id_type> & global_ids)
     970             :     {
     971       70338 :       filled_request[pid] = global_ids;
     972             :     };
     973             : 
     974       11787 :   const dof_id_type * ex = nullptr;
     975             :   Parallel::pull_parallel_vector_data
     976       35175 :     (communicator, requested_ids, gather_functor, action_functor, ex);
     977             : 
     978             :     // We now have all the filled requests, so we can loop through our
     979             :     // nodes once and assign the global index to each one.
     980             :     {
     981             :       std::vector<std::vector<dof_id_type>::const_iterator>
     982       46962 :         next_obj_on_proc; next_obj_on_proc.reserve(communicator.size());
     983      105516 :       for (auto pid : make_range(communicator.size()))
     984      117109 :         next_obj_on_proc.push_back(filled_request[pid].begin());
     985             : 
     986       11787 :       unsigned int cnt=0;
     987     4524367 :       for (ForwardIterator it = begin; it != end; ++it, cnt++)
     988             :         {
     989     1145788 :           const processor_id_type pid = cast_int<processor_id_type>
     990     3343404 :             (index_map[cnt]);
     991             : 
     992     1145788 :           libmesh_assert_less (pid, communicator.size());
     993     1145788 :           libmesh_assert (next_obj_on_proc[pid] != filled_request[pid].end());
     994             : 
     995     3343404 :           const dof_id_type global_index = *next_obj_on_proc[pid];
     996     3343404 :           index_map[cnt] = global_index;
     997             : 
     998     1145788 :           ++next_obj_on_proc[pid];
     999             :         }
    1000             :     }
    1001             :   }
    1002             : 
    1003       11787 :   libmesh_assert_equal_to(index_map.size(), n_objects);
    1004       46776 : }
    1005             : #else // LIBMESH_HAVE_LIBHILBERT, LIBMESH_HAVE_MPI
    1006             : template <typename ForwardIterator>
    1007             : void MeshCommunication::find_local_indices (const libMesh::BoundingBox &,
    1008             :                                             const ForwardIterator & begin,
    1009             :                                             const ForwardIterator & end,
    1010             :                                             std::unordered_map<dof_id_type, dof_id_type> & index_map) const
    1011             : {
    1012             :   index_map.clear();
    1013             :   dof_id_type index = 0;
    1014             :   for (ForwardIterator it=begin; it!=end; ++it)
    1015             :     index_map[(*it)->id()] = (index++);
    1016             : }
    1017             : 
    1018             : template <typename ForwardIterator>
    1019             : void MeshCommunication::find_global_indices (const Parallel::Communicator &,
    1020             :                                              const libMesh::BoundingBox &,
    1021             :                                              const ForwardIterator & begin,
    1022             :                                              const ForwardIterator & end,
    1023             :                                              std::vector<dof_id_type> & index_map) const
    1024             : {
    1025             :   index_map.clear();
    1026             :   index_map.reserve(std::distance (begin, end));
    1027             :   unsigned int index = 0;
    1028             :   for (ForwardIterator it=begin; it!=end; ++it)
    1029             :     index_map.push_back(index++);
    1030             : }
    1031             : #endif // LIBMESH_HAVE_LIBHILBERT, LIBMESH_HAVE_MPI
    1032             : 
    1033             : 
    1034             : 
    1035             : //------------------------------------------------------------------
    1036             : template LIBMESH_EXPORT void MeshCommunication::find_global_indices<MeshBase::const_node_iterator> (const Parallel::Communicator &,
    1037             :                                                                                      const libMesh::BoundingBox &,
    1038             :                                                                                      const MeshBase::const_node_iterator &,
    1039             :                                                                                      const MeshBase::const_node_iterator &,
    1040             :                                                                                      std::vector<dof_id_type> &) const;
    1041             : 
    1042             : template LIBMESH_EXPORT void MeshCommunication::find_global_indices<MeshBase::const_element_iterator> (const Parallel::Communicator &,
    1043             :                                                                                         const libMesh::BoundingBox &,
    1044             :                                                                                         const MeshBase::const_element_iterator &,
    1045             :                                                                                         const MeshBase::const_element_iterator &,
    1046             :                                                                                         std::vector<dof_id_type> &) const;
    1047             : template LIBMESH_EXPORT void MeshCommunication::find_global_indices<MeshBase::node_iterator> (const Parallel::Communicator &,
    1048             :                                                                                const libMesh::BoundingBox &,
    1049             :                                                                                const MeshBase::node_iterator &,
    1050             :                                                                                const MeshBase::node_iterator &,
    1051             :                                                                                std::vector<dof_id_type> &) const;
    1052             : 
    1053             : template LIBMESH_EXPORT void MeshCommunication::find_global_indices<MeshBase::element_iterator> (const Parallel::Communicator &,
    1054             :                                                                                   const libMesh::BoundingBox &,
    1055             :                                                                                   const MeshBase::element_iterator &,
    1056             :                                                                                   const MeshBase::element_iterator &,
    1057             :                                                                                   std::vector<dof_id_type> &) const;
    1058             : template LIBMESH_EXPORT void MeshCommunication::find_local_indices<MeshBase::const_element_iterator> (const libMesh::BoundingBox &,
    1059             :                                                                                        const MeshBase::const_element_iterator &,
    1060             :                                                                                        const MeshBase::const_element_iterator &,
    1061             :                                                                                        std::unordered_map<dof_id_type, dof_id_type> &) const;
    1062             : 
    1063             : } // namespace libMesh

Generated by: LCOV version 1.14