LCOV - code coverage report
Current view: top level - src/mesh - mesh_tools.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4640 (c5f9f0) with base a68dd6 Lines: 855 1159 73.8 %
Date: 2026-11-02 23:02:27 Functions: 86 120 71.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
       3             : 
       4             : // This library is free software; you can redistribute it and/or
       5             : // modify it under the terms of the GNU Lesser General Public
       6             : // License as published by the Free Software Foundation; either
       7             : // version 2.1 of the License, or (at your option) any later version.
       8             : 
       9             : // This library is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             : // Lesser General Public License for more details.
      13             : 
      14             : // You should have received a copy of the GNU Lesser General Public
      15             : // License along with this library; if not, write to the Free Software
      16             : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      17             : 
      18             : 
      19             : 
      20             : // Local includes
      21             : #include "libmesh/elem.h"
      22             : #include "libmesh/elem_range.h"
      23             : #include "libmesh/libmesh_logging.h"
      24             : #include "libmesh/mesh_base.h"
      25             : #include "libmesh/mesh_communication.h"
      26             : #include "libmesh/mesh_serializer.h"
      27             : #include "libmesh/mesh_tools.h"
      28             : #include "libmesh/node_range.h"
      29             : #include "libmesh/parallel.h"
      30             : #include "libmesh/parallel_algebra.h"
      31             : #include "libmesh/parallel_ghost_sync.h"
      32             : #include "libmesh/sphere.h"
      33             : #include "libmesh/threads.h"
      34             : #include "libmesh/enum_to_string.h"
      35             : #include "libmesh/enum_elem_type.h"
      36             : #include "libmesh/int_range.h"
      37             : #include "libmesh/utility.h"
      38             : #include "libmesh/boundary_info.h"
      39             : 
      40             : #ifndef NDEBUG
      41             : #  include "libmesh/remote_elem.h"
      42             : #endif
      43             : 
      44             : // C++ includes
      45             : #include <limits>
      46             : #include <numeric> // for std::accumulate
      47             : #include <set>
      48             : #include <unordered_map>
      49             : #include <unordered_set>
      50             : 
      51             : 
      52             : 
      53             : // ------------------------------------------------------------
      54             : // anonymous namespace for helper classes
      55             : namespace {
      56             : 
      57             : using namespace libMesh;
      58             : 
      59             : /**
      60             :  * SumElemWeight(Range) sums the number of nodes per element
      61             :  * for each element in the provided range. The join() method
      62             :  * defines how to combine the reduction operation from two
      63             :  * distinct instances of this class which may be executed on
      64             :  * separate threads.
      65             :  */
      66             : class SumElemWeight
      67             : {
      68             : public:
      69           0 :   SumElemWeight () :
      70           0 :     _weight(0)
      71           0 :   {}
      72             : 
      73           0 :   SumElemWeight (SumElemWeight &, Threads::split) :
      74           0 :     _weight(0)
      75           0 :   {}
      76             : 
      77           0 :   void operator()(const ConstElemRange & range)
      78             :   {
      79           0 :     for (const auto & elem : range)
      80           0 :       _weight += elem->n_nodes();
      81           0 :   }
      82             : 
      83           0 :   dof_id_type weight() const
      84           0 :   { return _weight; }
      85             : 
      86             :   // If we don't have threads we never need a join, and icpc yells a
      87             :   // warning if it sees an anonymous function that's never used
      88             : #if LIBMESH_USING_THREADS
      89           0 :   void join (const SumElemWeight & other)
      90           0 :   { _weight += other.weight(); }
      91             : #endif
      92             : 
      93             : private:
      94             :   dof_id_type _weight;
      95             : };
      96             : 
      97             : 
      98             : /**
      99             :  * FindBBox(Range) computes the bounding box for the objects
     100             :  * in the specified range.  This class may be split and subranges
     101             :  * can be executed on separate threads.  The join() method
     102             :  * defines how the results from two separate threads are combined.
     103             :  */
     104             : class FindBBox
     105             : {
     106             : public:
     107     3188843 :   FindBBox () : _bbox()
     108       28870 :   {}
     109             : 
     110         746 :   FindBBox (FindBBox & other, Threads::split) :
     111         746 :     _bbox(other._bbox)
     112         250 :   {}
     113             : 
     114       41718 :   void operator()(const ConstNodeRange & range)
     115             :   {
     116     5123960 :     for (const auto & node : range)
     117             :       {
     118      461782 :         libmesh_assert(node);
     119     5082242 :         _bbox.union_with(*node);
     120             :       }
     121       41718 :   }
     122             : 
     123     3197290 :   void operator()(const ConstElemRange & range)
     124             :   {
     125    26415734 :     for (const auto & elem : range)
     126             :       {
     127      792029 :         libmesh_assert(elem);
     128    23218444 :         _bbox.union_with(elem->loose_bounding_box());
     129             :       }
     130     3197290 :   }
     131             : 
     132       14731 :   Point & min() { return _bbox.min(); }
     133             : 
     134       14731 :   Point & max() { return _bbox.max(); }
     135             : 
     136             :   // If we don't have threads we never need a join, and icpc yells a
     137             :   // warning if it sees an anonymous function that's never used
     138             : #if LIBMESH_USING_THREADS
     139         250 :   void join (const FindBBox & other)
     140             :   {
     141         746 :     _bbox.union_with(other._bbox);
     142         496 :   }
     143             : #endif
     144             : 
     145       43009 :   libMesh::BoundingBox & bbox ()
     146             :   {
     147       43009 :     return _bbox;
     148             :   }
     149             : 
     150             : private:
     151             :   BoundingBox _bbox;
     152             : };
     153             : 
     154             : #ifdef DEBUG
     155     3027608 : void assert_semiverify_dofobj(const Parallel::Communicator & communicator,
     156             :                               const DofObject * d,
     157             :                               unsigned int sysnum = libMesh::invalid_uint)
     158             : {
     159     3027608 :   if (d)
     160             :     {
     161     2981720 :       const unsigned int n_sys = d->n_systems();
     162             : 
     163     5963440 :       std::vector<unsigned int> n_vars (n_sys, 0);
     164     6660558 :       for (unsigned int s = 0; s != n_sys; ++s)
     165     3678838 :         if (sysnum == s ||
     166             :             sysnum == libMesh::invalid_uint)
     167     2981720 :           n_vars[s] = d->n_vars(s);
     168             : 
     169             :       const unsigned int tot_n_vars =
     170     2981720 :         std::accumulate(n_vars.begin(), n_vars.end(), 0);
     171             : 
     172     5963440 :       std::vector<unsigned int> n_comp (tot_n_vars, 0);
     173     5963440 :       std::vector<dof_id_type> first_dof (tot_n_vars, 0);
     174             : 
     175     6660558 :       for (unsigned int s = 0, i=0; s != n_sys; ++s)
     176             :         {
     177     3678838 :           if (sysnum != s &&
     178             :               sysnum != libMesh::invalid_uint)
     179      697118 :             continue;
     180             : 
     181     9922636 :           for (unsigned int v = 0; v != n_vars[s]; ++v, ++i)
     182             :             {
     183     6940916 :               n_comp[i] = d->n_comp(s,v);
     184     6940916 :               first_dof[i] = n_comp[i] ? d->dof_number(s,v,0) : DofObject::invalid_id;
     185             :             }
     186             :         }
     187             : 
     188     2981720 :       libmesh_assert(communicator.semiverify(&n_sys));
     189     2981720 :       libmesh_assert(communicator.semiverify(&n_vars));
     190     2981720 :       libmesh_assert(communicator.semiverify(&n_comp));
     191     2981720 :       libmesh_assert(communicator.semiverify(&first_dof));
     192             :     }
     193             :   else // ranks w/o a DofObject d must still perform matching calls to the semiverify() collective
     194             :     {
     195       45888 :       const unsigned int * p_ui = nullptr;
     196       45888 :       const std::vector<unsigned int> * p_vui = nullptr;
     197       45888 :       const std::vector<dof_id_type> * p_vdid = nullptr;
     198             : 
     199       45888 :       libmesh_assert(communicator.semiverify(p_ui));
     200       45888 :       libmesh_assert(communicator.semiverify(p_vui));
     201       45888 :       libmesh_assert(communicator.semiverify(p_vui));
     202       45888 :       libmesh_assert(communicator.semiverify(p_vdid));
     203             :     }
     204     3027608 : }
     205             : 
     206             : 
     207             : 
     208             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
     209     9471096 : void assert_dofobj_unique_id(const Parallel::Communicator & comm,
     210             :                              const DofObject * d,
     211             :                              const std::unordered_set<unique_id_type> & unique_ids)
     212             : {
     213             :   // Duplicating some semiverify code here so we can reuse
     214             :   // tempmin,tempmax afterward
     215             : 
     216             :   unique_id_type tempmin, tempmax;
     217     9471096 :   if (d)
     218             :     {
     219     9141624 :       tempmin = tempmax = d->unique_id();
     220             :     }
     221             :   else
     222             :     {
     223      329472 :       TIMPI::Attributes<unique_id_type>::set_highest(tempmin);
     224      329472 :       TIMPI::Attributes<unique_id_type>::set_lowest(tempmax);
     225             :     }
     226     9471096 :   comm.min(tempmin);
     227     9471096 :   comm.max(tempmax);
     228    18612720 :   bool invalid = d && ((d->unique_id() != tempmin) ||
     229     9141624 :                        (d->unique_id() != tempmax));
     230     9471096 :   comm.max(invalid);
     231             : 
     232             :   // First verify that everything is in sync
     233     9471096 :   libmesh_assert(!invalid);
     234             : 
     235             :   // Then verify that any remote id doesn't duplicate a local one.
     236     9471096 :   if (!d && tempmin == tempmax)
     237       52832 :     libmesh_assert(!unique_ids.count(tempmin));
     238     9471096 : }
     239             : #endif // LIBMESH_ENABLE_UNIQUE_ID
     240             : #endif // DEBUG
     241             : 
     242      227170 : void find_nodal_neighbors_helper(const dof_id_type global_id,
     243             :                                  const std::vector<const Elem *> & node_to_elem_vec,
     244             :                                  std::vector<const Node *> & neighbors)
     245             : {
     246             :   // We'll construct a std::set<const Node *> for more efficient
     247             :   // searching while finding the nodal neighbors, and return it to the
     248             :   // user in a std::vector.
     249       16232 :   std::set<const Node *> neighbor_set;
     250             : 
     251             :   // Look through the elements that contain this node
     252             :   // find the local node id... then find the side that
     253             :   // node lives on in the element
     254             :   // next, look for the _other_ node on that side
     255             :   // That other node is a "nodal_neighbor"... save it
     256     1464478 :   for (const auto & elem : node_to_elem_vec)
     257             :     {
     258             :       // We only care about active elements...
     259     1237308 :       if (elem->active())
     260             :         {
     261             :           // Which local node number is global_id?
     262     1195954 :           unsigned local_node_number = elem->local_node(global_id);
     263             : 
     264             :           // Make sure it was found
     265       41354 :           libmesh_assert_not_equal_to(local_node_number, libMesh::invalid_uint);
     266             : 
     267     1237308 :           const unsigned short n_edges = elem->n_edges();
     268             : 
     269             :           // If this element has no edges, the edge-based algorithm below doesn't make sense.
     270     1237308 :           if (!n_edges)
     271             :             {
     272        1374 :               switch (elem->type())
     273             :                 {
     274         592 :                 case EDGE2:
     275             :                   {
     276          18 :                     switch (local_node_number)
     277             :                       {
     278         296 :                       case 0:
     279             :                         // The other node is a nodal neighbor
     280         305 :                         neighbor_set.insert(elem->node_ptr(1));
     281         296 :                         break;
     282             : 
     283         296 :                       case 1:
     284             :                         // The other node is a nodal neighbor
     285         305 :                         neighbor_set.insert(elem->node_ptr(0));
     286         296 :                         break;
     287             : 
     288           0 :                       default:
     289           0 :                         libmesh_error_msg("Invalid local node number: " << local_node_number << " found." << std::endl);
     290             :                       }
     291          18 :                     break;
     292             :                   }
     293             : 
     294         498 :                 case EDGE3:
     295             :                   {
     296          18 :                     switch (local_node_number)
     297             :                       {
     298             :                         // The outside nodes have node 2 as a neighbor
     299         356 :                       case 0:
     300             :                       case 1:
     301         370 :                         neighbor_set.insert(elem->node_ptr(2));
     302         356 :                         break;
     303             : 
     304             :                         // The middle node has the outer nodes as neighbors
     305         142 :                       case 2:
     306         146 :                         neighbor_set.insert(elem->node_ptr(0));
     307         146 :                         neighbor_set.insert(elem->node_ptr(1));
     308         142 :                         break;
     309             : 
     310           0 :                       default:
     311           0 :                         libmesh_error_msg("Invalid local node number: " << local_node_number << " found." << std::endl);
     312             :                       }
     313          18 :                     break;
     314             :                   }
     315             : 
     316         284 :                 case EDGE4:
     317             :                   {
     318           8 :                     switch (local_node_number)
     319             :                       {
     320          71 :                       case 0:
     321             :                         // The left-middle node is a nodal neighbor
     322          73 :                         neighbor_set.insert(elem->node_ptr(2));
     323          71 :                         break;
     324             : 
     325          71 :                       case 1:
     326             :                         // The right-middle node is a nodal neighbor
     327          73 :                         neighbor_set.insert(elem->node_ptr(3));
     328          71 :                         break;
     329             : 
     330             :                         // The left-middle node
     331          71 :                       case 2:
     332          73 :                         neighbor_set.insert(elem->node_ptr(0));
     333          73 :                         neighbor_set.insert(elem->node_ptr(3));
     334          71 :                         break;
     335             : 
     336             :                         // The right-middle node
     337          71 :                       case 3:
     338          73 :                         neighbor_set.insert(elem->node_ptr(1));
     339          73 :                         neighbor_set.insert(elem->node_ptr(2));
     340          71 :                         break;
     341             : 
     342           0 :                       default:
     343           0 :                         libmesh_error_msg("Invalid local node number: " << local_node_number << " found." << std::endl);
     344             :                       }
     345           8 :                     break;
     346             :                   }
     347             : 
     348           0 :                 default:
     349           0 :                   libmesh_error_msg("Unrecognized ElemType: " << Utility::enum_to_string(elem->type()) << std::endl);
     350             :                 }
     351             :             }
     352             : 
     353     1237308 :           const auto elem_order = Elem::type_to_default_order_map[elem->type()];
     354             : 
     355             :           // Index of the current edge
     356       41354 :           unsigned current_edge = 0;
     357             : 
     358     1237308 :           const unsigned short n_nodes = elem->n_nodes();
     359             : 
     360     5034507 :           while (current_edge < n_edges)
     361             :             {
     362             :               // Find the edge the node is on
     363      126386 :               bool found_edge = false;
     364     9178151 :               for (; current_edge<n_edges; ++current_edge)
     365     8414460 :                 if (elem->is_node_on_edge(local_node_number, current_edge))
     366             :                   {
     367      100178 :                     found_edge = true;
     368      100178 :                     break;
     369             :                   }
     370             : 
     371             :               // Did we find one?
     372     3797199 :               if (found_edge)
     373             :                 {
     374     3033508 :                   const Node * node_to_save = nullptr;
     375             : 
     376             :                   // Find another node in this element on this edge
     377    22556636 :                   for (unsigned other_node_this_edge = 0; other_node_this_edge != n_nodes; other_node_this_edge++)
     378             :                     {
     379    35732296 :                       const bool both_vertices = elem->is_vertex(local_node_number) &&
     380    16209168 :                                                  elem->is_vertex(other_node_this_edge);
     381    26046258 :                       if ( elem->is_node_on_edge(other_node_this_edge, current_edge) && // On the current edge
     382    19571684 :                            elem->node_id(other_node_this_edge) != global_id          && // But not the original node
     383             :                             // vertex nodes on the same edge of higher order elements are not nodal neighbors
     384     3638356 :                           (elem_order == 1 || !both_vertices))
     385             :                         {
     386             :                           // We've found a nodal neighbor!  Save a pointer to it..
     387     3294908 :                           node_to_save = elem->node_ptr(other_node_this_edge);
     388             : 
     389             :                           // Make sure we found something
     390      109454 :                           libmesh_assert(node_to_save != nullptr);
     391             : 
     392     3185454 :                           neighbor_set.insert(node_to_save);
     393             :                         }
     394             :                     }
     395             :                 }
     396             : 
     397             :               // Keep looking for edges, node may be on more than one edge
     398     3797199 :               current_edge++;
     399             :             }
     400             :         } // if (elem->active())
     401             :     } // for
     402             : 
     403             :   // Assign the entries from the set to the vector.  Note: this
     404             :   // replaces any existing contents in neighbors and modifies its size
     405             :   // accordingly.
     406      219054 :   neighbors.assign(neighbor_set.begin(), neighbor_set.end());
     407      227170 : }
     408             : 
     409             : }
     410             : 
     411             : 
     412             : namespace libMesh
     413             : {
     414             : 
     415             : // ------------------------------------------------------------
     416             : // MeshTools functions
     417             : 
     418             : namespace MeshTools
     419             : {
     420             : 
     421           0 : dof_id_type total_weight(const MeshBase & mesh)
     422             : {
     423           0 :   if (!mesh.is_serial())
     424             :     {
     425           0 :       libmesh_parallel_only(mesh.comm());
     426           0 :       dof_id_type weight = MeshTools::weight (mesh, mesh.processor_id());
     427           0 :       mesh.comm().sum(weight);
     428             :       dof_id_type unpartitioned_weight =
     429           0 :         MeshTools::weight (mesh, DofObject::invalid_processor_id);
     430           0 :       return weight + unpartitioned_weight;
     431             :     }
     432             : 
     433           0 :   SumElemWeight sew;
     434             : 
     435           0 :   Threads::parallel_reduce (ConstElemRange (mesh.elements_begin(),
     436           0 :                                             mesh.elements_end()),
     437             :                             sew);
     438           0 :   return sew.weight();
     439             : 
     440             : }
     441             : 
     442             : 
     443             : 
     444           0 : dof_id_type weight(const MeshBase & mesh, const processor_id_type pid)
     445             : {
     446           0 :   SumElemWeight sew;
     447             : 
     448           0 :   Threads::parallel_reduce (ConstElemRange (mesh.pid_elements_begin(pid),
     449           0 :                                             mesh.pid_elements_end(pid)),
     450             :                             sew);
     451           0 :   return sew.weight();
     452             : }
     453             : 
     454             : 
     455             : 
     456       12143 : void build_nodes_to_elem_map (const MeshBase & mesh,
     457             :                               std::vector<std::vector<dof_id_type>> & nodes_to_elem_map)
     458             : {
     459             :   // A vector indexed over all nodes is too inefficient to use for a
     460             :   // distributed mesh.  Use the unordered_map API instead.
     461       12143 :   if (!mesh.is_serial())
     462             :     libmesh_deprecated();
     463             : 
     464       12143 :   nodes_to_elem_map.resize (mesh.max_node_id());
     465             : 
     466      315619 :   for (const auto & elem : mesh.element_ptr_range())
     467      604940 :     for (auto & node : elem->node_ref_range())
     468             :       {
     469       12948 :         libmesh_assert_less (node.id(), nodes_to_elem_map.size());
     470       12948 :         libmesh_assert_less (elem->id(), mesh.n_elem());
     471             : 
     472      463416 :         nodes_to_elem_map[node.id()].push_back(elem->id());
     473       11449 :       }
     474       12143 : }
     475             : 
     476             : 
     477             : 
     478           0 : void build_nodes_to_elem_map (const MeshBase & mesh,
     479             :                               std::vector<std::vector<const Elem *>> & nodes_to_elem_map)
     480             : {
     481             :   // A vector indexed over all nodes is too inefficient to use for a
     482             :   // distributed mesh.  Use the unordered_map API instead.
     483           0 :   if (!mesh.is_serial())
     484             :     libmesh_deprecated();
     485             : 
     486           0 :   nodes_to_elem_map.resize (mesh.max_node_id());
     487             : 
     488           0 :   for (const auto & elem : mesh.element_ptr_range())
     489           0 :     for (auto & node : elem->node_ref_range())
     490             :       {
     491           0 :         libmesh_assert_less (node.id(), nodes_to_elem_map.size());
     492             : 
     493           0 :         nodes_to_elem_map[node.id()].push_back(elem);
     494           0 :       }
     495           0 : }
     496             : 
     497             : 
     498             : 
     499      369958 : void build_nodes_to_elem_map (const MeshBase & mesh,
     500             :                               std::unordered_map<dof_id_type, std::vector<dof_id_type>> & nodes_to_elem_map)
     501             : {
     502       10794 :   nodes_to_elem_map.clear();
     503             : 
     504    62221890 :   for (const auto & elem : mesh.element_ptr_range())
     505   188562945 :     for (auto & node : elem->node_ref_range())
     506   154901065 :       nodes_to_elem_map[node.id()].push_back(elem->id());
     507      369958 : }
     508             : 
     509             : 
     510             : 
     511        5965 : void build_nodes_to_elem_map (const MeshBase & mesh,
     512             :                               std::unordered_map<dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map)
     513             : {
     514         168 :   nodes_to_elem_map.clear();
     515             : 
     516      776980 :   for (const auto & elem : mesh.element_ptr_range())
     517     2752339 :     for (auto & node : elem->node_ref_range())
     518     2341627 :       nodes_to_elem_map[node.id()].push_back(elem);
     519        5965 : }
     520             : 
     521             : 
     522             : 
     523             : std::unordered_set<dof_id_type>
     524        6261 : find_boundary_nodes(const MeshBase & mesh)
     525             : {
     526         182 :   std::unordered_set<dof_id_type> boundary_nodes;
     527             : 
     528             :   // Loop over elements, find those on boundary, and
     529             :   // mark them as true in on_boundary.
     530     1096532 :   for (const auto & elem : mesh.active_element_ptr_range())
     531     3126182 :     for (auto s : elem->side_index_range())
     532     2649090 :       if (elem->neighbor_ptr(s) == nullptr) // on the boundary
     533             :         {
     534      148240 :           auto nodes_on_side = elem->nodes_on_side(s);
     535             : 
     536      756684 :           for (auto & local_id : nodes_on_side)
     537      644632 :             boundary_nodes.insert(elem->node_ptr(local_id)->id());
     538        5897 :         }
     539             : 
     540        6261 :   return boundary_nodes;
     541             : }
     542             : 
     543             : std::unordered_set<dof_id_type>
     544        3137 : find_block_boundary_nodes(const MeshBase & mesh)
     545             : {
     546          94 :   std::unordered_set<dof_id_type> block_boundary_nodes;
     547             : 
     548             :   // Loop over elements, find those on boundary, and
     549             :   // mark them as true in on_boundary.
     550      864512 :   for (const auto & elem : mesh.active_element_ptr_range())
     551     2469648 :     for (auto s : elem->side_index_range())
     552     2090882 :       if (elem->neighbor_ptr(s) && (elem->neighbor_ptr(s)->subdomain_id() != elem->subdomain_id()))
     553             :         {
     554           0 :           auto nodes_on_side = elem->nodes_on_side(s);
     555             : 
     556           0 :           for (auto & local_id : nodes_on_side)
     557           0 :             block_boundary_nodes.insert(elem->node_ptr(local_id)->id());
     558        2949 :         }
     559             : 
     560        3137 :   return block_boundary_nodes;
     561             : }
     562             : 
     563             : 
     564             : 
     565             : libMesh::BoundingBox
     566     1598578 : create_bounding_box (const MeshBase & mesh)
     567             : {
     568             :   // This function must be run on all processors at once
     569       14139 :   libmesh_parallel_only(mesh.comm());
     570             : 
     571       14139 :   FindBBox find_bbox;
     572             : 
     573             :   // Start with any unpartitioned elements we know about locally
     574     3197156 :   Threads::parallel_reduce (ConstElemRange (mesh.pid_elements_begin(DofObject::invalid_processor_id),
     575     1612717 :                                             mesh.pid_elements_end(DofObject::invalid_processor_id)),
     576             :                             find_bbox);
     577             : 
     578             :   // And combine with our local elements
     579     1598578 :   find_bbox.bbox().union_with(create_local_bounding_box(mesh));
     580             : 
     581             :   // Compare the bounding boxes across processors
     582     1598578 :   mesh.comm().min(find_bbox.min());
     583     1598578 :   mesh.comm().max(find_bbox.max());
     584             : 
     585     1598578 :   return find_bbox.bbox();
     586             : }
     587             : 
     588             : 
     589             : 
     590             : libMesh::BoundingBox
     591       20553 : create_nodal_bounding_box (const MeshBase & mesh)
     592             : {
     593             :   // This function must be run on all processors at once
     594         592 :   libmesh_parallel_only(mesh.comm());
     595             : 
     596         592 :   FindBBox find_bbox;
     597             : 
     598             :   // Start with any unpartitioned nodes we know about locally
     599       41106 :   Threads::parallel_reduce (ConstNodeRange (mesh.pid_nodes_begin(DofObject::invalid_processor_id),
     600       41106 :                                             mesh.pid_nodes_end(DofObject::invalid_processor_id)),
     601             :                             find_bbox);
     602             : 
     603             :   // Add our local nodes
     604       41106 :   Threads::parallel_reduce (ConstNodeRange (mesh.local_nodes_begin(),
     605       40514 :                                             mesh.local_nodes_end()),
     606             :                             find_bbox);
     607             : 
     608             :   // Compare the bounding boxes across processors
     609       20553 :   mesh.comm().min(find_bbox.min());
     610       20553 :   mesh.comm().max(find_bbox.max());
     611             : 
     612       20553 :   return find_bbox.bbox();
     613             : }
     614             : 
     615             : 
     616             : 
     617             : Sphere
     618           0 : bounding_sphere(const MeshBase & mesh)
     619             : {
     620           0 :   libMesh::BoundingBox bbox = create_bounding_box(mesh);
     621             : 
     622           0 :   const Real  diag = (bbox.second - bbox.first).norm();
     623           0 :   const Point cent = (bbox.second + bbox.first)/2;
     624             : 
     625           0 :   return Sphere (cent, .5*diag);
     626             : }
     627             : 
     628             : 
     629             : 
     630             : libMesh::BoundingBox
     631     1598578 : create_local_bounding_box (const MeshBase & mesh)
     632             : {
     633       14139 :   FindBBox find_bbox;
     634             : 
     635     1598578 :   Threads::parallel_reduce (mesh.active_local_element_stored_range(),
     636             :                             find_bbox);
     637             : 
     638     1598578 :   return find_bbox.bbox();
     639             : }
     640             : 
     641             : 
     642             : 
     643             : libMesh::BoundingBox
     644           0 : create_processor_bounding_box (const MeshBase & mesh,
     645             :                                const processor_id_type pid)
     646             : {
     647             :   // This can only be run in parallel, with consistent arguments.
     648           0 :   libmesh_parallel_only(mesh.comm());
     649           0 :   libmesh_assert(mesh.comm().verify(pid));
     650             : 
     651           0 :   libmesh_assert_less (pid, mesh.n_processors());
     652             : 
     653           0 :   FindBBox find_bbox;
     654             : 
     655           0 :   Threads::parallel_reduce (ConstElemRange (mesh.pid_elements_begin(pid),
     656           0 :                                             mesh.pid_elements_end(pid)),
     657             :                             find_bbox);
     658             : 
     659             :   // Compare the bounding boxes across processors
     660           0 :   mesh.comm().min(find_bbox.min());
     661           0 :   mesh.comm().max(find_bbox.max());
     662             : 
     663           0 :   return find_bbox.bbox();
     664             : }
     665             : 
     666             : 
     667             : 
     668             : Sphere
     669           0 : processor_bounding_sphere (const MeshBase & mesh,
     670             :                                       const processor_id_type pid)
     671             : {
     672             :   libMesh::BoundingBox bbox =
     673           0 :     create_processor_bounding_box(mesh, pid);
     674             : 
     675           0 :   const Real  diag = (bbox.second - bbox.first).norm();
     676           0 :   const Point cent = (bbox.second + bbox.first)/2;
     677             : 
     678           0 :   return Sphere (cent, .5*diag);
     679             : }
     680             : 
     681             : 
     682             : 
     683             : libMesh::BoundingBox
     684           0 : create_subdomain_bounding_box (const MeshBase & mesh,
     685             :                                const subdomain_id_type sid)
     686             : {
     687             :   // This can only be run in parallel, with consistent arguments.
     688           0 :   libmesh_parallel_only(mesh.comm());
     689           0 :   libmesh_assert(mesh.comm().verify(sid));
     690             : 
     691           0 :   FindBBox find_bbox;
     692             : 
     693             :   Threads::parallel_reduce
     694           0 :     (ConstElemRange (mesh.active_local_subdomain_elements_begin(sid),
     695           0 :                      mesh.active_local_subdomain_elements_end(sid)),
     696             :      find_bbox);
     697             : 
     698             :   // Compare the bounding boxes across processors
     699           0 :   mesh.comm().min(find_bbox.min());
     700           0 :   mesh.comm().max(find_bbox.max());
     701             : 
     702           0 :   return find_bbox.bbox();
     703             : }
     704             : 
     705             : 
     706             : 
     707             : Sphere
     708           0 : subdomain_bounding_sphere (const MeshBase & mesh,
     709             :                            const subdomain_id_type sid)
     710             : {
     711             :   libMesh::BoundingBox bbox =
     712           0 :     create_subdomain_bounding_box(mesh, sid);
     713             : 
     714           0 :   const Real  diag = (bbox.second - bbox.first).norm();
     715           0 :   const Point cent = (bbox.second + bbox.first)/2;
     716             : 
     717           0 :   return Sphere (cent, .5*diag);
     718             : }
     719             : 
     720             : 
     721             : 
     722           0 : void elem_types (const MeshBase & mesh,
     723             :                  std::vector<ElemType> & et)
     724             : {
     725             :   // Loop over the the elements.  If the current element type isn't in
     726             :   // the vector, insert it.
     727           0 :   for (const auto & elem : mesh.element_ptr_range())
     728           0 :     if (!std::count(et.begin(), et.end(), elem->type()))
     729           0 :       et.push_back(elem->type());
     730           0 : }
     731             : 
     732             : 
     733             : 
     734           0 : dof_id_type n_elem_of_type (const MeshBase & mesh,
     735             :                             const ElemType type)
     736             : {
     737           0 :   return static_cast<dof_id_type>(std::distance(mesh.type_elements_begin(type),
     738           0 :                                                 mesh.type_elements_end  (type)));
     739             : }
     740             : 
     741             : 
     742             : 
     743           0 : dof_id_type n_active_elem_of_type (const MeshBase & mesh,
     744             :                                    const ElemType type)
     745             : {
     746           0 :   return static_cast<dof_id_type>(std::distance(mesh.active_type_elements_begin(type),
     747           0 :                                                 mesh.active_type_elements_end  (type)));
     748             : }
     749             : 
     750           0 : dof_id_type n_non_subactive_elem_of_type_at_level(const MeshBase & mesh,
     751             :                                                   const ElemType type,
     752             :                                                   const unsigned int level)
     753             : {
     754           0 :   dof_id_type cnt = 0;
     755             : 
     756             :   // iterate over the elements of the specified type
     757           0 :   for (const auto & elem : as_range(mesh.type_elements_begin(type),
     758           0 :                                     mesh.type_elements_end(type)))
     759           0 :     if ((elem->level() == level) && !elem->subactive())
     760           0 :       cnt++;
     761             : 
     762           0 :   return cnt;
     763             : }
     764             : 
     765             : 
     766        5471 : unsigned int n_active_local_levels(const MeshBase & mesh)
     767             : {
     768             :   struct LevelCounter {
     769             :     unsigned int nl;
     770             : 
     771        5471 :     LevelCounter () : nl(0) {}
     772             : 
     773           0 :     LevelCounter (LevelCounter &, Threads::split) :
     774           0 :       nl(0) {}
     775             : 
     776        5471 :     void operator()(const ConstElemRange & range) {
     777       70642 :       for (const Elem * elem : range)
     778       69039 :         nl = std::max(elem->level() + 1, nl);
     779        5471 :     }
     780             : 
     781           0 :     void join(const LevelCounter & other) {
     782           0 :       nl = std::max(nl, other.nl);
     783           0 :     }
     784             :   };
     785             : 
     786         156 :   LevelCounter counter;
     787             : 
     788        5471 :   Threads::parallel_reduce(mesh.active_local_element_stored_range(), counter);
     789             : 
     790        5471 :   return counter.nl;
     791             : }
     792             : 
     793             : 
     794             : 
     795        5471 : unsigned int n_active_levels(const MeshBase & mesh)
     796             : {
     797         156 :   libmesh_parallel_only(mesh.comm());
     798             : 
     799        5471 :   unsigned int nl = n_active_local_levels(mesh);
     800             : 
     801       10786 :   for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
     802       21572 :                                     mesh.unpartitioned_elements_end()))
     803           0 :     if (elem->active())
     804        5159 :       nl = std::max(elem->level() + 1, nl);
     805             : 
     806        5471 :   mesh.comm().max(nl);
     807        5471 :   return nl;
     808             : }
     809             : 
     810             : 
     811             : 
     812     2441849 : unsigned int n_local_levels(const MeshBase & mesh)
     813             : {
     814     2441849 :   unsigned int nl = 0;
     815             : 
     816     6545837 :   for (const auto & elem : as_range(mesh.local_elements_begin(),
     817    43415282 :                                     mesh.local_elements_end()))
     818    38240297 :     nl = std::max(elem->level() + 1, nl);
     819             : 
     820     2441849 :   return nl;
     821             : }
     822             : 
     823             : 
     824             : 
     825     2441849 : unsigned int n_levels(const MeshBase & mesh)
     826             : {
     827       40673 :   libmesh_parallel_only(mesh.comm());
     828             : 
     829     2441849 :   unsigned int nl = n_local_levels(mesh);
     830             : 
     831     5255865 :   for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
     832    24802460 :                                     mesh.unpartitioned_elements_end()))
     833    17476937 :     nl = std::max(elem->level() + 1, nl);
     834             : 
     835     2441849 :   mesh.comm().max(nl);
     836             : 
     837             :   // n_levels() is only valid and should only be called in cases where
     838             :   // the mesh is validly distributed (or serialized).  Let's run an
     839             :   // expensive test in debug mode to make sure this is such a case.
     840             : #ifdef DEBUG
     841       40673 :   const unsigned int paranoid_nl = paranoid_n_levels(mesh);
     842       40673 :   libmesh_assert_equal_to(nl, paranoid_nl);
     843             : #endif
     844     2441849 :   return nl;
     845             : }
     846             : 
     847             : 
     848             : 
     849       55692 : unsigned int paranoid_n_levels(const MeshBase & mesh)
     850             : {
     851       41117 :   libmesh_parallel_only(mesh.comm());
     852             : 
     853       55692 :   unsigned int nl = 0;
     854     4316161 :   for (const auto & elem : mesh.element_ptr_range())
     855     4260025 :     nl = std::max(elem->level() + 1, nl);
     856             : 
     857       55692 :   mesh.comm().max(nl);
     858       55692 :   return nl;
     859             : }
     860             : 
     861             : 
     862             : 
     863         639 : dof_id_type n_connected_components(const MeshBase & mesh,
     864             :                                    Real constraint_tol)
     865             : {
     866          54 :   LOG_SCOPE("n_connected_components()", "MeshTools");
     867             : 
     868             :   // Yes, I'm being lazy.  This is for mesh analysis before a
     869             :   // simulation, not anything going in any loops.
     870         639 :   if (!mesh.is_serial_on_zero())
     871           0 :     libmesh_not_implemented();
     872             : 
     873         639 :   dof_id_type n_components = 0;
     874             : 
     875         657 :   if (mesh.processor_id())
     876             :   {
     877         522 :     mesh.comm().broadcast(n_components);
     878         531 :     return n_components;
     879             :   }
     880             : 
     881             :   // All nodes in a set here are connected (at least indirectly) to
     882             :   // all other nodes in the same set, but have not yet been discovered
     883             :   // to be connected to nodes in other sets.
     884             :   //
     885             :   // Using an unordered_set of ids rather than a set of pointers seems
     886             :   // to be roughly 150% faster?
     887             :   // typedef const Node * node_entry_type
     888             :   typedef dof_id_type node_entry_type;
     889           9 :   std::vector<std::unordered_set<node_entry_type>> components;
     890             : 
     891             :   // With a typical mesh with few components and somewhat-contiguous
     892             :   // ordering, vector performance should be fine.  With a mesh with
     893             :   // many components or completely scrambled ordering, performance
     894             :   // can be a disaster.
     895        2742 :   auto find_component = [&components](node_entry_type n) {
     896        6158 :     for (auto & c: components)
     897        4064 :       if (c.find(n) != c.end())
     898          54 :         return &c;
     899             : 
     900         162 :     return (std::unordered_set<node_entry_type> *)(nullptr);
     901         108 :   };
     902             : 
     903             :   auto add_to_component =
     904        2010 :     [&find_component]
     905        2202 :     (std::unordered_set<node_entry_type> & component, node_entry_type n) {
     906             :     // We may already be in the desired component
     907        2412 :     if (component.find(n) != component.end())
     908          51 :       return;
     909             : 
     910        1950 :     auto current_component = find_component(n);
     911             : 
     912             :     // Didn't we *just* check this?
     913         150 :     libmesh_assert (&component != current_component);
     914             : 
     915             :     // If we're unknown, we should be in the desired component
     916        1950 :     if (!current_component)
     917         147 :       component.insert(n);
     918             : 
     919             :     // If we think we're in another component, it should actually be
     920             :     // part of the desired component
     921             :     else
     922             :       {
     923             :         // Merge the component likely to be smaller into the one
     924             :         // likely to be larger - this is orders of magnitude faster
     925             :         // than the other way around!
     926           3 :         current_component->merge(component);
     927           3 :         current_component->swap(component);
     928           3 :         libmesh_assert(current_component->empty());
     929             :       }
     930         108 :   };
     931             : 
     932           9 :   const auto & constraint_rows = mesh.get_constraint_rows();
     933             : 
     934        1659 :   for (const auto & elem : mesh.element_ptr_range())
     935             :     {
     936             :       // const node_entry_type first_node = elem->node_ptr(0);
     937         792 :       const node_entry_type first_node = elem->node_id(0);
     938             : 
     939         792 :       auto component = find_component(first_node);
     940             : 
     941             :       // If we didn't find one, make a new one, reusing an existing
     942             :       // slot if possible or growing our vector if necessary
     943         792 :       if (!component)
     944         684 :         for (auto & c: components)
     945         354 :           if (c.empty())
     946           0 :             component = &c;
     947             : 
     948         432 :       if (!component)
     949         219 :         component = &components.emplace_back();
     950             : 
     951        3234 :       for (const Node & node : elem->node_ref_range())
     952             :         {
     953             :           // const node_entry_type n = &node;
     954         396 :           const node_entry_type n = node.id();
     955        2376 :           add_to_component(*component, n);
     956             : 
     957        2376 :           auto it = constraint_rows.find(&node);
     958        2376 :           if (it == constraint_rows.end())
     959         195 :             continue;
     960             : 
     961          72 :           for (const auto & [pr, val] : it->second)
     962             :             {
     963             :               // Ignore too-trivial constraint coefficients if
     964             :               // we get a non-default-0 constraint_tol
     965          39 :               if (std::abs(val) < constraint_tol)
     966           0 :                 continue;
     967             : 
     968          36 :               const Elem * spline_elem = pr.first;
     969           3 :               libmesh_assert(spline_elem == mesh.elem_ptr(spline_elem->id()));
     970             : 
     971             :               const Node * spline_node =
     972          36 :                 spline_elem->node_ptr(pr.second);
     973             : 
     974             :               // add_to_component(*component, spline_node);
     975          36 :               add_to_component(*component, spline_node->id());
     976             :             }
     977             :         }
     978          90 :     }
     979             : 
     980         327 :   for (auto & component : components)
     981         219 :     if (!component.empty())
     982         144 :       ++n_components;
     983             : 
     984             :   // We calculated this on proc 0; now let everyone else know too
     985         108 :   mesh.comm().broadcast(n_components);
     986             : 
     987         108 :   return n_components;
     988          90 : }
     989             : 
     990             : 
     991             : 
     992           0 : void get_not_subactive_node_ids(const MeshBase & mesh,
     993             :                                 std::set<dof_id_type> & not_subactive_node_ids)
     994             : {
     995           0 :   for (const auto & elem : mesh.element_ptr_range())
     996           0 :     if (!elem->subactive())
     997           0 :       for (auto & n : elem->node_ref_range())
     998           0 :         not_subactive_node_ids.insert(n.id());
     999           0 : }
    1000             : 
    1001             : 
    1002             : 
    1003      686411 : dof_id_type n_elem (const MeshBase::const_element_iterator & begin,
    1004             :                     const MeshBase::const_element_iterator & end)
    1005             : {
    1006     1346584 :   return cast_int<dof_id_type>(std::distance(begin, end));
    1007             : }
    1008             : 
    1009             : 
    1010             : 
    1011           0 : dof_id_type n_nodes (const MeshBase::const_node_iterator & begin,
    1012             :                      const MeshBase::const_node_iterator & end)
    1013             : {
    1014           0 :   return cast_int<dof_id_type>(std::distance(begin, end));
    1015             : }
    1016             : 
    1017             : 
    1018             : 
    1019        5268 : Real volume (const MeshBase & mesh,
    1020             :              unsigned int dim)
    1021             : {
    1022         148 :   libmesh_parallel_only(mesh.comm());
    1023             : 
    1024        5268 :   if (dim == libMesh::invalid_uint)
    1025        5268 :     dim = mesh.mesh_dimension();
    1026             : 
    1027        5268 :   Real vol = 0;
    1028             : 
    1029             :   // first my local elements
    1030       12243 :   for (const auto & elem : as_range(mesh.local_elements_begin(),
    1031       63639 :                                     mesh.local_elements_end()))
    1032       22359 :     if (elem->dim() == dim)
    1033       27331 :       vol += elem->volume();
    1034             : 
    1035             :   // then count any unpartitioned objects, once
    1036        5416 :   if (mesh.processor_id() == 0)
    1037        1730 :     for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
    1038        3460 :                                       mesh.unpartitioned_elements_end()))
    1039           0 :       if (elem->dim() == dim)
    1040         754 :         vol += elem->volume();
    1041             : 
    1042        5268 :   mesh.comm().sum(vol);
    1043        5268 :   return vol;
    1044             : }
    1045             : 
    1046             : 
    1047             : 
    1048        5471 : unsigned int n_p_levels (const MeshBase & mesh)
    1049             : {
    1050         156 :   libmesh_parallel_only(mesh.comm());
    1051             : 
    1052        5471 :   unsigned int max_p_level = 0;
    1053             : 
    1054             :   // first my local elements
    1055       17480 :   for (const auto & elem : as_range(mesh.local_elements_begin(),
    1056       93386 :                                     mesh.local_elements_end()))
    1057       83667 :     max_p_level = std::max(elem->p_level(), max_p_level);
    1058             : 
    1059             :   // then any unpartitioned objects
    1060       10786 :   for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
    1061       21572 :                                     mesh.unpartitioned_elements_end()))
    1062        5159 :     max_p_level = std::max(elem->p_level(), max_p_level);
    1063             : 
    1064        5471 :   mesh.comm().max(max_p_level);
    1065        5471 :   return max_p_level + 1;
    1066             : }
    1067             : 
    1068             : 
    1069             : 
    1070           0 : void find_nodal_neighbors(const MeshBase &,
    1071             :                           const Node & node,
    1072             :                           const std::vector<std::vector<const Elem *>> & nodes_to_elem_map,
    1073             :                           std::vector<const Node *> & neighbors)
    1074             : {
    1075           0 :   find_nodal_neighbors_helper(node.id(), nodes_to_elem_map[node.id()],
    1076             :                               neighbors);
    1077           0 : }
    1078             : 
    1079             : 
    1080             : 
    1081      227170 : void find_nodal_neighbors(const MeshBase &,
    1082             :                           const Node & node,
    1083             :                           const std::unordered_map<dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map,
    1084             :                           std::vector<const Node *> & neighbors)
    1085             : {
    1086             :   const std::vector<const Elem *> node_to_elem_vec =
    1087      235286 :     libmesh_map_find(nodes_to_elem_map, node.id());
    1088      227170 :   find_nodal_neighbors_helper(node.id(), node_to_elem_vec, neighbors);
    1089      227170 : }
    1090             : 
    1091       50735 : void find_nodal_or_face_neighbors(
    1092             :     const MeshBase & mesh,
    1093             :     const Node & node,
    1094             :     const std::unordered_map<dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map,
    1095             :     std::vector<const Node *> & neighbors)
    1096             : {
    1097             :   // Find all the nodal neighbors... that is the nodes directly connected
    1098             :   // to this node through one edge.
    1099       50735 :   find_nodal_neighbors(mesh, node, nodes_to_elem_map, neighbors);
    1100             : 
    1101             :   // If no neighbors are found, use all nodes on the containing side as
    1102             :   // neighbors.
    1103       50735 :   if (!neighbors.size())
    1104             :     {
    1105             :       // Grab the element containing node
    1106        6804 :       const auto * elem = libmesh_map_find(nodes_to_elem_map, node.id()).front();
    1107             :       // Find the element side containing node
    1108       17503 :       for (const auto &side : elem->side_index_range())
    1109             :         {
    1110       17503 :           const auto &nodes_on_side = elem->nodes_on_side(side);
    1111             :           const auto it =
    1112       23074 :               std::find_if(nodes_on_side.begin(), nodes_on_side.end(), [&](auto local_node_id) {
    1113       64593 :                 return elem->node_id(local_node_id) == node.id();
    1114        1946 :               });
    1115             : 
    1116       18476 :           if (it != nodes_on_side.end())
    1117             :             {
    1118       58008 :               for (const auto &local_node_id : nodes_on_side)
    1119             :                 // No need to add node itself as a neighbor
    1120       53931 :                 if (const auto *node_ptr = elem->node_ptr(local_node_id);
    1121        2727 :                     *node_ptr != node)
    1122       44400 :                   neighbors.push_back(node_ptr);
    1123         347 :               break;
    1124             :             }
    1125             :         }
    1126             :     }
    1127        3146 :   libmesh_assert(neighbors.size());
    1128       50735 : }
    1129             : 
    1130             : 
    1131             : 
    1132           0 : void find_hanging_nodes_and_parents(const MeshBase & mesh,
    1133             :                                     std::map<dof_id_type, std::vector<dof_id_type>> & hanging_nodes)
    1134             : {
    1135             :   // Loop through all the elements
    1136           0 :   for (auto & elem : mesh.active_local_element_ptr_range())
    1137           0 :     if (elem->type() == QUAD4)
    1138           0 :       for (auto s : elem->side_index_range())
    1139             :         {
    1140             :           // Loop over the sides looking for sides that have hanging nodes
    1141             :           // This code is inspired by compute_proj_constraints()
    1142           0 :           const Elem * neigh = elem->neighbor_ptr(s);
    1143             : 
    1144             :           // If not a boundary side
    1145           0 :           if (neigh != nullptr)
    1146             :             {
    1147             :               // Is there a coarser element next to this one?
    1148           0 :               if (neigh->level() < elem->level())
    1149             :                 {
    1150           0 :                   const Elem * ancestor = elem;
    1151           0 :                   while (neigh->level() < ancestor->level())
    1152           0 :                     ancestor = ancestor->parent();
    1153           0 :                   unsigned int s_neigh = neigh->which_neighbor_am_i(ancestor);
    1154           0 :                   libmesh_assert_less (s_neigh, neigh->n_neighbors());
    1155             : 
    1156             :                   // Couple of helper uints...
    1157           0 :                   unsigned int local_node1=0;
    1158           0 :                   unsigned int local_node2=0;
    1159             : 
    1160           0 :                   bool found_in_neighbor = false;
    1161             : 
    1162             :                   // Find the two vertices that make up this side
    1163           0 :                   while (!elem->is_node_on_side(local_node1++,s)) { }
    1164           0 :                   local_node1--;
    1165             : 
    1166             :                   // Start looking for the second one with the next node
    1167           0 :                   local_node2=local_node1+1;
    1168             : 
    1169             :                   // Find the other one
    1170           0 :                   while (!elem->is_node_on_side(local_node2++,s)) { }
    1171           0 :                   local_node2--;
    1172             : 
    1173             :                   //Pull out their global ids:
    1174           0 :                   dof_id_type node1 = elem->node_id(local_node1);
    1175           0 :                   dof_id_type node2 = elem->node_id(local_node2);
    1176             : 
    1177             :                   // Now find which node is present in the neighbor
    1178             :                   // FIXME This assumes a level one rule!
    1179             :                   // The _other_ one is the hanging node
    1180             : 
    1181             :                   // First look for the first one
    1182             :                   // FIXME could be streamlined a bit
    1183           0 :                   for (unsigned int n=0;n<neigh->n_sides();n++)
    1184           0 :                     if (neigh->node_id(n) == node1)
    1185           0 :                       found_in_neighbor=true;
    1186             : 
    1187           0 :                   dof_id_type hanging_node=0;
    1188             : 
    1189           0 :                   if (!found_in_neighbor)
    1190           0 :                     hanging_node=node1;
    1191             :                   else // If it wasn't node1 then it must be node2!
    1192           0 :                     hanging_node=node2;
    1193             : 
    1194             :                   // Reset for reuse
    1195           0 :                   local_node1=0;
    1196             : 
    1197             :                   // Find the first node that makes up the side in the neighbor (these should be the parent nodes)
    1198           0 :                   while (!neigh->is_node_on_side(local_node1++,s_neigh)) { }
    1199           0 :                   local_node1--;
    1200             : 
    1201           0 :                   local_node2=local_node1+1;
    1202             : 
    1203             :                   // Find the second node...
    1204           0 :                   while (!neigh->is_node_on_side(local_node2++,s_neigh)) { }
    1205           0 :                   local_node2--;
    1206             : 
    1207             :                   // Save them if we haven't already found the parents for this one
    1208           0 :                   if (hanging_nodes[hanging_node].size()<2)
    1209             :                     {
    1210           0 :                       hanging_nodes[hanging_node].push_back(neigh->node_id(local_node1));
    1211           0 :                       hanging_nodes[hanging_node].push_back(neigh->node_id(local_node2));
    1212             :                     }
    1213             :                 }
    1214             :             }
    1215           0 :         }
    1216           0 : }
    1217             : 
    1218             : 
    1219             : 
    1220          36 : void clear_spline_nodes(MeshBase & mesh)
    1221             : {
    1222           6 :   std::vector<Elem *> nodeelem_to_delete;
    1223             : 
    1224        8737 :   for (auto & elem : mesh.element_ptr_range())
    1225        5073 :     if (elem->type() == NODEELEM &&
    1226        4140 :         elem->mapping_type() == RATIONAL_BERNSTEIN_MAP)
    1227        4170 :       nodeelem_to_delete.push_back(elem);
    1228             : 
    1229           3 :   auto & constraint_rows = mesh.set_constraint_rows();
    1230             : 
    1231             :   // All our constraint_rows ought to be for spline constraints we're
    1232             :   // about to get rid of.
    1233             : #ifndef NDEBUG
    1234         762 :   for (auto & node_row : constraint_rows)
    1235        2064 :     for (auto pr : node_row.second)
    1236             :       {
    1237        1305 :         const Elem * elem = pr.first.first;
    1238        1305 :         libmesh_assert(elem->type() == NODEELEM);
    1239        1305 :         libmesh_assert(elem->mapping_type() == RATIONAL_BERNSTEIN_MAP);
    1240             :       }
    1241             : #endif
    1242             : 
    1243           3 :   constraint_rows.clear();
    1244             : 
    1245        4176 :   for (Elem * elem : nodeelem_to_delete)
    1246             :     {
    1247         690 :       Node * node = elem->node_ptr(0);
    1248        4140 :       mesh.delete_elem(elem);
    1249        4140 :       mesh.delete_node(node);
    1250             :     }
    1251          36 : }
    1252             : 
    1253             : 
    1254             : 
    1255        1798 : bool valid_is_prepared (const MeshBase & mesh)
    1256             : {
    1257         202 :   LOG_SCOPE("valid_is_prepared()", "MeshTools");
    1258             : 
    1259        1798 :   if (!mesh.is_prepared())
    1260           2 :     return true;
    1261             : 
    1262        1870 :   std::unique_ptr<MeshBase> mesh_clone = mesh.clone();
    1263             : 
    1264             :   // Try preparing (without allowing repartitioning or renumbering, to
    1265             :   // avoid false assertion failures)
    1266         124 :   bool old_allow_renumbering = mesh_clone->allow_renumbering();
    1267          74 :   mesh_clone->allow_renumbering(false);
    1268             : 
    1269         124 :   bool old_skip_partitioning = mesh_clone->skip_partitioning();
    1270          74 :   mesh_clone->skip_partitioning(true);
    1271             : 
    1272         124 :   bool old_allow_remote_element_removal = mesh_clone->allow_remote_element_removal();
    1273          74 :   mesh_clone->allow_remote_element_removal(false);
    1274             : 
    1275             :   // Call prepare_for_use() on the clone
    1276        1796 :   mesh_clone->prepare_for_use();
    1277             : 
    1278             :   // Restore original flag values
    1279          74 :   mesh_clone->allow_renumbering(old_allow_renumbering);
    1280          74 :   mesh_clone->skip_partitioning(old_skip_partitioning);
    1281          74 :   mesh_clone->allow_remote_element_removal(old_allow_remote_element_removal);
    1282             : 
    1283             :   // Check whether the original and clone compare equal
    1284        1796 :   return (mesh == *mesh_clone);
    1285        1672 : }
    1286             : 
    1287             : 
    1288             : 
    1289             : #ifndef NDEBUG
    1290             : 
    1291         136 : void libmesh_assert_equal_n_systems (const MeshBase & mesh)
    1292             : {
    1293         272 :   LOG_SCOPE("libmesh_assert_equal_n_systems()", "MeshTools");
    1294             : 
    1295         136 :   unsigned int n_sys = libMesh::invalid_uint;
    1296             : 
    1297       12632 :   for (const auto & elem : mesh.element_ptr_range())
    1298             :     {
    1299       12496 :       if (n_sys == libMesh::invalid_uint)
    1300         136 :         n_sys = elem->n_systems();
    1301             :       else
    1302       12360 :         libmesh_assert_equal_to (elem->n_systems(), n_sys);
    1303             :     }
    1304             : 
    1305       31283 :   for (const auto & node : mesh.node_ptr_range())
    1306             :     {
    1307       31147 :       if (n_sys == libMesh::invalid_uint)
    1308           0 :         n_sys = node->n_systems();
    1309             :       else
    1310       31147 :         libmesh_assert_equal_to (node->n_systems(), n_sys);
    1311             :     }
    1312         136 : }
    1313             : 
    1314             : 
    1315             : 
    1316             : #ifdef LIBMESH_ENABLE_AMR
    1317           0 : void libmesh_assert_old_dof_objects (const MeshBase & mesh)
    1318             : {
    1319           0 :   LOG_SCOPE("libmesh_assert_old_dof_objects()", "MeshTools");
    1320             : 
    1321           0 :   for (const auto & elem : mesh.element_ptr_range())
    1322             :     {
    1323           0 :       if (elem->refinement_flag() == Elem::JUST_REFINED ||
    1324           0 :           elem->refinement_flag() == Elem::INACTIVE)
    1325           0 :         continue;
    1326             : 
    1327           0 :       if (elem->has_dofs())
    1328           0 :         libmesh_assert(elem->get_old_dof_object());
    1329             : 
    1330           0 :       for (auto & node : elem->node_ref_range())
    1331           0 :         if (node.has_dofs())
    1332           0 :           libmesh_assert(node.get_old_dof_object());
    1333             :     }
    1334           0 : }
    1335             : #else
    1336             : void libmesh_assert_old_dof_objects (const MeshBase &) {}
    1337             : #endif // LIBMESH_ENABLE_AMR
    1338             : 
    1339             : 
    1340             : 
    1341           0 : void libmesh_assert_valid_node_pointers(const MeshBase & mesh)
    1342             : {
    1343           0 :   LOG_SCOPE("libmesh_assert_valid_node_pointers()", "MeshTools");
    1344             : 
    1345             :   // Here we specifically do not want "auto &" because we need to
    1346             :   // reseat the (temporary) pointer variable in the loop below,
    1347             :   // without modifying the original.
    1348           0 :   for (const Elem * elem : mesh.element_ptr_range())
    1349             :     {
    1350           0 :       libmesh_assert (elem);
    1351           0 :       while (elem)
    1352             :         {
    1353           0 :           elem->libmesh_assert_valid_node_pointers();
    1354           0 :           for (auto n : elem->neighbor_ptr_range())
    1355           0 :             if (n && n != remote_elem)
    1356           0 :               n->libmesh_assert_valid_node_pointers();
    1357             : 
    1358           0 :           libmesh_assert_not_equal_to (elem->parent(), remote_elem);
    1359           0 :           elem = elem->parent();
    1360             :         }
    1361             :     }
    1362           0 : }
    1363             : 
    1364             : 
    1365             : 
    1366       11787 : void libmesh_assert_valid_remote_elems(const MeshBase & mesh)
    1367             : {
    1368       23574 :   LOG_SCOPE("libmesh_assert_valid_remote_elems()", "MeshTools");
    1369             : 
    1370      689781 :   for (const auto & elem : as_range(mesh.local_elements_begin(),
    1371     1379562 :                                     mesh.local_elements_end()))
    1372             :     {
    1373      677994 :       libmesh_assert (elem);
    1374             : 
    1375             :       // We currently don't allow active_local_elements to have
    1376             :       // remote_elem neighbors
    1377      677994 :       if (elem->active())
    1378     2953671 :         for (auto n : elem->neighbor_ptr_range())
    1379     2376655 :           libmesh_assert_not_equal_to (n, remote_elem);
    1380             : 
    1381             : #ifdef LIBMESH_ENABLE_AMR
    1382      677994 :       const Elem * parent = elem->parent();
    1383      677994 :       if (parent)
    1384      377198 :         libmesh_assert_not_equal_to (parent, remote_elem);
    1385             : 
    1386             :       // We can only be strict about active elements' subactive
    1387             :       // children
    1388      677994 :       if (elem->active() && elem->has_children())
    1389       30186 :         for (auto & child : elem->child_ref_range())
    1390       24168 :           libmesh_assert_not_equal_to (&child, remote_elem);
    1391             : #endif
    1392             :     }
    1393       11787 : }
    1394             : 
    1395             : 
    1396             : 
    1397         424 : void libmesh_assert_valid_elem_ids(const MeshBase & mesh)
    1398             : {
    1399         848 :   LOG_SCOPE("libmesh_assert_valid_elem_ids()", "MeshTools");
    1400             : 
    1401         424 :   processor_id_type lastprocid = 0;
    1402         424 :   dof_id_type lastelemid = 0;
    1403             : 
    1404       13881 :   for (const auto & elem : mesh.active_element_ptr_range())
    1405             :     {
    1406       13457 :       libmesh_assert (elem);
    1407       13457 :       processor_id_type elemprocid = elem->processor_id();
    1408       13457 :       dof_id_type elemid = elem->id();
    1409             : 
    1410       13457 :       libmesh_assert_greater_equal (elemid, lastelemid);
    1411       13457 :       libmesh_assert_greater_equal (elemprocid, lastprocid);
    1412             : 
    1413       13457 :       lastelemid = elemid;
    1414       13457 :       lastprocid = elemprocid;
    1415             :     }
    1416         424 : }
    1417             : 
    1418             : 
    1419             : 
    1420         866 : void libmesh_assert_valid_amr_elem_ids(const MeshBase & mesh)
    1421             : {
    1422        1732 :   LOG_SCOPE("libmesh_assert_valid_amr_elem_ids()", "MeshTools");
    1423             : 
    1424      307434 :   for (const auto & elem : mesh.element_ptr_range())
    1425             :     {
    1426      306568 :       libmesh_assert (elem);
    1427             : 
    1428      306568 :       const Elem * parent = elem->parent();
    1429             : 
    1430      306568 :       if (parent)
    1431             :         {
    1432       24404 :           libmesh_assert_greater_equal (elem->id(), parent->id());
    1433       24404 :           libmesh_assert_greater_equal (elem->processor_id(), parent->processor_id());
    1434             :         }
    1435             :     }
    1436         866 : }
    1437             : 
    1438             : 
    1439             : 
    1440       17830 : void libmesh_assert_valid_amr_interior_parents(const MeshBase & mesh)
    1441             : {
    1442       35660 :   LOG_SCOPE("libmesh_assert_valid_amr_interior_parents()", "MeshTools");
    1443             : 
    1444     1486673 :   for (const auto & elem : mesh.element_ptr_range())
    1445             :     {
    1446     1468843 :       libmesh_assert (elem);
    1447             : 
    1448             :       // We can skip to the next element if we're full-dimension
    1449             :       // and therefore don't have any interior parents
    1450     1468843 :       if (elem->dim() >= LIBMESH_DIM)
    1451      657582 :         continue;
    1452             : 
    1453      811261 :       const Elem * ip = elem->interior_parent();
    1454             : 
    1455      811261 :       const Elem * parent = elem->parent();
    1456             : 
    1457      811261 :       if (ip && (ip != remote_elem) && parent)
    1458             :         {
    1459        1000 :           libmesh_assert_equal_to (ip->top_parent(),
    1460             :                                    elem->top_parent()->interior_parent());
    1461             : 
    1462        1000 :           if (ip->level() == elem->level())
    1463        1000 :             libmesh_assert_equal_to (ip->parent(),
    1464             :                                      parent->interior_parent());
    1465             :           else
    1466             :             {
    1467           0 :               libmesh_assert_less (ip->level(), elem->level());
    1468           0 :               libmesh_assert_equal_to (ip, parent->interior_parent());
    1469             :             }
    1470             :         }
    1471             :     }
    1472       17830 : }
    1473             : 
    1474             : 
    1475             : 
    1476           0 : void libmesh_assert_contiguous_dof_ids(const MeshBase & mesh, unsigned int sysnum)
    1477             : {
    1478           0 :   LOG_SCOPE("libmesh_assert_contiguous_dof_ids()", "MeshTools");
    1479             : 
    1480           0 :   if (mesh.n_processors() == 1)
    1481           0 :     return;
    1482             : 
    1483           0 :   libmesh_parallel_only(mesh.comm());
    1484             : 
    1485           0 :   dof_id_type min_dof_id = std::numeric_limits<dof_id_type>::max(),
    1486           0 :               max_dof_id = std::numeric_limits<dof_id_type>::min();
    1487             : 
    1488             :   // Figure out what our local dof id range is
    1489           0 :   for (const auto * node : mesh.local_node_ptr_range())
    1490             :     {
    1491           0 :       for (auto v : make_range(node->n_vars(sysnum)))
    1492           0 :         for (auto c : make_range(node->n_comp(sysnum, v)))
    1493             :           {
    1494           0 :             dof_id_type id = node->dof_number(sysnum, v, c);
    1495           0 :             min_dof_id = std::min (min_dof_id, id);
    1496           0 :             max_dof_id = std::max (max_dof_id, id);
    1497             :           }
    1498             :     }
    1499             : 
    1500             :   // Make sure no other processors' ids are inside it
    1501           0 :   for (const auto * node : mesh.node_ptr_range())
    1502             :     {
    1503           0 :       if (node->processor_id() == mesh.processor_id())
    1504           0 :         continue;
    1505           0 :       for (auto v : make_range(node->n_vars(sysnum)))
    1506           0 :         for (auto c : make_range(node->n_comp(sysnum, v)))
    1507             :           {
    1508           0 :             dof_id_type id = node->dof_number(sysnum, v, c);
    1509           0 :             libmesh_assert (id < min_dof_id ||
    1510             :                             id > max_dof_id);
    1511             :           }
    1512             :     }
    1513             : }
    1514             : 
    1515             : 
    1516             : 
    1517             : template <>
    1518       24018 : void libmesh_assert_topology_consistent_procids<Elem>(const MeshBase & mesh)
    1519             : {
    1520       48036 :   LOG_SCOPE("libmesh_assert_topology_consistent_procids()", "MeshTools");
    1521             : 
    1522             :   // This parameter is not used when !LIBMESH_ENABLE_AMR
    1523       24018 :   libmesh_ignore(mesh);
    1524             : 
    1525             :   // If we're adaptively refining, check processor ids for consistency
    1526             :   // between parents and children.
    1527             : #ifdef LIBMESH_ENABLE_AMR
    1528             : 
    1529             :   // Ancestor elements we won't worry about, but subactive and active
    1530             :   // elements ought to have parents with consistent processor ids
    1531     2817350 :   for (const auto & elem : mesh.element_ptr_range())
    1532             :     {
    1533     2793332 :       libmesh_assert(elem);
    1534             : 
    1535     2793332 :       if (!elem->active() && !elem->subactive())
    1536      306696 :         continue;
    1537             : 
    1538     2486636 :       const Elem * parent = elem->parent();
    1539             : 
    1540     2486636 :       if (parent)
    1541             :         {
    1542     1225076 :           libmesh_assert(parent->has_children());
    1543     1225076 :           processor_id_type parent_procid = parent->processor_id();
    1544     1225076 :           bool matching_child_id = false;
    1545             :           // If we've got a remote_elem then we don't know whether
    1546             :           // it's responsible for the parent's processor id; all
    1547             :           // we can do is assume it is and let its processor fail
    1548             :           // an assert if there's something wrong.
    1549     7551420 :           for (auto & child : parent->child_ref_range())
    1550    12649928 :             if (&child == remote_elem ||
    1551     6323584 :                 child.processor_id() == parent_procid)
    1552     6154620 :               matching_child_id = true;
    1553     1225076 :           libmesh_assert(matching_child_id);
    1554             :         }
    1555             :     }
    1556             : #endif
    1557       24018 : }
    1558             : 
    1559             : 
    1560             : 
    1561             : template <>
    1562       36821 : void libmesh_assert_topology_consistent_procids<Node>(const MeshBase & mesh)
    1563             : {
    1564       36821 :   LOG_SCOPE("libmesh_assert_topology_consistent_procids()", "MeshTools");
    1565             : 
    1566       36821 :   if (mesh.n_processors() == 1)
    1567           1 :     return;
    1568             : 
    1569       36820 :   libmesh_parallel_only(mesh.comm());
    1570             : 
    1571             :   // We want this test to be valid even when called after nodes have
    1572             :   // been added asynchronously but before they're renumbered.
    1573             :   //
    1574             :   // Plus, some code (looking at you, stitch_meshes) modifies
    1575             :   // DofObject ids without keeping max_elem_id()/max_node_id()
    1576             :   // consistent, but that's done in a safe way for performance
    1577             :   // reasons, so we'll play along and just figure out new max ids
    1578             :   // ourselves.
    1579       36820 :   dof_id_type parallel_max_node_id = 0;
    1580     7850961 :   for (const auto & node : mesh.node_ptr_range())
    1581     7814141 :     parallel_max_node_id = std::max<dof_id_type>(parallel_max_node_id,
    1582     7814141 :                                                  node->id()+1);
    1583       36820 :   mesh.comm().max(parallel_max_node_id);
    1584             : 
    1585             : 
    1586       73640 :   std::vector<bool> node_touched_by_me(parallel_max_node_id, false);
    1587             : 
    1588     2229608 :   for (const auto & elem : as_range(mesh.local_elements_begin(),
    1589     4459216 :                                     mesh.local_elements_end()))
    1590             :     {
    1591     2192788 :       libmesh_assert (elem);
    1592             : 
    1593    16866120 :       for (auto & node : elem->node_ref_range())
    1594             :         {
    1595    14673332 :           dof_id_type nodeid = node.id();
    1596    14673332 :           node_touched_by_me[nodeid] = true;
    1597             :         }
    1598             :     }
    1599       73640 :   std::vector<bool> node_touched_by_anyone(node_touched_by_me);
    1600       36820 :   mesh.comm().max(node_touched_by_anyone);
    1601             : 
    1602     3931677 :   for (const auto & node : mesh.local_node_ptr_range())
    1603             :     {
    1604     3894857 :       libmesh_assert(node);
    1605     3894857 :       dof_id_type nodeid = node->id();
    1606     3894857 :       libmesh_assert(!node_touched_by_anyone[nodeid] ||
    1607             :                      node_touched_by_me[nodeid]);
    1608             :     }
    1609             : }
    1610             : 
    1611             : 
    1612             : 
    1613           0 : void libmesh_assert_canonical_node_procids (const MeshBase & mesh)
    1614             : {
    1615           0 :   for (const auto & elem : mesh.active_element_ptr_range())
    1616           0 :     for (auto & node : elem->node_ref_range())
    1617           0 :       libmesh_assert_equal_to
    1618             :         (node.processor_id(),
    1619             :          node.choose_processor_id(node.processor_id(),
    1620             :                                   elem->processor_id()));
    1621           0 : }
    1622             : 
    1623             : 
    1624             : 
    1625             : #ifdef LIBMESH_ENABLE_AMR
    1626        1336 : void libmesh_assert_valid_refinement_tree(const MeshBase & mesh)
    1627             : {
    1628        2672 :   LOG_SCOPE("libmesh_assert_valid_refinement_tree()", "MeshTools");
    1629             : 
    1630       68018 :   for (const auto & elem : mesh.element_ptr_range())
    1631             :     {
    1632       66682 :       libmesh_assert(elem);
    1633       66682 :       if (elem->has_children())
    1634       27268 :         for (auto & child : elem->child_ref_range())
    1635       23216 :           if (&child != remote_elem)
    1636       20800 :             libmesh_assert_equal_to (child.parent(), elem);
    1637       66682 :       if (elem->active())
    1638             :         {
    1639       62630 :           libmesh_assert(!elem->ancestor());
    1640       62630 :           libmesh_assert(!elem->subactive());
    1641             :         }
    1642        4052 :       else if (elem->ancestor())
    1643             :         {
    1644        4052 :           libmesh_assert(!elem->subactive());
    1645             :         }
    1646             :       else
    1647           0 :         libmesh_assert(elem->subactive());
    1648             : 
    1649       66682 :       if (elem->p_refinement_flag() == Elem::JUST_REFINED)
    1650           0 :         libmesh_assert_greater(elem->p_level(), 0);
    1651             :     }
    1652        1336 : }
    1653             : #else
    1654             : void libmesh_assert_valid_refinement_tree(const MeshBase &)
    1655             : {
    1656             : }
    1657             : #endif // LIBMESH_ENABLE_AMR
    1658             : 
    1659             : #endif // !NDEBUG
    1660             : 
    1661             : 
    1662             : 
    1663             : #ifdef DEBUG
    1664             : 
    1665           0 : void libmesh_assert_no_links_to_elem(const MeshBase & mesh,
    1666             :                                      const Elem * bad_elem)
    1667             : {
    1668           0 :   for (const auto & elem : mesh.element_ptr_range())
    1669             :     {
    1670           0 :       libmesh_assert (elem);
    1671           0 :       libmesh_assert_not_equal_to (elem->parent(), bad_elem);
    1672           0 :       for (auto n : elem->neighbor_ptr_range())
    1673           0 :         libmesh_assert_not_equal_to (n, bad_elem);
    1674             : 
    1675             : #ifdef LIBMESH_ENABLE_AMR
    1676           0 :       if (elem->has_children())
    1677           0 :         for (auto & child : elem->child_ref_range())
    1678           0 :           libmesh_assert_not_equal_to (&child, bad_elem);
    1679             : #endif
    1680             :     }
    1681           0 : }
    1682             : 
    1683             : 
    1684         570 : void libmesh_assert_equal_points (const MeshBase & mesh)
    1685             : {
    1686        1140 :   LOG_SCOPE("libmesh_assert_equal_points()", "MeshTools");
    1687             : 
    1688         570 :   dof_id_type pmax_node_id = mesh.max_node_id();
    1689         570 :   mesh.comm().max(pmax_node_id);
    1690             : 
    1691      922414 :   for (dof_id_type i=0; i != pmax_node_id; ++i)
    1692             :     {
    1693      921844 :       const Point * p = mesh.query_node_ptr(i);
    1694             : 
    1695      921844 :       libmesh_assert(mesh.comm().semiverify(p));
    1696             :     }
    1697         570 : }
    1698             : 
    1699             : 
    1700         570 : void libmesh_assert_equal_connectivity (const MeshBase & mesh)
    1701             : {
    1702        1140 :   LOG_SCOPE("libmesh_assert_equal_connectivity()", "MeshTools");
    1703             : 
    1704         570 :   dof_id_type pmax_elem_id = mesh.max_elem_id();
    1705         570 :   mesh.comm().max(pmax_elem_id);
    1706             : 
    1707     1144326 :   for (dof_id_type i=0; i != pmax_elem_id; ++i)
    1708             :     {
    1709     1143756 :       const Elem * e = mesh.query_elem_ptr(i);
    1710             : 
    1711     2287512 :       std::vector<dof_id_type> nodes;
    1712     1143756 :       if (e)
    1713     5792488 :         for (auto n : e->node_index_range())
    1714     4648848 :           nodes.push_back(e->node_id(n));
    1715             : 
    1716     1143756 :       libmesh_assert(mesh.comm().semiverify(e ? &nodes : nullptr));
    1717             :     }
    1718         570 : }
    1719             : 
    1720             : 
    1721           0 : void libmesh_assert_connected_nodes (const MeshBase & mesh)
    1722             : {
    1723           0 :   LOG_SCOPE("libmesh_assert_connected_nodes()", "MeshTools");
    1724             : 
    1725           0 :   std::set<const Node *> used_nodes;
    1726             : 
    1727           0 :   for (const auto & elem : mesh.element_ptr_range())
    1728             :     {
    1729           0 :       libmesh_assert (elem);
    1730             : 
    1731           0 :       for (auto & n : elem->node_ref_range())
    1732           0 :         used_nodes.insert(&n);
    1733             :     }
    1734             : 
    1735           0 :   for (const auto & node : mesh.node_ptr_range())
    1736             :     {
    1737           0 :       libmesh_assert(node);
    1738           0 :       libmesh_assert(used_nodes.count(node));
    1739             :     }
    1740           0 : }
    1741             : 
    1742             : 
    1743             : 
    1744       26922 : void libmesh_assert_valid_constraint_rows (const MeshBase & mesh)
    1745             : {
    1746       26922 :   libmesh_parallel_only(mesh.comm());
    1747             : 
    1748       26922 :   const auto & constraint_rows = mesh.get_constraint_rows();
    1749             : 
    1750       26922 :   bool have_constraint_rows = !constraint_rows.empty();
    1751       26922 :   mesh.comm().max(have_constraint_rows);
    1752       26922 :   if (!have_constraint_rows)
    1753       26882 :     return;
    1754             : 
    1755       12700 :   for (auto & row : constraint_rows)
    1756             :     {
    1757       12660 :       const Node * node = row.first;
    1758       12660 :       libmesh_assert(node == mesh.node_ptr(node->id()));
    1759             : 
    1760       44126 :       for (auto & pr : row.second)
    1761             :         {
    1762       31466 :           const Elem * spline_elem = pr.first.first;
    1763       31466 :           libmesh_assert(spline_elem == mesh.elem_ptr(spline_elem->id()));
    1764             :         }
    1765             :     }
    1766             : 
    1767          40 :   dof_id_type pmax_node_id = mesh.max_node_id();
    1768          40 :   mesh.comm().max(pmax_node_id);
    1769             : 
    1770       16990 :   for (dof_id_type i=0; i != pmax_node_id; ++i)
    1771             :     {
    1772       16950 :       const Node * node = mesh.query_node_ptr(i);
    1773             : 
    1774       16950 :       bool have_constraint = constraint_rows.count(node);
    1775             : 
    1776       16950 :       const std::size_t my_n_constraints = have_constraint ?
    1777       12660 :         libmesh_map_find(constraint_rows, node).size() : std::size_t(-1);
    1778       16950 :       const std::size_t * n_constraints = node ?
    1779       16950 :         &my_n_constraints : nullptr;
    1780             : 
    1781       16950 :       libmesh_assert(mesh.comm().semiverify(n_constraints));
    1782             :     }
    1783             : }
    1784             : 
    1785             : 
    1786             : 
    1787       64006 : void libmesh_assert_valid_boundary_ids(const MeshBase & mesh)
    1788             : {
    1789       64006 :   LOG_SCOPE("libmesh_assert_valid_boundary_ids()", "MeshTools");
    1790             : 
    1791       64006 :   if (mesh.n_processors() == 1)
    1792        1164 :     return;
    1793             : 
    1794       62842 :   libmesh_parallel_only(mesh.comm());
    1795             : 
    1796       62842 :   const BoundaryInfo & boundary_info = mesh.get_boundary_info();
    1797             : 
    1798       62842 :   dof_id_type pmax_elem_id = mesh.max_elem_id();
    1799       62842 :   mesh.comm().max(pmax_elem_id);
    1800             : 
    1801     4191636 :   for (dof_id_type i=0; i != pmax_elem_id; ++i)
    1802             :     {
    1803     4128794 :       const Elem * elem = mesh.query_elem_ptr(i);
    1804     4128794 :       const unsigned int my_n_nodes = elem ? elem->n_nodes() : 0;
    1805     4128794 :       const unsigned int my_n_edges = elem ? elem->n_edges() : 0;
    1806     4128794 :       const unsigned int my_n_sides = elem ? elem->n_sides() : 0;
    1807             :       unsigned int
    1808     4128794 :         n_nodes = my_n_nodes,
    1809     4128794 :         n_edges = my_n_edges,
    1810     4128794 :         n_sides = my_n_sides;
    1811             : 
    1812     4128794 :       mesh.comm().max(n_nodes);
    1813     4128794 :       mesh.comm().max(n_edges);
    1814     4128794 :       mesh.comm().max(n_sides);
    1815             : 
    1816     4128794 :       if (elem)
    1817             :         {
    1818     4052609 :           libmesh_assert_equal_to(my_n_nodes, n_nodes);
    1819     4052609 :           libmesh_assert_equal_to(my_n_edges, n_edges);
    1820     4052609 :           libmesh_assert_equal_to(my_n_sides, n_sides);
    1821             :         }
    1822             : 
    1823             :       // Let's test all IDs on the element with one communication
    1824             :       // rather than n_nodes + n_edges + n_sides communications, to
    1825             :       // cut down on latency in dbg modes.
    1826     8257588 :       std::vector<boundary_id_type> all_bcids;
    1827             : 
    1828    33598754 :       for (unsigned int n=0; n != n_nodes; ++n)
    1829             :         {
    1830    58939920 :           std::vector<boundary_id_type> bcids;
    1831    29469960 :           if (elem)
    1832             :             {
    1833    29295094 :               boundary_info.boundary_ids(elem->node_ptr(n), bcids);
    1834             : 
    1835             :               // Ordering of boundary ids shouldn't matter
    1836    29295094 :               std::sort(bcids.begin(), bcids.end());
    1837             :             }
    1838             :           // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
    1839             : 
    1840    29469960 :           all_bcids.insert(all_bcids.end(), bcids.begin(),
    1841    58939920 :                            bcids.end());
    1842             :           // Separator
    1843    29469960 :           all_bcids.push_back(BoundaryInfo::invalid_id);
    1844             :         }
    1845             : 
    1846    25685780 :       for (unsigned short e=0; e != n_edges; ++e)
    1847             :         {
    1848    43113972 :           std::vector<boundary_id_type> bcids;
    1849             : 
    1850    21556986 :           if (elem)
    1851             :             {
    1852    21439690 :               boundary_info.edge_boundary_ids(elem, e, bcids);
    1853             : 
    1854             :               // Ordering of boundary ids shouldn't matter
    1855    21439690 :               std::sort(bcids.begin(), bcids.end());
    1856             :             }
    1857             : 
    1858             :           // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
    1859             : 
    1860    21556986 :           all_bcids.insert(all_bcids.end(), bcids.begin(),
    1861    43113972 :                            bcids.end());
    1862             :           // Separator
    1863    21556986 :           all_bcids.push_back(BoundaryInfo::invalid_id);
    1864             : 
    1865    21556986 :           if (elem)
    1866             :             {
    1867    21439690 :               boundary_info.raw_edge_boundary_ids(elem, e, bcids);
    1868             : 
    1869             :               // Ordering of boundary ids shouldn't matter
    1870    21439690 :               std::sort(bcids.begin(), bcids.end());
    1871             : 
    1872    21439690 :               all_bcids.insert(all_bcids.end(), bcids.begin(),
    1873    42879380 :                                bcids.end());
    1874             :               // Separator
    1875    21439690 :               all_bcids.push_back(BoundaryInfo::invalid_id);
    1876             :             }
    1877             : 
    1878             :           // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
    1879             :         }
    1880             : 
    1881    20471752 :       for (unsigned short s=0; s != n_sides; ++s)
    1882             :         {
    1883    32685916 :           std::vector<boundary_id_type> bcids;
    1884             : 
    1885    16342958 :           if (elem)
    1886             :             {
    1887    16260150 :               boundary_info.boundary_ids(elem, s, bcids);
    1888             : 
    1889             :               // Ordering of boundary ids shouldn't matter
    1890    16260150 :               std::sort(bcids.begin(), bcids.end());
    1891             : 
    1892    16260150 :               all_bcids.insert(all_bcids.end(), bcids.begin(),
    1893    32520300 :                                bcids.end());
    1894             :               // Separator
    1895    16260150 :               all_bcids.push_back(BoundaryInfo::invalid_id);
    1896             :             }
    1897             : 
    1898             :           // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
    1899             : 
    1900    16342958 :           if (elem)
    1901             :             {
    1902    16260150 :               boundary_info.raw_boundary_ids(elem, s, bcids);
    1903             : 
    1904             :               // Ordering of boundary ids shouldn't matter
    1905    16260150 :               std::sort(bcids.begin(), bcids.end());
    1906             : 
    1907    16260150 :               all_bcids.insert(all_bcids.end(), bcids.begin(),
    1908    32520300 :                                bcids.end());
    1909             :               // Separator
    1910    16260150 :               all_bcids.push_back(BoundaryInfo::invalid_id);
    1911             :             }
    1912             : 
    1913             :           // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
    1914             :         }
    1915             : 
    1916    12386382 :       for (unsigned short sf=0; sf != 2; ++sf)
    1917             :         {
    1918    16515176 :           std::vector<boundary_id_type> bcids;
    1919             : 
    1920     8257588 :           if (elem)
    1921             :             {
    1922     8105218 :               boundary_info.shellface_boundary_ids(elem, sf, bcids);
    1923             : 
    1924             :               // Ordering of boundary ids shouldn't matter
    1925     8105218 :               std::sort(bcids.begin(), bcids.end());
    1926             : 
    1927     8105218 :               all_bcids.insert(all_bcids.end(), bcids.begin(),
    1928    16210436 :                                bcids.end());
    1929             :               // Separator
    1930     8105218 :               all_bcids.push_back(BoundaryInfo::invalid_id);
    1931             :             }
    1932             : 
    1933             :           // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
    1934             : 
    1935     8257588 :           if (elem)
    1936             :             {
    1937     8105218 :               boundary_info.raw_shellface_boundary_ids(elem, sf, bcids);
    1938             : 
    1939             :               // Ordering of boundary ids shouldn't matter
    1940     8105218 :               std::sort(bcids.begin(), bcids.end());
    1941             : 
    1942     8105218 :               all_bcids.insert(all_bcids.end(), bcids.begin(),
    1943    16210436 :                                bcids.end());
    1944             :               // Separator
    1945     8105218 :               all_bcids.push_back(BoundaryInfo::invalid_id);
    1946             :             }
    1947             : 
    1948             :           // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
    1949             :         }
    1950             : 
    1951     4128794 :       libmesh_assert(mesh.comm().semiverify
    1952             :                      (elem ? &all_bcids : nullptr));
    1953             :     }
    1954             : }
    1955             : 
    1956             : 
    1957       11176 : void libmesh_assert_valid_dof_ids(const MeshBase & mesh, unsigned int sysnum)
    1958             : {
    1959       11176 :   LOG_SCOPE("libmesh_assert_valid_dof_ids()", "MeshTools");
    1960             : 
    1961       11176 :   if (mesh.n_processors() == 1)
    1962           0 :     return;
    1963             : 
    1964       11176 :   libmesh_parallel_only(mesh.comm());
    1965             : 
    1966       11176 :   dof_id_type pmax_elem_id = mesh.max_elem_id();
    1967       11176 :   mesh.comm().max(pmax_elem_id);
    1968             : 
    1969     1061176 :   for (dof_id_type i=0; i != pmax_elem_id; ++i)
    1970     1050000 :     assert_semiverify_dofobj(mesh.comm(),
    1971     1050000 :                              mesh.query_elem_ptr(i),
    1972             :                              sysnum);
    1973             : 
    1974       11176 :   dof_id_type pmax_node_id = mesh.max_node_id();
    1975       11176 :   mesh.comm().max(pmax_node_id);
    1976             : 
    1977     1988784 :   for (dof_id_type i=0; i != pmax_node_id; ++i)
    1978     1977608 :     assert_semiverify_dofobj(mesh.comm(),
    1979     1977608 :                              mesh.query_node_ptr(i),
    1980             :                              sysnum);
    1981             : }
    1982             : 
    1983             : 
    1984             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
    1985       52776 : void libmesh_assert_valid_unique_ids(const MeshBase & mesh)
    1986             : {
    1987      105552 :   LOG_SCOPE("libmesh_assert_valid_unique_ids()", "MeshTools");
    1988             : 
    1989       52776 :   libmesh_parallel_only(mesh.comm());
    1990             : 
    1991             :   // Storage for semi-local DofObject ids.
    1992      105552 :   std::unordered_set<unique_id_type> semilocal_unique_ids;
    1993             : 
    1994       52776 :   auto gather_elem_ids = [&]()
    1995             :   {
    1996     2587985 :     for (auto const & elem : mesh.active_element_ptr_range())
    1997             :       {
    1998     2535209 :         auto [it, inserted] = semilocal_unique_ids.insert(elem->unique_id());
    1999     2535209 :         libmesh_assert(inserted);
    2000     2535209 :         libmesh_ignore(it);
    2001             :       }
    2002       52776 :   };
    2003             : 
    2004       52776 :   auto gather_node_ids = [&]()
    2005             :   {
    2006     6175137 :     for (auto const & node : mesh.node_ptr_range())
    2007             :       {
    2008     6122361 :         auto [it, inserted] = semilocal_unique_ids.insert(node->unique_id());
    2009     6122361 :         libmesh_assert(inserted);
    2010     6122361 :         libmesh_ignore(it);
    2011             :       }
    2012       52776 :   };
    2013             : 
    2014       52776 :   auto verify_elems = [&]()
    2015             :   {
    2016       52776 :     dof_id_type pmax_elem_id = mesh.max_elem_id();
    2017       52776 :     mesh.comm().max(pmax_elem_id);
    2018             : 
    2019     3145816 :     for (auto i : make_range(pmax_elem_id))
    2020             :       {
    2021     3093040 :         const Elem * elem = mesh.query_elem_ptr(i);
    2022     3093040 :         assert_dofobj_unique_id(mesh.comm(), elem, semilocal_unique_ids);
    2023             :       }
    2024       52776 :   };
    2025             : 
    2026       52776 :   auto verify_nodes = [&]()
    2027             :   {
    2028       52776 :     dof_id_type pmax_node_id = mesh.max_node_id();
    2029       52776 :     mesh.comm().max(pmax_node_id);
    2030             : 
    2031     6430832 :     for (auto i : make_range(pmax_node_id))
    2032             :       {
    2033     6378056 :         const Node * node = mesh.query_node_ptr(i);
    2034     6378056 :         assert_dofobj_unique_id(mesh.comm(), node, semilocal_unique_ids);
    2035             :       }
    2036       52776 :   };
    2037             : 
    2038       52776 :   if (!mesh.allow_node_and_elem_unique_id_overlap())
    2039             :   {
    2040             :     // First collect all the unique_ids we can see and make sure there's
    2041             :     // no duplicates
    2042       52776 :     gather_elem_ids();
    2043       52776 :     gather_node_ids();
    2044             : 
    2045             :     // Then make sure elements/nodes are all in sync and remote
    2046             :     // elements don't duplicate semilocal
    2047       52776 :     verify_elems();
    2048       52776 :     verify_nodes();
    2049             :   }
    2050             :   else
    2051             :   {
    2052             :     // If the mesh allows Node and Elem unique_ids to overlap, then we only
    2053             :     // check for validity and uniqueness of an Elem (resp. Node) unique id
    2054             :     // within the set of Elem (resp. Node) unique_ids.
    2055           0 :     gather_elem_ids();
    2056           0 :     verify_elems();
    2057             : 
    2058             :     // Clear id list before checking Nodes
    2059           0 :     semilocal_unique_ids.clear();
    2060             : 
    2061             :     // Finally, check Nodes
    2062           0 :     gather_node_ids();
    2063           0 :     verify_nodes();
    2064             :   }
    2065       52776 : }
    2066             : #endif
    2067             : 
    2068           0 : void libmesh_assert_consistent_distributed(const MeshBase & mesh)
    2069             : {
    2070           0 :   libmesh_parallel_only(mesh.comm());
    2071             : 
    2072           0 :   dof_id_type parallel_max_elem_id = mesh.max_elem_id();
    2073           0 :   mesh.comm().max(parallel_max_elem_id);
    2074             : 
    2075           0 :   for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
    2076             :     {
    2077           0 :       const Elem * elem = mesh.query_elem_ptr(i);
    2078             :       processor_id_type pid =
    2079           0 :         elem ? elem->processor_id() : DofObject::invalid_processor_id;
    2080           0 :       mesh.comm().min(pid);
    2081           0 :       libmesh_assert(elem || pid != mesh.processor_id());
    2082             :     }
    2083             : 
    2084           0 :   dof_id_type parallel_max_node_id = mesh.max_node_id();
    2085           0 :   mesh.comm().max(parallel_max_node_id);
    2086             : 
    2087           0 :   for (dof_id_type i=0; i != parallel_max_node_id; ++i)
    2088             :     {
    2089           0 :       const Node * node = mesh.query_node_ptr(i);
    2090             :       processor_id_type pid =
    2091           0 :         node ? node->processor_id() : DofObject::invalid_processor_id;
    2092           0 :       mesh.comm().min(pid);
    2093           0 :       libmesh_assert(node || pid != mesh.processor_id());
    2094             :     }
    2095           0 : }
    2096             : 
    2097             : 
    2098           0 : void libmesh_assert_consistent_distributed_nodes(const MeshBase & mesh)
    2099             : {
    2100           0 :   libmesh_parallel_only(mesh.comm());
    2101           0 :   auto locator = mesh.sub_point_locator();
    2102             : 
    2103           0 :   dof_id_type parallel_max_elem_id = mesh.max_elem_id();
    2104           0 :   mesh.comm().max(parallel_max_elem_id);
    2105             : 
    2106           0 :   for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
    2107             :     {
    2108           0 :       const Elem * elem = mesh.query_elem_ptr(i);
    2109             : 
    2110           0 :       const unsigned int my_n_nodes = elem ? elem->n_nodes() : 0;
    2111           0 :       unsigned int n_nodes = my_n_nodes;
    2112           0 :       mesh.comm().max(n_nodes);
    2113             : 
    2114           0 :       if (n_nodes)
    2115           0 :         libmesh_assert(mesh.comm().semiverify(elem ? &my_n_nodes : nullptr));
    2116             : 
    2117           0 :       for (unsigned int n=0; n != n_nodes; ++n)
    2118             :         {
    2119           0 :           const Node * node = elem ? elem->node_ptr(n) : nullptr;
    2120             :           processor_id_type pid =
    2121           0 :             node ? node->processor_id() : DofObject::invalid_processor_id;
    2122           0 :           mesh.comm().min(pid);
    2123           0 :           libmesh_assert(node || pid != mesh.processor_id());
    2124             :         }
    2125             :     }
    2126           0 : }
    2127             : 
    2128             : 
    2129             : 
    2130             : template <>
    2131       24018 : void libmesh_assert_parallel_consistent_procids<Elem>(const MeshBase & mesh)
    2132             : {
    2133       24018 :   LOG_SCOPE("libmesh_assert_parallel_consistent_procids()", "MeshTools");
    2134             : 
    2135       24018 :   if (mesh.n_processors() == 1)
    2136           2 :     return;
    2137             : 
    2138       24016 :   libmesh_parallel_only(mesh.comm());
    2139             : 
    2140             :   // Some code (looking at you, stitch_meshes) modifies DofObject ids
    2141             :   // without keeping max_elem_id()/max_node_id() consistent, but
    2142             :   // that's done in a safe way for performance reasons, so we'll play
    2143             :   // along and just figure out new max ids ourselves.
    2144       24016 :   dof_id_type parallel_max_elem_id = 0;
    2145     2817340 :   for (const auto & elem : mesh.element_ptr_range())
    2146     2793324 :     parallel_max_elem_id = std::max<dof_id_type>(parallel_max_elem_id,
    2147     2793324 :                                                  elem->id()+1);
    2148       24016 :   mesh.comm().max(parallel_max_elem_id);
    2149             : 
    2150             :   // Check processor ids for consistency between processors
    2151             : 
    2152     2884390 :   for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
    2153             :     {
    2154     2860374 :       const Elem * elem = mesh.query_elem_ptr(i);
    2155             : 
    2156             :       processor_id_type min_id =
    2157     2793324 :         elem ? elem->processor_id() :
    2158     2860374 :         std::numeric_limits<processor_id_type>::max();
    2159     2860374 :       mesh.comm().min(min_id);
    2160             : 
    2161             :       processor_id_type max_id =
    2162     2793324 :         elem ? elem->processor_id() :
    2163     2860374 :         std::numeric_limits<processor_id_type>::min();
    2164     2860374 :       mesh.comm().max(max_id);
    2165             : 
    2166     2860374 :       if (elem)
    2167             :         {
    2168     2793324 :           libmesh_assert_equal_to (min_id, elem->processor_id());
    2169     2793324 :           libmesh_assert_equal_to (max_id, elem->processor_id());
    2170             :         }
    2171             : 
    2172     2860374 :       if (min_id == mesh.processor_id())
    2173     1357933 :         libmesh_assert(elem);
    2174             :     }
    2175             : }
    2176             : 
    2177             : 
    2178             : 
    2179          76 : void libmesh_assert_parallel_consistent_new_node_procids(const MeshBase & mesh)
    2180             : {
    2181          76 :   LOG_SCOPE("libmesh_assert_parallel_consistent_new_node_procids()", "MeshTools");
    2182             : 
    2183          76 :   if (mesh.n_processors() == 1)
    2184           0 :     return;
    2185             : 
    2186          76 :   libmesh_parallel_only(mesh.comm());
    2187             : 
    2188             :   // We want this test to hit every node when called even after nodes
    2189             :   // have been added asynchronously but before everything has been
    2190             :   // renumbered.
    2191          76 :   dof_id_type parallel_max_elem_id = mesh.max_elem_id();
    2192          76 :   mesh.comm().max(parallel_max_elem_id);
    2193             : 
    2194         152 :   std::vector<bool> elem_touched_by_anyone(parallel_max_elem_id, false);
    2195             : 
    2196       42408 :   for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
    2197             :     {
    2198       42332 :       const Elem * elem = mesh.query_elem_ptr(i);
    2199             : 
    2200       42332 :       const unsigned int my_n_nodes = elem ? elem->n_nodes() : 0;
    2201       42332 :       unsigned int n_nodes = my_n_nodes;
    2202       42332 :       mesh.comm().max(n_nodes);
    2203             : 
    2204       42332 :       if (n_nodes)
    2205       15840 :         libmesh_assert(mesh.comm().semiverify(elem ? &my_n_nodes : nullptr));
    2206             : 
    2207      172564 :       for (unsigned int n=0; n != n_nodes; ++n)
    2208             :         {
    2209      130232 :           const Node * node = elem ? elem->node_ptr(n) : nullptr;
    2210      130232 :           const processor_id_type pid = node ? node->processor_id() : 0;
    2211      130232 :           libmesh_assert(mesh.comm().semiverify (node ? &pid : nullptr));
    2212             :         }
    2213             :     }
    2214             : }
    2215             : 
    2216             : template <>
    2217       37933 : void libmesh_assert_parallel_consistent_procids<Node>(const MeshBase & mesh)
    2218             : {
    2219       37933 :   LOG_SCOPE("libmesh_assert_parallel_consistent_procids()", "MeshTools");
    2220             : 
    2221       37933 :   if (mesh.n_processors() == 1)
    2222           1 :     return;
    2223             : 
    2224       37932 :   libmesh_parallel_only(mesh.comm());
    2225             : 
    2226             :   // We want this test to be valid even when called even after nodes
    2227             :   // have been added asynchronously but before they're renumbered
    2228             :   //
    2229             :   // Plus, some code (looking at you, stitch_meshes) modifies
    2230             :   // DofObject ids without keeping max_elem_id()/max_node_id()
    2231             :   // consistent, but that's done in a safe way for performance
    2232             :   // reasons, so we'll play along and just figure out new max ids
    2233             :   // ourselves.
    2234       37932 :   dof_id_type parallel_max_node_id = 0;
    2235     8287727 :   for (const auto & node : mesh.node_ptr_range())
    2236     8249795 :     parallel_max_node_id = std::max<dof_id_type>(parallel_max_node_id,
    2237     8249795 :                                                  node->id()+1);
    2238       37932 :   mesh.comm().max(parallel_max_node_id);
    2239             : 
    2240       75864 :   std::vector<bool> node_touched_by_anyone(parallel_max_node_id, false);
    2241             : 
    2242     2430581 :   for (const auto & elem : as_range(mesh.local_elements_begin(),
    2243     4861162 :                                     mesh.local_elements_end()))
    2244             :     {
    2245     2392649 :       libmesh_assert (elem);
    2246             : 
    2247    18172622 :       for (auto & node : elem->node_ref_range())
    2248             :         {
    2249    15779973 :           dof_id_type nodeid = node.id();
    2250    15779973 :           node_touched_by_anyone[nodeid] = true;
    2251             :         }
    2252             :     }
    2253       37932 :   mesh.comm().max(node_touched_by_anyone);
    2254             : 
    2255             :   // Check processor ids for consistency between processors
    2256             :   // on any node an element touches
    2257     8706252 :   for (dof_id_type i=0; i != parallel_max_node_id; ++i)
    2258             :     {
    2259     8668320 :       if (!node_touched_by_anyone[i])
    2260      420946 :         continue;
    2261             : 
    2262     8247374 :       const Node * node = mesh.query_node_ptr(i);
    2263     8247374 :       const processor_id_type pid = node ? node->processor_id() : 0;
    2264             : 
    2265     8247374 :       libmesh_assert(mesh.comm().semiverify (node ? &pid : nullptr));
    2266             :     }
    2267             : }
    2268             : 
    2269             : 
    2270             : 
    2271             : #ifdef LIBMESH_ENABLE_AMR
    2272           0 : void libmesh_assert_valid_refinement_flags(const MeshBase & mesh)
    2273             : {
    2274           0 :   LOG_SCOPE("libmesh_assert_valid_refinement_flags()", "MeshTools");
    2275             : 
    2276           0 :   libmesh_parallel_only(mesh.comm());
    2277           0 :   if (mesh.n_processors() == 1)
    2278           0 :     return;
    2279             : 
    2280           0 :   dof_id_type pmax_elem_id = mesh.max_elem_id();
    2281           0 :   mesh.comm().max(pmax_elem_id);
    2282             : 
    2283           0 :   std::vector<unsigned char> my_elem_h_state(pmax_elem_id, 255);
    2284           0 :   std::vector<unsigned char> my_elem_p_state(pmax_elem_id, 255);
    2285             : 
    2286           0 :   for (const auto & elem : mesh.element_ptr_range())
    2287             :     {
    2288           0 :       libmesh_assert (elem);
    2289           0 :       dof_id_type elemid = elem->id();
    2290             : 
    2291           0 :       my_elem_h_state[elemid] =
    2292           0 :         static_cast<unsigned char>(elem->refinement_flag());
    2293             : 
    2294           0 :       my_elem_p_state[elemid] =
    2295           0 :         static_cast<unsigned char>(elem->p_refinement_flag());
    2296             :     }
    2297           0 :   std::vector<unsigned char> min_elem_h_state(my_elem_h_state);
    2298           0 :   mesh.comm().min(min_elem_h_state);
    2299             : 
    2300           0 :   std::vector<unsigned char> min_elem_p_state(my_elem_p_state);
    2301           0 :   mesh.comm().min(min_elem_p_state);
    2302             : 
    2303           0 :   for (dof_id_type i=0; i!= pmax_elem_id; ++i)
    2304             :     {
    2305           0 :       libmesh_assert(my_elem_h_state[i] == 255 ||
    2306             :                      my_elem_h_state[i] == min_elem_h_state[i]);
    2307           0 :       libmesh_assert(my_elem_p_state[i] == 255 ||
    2308             :                      my_elem_p_state[i] == min_elem_p_state[i]);
    2309             :     }
    2310             : }
    2311             : #else
    2312             : void libmesh_assert_valid_refinement_flags(const MeshBase &)
    2313             : {
    2314             : }
    2315             : #endif // LIBMESH_ENABLE_AMR
    2316             : 
    2317             : 
    2318             : 
    2319       21120 : void libmesh_assert_valid_neighbors(const MeshBase & mesh,
    2320             :                                     bool assert_valid_remote_elems)
    2321             : {
    2322       21120 :   LOG_SCOPE("libmesh_assert_valid_neighbors()", "MeshTools");
    2323             : 
    2324     2893986 :   for (const auto & elem : mesh.element_ptr_range())
    2325             :     {
    2326     2872866 :       libmesh_assert (elem);
    2327     2872866 :       elem->libmesh_assert_valid_neighbors();
    2328             :     }
    2329             : 
    2330       21120 :   if (mesh.n_processors() == 1)
    2331         812 :     return;
    2332             : 
    2333       20308 :   libmesh_parallel_only(mesh.comm());
    2334             : 
    2335       20308 :   dof_id_type pmax_elem_id = mesh.max_elem_id();
    2336       20308 :   mesh.comm().max(pmax_elem_id);
    2337             : 
    2338     2989962 :   for (dof_id_type i=0; i != pmax_elem_id; ++i)
    2339             :     {
    2340     2969654 :       const Elem * elem = mesh.query_elem_ptr(i);
    2341             : 
    2342     2969654 :       const unsigned int my_n_neigh = elem ? elem->n_neighbors() : 0;
    2343     2969654 :       unsigned int n_neigh = my_n_neigh;
    2344     2969654 :       mesh.comm().max(n_neigh);
    2345     2969654 :       if (elem)
    2346     2867762 :         libmesh_assert_equal_to (my_n_neigh, n_neigh);
    2347             : 
    2348    14320524 :       for (unsigned int n = 0; n != n_neigh; ++n)
    2349             :         {
    2350    11350870 :           dof_id_type my_neighbor = DofObject::invalid_id;
    2351    11350870 :           dof_id_type * p_my_neighbor = nullptr;
    2352             : 
    2353             :           // If we have a non-remote_elem neighbor link, then we can
    2354             :           // verify it.
    2355    11350870 :           if (elem && elem->neighbor_ptr(n) != remote_elem)
    2356             :             {
    2357    11258076 :               p_my_neighbor = &my_neighbor;
    2358    11258076 :               if (elem->neighbor_ptr(n))
    2359    10621260 :                 my_neighbor = elem->neighbor_ptr(n)->id();
    2360             : 
    2361             :               // But wait - if we haven't set remote_elem links yet then
    2362             :               // some nullptr links on ghost elements might be
    2363             :               // future-remote_elem links, so we can't verify those.
    2364    22523096 :               if (!assert_valid_remote_elems &&
    2365    11259726 :                   !elem->neighbor_ptr(n) &&
    2366        1650 :                   elem->processor_id() != mesh.processor_id())
    2367         773 :                 p_my_neighbor = nullptr;
    2368             :             }
    2369    11350870 :           libmesh_assert(mesh.comm().semiverify(p_my_neighbor));
    2370             :         }
    2371             :     }
    2372             : }
    2373             : #endif // DEBUG
    2374             : 
    2375             : 
    2376             : 
    2377             : // Functors for correct_node_proc_ids
    2378             : namespace {
    2379             : 
    2380             : typedef std::unordered_map<dof_id_type, processor_id_type> proc_id_map_type;
    2381             : 
    2382             : struct SyncNodeSet
    2383             : {
    2384             :   typedef unsigned char datum; // bool but without bit twiddling issues
    2385             : 
    2386         182 :   SyncNodeSet(std::unordered_set<const Node *> & _set,
    2387        9199 :               MeshBase & _mesh) :
    2388        9199 :     node_set(_set), mesh(_mesh) {}
    2389             : 
    2390             :   std::unordered_set<const Node *> & node_set;
    2391             : 
    2392             :   MeshBase & mesh;
    2393             : 
    2394             :   // ------------------------------------------------------------
    2395       41122 :   void gather_data (const std::vector<dof_id_type> & ids,
    2396             :                     std::vector<datum> & data)
    2397             :   {
    2398             :     // Find whether each requested node belongs in the set
    2399       41122 :     data.resize(ids.size());
    2400             : 
    2401     1867830 :     for (auto i : index_range(ids))
    2402             :       {
    2403     1826708 :         const dof_id_type id = ids[i];
    2404             : 
    2405             :         // We'd better have every node we're asked for
    2406     1826708 :         Node * node = mesh.node_ptr(id);
    2407             : 
    2408             :         // Return if the node is in the set.
    2409     2954958 :         data[i] = node_set.count(node);
    2410             :       }
    2411       41122 :   }
    2412             : 
    2413             :   // ------------------------------------------------------------
    2414       41122 :   bool act_on_data (const std::vector<dof_id_type> & ids,
    2415             :                     const std::vector<datum> in_set)
    2416             :   {
    2417         167 :     bool data_changed = false;
    2418             : 
    2419             :     // Add nodes we've been informed of to our own set
    2420     1867830 :     for (auto i : index_range(ids))
    2421             :       {
    2422     1826708 :         if (in_set[i])
    2423             :           {
    2424     1196849 :             Node * node = mesh.node_ptr(ids[i]);
    2425     1196849 :             if (!node_set.count(node))
    2426             :               {
    2427         256 :                 node_set.insert(node);
    2428         256 :                 data_changed = true;
    2429             :               }
    2430             :           }
    2431             :       }
    2432             : 
    2433       41122 :     return data_changed;
    2434             :   }
    2435             : };
    2436             : 
    2437             : 
    2438        8835 : struct NodesNotInSet
    2439             : {
    2440         182 :   NodesNotInSet(const std::unordered_set<const Node *> _set)
    2441        9017 :     : node_set(_set) {}
    2442             : 
    2443      479564 :   bool operator() (const Node * node) const
    2444             :   {
    2445      959128 :     if (node_set.count(node))
    2446      287060 :       return false;
    2447      192504 :     return true;
    2448             :   }
    2449             : 
    2450             :   const std::unordered_set<const Node *> node_set;
    2451             : };
    2452             : 
    2453             : 
    2454             : struct SyncProcIdsFromMap
    2455             : {
    2456             :   typedef processor_id_type datum;
    2457             : 
    2458         218 :   SyncProcIdsFromMap(const proc_id_map_type & _map,
    2459       38187 :                      MeshBase & _mesh) :
    2460       38187 :     new_proc_ids(_map), mesh(_mesh) {}
    2461             : 
    2462             :   const proc_id_map_type & new_proc_ids;
    2463             : 
    2464             :   MeshBase & mesh;
    2465             : 
    2466             :   // ------------------------------------------------------------
    2467      132325 :   void gather_data (const std::vector<dof_id_type> & ids,
    2468             :                     std::vector<datum> & data)
    2469             :   {
    2470             :     // Find the new processor id of each requested node
    2471      132325 :     data.resize(ids.size());
    2472             : 
    2473     7264017 :     for (auto i : index_range(ids))
    2474             :       {
    2475     7131692 :         const dof_id_type id = ids[i];
    2476             : 
    2477             :         // Return the node's new processor id if it has one, or its
    2478             :         // old processor id if not.
    2479     7131692 :         if (const auto it = new_proc_ids.find(id);
    2480       47012 :             it != new_proc_ids.end())
    2481     6500176 :           data[i] = it->second;
    2482             :         else
    2483             :           {
    2484             :             // We'd better find every node we're asked for
    2485      631516 :             const Node & node = mesh.node_ref(id);
    2486      631516 :             data[i] = node.processor_id();
    2487             :           }
    2488             :       }
    2489      132325 :   }
    2490             : 
    2491             :   // ------------------------------------------------------------
    2492      132325 :   void act_on_data (const std::vector<dof_id_type> & ids,
    2493             :                     const std::vector<datum> proc_ids)
    2494             :   {
    2495             :     // Set the node processor ids we've now been informed of
    2496     7264017 :     for (auto i : index_range(ids))
    2497             :       {
    2498     7131692 :         Node & node = mesh.node_ref(ids[i]);
    2499     7131692 :         node.processor_id() = proc_ids[i];
    2500             :       }
    2501      132325 :   }
    2502             : };
    2503             : }
    2504             : 
    2505             : 
    2506             : 
    2507       38187 : void correct_node_proc_ids (MeshBase & mesh)
    2508             : {
    2509         654 :   LOG_SCOPE("correct_node_proc_ids()","MeshTools");
    2510             : 
    2511             :   // This function must be run on all processors at once
    2512         218 :   libmesh_parallel_only(mesh.comm());
    2513             : 
    2514             :   // We require all processors to agree on nodal processor ids before
    2515             :   // going into this algorithm.
    2516             : #ifdef DEBUG
    2517         218 :   libmesh_assert_parallel_consistent_procids<Node>(mesh);
    2518             : #endif
    2519             : 
    2520             :   // If we have any unpartitioned elements at this
    2521             :   // stage there is a problem
    2522         218 :   libmesh_assert (n_elem(mesh.unpartitioned_elements_begin(),
    2523             :                          mesh.unpartitioned_elements_end()) == 0);
    2524             : 
    2525             :   // Fix nodes' processor ids.  Coarsening may have left us with nodes
    2526             :   // which are no longer touched by any elements of the same processor
    2527             :   // id, and for DofMap to work we need to fix that.
    2528             : 
    2529             :   // This is harder now that libMesh no longer requires a distributed
    2530             :   // mesh to ghost all nodal neighbors: it is possible for two active
    2531             :   // elements on two different processors to share the same node in
    2532             :   // such a way that neither processor knows the others' element
    2533             :   // exists!
    2534             : 
    2535             :   // While we're at it, if this mesh is configured to allow
    2536             :   // repartitioning, we'll repartition *all* the nodes' processor ids
    2537             :   // using the canonical Node heuristic, to try and improve DoF load
    2538             :   // balancing.  But if the mesh is disallowing repartitioning, we
    2539             :   // won't touch processor_id on any node where it's valid, regardless
    2540             :   // of whether or not it's canonical.
    2541         218 :   bool repartition_all_nodes = !mesh.skip_noncritical_partitioning();
    2542         436 :   std::unordered_set<const Node *> valid_nodes;
    2543             : 
    2544             :   // If we aren't allowed to repartition, then we're going to leave
    2545             :   // every node we can at its current processor_id, and *only*
    2546             :   // repartition the nodes whose current processor id is incompatible
    2547             :   // with DoFMap (because it doesn't touch an active element, e.g. due
    2548             :   // to coarsening)
    2549       38187 :   if (!repartition_all_nodes)
    2550             :     {
    2551     1158938 :       for (const auto & elem : mesh.active_element_ptr_range())
    2552     5946053 :         for (const auto & node : elem->node_ref_range())
    2553     5305120 :           if (elem->processor_id() == node.processor_id())
    2554     4909802 :             valid_nodes.insert(&node);
    2555             : 
    2556         182 :       SyncNodeSet syncv(valid_nodes, mesh);
    2557             : 
    2558             :       Parallel::sync_dofobject_data_by_id
    2559       18216 :         (mesh.comm(), mesh.nodes_begin(), mesh.nodes_end(), syncv);
    2560             :     }
    2561             : 
    2562             :   // We build up a set of compatible processor ids for each node
    2563         436 :   proc_id_map_type new_proc_ids;
    2564             : 
    2565     8999106 :   for (auto & elem : mesh.active_element_ptr_range())
    2566             :     {
    2567     4500525 :       processor_id_type pid = elem->processor_id();
    2568             : 
    2569    44099773 :       for (auto & node : elem->node_ref_range())
    2570             :         {
    2571    39599248 :           const dof_id_type id = node.id();
    2572    39599248 :           if (auto it = new_proc_ids.find(id);
    2573      350660 :               it == new_proc_ids.end())
    2574      155764 :             new_proc_ids.emplace(id, pid);
    2575             :           else
    2576    28311685 :             it->second = node.choose_processor_id(it->second, pid);
    2577             :         }
    2578       37751 :     }
    2579             : 
    2580             :   // Sort the new pids to push to each processor
    2581             :   std::map<processor_id_type, std::vector<std::pair<dof_id_type, processor_id_type>>>
    2582         436 :     ids_to_push;
    2583             : 
    2584    25751072 :   for (const auto & node : mesh.node_ptr_range())
    2585    13089710 :     if (const auto it = std::as_const(new_proc_ids).find(node->id());
    2586    13089710 :         it != new_proc_ids.end() && node->processor_id() != DofObject::invalid_processor_id)
    2587    11325314 :       ids_to_push[node->processor_id()].emplace_back(node->id(), /*pid=*/it->second);
    2588             : 
    2589             :   auto action_functor =
    2590      172786 :     [& mesh, & new_proc_ids]
    2591             :     (processor_id_type,
    2592    11755265 :      const std::vector<std::pair<dof_id_type, processor_id_type>> & data)
    2593             :     {
    2594    11461169 :       for (const auto & [id, pid] : data)
    2595             :         {
    2596    11287563 :           if (const auto it = new_proc_ids.find(id);
    2597      155764 :               it == new_proc_ids.end())
    2598           0 :             new_proc_ids.emplace(id, pid);
    2599             :           else
    2600             :             {
    2601    11287563 :               const Node & node = mesh.node_ref(id);
    2602    11287563 :               it->second = node.choose_processor_id(it->second, pid);
    2603             :             }
    2604             :         }
    2605       38789 :     };
    2606             : 
    2607             :   Parallel::push_parallel_vector_data
    2608       38187 :     (mesh.comm(), ids_to_push, action_functor);
    2609             : 
    2610             :   // Now new_proc_ids is correct for every node we used to own.  Let's
    2611             :   // ask every other processor about the nodes they used to own.  But
    2612             :   // first we'll need to keep track of which nodes we used to own,
    2613             :   // lest we get them confused with nodes we newly own.
    2614         436 :   std::unordered_set<Node *> ex_local_nodes;
    2615     9093990 :   for (auto & node : mesh.local_node_ptr_range())
    2616     4645558 :     if (const auto it = new_proc_ids.find(node->id());
    2617     4645558 :         it != new_proc_ids.end() && it->second != mesh.processor_id())
    2618       37800 :       ex_local_nodes.insert(node);
    2619             : 
    2620         218 :   SyncProcIdsFromMap sync(new_proc_ids, mesh);
    2621       38187 :   if (repartition_all_nodes)
    2622             :     Parallel::sync_dofobject_data_by_id
    2623       57940 :       (mesh.comm(), mesh.nodes_begin(), mesh.nodes_end(), sync);
    2624             :   else
    2625             :     {
    2626         182 :       NodesNotInSet nnis(valid_nodes);
    2627             : 
    2628             :       Parallel::sync_dofobject_data_by_id
    2629       18216 :         (mesh.comm(), mesh.nodes_begin(), mesh.nodes_end(), nnis, sync);
    2630             :     }
    2631             : 
    2632             :   // And finally let's update the nodes we used to own.
    2633       42818 :   for (const auto & node : ex_local_nodes)
    2634             :     {
    2635        2103 :       if (valid_nodes.count(node))
    2636        2083 :         continue;
    2637             : 
    2638        2548 :       const dof_id_type id = node->id();
    2639          10 :       const proc_id_map_type::iterator it = new_proc_ids.find(id);
    2640          10 :       libmesh_assert(it != new_proc_ids.end());
    2641        2548 :       node->processor_id() = it->second;
    2642             :     }
    2643             : 
    2644             :   // We should still have consistent nodal processor ids coming out of
    2645             :   // this algorithm, but if we're allowed to repartition the mesh then
    2646             :   // they should be canonically correct too.
    2647             : #ifdef DEBUG
    2648         218 :   libmesh_assert_valid_procids<Node>(mesh);
    2649             :   //if (repartition_all_nodes)
    2650             :   //  libmesh_assert_canonical_node_procids(mesh);
    2651             : #endif
    2652       38187 : }
    2653             : 
    2654             : 
    2655             : 
    2656       19630 : void Private::globally_renumber_nodes_and_elements (MeshBase & mesh)
    2657             : {
    2658       19630 :   MeshCommunication().assign_global_indices(mesh);
    2659       19630 : }
    2660             : 
    2661             : } // namespace MeshTools
    2662             : 
    2663             : } // namespace libMesh

Generated by: LCOV version 1.14