|           Line data    Source code 
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
       3             : 
       4             : // This library is free software; you can redistribute it and/or
       5             : // modify it under the terms of the GNU Lesser General Public
       6             : // License as published by the Free Software Foundation; either
       7             : // version 2.1 of the License, or (at your option) any later version.
       8             : 
       9             : // This library is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             : // Lesser General Public License for more details.
      13             : 
      14             : // You should have received a copy of the GNU Lesser General Public
      15             : // License along with this library; if not, write to the Free Software
      16             : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      17             : 
      18             : 
      19             : 
      20             : // Local includes
      21             : #include "libmesh/libmesh_config.h"
      22             : #include "libmesh/libmesh_logging.h"
      23             : #include "libmesh/boundary_info.h"
      24             : #include "libmesh/distributed_mesh.h"
      25             : #include "libmesh/elem.h"
      26             : #include "libmesh/mesh_communication.h"
      27             : #include "libmesh/mesh_serializer.h"
      28             : #include "libmesh/parallel.h"
      29             : #include "libmesh/partitioner.h"
      30             : #include "libmesh/remote_elem.h"
      31             : #include "libmesh/unstructured_mesh.h"
      32             : #include "libmesh/elem_side_builder.h"
      33             : 
      34             : // TIMPI includes
      35             : #include "timpi/parallel_sync.h"
      36             : 
      37             : // C++ includes
      38             : #include <iterator>  // std::distance
      39             : #include <algorithm> // std::max_element
      40             : 
      41             : namespace
      42             : {
      43             : 
      44             : // Templated helper function for removing a subset of keys from a
      45             : // multimap that further satisfy a given predicate on the
      46             : // corresponding values.
      47             : template <class Key, class T, class Pred>
      48     1224633 : void erase_if(std::multimap<Key,T> & map, Key k, Pred pred)
      49             : {
      50       60032 :   auto rng = map.equal_range(k);
      51       60032 :   auto it = rng.first;
      52     1494536 :   while (it != rng.second)
      53             :     {
      54      252067 :       if (pred(it->second))
      55       65051 :         it = map.erase(it);
      56             :       else
      57        8807 :         ++it;
      58             :     }
      59     1224633 : }
      60             : 
      61             : // Similar to the helper function above but doesn't take a key,
      62             : // instead it applies the predicate to every value in the map.
      63             : template <class Key, class T, class Pred>
      64        2840 : void erase_if(std::multimap<Key,T> & map, Pred pred)
      65             : {
      66          80 :   auto it = map.begin();
      67        8335 :   while (it != map.end())
      68             :     {
      69        5495 :       if (pred(it->second))
      70        1947 :         it = map.erase(it);
      71             :       else
      72         196 :         ++it;
      73             :     }
      74        2840 : }
      75             : 
      76             : // Helper func for renumber_id
      77             : template <typename Map, typename T>
      78        6358 : void renumber_name(Map & m, T old_id, T new_id)
      79             : {
      80        6358 :   if (const auto it = std::as_const(m).find(old_id);
      81         180 :       it != m.end())
      82             :     {
      83        4370 :       m[new_id] = it->second;
      84        4246 :       m.erase(it);
      85             :     }
      86        6358 : }
      87             : 
      88             : }
      89             : 
      90             : namespace libMesh
      91             : {
      92             : 
      93             : 
      94             : 
      95             : //------------------------------------------------------
      96             : // BoundaryInfo static member initializations
      97             : const boundary_id_type BoundaryInfo::invalid_id = -123;
      98             : 
      99             : 
     100             : 
     101             : //------------------------------------------------------
     102             : // BoundaryInfo functions
     103      329832 : BoundaryInfo::BoundaryInfo(MeshBase & m) :
     104             :   ParallelObject(m.comm()),
     105      310416 :   _mesh (&m),
     106      349232 :   _children_on_boundary(false)
     107             : {
     108      329832 : }
     109             : 
     110       23568 : BoundaryInfo & BoundaryInfo::operator=(const BoundaryInfo & other_boundary_info)
     111             : {
     112             :   // Overwrite any preexisting boundary info
     113       23568 :   this->clear();
     114             : 
     115             :   /**
     116             :    * We're going to attempt to pull _new_ pointers out of the mesh
     117             :    * assigned to this boundary info.
     118             :    *
     119             :    * This will only work if the mesh assigned to this BoundaryInfo is
     120             :    * the same mesh object as other_boundary_info _or_ was constructed
     121             :    * in exactly the same way (or constructed as a copy, or a refined
     122             :    * copy without renumbering, etc.).
     123             :    */
     124             : 
     125             :   // Copy node boundary info
     126      529926 :   for (const auto & [node, bid] : other_boundary_info._boundary_node_id)
     127      506358 :     _boundary_node_id.emplace(_mesh->node_ptr(node->id()), bid);
     128             : 
     129             :   // Copy edge boundary info
     130       23682 :   for (const auto & [elem, id_pair] : other_boundary_info._boundary_edge_id)
     131         114 :     _boundary_edge_id.emplace(_mesh->elem_ptr(elem->id()), id_pair);
     132             : 
     133             :   // Copy shellface boundary info
     134       23612 :   for (const auto & [elem, id_pair] : other_boundary_info._boundary_shellface_id)
     135          44 :     _boundary_shellface_id.emplace(_mesh->elem_ptr(elem->id()), id_pair);
     136             : 
     137             :   // Copy side boundary info
     138      586154 :   for (const auto & [elem, id_pair] : other_boundary_info._boundary_side_id)
     139      562586 :     _boundary_side_id.emplace(_mesh->elem_ptr(elem->id()), id_pair);
     140             : 
     141         780 :   _boundary_ids = other_boundary_info._boundary_ids;
     142         780 :   _global_boundary_ids = other_boundary_info._global_boundary_ids;
     143         780 :   _side_boundary_ids = other_boundary_info._side_boundary_ids;
     144         780 :   _node_boundary_ids = other_boundary_info._node_boundary_ids;
     145         780 :   _edge_boundary_ids = other_boundary_info._edge_boundary_ids;
     146         780 :   _shellface_boundary_ids = other_boundary_info._shellface_boundary_ids;
     147             : 
     148         780 :   _ss_id_to_name = other_boundary_info._ss_id_to_name;
     149         780 :   _ns_id_to_name = other_boundary_info._ns_id_to_name;
     150         780 :   _es_id_to_name = other_boundary_info._es_id_to_name;
     151             : 
     152       23568 :   return *this;
     153             : }
     154             : 
     155             : 
     156        6534 : bool BoundaryInfo::operator==(const BoundaryInfo & other_boundary_info) const
     157             : {
     158      293410 :   for (const auto & [other_node, bid] : other_boundary_info._boundary_node_id)
     159             :     {
     160      286876 :       const Node * node = this->_mesh->query_node_ptr(other_node->id());
     161      286876 :       if (!node)
     162           0 :         return false;
     163      286876 :       if (!this->has_boundary_id(node, bid))
     164           0 :         return false;
     165             :     }
     166      293144 :   for (const auto & [node, bid] : this->_boundary_node_id)
     167             :     {
     168             :       const Node * other_node =
     169      286645 :         other_boundary_info._mesh->query_node_ptr(node->id());
     170      286645 :       if (!other_node)
     171           2 :         return false;
     172      286645 :       if (!other_boundary_info.has_boundary_id(other_node, bid))
     173           2 :         return false;
     174             :     }
     175             : 
     176         188 :   auto compare_edges = [&](const Elem * elem,
     177             :                            const Elem * other_elem,
     178          60 :                            unsigned short int edge)
     179             :   {
     180         248 :     if (!elem)
     181           0 :       return false;
     182         248 :     if (!other_elem)
     183           0 :       return false;
     184             : 
     185          80 :     std::vector<boundary_id_type> our_edges, other_edges;
     186         248 :     this->edge_boundary_ids(elem, edge, our_edges);
     187         248 :     other_boundary_info.edge_boundary_ids(other_elem, edge, other_edges);
     188         288 :     if (our_edges.size() != other_edges.size())
     189           0 :       return false;
     190             : 
     191         248 :     std::sort(our_edges.begin(), our_edges.end());
     192         248 :     std::sort(other_edges.begin(), other_edges.end());
     193         496 :     for (auto i : index_range(our_edges))
     194         268 :       if (our_edges[i] != other_edges[i])
     195           0 :         return false;
     196          60 :     return true;
     197        6499 :   };
     198             : 
     199        6623 :   for (const auto & [other_elem, edge_id_pair] : other_boundary_info._boundary_edge_id)
     200             :     {
     201         124 :       const Elem * elem = this->_mesh->query_elem_ptr(other_elem->id());
     202         124 :       if (!compare_edges(elem, other_elem, edge_id_pair.first))
     203           0 :         return false;
     204             :     }
     205             : 
     206        6623 :   for (const auto & [elem, edge_id_pair] : this->_boundary_edge_id)
     207             :     {
     208         124 :       const Elem * other_elem = other_boundary_info._mesh->query_elem_ptr(elem->id());
     209         124 :       if (!compare_edges(elem, other_elem, edge_id_pair.first))
     210           0 :         return false;
     211             :     }
     212             : 
     213      204284 :   auto compare_sides = [&](const Elem * elem,
     214             :                            const Elem * other_elem,
     215       44784 :                            unsigned short int side)
     216             :   {
     217      249068 :     if (!elem)
     218           0 :       return false;
     219      249068 :     if (!other_elem)
     220           0 :       return false;
     221             : 
     222       76288 :     std::vector<boundary_id_type> our_sides, other_sides;
     223      249068 :     this->boundary_ids(elem, side, our_sides);
     224      249068 :     other_boundary_info.boundary_ids(other_elem, side, other_sides);
     225      262348 :     if (our_sides.size() != other_sides.size())
     226           0 :       return false;
     227             : 
     228      249068 :     std::sort(our_sides.begin(), our_sides.end());
     229      249068 :     std::sort(other_sides.begin(), other_sides.end());
     230      498136 :     for (auto i : index_range(our_sides))
     231      255708 :       if (our_sides[i] != other_sides[i])
     232           0 :         return false;
     233       44784 :     return true;
     234        6499 :   };
     235             : 
     236      131033 :   for (const auto & [other_elem, side_id_pair] : other_boundary_info._boundary_side_id)
     237             :     {
     238      124534 :       const Elem * elem = this->_mesh->query_elem_ptr(other_elem->id());
     239      124534 :       if (!compare_sides(elem, other_elem, side_id_pair.first))
     240           0 :         return false;
     241             :     }
     242             : 
     243      131033 :   for (const auto & [elem, side_id_pair] : this->_boundary_side_id)
     244             :     {
     245      124534 :       const Elem * other_elem = other_boundary_info._mesh->query_elem_ptr(elem->id());
     246      124534 :       if (!compare_sides(elem, other_elem, side_id_pair.first))
     247           0 :         return false;
     248             :     }
     249             : 
     250          72 :   auto compare_shellfaces = [&](const Elem * elem,
     251             :                                 const Elem * other_elem,
     252          24 :                                 unsigned short int shellface)
     253             :   {
     254          96 :     if (!elem)
     255           0 :       return false;
     256          96 :     if (!other_elem)
     257           0 :       return false;
     258             : 
     259          32 :     std::vector<boundary_id_type> our_shellfaces, other_shellfaces;
     260          96 :     this->shellface_boundary_ids(elem, shellface, our_shellfaces);
     261          96 :     other_boundary_info.shellface_boundary_ids(other_elem, shellface, other_shellfaces);
     262         112 :     if (our_shellfaces.size() != other_shellfaces.size())
     263           0 :       return false;
     264             : 
     265          96 :     std::sort(our_shellfaces.begin(), our_shellfaces.end());
     266          96 :     std::sort(other_shellfaces.begin(), other_shellfaces.end());
     267         192 :     for (auto i : index_range(our_shellfaces))
     268         104 :       if (our_shellfaces[i] != other_shellfaces[i])
     269           0 :         return false;
     270          24 :     return true;
     271        6499 :   };
     272             : 
     273        6547 :   for (const auto & [other_elem, shellface_id_pair] : other_boundary_info._boundary_shellface_id)
     274             :     {
     275          48 :       const Elem * elem = this->_mesh->query_elem_ptr(other_elem->id());
     276          48 :       if (!compare_shellfaces(elem, other_elem, shellface_id_pair.first))
     277           0 :         return false;
     278             :     }
     279             : 
     280        6547 :   for (const auto & [elem, shellface_id_pair] : this->_boundary_shellface_id)
     281             :     {
     282          48 :       const Elem * other_elem = other_boundary_info._mesh->query_elem_ptr(elem->id());
     283          48 :       if (!compare_shellfaces(elem, other_elem, shellface_id_pair.first))
     284           0 :         return false;
     285             :     }
     286             : 
     287        6499 :   if (_children_on_boundary != other_boundary_info._children_on_boundary)
     288           0 :     return false;
     289             : 
     290       38994 :   auto compare_sets = [](const auto & set1, const auto & set2)
     291             :   {
     292       38994 :     if (set1.size() != set2.size())
     293           0 :       return false;
     294      138152 :     for (boundary_id_type bid : set1)
     295        7574 :       if (!set2.count(bid))
     296           0 :         return false;
     297             : 
     298       38010 :     return true;
     299             :   };
     300             : 
     301        8083 :   if (!compare_sets(_boundary_ids,
     302       12206 :                     other_boundary_info._boundary_ids) ||
     303        6499 :       !compare_sets(_global_boundary_ids,
     304        6663 :                     other_boundary_info._global_boundary_ids) ||
     305        6499 :       !compare_sets(_edge_boundary_ids,
     306        6663 :                     other_boundary_info._edge_boundary_ids) ||
     307        6499 :       !compare_sets(_node_boundary_ids,
     308        6663 :                     other_boundary_info._node_boundary_ids) ||
     309        6499 :       !compare_sets(_shellface_boundary_ids,
     310       13954 :                     other_boundary_info._shellface_boundary_ids) ||
     311        6499 :       !compare_sets(_side_boundary_ids,
     312        6499 :                     other_boundary_info._side_boundary_ids))
     313           0 :     return false;
     314             : 
     315       19425 :   auto compare_maps = [](const auto & map1, const auto & map2)
     316             :   {
     317       19425 :     if (map1.size() != map2.size())
     318           0 :       return false;
     319       69921 :     for (const auto & pair : map1)
     320       99688 :       if (!map2.count(pair.first) ||
     321       50532 :           map2.at(pair.first) != pair.second)
     322           0 :         return false;
     323             : 
     324        2376 :     return true;
     325             :   };
     326             : 
     327        8083 :   if (!compare_maps(_ss_id_to_name,
     328       12170 :                     other_boundary_info._ss_id_to_name) ||
     329        6463 :       !compare_maps(_ns_id_to_name,
     330       12962 :                     other_boundary_info._ns_id_to_name) ||
     331        6463 :       !compare_maps(_es_id_to_name,
     332        6463 :                     other_boundary_info._es_id_to_name))
     333          36 :     return false;
     334             : 
     335         792 :   return true;
     336             : }
     337             : 
     338             : 
     339      620899 : BoundaryInfo::~BoundaryInfo() = default;
     340             : 
     341             : 
     342             : 
     343      974458 : void BoundaryInfo::clear()
     344             : {
     345       29414 :   _boundary_node_id.clear();
     346       29414 :   _boundary_side_id.clear();
     347       29414 :   _boundary_edge_id.clear();
     348       29414 :   _boundary_shellface_id.clear();
     349       29414 :   _boundary_ids.clear();
     350       29414 :   _side_boundary_ids.clear();
     351       29414 :   _node_boundary_ids.clear();
     352       29414 :   _edge_boundary_ids.clear();
     353       29414 :   _shellface_boundary_ids.clear();
     354       29414 :   _ss_id_to_name.clear();
     355       29414 :   _ns_id_to_name.clear();
     356       29414 :   _es_id_to_name.clear();
     357      974458 : }
     358             : 
     359             : 
     360             : 
     361      826964 : void BoundaryInfo::regenerate_id_sets()
     362             : {
     363       27172 :   const auto old_ss_id_to_name = _ss_id_to_name;
     364       27172 :   const auto old_ns_id_to_name = _ns_id_to_name;
     365       27172 :   const auto old_es_id_to_name = _es_id_to_name;
     366             : 
     367             :   // Clear the old caches
     368       13586 :   _boundary_ids.clear();
     369       13586 :   _side_boundary_ids.clear();
     370       13586 :   _node_boundary_ids.clear();
     371       13586 :   _edge_boundary_ids.clear();
     372       13586 :   _shellface_boundary_ids.clear();
     373       27140 :   _ss_id_to_name.clear();
     374       27140 :   _ns_id_to_name.clear();
     375       27140 :   _es_id_to_name.clear();
     376             : 
     377             :   // Loop over id maps to regenerate each set.
     378    17652891 :   for (const auto & pr : _boundary_node_id)
     379             :     {
     380    16825927 :       const boundary_id_type id = pr.second;
     381    16329505 :       _boundary_ids.insert(id);
     382    16329505 :       _node_boundary_ids.insert(id);
     383    16825927 :       if (const auto it = old_ns_id_to_name.find(id);
     384      496470 :           it != old_ns_id_to_name.end())
     385    14827056 :         _ns_id_to_name.emplace(id, it->second);
     386             :     }
     387             : 
     388      830547 :   for (const auto & pr : _boundary_edge_id)
     389             :     {
     390        3583 :       const boundary_id_type id = pr.second.second;
     391        3477 :       _boundary_ids.insert(id);
     392        3477 :       _edge_boundary_ids.insert(id);
     393        3583 :       if (const auto it = old_es_id_to_name.find(id);
     394         106 :           it != old_es_id_to_name.end())
     395        1456 :         _es_id_to_name.emplace(id, it->second);
     396             :     }
     397             : 
     398     8490826 :   for (const auto & pr : _boundary_side_id)
     399             :     {
     400     7663862 :       const boundary_id_type id = pr.second.second;
     401     7430538 :       _boundary_ids.insert(id);
     402     7430538 :       _side_boundary_ids.insert(id);
     403     7663862 :       if (const auto it = old_ss_id_to_name.find(id);
     404      234104 :           it != old_ss_id_to_name.end())
     405     5828885 :         _ss_id_to_name.emplace(id, it->second);
     406             :     }
     407             : 
     408     1032454 :   for (const auto & pr : _boundary_shellface_id)
     409             :     {
     410      205490 :       const boundary_id_type id = pr.second.second;
     411      192174 :       _boundary_ids.insert(id);
     412      192174 :       _shellface_boundary_ids.insert(id);
     413             :     }
     414             : 
     415             :   // Handle global data
     416       13586 :   libmesh_assert(_mesh);
     417      826964 :   if (!_mesh->is_serial())
     418             :     {
     419      738402 :       _communicator.set_union(_ss_id_to_name);
     420      738402 :       _communicator.set_union(_ns_id_to_name);
     421      738402 :       _communicator.set_union(_es_id_to_name);
     422             :     }
     423             : 
     424      826964 :   this->synchronize_global_id_set();
     425      826964 : }
     426             : 
     427             : 
     428             : 
     429      827035 : void BoundaryInfo::synchronize_global_id_set()
     430             : {
     431             :   // Handle global data
     432       13588 :   _global_boundary_ids = _boundary_ids;
     433       13588 :   libmesh_assert(_mesh);
     434      827035 :   if (!_mesh->is_serial())
     435      738466 :     _communicator.set_union(_global_boundary_ids);
     436      827035 : }
     437             : 
     438             : 
     439             : 
     440           0 : void BoundaryInfo::sync (UnstructuredMesh & boundary_mesh)
     441             : {
     442           0 :   std::set<boundary_id_type> request_boundary_ids(_boundary_ids);
     443           0 :   request_boundary_ids.insert(invalid_id);
     444           0 :   if (!_mesh->is_serial())
     445           0 :     this->comm().set_union(request_boundary_ids);
     446             : 
     447           0 :   this->sync(request_boundary_ids,
     448             :              boundary_mesh);
     449           0 : }
     450             : 
     451             : 
     452             : 
     453        1491 : void BoundaryInfo::sync (const std::set<boundary_id_type> & requested_boundary_ids,
     454             :                          UnstructuredMesh & boundary_mesh)
     455             : {
     456             :   // Call the 3 argument version of this function with a dummy value for the third set.
     457          84 :   std::set<subdomain_id_type> subdomains_relative_to;
     458        1449 :   subdomains_relative_to.insert(Elem::invalid_subdomain_id);
     459             : 
     460        1491 :   this->sync(requested_boundary_ids,
     461             :              boundary_mesh,
     462             :              subdomains_relative_to);
     463        1491 : }
     464             : 
     465             : 
     466             : 
     467        1917 : void BoundaryInfo::sync (const std::set<boundary_id_type> & requested_boundary_ids,
     468             :                          UnstructuredMesh & boundary_mesh,
     469             :                          const std::set<subdomain_id_type> & subdomains_relative_to)
     470             : {
     471         108 :   LOG_SCOPE("sync()", "BoundaryInfo");
     472             : 
     473        1917 :   boundary_mesh.clear();
     474             : 
     475             :   /**
     476             :    * Deleting 0 elements seems weird, but it's better encapsulating
     477             :    * than exposing a set_is_serial(false) capability that might be
     478             :    * easily misused.
     479             :    */
     480        1917 :   if (!_mesh->is_serial())
     481        1728 :     boundary_mesh.delete_remote_elements();
     482             : 
     483             :   /**
     484             :    * If the boundary_mesh is still serial, that means we *can't*
     485             :    * parallelize it, so to make sure we can construct it in full on
     486             :    * every processor we'll serialize the interior mesh.
     487             :    *
     488             :    * We'll use a temporary MeshSerializer here, but as soon as we
     489             :    * unserialize we'll be turning a bunch of interior_parent() links
     490             :    * into dangling pointers, and it won't be easy to tell which.  So
     491             :    * we'll keep around a distributed copy for that case, and query it
     492             :    * to fix up interior_parent() links as necessary.
     493             :    *
     494             :    * We'll also need to make sure to unserialize the mesh *before* we
     495             :    * prepare the boundary mesh for use, or the prepare_for_use() call
     496             :    * on a refined boundary mesh will happily notice that it can find
     497             :    * and restore some refined elements' interior_parent pointers, not
     498             :    * knowing that those interior parents are about to go remote.
     499             :    */
     500        1863 :   std::unique_ptr<MeshBase> mesh_copy;
     501        1917 :   if (boundary_mesh.is_serial() && !_mesh->is_serial())
     502         896 :     mesh_copy = _mesh->clone();
     503             : 
     504             :   auto serializer = std::make_unique<MeshSerializer>
     505        1971 :     (const_cast<MeshBase &>(*_mesh), boundary_mesh.is_serial());
     506             : 
     507             :   /**
     508             :    * Re-create the boundary mesh.
     509             :    */
     510             : 
     511        1917 :   boundary_mesh.set_n_partitions() = _mesh->n_partitions();
     512             : 
     513         108 :   std::map<dof_id_type, dof_id_type> node_id_map;
     514         108 :   std::map<std::pair<dof_id_type, unsigned char>, dof_id_type> side_id_map;
     515             : 
     516        1917 :   this->_find_id_maps(requested_boundary_ids, 0, &node_id_map, 0, &side_id_map, subdomains_relative_to);
     517             : 
     518             :   // Let's add all the boundary nodes we found to the boundary mesh
     519      282602 :   for (const auto & node : _mesh->node_ptr_range())
     520             :     {
     521      146285 :       dof_id_type node_id = node->id();
     522        6874 :       if (node_id_map.count(node_id))
     523             :         {
     524       24369 :           boundary_mesh.add_point(*node, node_id_map[node_id], node->processor_id());
     525             : 
     526             :           // Copy over all the node's boundary IDs to boundary_mesh
     527        2484 :           std::vector<boundary_id_type> node_boundary_ids;
     528       24369 :           this->boundary_ids(node, node_boundary_ids);
     529       42381 :           for (const auto & node_bid : node_boundary_ids)
     530       18012 :             boundary_mesh.get_boundary_info().add_node(node_id_map[node_id], node_bid);
     531             :         }
     532        1809 :     }
     533             : 
     534             :   // Add the elements. When syncing a boundary mesh, we also store the
     535             :   // parent side ids in addition to the interior_parent pointers,
     536             :   // since this information is frequently needed on boundary meshes.
     537        1917 :   this->add_elements(requested_boundary_ids,
     538             :                      boundary_mesh,
     539             :                      subdomains_relative_to,
     540             :                      /*store_parent_side_ids=*/true);
     541             : 
     542             :   // The new elements are currently using the interior mesh's nodes;
     543             :   // we want them to use the boundary mesh's nodes instead.
     544             : 
     545             :   // This side's Node pointers still point to the nodes of the original mesh.
     546             :   // We need to re-point them to the boundary mesh's nodes!  Since we copied *ALL* of
     547             :   // the original mesh's nodes over, we should be guaranteed to have the same ordering.
     548       28360 :   for (auto & new_elem : boundary_mesh.element_ptr_range())
     549             :     {
     550       52494 :       for (auto nn : new_elem->node_index_range())
     551             :         {
     552             :           // Get the correct node pointer, based on the id()
     553             :           Node * new_node =
     554       40934 :             boundary_mesh.node_ptr(node_id_map[new_elem->node_id(nn)]);
     555             : 
     556             :           // sanity check: be sure that the new Node exists and its
     557             :           // global id really matches
     558        2230 :           libmesh_assert (new_node);
     559        2230 :           libmesh_assert_equal_to (new_node->id(),
     560             :                                    node_id_map[new_elem->node_id(nn)]);
     561             : 
     562             :           // Assign the new node pointer
     563       38704 :           new_elem->set_node(nn, new_node);
     564             :         }
     565        1809 :     }
     566             : 
     567             :   // The new elements might have interior parent pointers aimed at
     568             :   // _mesh elements which are about to go remote, and we don't to
     569             :   // leave those pointers dangling.  Fix them up if needed.
     570        1917 :   if (mesh_copy.get())
     571             :     {
     572        7552 :       for (auto & new_elem : boundary_mesh.element_ptr_range())
     573             :         {
     574             :           const dof_id_type interior_parent_id =
     575        3328 :             new_elem->interior_parent()->id();
     576             : 
     577        3328 :           if (!mesh_copy->query_elem_ptr(interior_parent_id))
     578             :             new_elem->set_interior_parent
     579        2318 :               (const_cast<RemoteElem *>(remote_elem));
     580         448 :         }
     581             :     }
     582             : 
     583             :   // Don't repartition this mesh; we want it to stay in sync with the
     584             :   // interior partitioning.
     585        1917 :   boundary_mesh.partitioner().reset(nullptr);
     586             : 
     587             :   // Deserialize the interior mesh before the boundary mesh
     588             :   // prepare_for_use() can come to erroneous conclusions about which
     589             :   // of its elements are semilocal
     590          54 :   serializer.reset();
     591             : 
     592             :   // Make boundary_mesh nodes and elements contiguous
     593        1917 :   boundary_mesh.prepare_for_use();
     594             : 
     595             :   // and finally distribute element partitioning to the nodes
     596        1917 :   Partitioner::set_node_processor_ids(boundary_mesh);
     597        1917 : }
     598             : 
     599             : 
     600           0 : void BoundaryInfo::get_side_and_node_maps (UnstructuredMesh & boundary_mesh,
     601             :                                            std::map<dof_id_type, dof_id_type> & node_id_map,
     602             :                                            std::map<dof_id_type, unsigned char> & side_id_map,
     603             :                                            Real tolerance)
     604             : {
     605           0 :   LOG_SCOPE("get_side_and_node_maps()", "BoundaryInfo");
     606             : 
     607           0 :   node_id_map.clear();
     608           0 :   side_id_map.clear();
     609             : 
     610             :   // For building element sides without extraneous allocation
     611           0 :   ElemSideBuilder side_builder;
     612             :   // Pull objects out of the loop to reduce heap operations
     613             :   const Elem * interior_parent_side;
     614             : 
     615           0 :   for (const auto & boundary_elem : boundary_mesh.active_element_ptr_range())
     616             :     {
     617           0 :       const Elem * interior_parent = boundary_elem->interior_parent();
     618             : 
     619             :       // Find out which side of interior_parent boundary_elem corresponds to.
     620             :       // Use distance between average vertex location as a way to check.
     621           0 :       unsigned char interior_parent_side_index = 0;
     622           0 :       bool found_matching_sides = false;
     623           0 :       for (auto side : interior_parent->side_index_range())
     624             :         {
     625           0 :           interior_parent_side = &side_builder(*interior_parent, side);
     626           0 :           Real va_distance = (boundary_elem->vertex_average() - interior_parent_side->vertex_average()).norm();
     627             : 
     628           0 :           if (va_distance < (tolerance * boundary_elem->hmin()))
     629             :             {
     630           0 :               interior_parent_side_index = cast_int<unsigned char>(side);
     631           0 :               found_matching_sides = true;
     632           0 :               break;
     633             :             }
     634             :         }
     635             : 
     636           0 :       libmesh_error_msg_if(!found_matching_sides, "No matching side found within the specified tolerance");
     637             : 
     638           0 :       side_id_map[boundary_elem->id()] = interior_parent_side_index;
     639             : 
     640           0 :       for (auto local_node_index : boundary_elem->node_index_range())
     641             :         {
     642           0 :           dof_id_type boundary_node_id = boundary_elem->node_id(local_node_index);
     643           0 :           dof_id_type interior_node_id = interior_parent_side->node_id(local_node_index);
     644             : 
     645           0 :           node_id_map[interior_node_id] = boundary_node_id;
     646             :         }
     647           0 :     }
     648           0 : }
     649             : 
     650             : 
     651             : 
     652         568 : void BoundaryInfo::add_elements(const std::set<boundary_id_type> & requested_boundary_ids,
     653             :                                 UnstructuredMesh & boundary_mesh,
     654             :                                 bool store_parent_side_ids)
     655             : {
     656             :   // Call the 3 argument version of this function with a dummy value for the third arg.
     657          32 :   std::set<subdomain_id_type> subdomains_relative_to;
     658         552 :   subdomains_relative_to.insert(Elem::invalid_subdomain_id);
     659             : 
     660         568 :   this->add_elements(requested_boundary_ids,
     661             :                      boundary_mesh,
     662             :                      subdomains_relative_to,
     663             :                      store_parent_side_ids);
     664         568 : }
     665             : 
     666             : 
     667             : 
     668        2485 : void BoundaryInfo::add_elements(const std::set<boundary_id_type> & requested_boundary_ids,
     669             :                                 UnstructuredMesh & boundary_mesh,
     670             :                                 const std::set<subdomain_id_type> & subdomains_relative_to,
     671             :                                 bool store_parent_side_ids)
     672             : {
     673         140 :   LOG_SCOPE("add_elements()", "BoundaryInfo");
     674             : 
     675             :   // We're not prepared to mix serial and distributed meshes in this
     676             :   // method, so make sure their statuses match from the start.
     677             :   //
     678             :   // Specifically test *is_serial* here - we can handle a mix of
     679             :   // ReplicatedMesh and serialized DistributedMesh.
     680          70 :   libmesh_assert_equal_to(_mesh->is_serial(),
     681             :                           boundary_mesh.is_serial());
     682             : 
     683             :   // If the boundary mesh already has interior pointers pointing at
     684             :   // elements in a third mesh then we're in trouble
     685          70 :   libmesh_assert(&boundary_mesh.interior_mesh() == &boundary_mesh ||
     686             :                  &boundary_mesh.interior_mesh() == _mesh);
     687             : 
     688             :   // And now we're going to add interior pointers to elements from
     689             :   // this mesh
     690        2485 :   boundary_mesh.set_interior_mesh(*_mesh);
     691             : 
     692         140 :   std::map<std::pair<dof_id_type, unsigned char>, dof_id_type> side_id_map;
     693        2485 :   this->_find_id_maps(requested_boundary_ids,
     694             :                       0,
     695             :                       nullptr,
     696        2485 :                       boundary_mesh.max_elem_id(),
     697             :                       &side_id_map,
     698             :                       subdomains_relative_to);
     699             : 
     700             :   // We have to add sides *outside* any element loop, because if
     701             :   // boundary_mesh and _mesh are the same then those additions can
     702             :   // invalidate our element iterators.  So we just use the element
     703             :   // loop to make a list of sides to add.
     704             :   typedef std::vector<std::pair<dof_id_type, unsigned char>>
     705             :     side_container;
     706         140 :   side_container sides_to_add;
     707             : 
     708       99142 :   for (const auto & elem : _mesh->element_ptr_range())
     709             :     {
     710             :       // If the subdomains_relative_to container has the
     711             :       // invalid_subdomain_id, we fall back on the "old" behavior of
     712             :       // adding sides regardless of this Elem's subdomain. Otherwise,
     713             :       // if the subdomains_relative_to container doesn't contain the
     714             :       // current Elem's subdomain_id(), we won't add any sides from
     715             :       // it.
     716        8168 :       if (!subdomains_relative_to.count(Elem::invalid_subdomain_id) &&
     717        7232 :           !subdomains_relative_to.count(elem->subdomain_id()))
     718        5212 :         continue;
     719             : 
     720      216197 :       for (auto s : elem->side_index_range())
     721             :         {
     722        9496 :           bool add_this_side = false;
     723             : 
     724             :           // Find all the boundary side ids for this Elem side.
     725       18992 :           std::vector<boundary_id_type> bcids;
     726      169416 :           this->boundary_ids(elem, s, bcids);
     727             : 
     728      196841 :           for (const boundary_id_type bcid : bcids)
     729             :             {
     730             :               // if the user wants this id, we want this side
     731        2236 :               if (requested_boundary_ids.count(bcid))
     732             :                 {
     733         892 :                   add_this_side = true;
     734         892 :                   break;
     735             :                 }
     736             :             }
     737             : 
     738             :           // We may still want to add this side if the user called
     739             :           // sync() with no requested_boundary_ids. This corresponds
     740             :           // to the "old" style of calling sync() in which the entire
     741             :           // boundary was copied to the BoundaryMesh, and handles the
     742             :           // case where elements on the geometric boundary are not in
     743             :           // any sidesets.
     744        9496 :           if (requested_boundary_ids.count(invalid_id) &&
     745           0 :               elem->neighbor_ptr(s) == nullptr)
     746           0 :             add_this_side = true;
     747             : 
     748      169416 :           if (add_this_side)
     749       14824 :             sides_to_add.emplace_back(elem->id(), s);
     750             :         }
     751        2345 :     }
     752             : 
     753             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
     754        2485 :   unique_id_type old_max_unique_id = boundary_mesh.parallel_max_unique_id();
     755             : #endif
     756             : 
     757             :   // Add an "extra" integer for storing the side index of the parent
     758             :   // Elem which each boundary Elem corresponds to. We do this once
     759             :   // before any Elems have been added.
     760        2485 :   unsigned int parent_side_index_tag = store_parent_side_ids ?
     761        4348 :     boundary_mesh.add_elem_integer("parent_side_index") : libMesh::invalid_uint;
     762             : 
     763       17309 :   for (const auto & [elem_id, s] : sides_to_add)
     764             :     {
     765       14824 :       Elem * elem = _mesh->elem_ptr(elem_id);
     766             : 
     767       15716 :       std::unique_ptr<Elem> side = elem->build_side_ptr(s);
     768             : 
     769       15716 :       side->processor_id() = elem->processor_id();
     770             : 
     771         892 :       const std::pair<dof_id_type, unsigned char> side_pair(elem_id, s);
     772             : 
     773         892 :       libmesh_assert(side_id_map.count(side_pair));
     774             : 
     775       14824 :       const dof_id_type new_side_id = side_id_map[side_pair];
     776             : 
     777         892 :       side->set_id(new_side_id);
     778             : 
     779             : #ifdef LIBMESH_ENABLE_UNIQUE_ID
     780       14824 :       side->set_unique_id(old_max_unique_id + new_side_id);
     781             : #endif
     782             : 
     783             :       // Add the side
     784       15716 :       Elem * new_elem = boundary_mesh.add_elem(std::move(side));
     785             : 
     786             :       // If requested, new_elem gets an "extra" integer equal to the
     787             :       // side id "s" of the interior_parent it corresponds to.
     788       14824 :       if (store_parent_side_ids)
     789       13040 :         new_elem->set_extra_integer(parent_side_index_tag, s);
     790             : 
     791             : #ifdef LIBMESH_ENABLE_AMR
     792        1784 :       new_elem->set_refinement_flag(elem->refinement_flag());
     793        1784 :       new_elem->set_p_refinement_flag(elem->p_refinement_flag());
     794             : 
     795             :       // Set parent links
     796       15716 :       if (elem->parent())
     797             :         {
     798         672 :           const std::pair<dof_id_type, unsigned char> parent_side_pair(elem->parent()->id(), s);
     799             : 
     800         336 :           libmesh_assert(side_id_map.count(parent_side_pair));
     801             : 
     802        4432 :           Elem * side_parent = boundary_mesh.elem_ptr(side_id_map[parent_side_pair]);
     803             : 
     804         336 :           libmesh_assert(side_parent);
     805             : 
     806         672 :           new_elem->set_parent(side_parent);
     807             : 
     808             :           // Figuring out which child we are of our parent
     809             :           // is a trick.  Due to libMesh child numbering
     810             :           // conventions, if we are an element on a vertex,
     811             :           // then we share that vertex with our parent, with
     812             :           // the same local index.
     813         336 :           bool found_child = false;
     814       13296 :           for (auto v : make_range(new_elem->n_vertices()))
     815       10208 :             if (new_elem->node_ptr(v) == side_parent->node_ptr(v))
     816             :               {
     817        4432 :                 side_parent->add_child(new_elem, v);
     818         336 :                 found_child = true;
     819             :               }
     820             : 
     821             :           // If we don't share any vertex with our parent,
     822             :           // then we're the fourth child (index 3) of a
     823             :           // triangle.
     824        4432 :           if (!found_child)
     825             :             {
     826           0 :               libmesh_assert_equal_to (new_elem->n_vertices(), 3);
     827           0 :               side_parent->add_child(new_elem, 3);
     828             :             }
     829             :         }
     830             : 
     831             :       // Set remote_elem child links if necessary.  Rather than
     832             :       // worrying about which interior child corresponds to which side
     833             :       // child we'll just set all null links to be remote and we'll
     834             :       // rely on our detection of actual semilocal children to
     835             :       // overwrite the links that shouldn't be remote.
     836         892 :       if (elem->has_children())
     837       16380 :         for (auto c : make_range(elem->n_children()))
     838       16486 :           if (elem->child_ptr(c) == remote_elem &&
     839        3382 :               elem->is_child_on_side(c, s))
     840             :             {
     841        6360 :               for (auto sc : make_range(new_elem->n_children()))
     842        2880 :                 if (!new_elem->raw_child_ptr(sc))
     843             :                   new_elem->add_child
     844        2720 :                     (const_cast<RemoteElem*>(remote_elem), sc);
     845             :             }
     846             : #endif
     847             : 
     848       14824 :       new_elem->set_interior_parent (elem);
     849             : 
     850             :       // On non-local elements on DistributedMesh we might have
     851             :       // RemoteElem neighbor links to construct
     852       14824 :       if (!_mesh->is_serial() &&
     853        8374 :           (elem->processor_id() != this->processor_id()))
     854             :         {
     855        5222 :           const unsigned short n_nodes = elem->n_nodes();
     856             : 
     857        5222 :           const unsigned short bdy_n_sides = new_elem->n_sides();
     858        5222 :           const unsigned short bdy_n_nodes = new_elem->n_nodes();
     859             : 
     860             :           // Check every interior side for a RemoteElem
     861       26054 :           for (auto interior_side : elem->side_index_range())
     862             :             {
     863             :               // Might this interior side have a RemoteElem that
     864             :               // needs a corresponding Remote on a boundary side?
     865       20832 :               if (elem->neighbor_ptr(interior_side) != remote_elem)
     866       16999 :                 continue;
     867             : 
     868             :               // Which boundary side?
     869        2944 :               for (unsigned short boundary_side = 0;
     870        6777 :                    boundary_side != bdy_n_sides; ++boundary_side)
     871             :                 {
     872             :                   // Look for matching node points.  This is safe in
     873             :                   // *this* context.
     874           0 :                   bool found_all_nodes = true;
     875        9963 :                   for (unsigned short boundary_node = 0;
     876       15972 :                        boundary_node != bdy_n_nodes; ++boundary_node)
     877             :                     {
     878       12907 :                       if (!new_elem->is_node_on_side(boundary_node,
     879           0 :                                                      boundary_side))
     880        6898 :                         continue;
     881             : 
     882           0 :                       bool found_this_node = false;
     883       31658 :                       for (unsigned short interior_node = 0;
     884       37667 :                            interior_node != n_nodes; ++interior_node)
     885             :                         {
     886       34723 :                           if (!elem->is_node_on_side(interior_node,
     887           0 :                                                      interior_side))
     888       21227 :                             continue;
     889             : 
     890           0 :                           if (new_elem->point(boundary_node) ==
     891           0 :                               elem->point(interior_node))
     892             :                             {
     893           0 :                               found_this_node = true;
     894           0 :                               break;
     895             :                             }
     896             :                         }
     897        6009 :                       if (!found_this_node)
     898             :                         {
     899           0 :                           found_all_nodes = false;
     900           0 :                           break;
     901             :                         }
     902             :                     }
     903             : 
     904        6009 :                   if (found_all_nodes)
     905             :                     {
     906             :                       new_elem->set_neighbor
     907        3065 :                         (boundary_side,
     908             :                          const_cast<RemoteElem *>(remote_elem));
     909           0 :                       break;
     910             :                     }
     911             :                 }
     912             :             }
     913             :         }
     914       13040 :     }
     915             : 
     916             :   // We haven't been bothering to keep unique ids consistent on ghost
     917             :   // elements or nodes, unless we're doing everything the same on
     918             :   // every processor.
     919        2485 :   if (!boundary_mesh.is_replicated())
     920        1841 :     MeshCommunication().make_node_unique_ids_parallel_consistent(boundary_mesh);
     921             : 
     922             :   // Make sure we didn't add ids inconsistently
     923             : #ifdef DEBUG
     924             : # ifdef LIBMESH_HAVE_RTTI
     925          70 :   DistributedMesh * parmesh = dynamic_cast<DistributedMesh *>(&boundary_mesh);
     926          70 :   if (parmesh)
     927          14 :     parmesh->libmesh_assert_valid_parallel_ids();
     928             : # endif
     929             : #endif
     930        2485 : }
     931             : 
     932             : 
     933             : 
     934      280626 : void BoundaryInfo::add_node(const dof_id_type node_id,
     935             :                             const boundary_id_type id)
     936             : {
     937      280626 :   const Node * node_ptr = _mesh->query_node_ptr(node_id);
     938             : 
     939             :   // The user could easily ask for an invalid node id, so let's throw
     940             :   // an easy-to-understand error message when this happens.
     941      280626 :   libmesh_error_msg_if(!node_ptr,
     942             :                        "BoundaryInfo::add_node(): Could not retrieve pointer for node "
     943             :                        << node_id
     944             :                        << ", no boundary id was added.");
     945             : 
     946      280626 :   this->add_node (node_ptr, id);
     947      280626 : }
     948             : 
     949             : 
     950             : 
     951    38763630 : void BoundaryInfo::add_node(const Node * node,
     952             :                             const boundary_id_type id)
     953             : {
     954    38763630 :   libmesh_error_msg_if(id == invalid_id,
     955             :                        "ERROR: You may not set a boundary ID of "
     956             :                        << invalid_id
     957             :                        << "\n That is reserved for internal use.");
     958             : 
     959             :   // Don't add the same ID twice
     960    57420238 :   for (const auto & pr : as_range(_boundary_node_id.equal_range(node)))
     961    40429516 :     if (pr.second == id)
     962        8700 :       return;
     963             : 
     964      454557 :   _boundary_node_id.emplace(node, id);
     965    16536165 :   _boundary_ids.insert(id);
     966    16536165 :   _node_boundary_ids.insert(id); // Also add this ID to the set of node boundary IDs
     967             : }
     968             : 
     969             : 
     970             : 
     971     6082039 : void BoundaryInfo::add_node(const Node * node,
     972             :                             const std::vector<boundary_id_type> & ids)
     973             : {
     974     6082039 :   if (ids.empty())
     975     5888762 :     return;
     976             : 
     977        1512 :   libmesh_assert(node);
     978             : 
     979             :   // Don't add the same ID twice
     980        1512 :   auto bounds = _boundary_node_id.equal_range(node);
     981             : 
     982             :   // The entries in the ids vector may be non-unique.  If we expected
     983             :   // *lots* of ids, it might be fastest to construct a std::set from
     984             :   // the entries, but for a small number of entries, which is more
     985             :   // typical, it is probably faster to copy the vector and do sort+unique.
     986             :   // http://stackoverflow.com/questions/1041620/whats-the-most-efficient-way-to-erase-duplicates-and-sort-a-vector
     987      194789 :   std::vector<boundary_id_type> unique_ids(ids.begin(), ids.end());
     988      193277 :   std::sort(unique_ids.begin(), unique_ids.end());
     989             :   std::vector<boundary_id_type>::iterator new_end =
     990      193277 :     std::unique(unique_ids.begin(), unique_ids.end());
     991             : 
     992      481026 :   for (auto & id : as_range(unique_ids.begin(), new_end))
     993             :     {
     994      287749 :       libmesh_error_msg_if(id == invalid_id,
     995             :                            "ERROR: You may not set a boundary ID of "
     996             :                            << invalid_id
     997             :                            << "\n That is reserved for internal use.");
     998             : 
     999        2400 :       bool already_inserted = false;
    1000      460595 :       for (const auto & pr : as_range(bounds))
    1001      423533 :         if (pr.second == id)
    1002             :           {
    1003        1356 :             already_inserted = true;
    1004        1356 :             break;
    1005             :           }
    1006      287749 :       if (already_inserted)
    1007      249331 :         continue;
    1008             : 
    1009        1044 :       _boundary_node_id.emplace(node, id);
    1010       36018 :       _boundary_ids.insert(id);
    1011       36018 :       _node_boundary_ids.insert(id); // Also add this ID to the set of node boundary IDs
    1012             :     }
    1013             : }
    1014             : 
    1015             : 
    1016             : 
    1017           0 : void BoundaryInfo::clear_boundary_node_ids()
    1018             : {
    1019           0 :   _boundary_node_id.clear();
    1020           0 : }
    1021             : 
    1022       40336 : void BoundaryInfo::add_edge(const dof_id_type e,
    1023             :                             const unsigned short int edge,
    1024             :                             const boundary_id_type id)
    1025             : {
    1026       40336 :   this->add_edge (_mesh->elem_ptr(e), edge, id);
    1027       40336 : }
    1028             : 
    1029             : 
    1030             : 
    1031       44611 : void BoundaryInfo::add_edge(const Elem * elem,
    1032             :                             const unsigned short int edge,
    1033             :                             const boundary_id_type id)
    1034             : {
    1035        1286 :   libmesh_assert(elem);
    1036             : 
    1037             :   // Only add BCs for level-0 elements.
    1038        1286 :   libmesh_assert_equal_to (elem->level(), 0);
    1039             : 
    1040             :   // Only add BCs for edges that exist.
    1041        1286 :   libmesh_assert_less (edge, elem->n_edges());
    1042             : 
    1043       44611 :   libmesh_error_msg_if(id == invalid_id,
    1044             :                        "ERROR: You may not set a boundary ID of "
    1045             :                        << invalid_id
    1046             :                        << "\n That is reserved for internal use.");
    1047             : 
    1048             :   // Don't add the same ID twice
    1049      107572 :   for (const auto & pr : as_range(_boundary_edge_id.equal_range(elem)))
    1050       74561 :     if (pr.second.first == edge &&
    1051       11600 :         pr.second.second == id)
    1052         320 :       return;
    1053             : 
    1054      228733 :   _boundary_edge_id.emplace(elem, std::make_pair(edge, id));
    1055       32045 :   _boundary_ids.insert(id);
    1056       32045 :   _edge_boundary_ids.insert(id); // Also add this ID to the set of edge boundary IDs
    1057             : }
    1058             : 
    1059             : 
    1060             : 
    1061    22000813 : void BoundaryInfo::add_edge(const Elem * elem,
    1062             :                             const unsigned short int edge,
    1063             :                             const std::vector<boundary_id_type> & ids)
    1064             : {
    1065    22000813 :   if (ids.empty())
    1066    22000813 :     return;
    1067             : 
    1068           0 :   libmesh_assert(elem);
    1069             : 
    1070             :   // Only add BCs for level-0 elements.
    1071           0 :   libmesh_assert_equal_to (elem->level(), 0);
    1072             : 
    1073             :   // Only add BCs for edges that exist.
    1074           0 :   libmesh_assert_less (edge, elem->n_edges());
    1075             : 
    1076             :   // Don't add the same ID twice
    1077           0 :   auto bounds = _boundary_edge_id.equal_range(elem);
    1078             : 
    1079             :   // The entries in the ids vector may be non-unique.  If we expected
    1080             :   // *lots* of ids, it might be fastest to construct a std::set from
    1081             :   // the entries, but for a small number of entries, which is more
    1082             :   // typical, it is probably faster to copy the vector and do sort+unique.
    1083             :   // http://stackoverflow.com/questions/1041620/whats-the-most-efficient-way-to-erase-duplicates-and-sort-a-vector
    1084           0 :   std::vector<boundary_id_type> unique_ids(ids.begin(), ids.end());
    1085           0 :   std::sort(unique_ids.begin(), unique_ids.end());
    1086             :   std::vector<boundary_id_type>::iterator new_end =
    1087           0 :     std::unique(unique_ids.begin(), unique_ids.end());
    1088             : 
    1089           0 :   for (auto & id : as_range(unique_ids.begin(), new_end))
    1090             :     {
    1091           0 :       libmesh_error_msg_if(id == invalid_id,
    1092             :                            "ERROR: You may not set a boundary ID of "
    1093             :                            << invalid_id
    1094             :                            << "\n That is reserved for internal use.");
    1095             : 
    1096           0 :       bool already_inserted = false;
    1097           0 :       for (const auto & pr : as_range(bounds))
    1098           0 :         if (pr.second.first == edge &&
    1099           0 :             pr.second.second == id)
    1100             :           {
    1101           0 :             already_inserted = true;
    1102           0 :             break;
    1103             :           }
    1104           0 :       if (already_inserted)
    1105           0 :         continue;
    1106             : 
    1107           0 :       _boundary_edge_id.emplace(elem, std::make_pair(edge, id));
    1108           0 :       _boundary_ids.insert(id);
    1109           0 :       _edge_boundary_ids.insert(id); // Also add this ID to the set of edge boundary IDs
    1110             :     }
    1111             : }
    1112             : 
    1113             : 
    1114             : 
    1115       39936 : void BoundaryInfo::add_shellface(const dof_id_type e,
    1116             :                                  const unsigned short int shellface,
    1117             :                                  const boundary_id_type id)
    1118             : {
    1119       39936 :   this->add_shellface (_mesh->elem_ptr(e), shellface, id);
    1120       39936 : }
    1121             : 
    1122             : 
    1123             : 
    1124      537274 : void BoundaryInfo::add_shellface(const Elem * elem,
    1125             :                                  const unsigned short int shellface,
    1126             :                                  const boundary_id_type id)
    1127             : {
    1128       13316 :   libmesh_assert(elem);
    1129             : 
    1130             :   // Only add BCs for level-0 elements.
    1131       13316 :   libmesh_assert_equal_to (elem->level(), 0);
    1132             : 
    1133             :   // Shells only have 2 faces
    1134       13316 :   libmesh_assert_less(shellface, 2);
    1135             : 
    1136      537274 :   libmesh_error_msg_if(id == invalid_id,
    1137             :                        "ERROR: You may not set a boundary ID of "
    1138             :                        << invalid_id
    1139             :                        << "\n That is reserved for internal use.");
    1140             : 
    1141             :   // Don't add the same ID twice
    1142      805840 :   for (const auto & pr : as_range(_boundary_shellface_id.equal_range(elem)))
    1143      314230 :     if (pr.second.first == shellface &&
    1144       45664 :         pr.second.second == id)
    1145           0 :       return;
    1146             : 
    1147      491610 :   _boundary_shellface_id.emplace(elem, std::make_pair(shellface, id));
    1148      478294 :   _boundary_ids.insert(id);
    1149      478294 :   _shellface_boundary_ids.insert(id); // Also add this ID to the set of shellface boundary IDs
    1150             : }
    1151             : 
    1152             : 
    1153             : 
    1154     7278436 : void BoundaryInfo::add_shellface(const Elem * elem,
    1155             :                                  const unsigned short int shellface,
    1156             :                                  const std::vector<boundary_id_type> & ids)
    1157             : {
    1158     7278436 :   if (ids.empty())
    1159     7278436 :     return;
    1160             : 
    1161           0 :   libmesh_assert(elem);
    1162             : 
    1163             :   // Only add BCs for level-0 elements.
    1164           0 :   libmesh_assert_equal_to (elem->level(), 0);
    1165             : 
    1166             :   // Shells only have 2 faces
    1167           0 :   libmesh_assert_less(shellface, 2);
    1168             : 
    1169             :   // Don't add the same ID twice
    1170           0 :   auto bounds = _boundary_shellface_id.equal_range(elem);
    1171             : 
    1172             :   // The entries in the ids vector may be non-unique.  If we expected
    1173             :   // *lots* of ids, it might be fastest to construct a std::set from
    1174             :   // the entries, but for a small number of entries, which is more
    1175             :   // typical, it is probably faster to copy the vector and do sort+unique.
    1176             :   // http://stackoverflow.com/questions/1041620/whats-the-most-efficient-way-to-erase-duplicates-and-sort-a-vector
    1177           0 :   std::vector<boundary_id_type> unique_ids(ids.begin(), ids.end());
    1178           0 :   std::sort(unique_ids.begin(), unique_ids.end());
    1179             :   std::vector<boundary_id_type>::iterator new_end =
    1180           0 :     std::unique(unique_ids.begin(), unique_ids.end());
    1181             : 
    1182           0 :   for (auto & id : as_range(unique_ids.begin(), new_end))
    1183             :     {
    1184           0 :       libmesh_error_msg_if(id == invalid_id,
    1185             :                            "ERROR: You may not set a boundary ID of "
    1186             :                            << invalid_id
    1187             :                            << "\n That is reserved for internal use.");
    1188             : 
    1189           0 :       bool already_inserted = false;
    1190           0 :       for (const auto & pr : as_range(bounds))
    1191           0 :         if (pr.second.first == shellface &&
    1192           0 :             pr.second.second == id)
    1193             :           {
    1194           0 :             already_inserted = true;
    1195           0 :             break;
    1196             :           }
    1197           0 :       if (already_inserted)
    1198           0 :         continue;
    1199             : 
    1200           0 :       _boundary_shellface_id.emplace(elem, std::make_pair(shellface, id));
    1201           0 :       _boundary_ids.insert(id);
    1202           0 :       _shellface_boundary_ids.insert(id); // Also add this ID to the set of shellface boundary IDs
    1203             :     }
    1204             : }
    1205             : 
    1206             : 
    1207      100366 : void BoundaryInfo::add_side(const dof_id_type e,
    1208             :                             const unsigned short int side,
    1209             :                             const boundary_id_type id)
    1210             : {
    1211      100366 :   this->add_side (_mesh->elem_ptr(e), side, id);
    1212      100366 : }
    1213             : 
    1214             : 
    1215             : 
    1216    18722846 : void BoundaryInfo::add_side(const Elem * elem,
    1217             :                             const unsigned short int side,
    1218             :                             const boundary_id_type id)
    1219             : {
    1220      184928 :   libmesh_assert(elem);
    1221             : 
    1222             :   // Only add BCs for sides that exist.
    1223      184928 :   libmesh_assert_less (side, elem->n_sides());
    1224             : 
    1225    18722846 :   libmesh_error_msg_if(id == invalid_id, "ERROR: You may not set a boundary ID of "
    1226             :                        << invalid_id
    1227             :                        << "\n That is reserved for internal use.");
    1228             : 
    1229             :   // Don't add the same ID twice
    1230    24009781 :   for (const auto & pr : as_range(_boundary_side_id.equal_range(elem)))
    1231    16936176 :     if (pr.second.first == side &&
    1232    11649552 :         pr.second.second == id)
    1233        3486 :       return;
    1234             : 
    1235             : #ifdef LIBMESH_ENABLE_AMR
    1236             :   // Users try to mark boundary on child elements
    1237             :   // If this happens, we will allow users to remove
    1238             :   // side from child elements as well
    1239     7073605 :   if (elem->level())
    1240             :   {
    1241         632 :     _children_on_boundary = true;
    1242             : 
    1243             :     // Here we have to stop and check if we already have this boundary defined on the
    1244             :     // parent (if yes, no need to add)
    1245          32 :     std::vector<boundary_id_type> bd_ids;
    1246         632 :     this->boundary_ids(elem,side,bd_ids);
    1247             : 
    1248         632 :     if(std::find(bd_ids.begin(), bd_ids.end(), id) != bd_ids.end())
    1249         167 :       libmesh_not_implemented_msg("Trying to add boundary ID "
    1250             :                                   + std::to_string(id)
    1251             :                                   + " which already exists on the ancestors.");
    1252             :   }
    1253             : #endif
    1254             : 
    1255     7073570 :   _boundary_side_id.emplace(elem, std::make_pair(side, id));
    1256     6892130 :   _boundary_ids.insert(id);
    1257     6892130 :   _side_boundary_ids.insert(id); // Also add this ID to the set of side boundary IDs
    1258             : }
    1259             : 
    1260             : 
    1261             : 
    1262    14961260 : void BoundaryInfo::add_side(const Elem * elem,
    1263             :                             const unsigned short int side,
    1264             :                             const std::vector<boundary_id_type> & ids)
    1265             : {
    1266    14961260 :   if (ids.empty())
    1267    13507955 :     return;
    1268             : 
    1269       43968 :   libmesh_assert(elem);
    1270             : 
    1271             :   // Only add BCs for sides that exist.
    1272       43968 :   libmesh_assert_less (side, elem->n_sides());
    1273             : 
    1274             : #ifdef LIBMESH_ENABLE_AMR
    1275             :   // Users try to mark boundary on child elements
    1276             :   // If this happens, we will allow users to remove
    1277             :   // side from child elements as well
    1278     1453305 :   if (elem->level())
    1279             :   {
    1280          35 :     _children_on_boundary = true;
    1281             : 
    1282             :     // Here we have to stop and check if we already have this boundary defined on the
    1283             :     // parent (if yes, no need to add)
    1284           4 :     std::vector<boundary_id_type> bd_ids;
    1285          35 :     this->boundary_ids(elem,side,bd_ids);
    1286             : 
    1287          35 :     for (const auto id : ids)
    1288          35 :       if(std::find(bd_ids.begin(), bd_ids.end(), id) != bd_ids.end())
    1289         167 :         libmesh_not_implemented_msg("Trying to add boundary ID "
    1290             :                                     + std::to_string(id)
    1291             :                                     + " which already exists on the ancestors.");
    1292             :   }
    1293             : #endif
    1294             : 
    1295             :   // Don't add the same ID twice
    1296       43966 :   auto bounds = _boundary_side_id.equal_range(elem);
    1297             : 
    1298             :   // The entries in the ids vector may be non-unique.  If we expected
    1299             :   // *lots* of ids, it might be fastest to construct a std::set from
    1300             :   // the entries, but for a small number of entries, which is more
    1301             :   // typical, it is probably faster to copy the vector and do sort+unique.
    1302             :   // http://stackoverflow.com/questions/1041620/whats-the-most-efficient-way-to-erase-duplicates-and-sort-a-vector
    1303     1497236 :   std::vector<boundary_id_type> unique_ids(ids.begin(), ids.end());
    1304     1453270 :   std::sort(unique_ids.begin(), unique_ids.end());
    1305             :   std::vector<boundary_id_type>::iterator new_end =
    1306     1453270 :     std::unique(unique_ids.begin(), unique_ids.end());
    1307             : 
    1308     2906540 :   for (auto & id : as_range(unique_ids.begin(), new_end))
    1309             :     {
    1310     1453270 :       libmesh_error_msg_if(id == invalid_id,
    1311             :                            "ERROR: You may not set a boundary ID of "
    1312             :                            << invalid_id
    1313             :                            << "\n That is reserved for internal use.");
    1314             : 
    1315       43966 :       bool already_inserted = false;
    1316     1500337 :       for (const auto & pr : as_range(bounds))
    1317       47067 :         if (pr.second.first == side && pr.second.second == id)
    1318             :           {
    1319           0 :             already_inserted = true;
    1320           0 :             break;
    1321             :           }
    1322     1453270 :       if (already_inserted)
    1323           0 :         continue;
    1324             : 
    1325     1453270 :       _boundary_side_id.emplace(elem, std::make_pair(side, id));
    1326     1409304 :       _boundary_ids.insert(id);
    1327     1409304 :       _side_boundary_ids.insert(id); // Also add this ID to the set of side boundary IDs
    1328             :     }
    1329             : }
    1330             : 
    1331             : 
    1332             : 
    1333      574921 : bool BoundaryInfo::has_boundary_id(const Node * const node,
    1334             :                                    const boundary_id_type id) const
    1335             : {
    1336      843607 :   for (const auto & pr : as_range(_boundary_node_id.equal_range(node)))
    1337      842452 :     if (pr.second == id)
    1338       33716 :       return true;
    1339             : 
    1340          66 :   return false;
    1341             : }
    1342             : 
    1343             : 
    1344             : 
    1345   108856188 : void BoundaryInfo::boundary_ids (const Node * node,
    1346             :                                  std::vector<boundary_id_type> & vec_to_fill) const
    1347             : {
    1348             :   // Clear out any previous contents
    1349    29947971 :   vec_to_fill.clear();
    1350             : 
    1351   123743197 :   for (const auto & pr : as_range(_boundary_node_id.equal_range(node)))
    1352    14887009 :     vec_to_fill.push_back(pr.second);
    1353   108856188 : }
    1354             : 
    1355             : 
    1356             : 
    1357    65454804 : unsigned int BoundaryInfo::n_boundary_ids(const Node * node) const
    1358             : {
    1359      143736 :   auto pos = _boundary_node_id.equal_range(node);
    1360    65598540 :   return cast_int<unsigned int>(std::distance(pos.first, pos.second));
    1361             : }
    1362             : 
    1363             : 
    1364             : 
    1365   178568920 : void BoundaryInfo::edge_boundary_ids (const Elem * const elem,
    1366             :                                       const unsigned short int edge,
    1367             :                                       std::vector<boundary_id_type> & vec_to_fill) const
    1368             : {
    1369    24936151 :   libmesh_assert(elem);
    1370             : 
    1371             :   // Clear out any previous contents
    1372    24936151 :   vec_to_fill.clear();
    1373             : 
    1374             :   // Only query BCs for edges that exist.
    1375    24936151 :   libmesh_assert_less (edge, elem->n_edges());
    1376             : 
    1377             :   // Only level-0 elements store BCs.  If this is not a level-0
    1378             :   // element get its level-0 parent and infer the BCs.
    1379   178568920 :   const Elem * searched_elem = elem;
    1380             : #ifdef LIBMESH_ENABLE_AMR
    1381   178568920 :   if (elem->level() != 0)
    1382             :     {
    1383             :       // Find all the sides that contain edge. If one of those is a boundary
    1384             :       // side, then this must be a boundary edge. In that case, we just use the
    1385             :       // top-level parent.
    1386    10574109 :       bool found_boundary_edge = false;
    1387    70455821 :       for (auto side : elem->side_index_range())
    1388             :         {
    1389    57706981 :           if (elem->is_edge_on_side(edge,side))
    1390             :             {
    1391    18731987 :               if (elem->neighbor_ptr(side) == nullptr)
    1392             :                 {
    1393      885340 :                   searched_elem = elem->top_parent ();
    1394      666184 :                   found_boundary_edge = true;
    1395      666184 :                   break;
    1396             :                 }
    1397             :             }
    1398             :         }
    1399             : 
    1400    10574109 :       if (!found_boundary_edge)
    1401             :         {
    1402             :           // Child element is not on external edge, but it may have internal
    1403             :           // "boundary" IDs.  We will walk up the tree, at each level checking that
    1404             :           // the current child is actually on the same edge of the parent that is
    1405             :           // currently being searched for (i.e. that was passed in as "edge").
    1406    21278758 :           while (searched_elem->parent() != nullptr)
    1407             :             {
    1408    15497756 :               const Elem * parent = searched_elem->parent();
    1409    19912550 :               if (parent->is_child_on_edge(parent->which_child_am_i(searched_elem), edge) == false)
    1410    11818396 :                 return;
    1411     8094154 :               searched_elem = parent;
    1412             :             }
    1413             :         }
    1414             :     }
    1415             : #endif
    1416             : 
    1417             :   // Check each element in the range to see if its edge matches the requested edge.
    1418   166774756 :   for (const auto & pr : as_range(_boundary_edge_id.equal_range(searched_elem)))
    1419       24232 :     if (pr.second.first == edge)
    1420        2474 :       vec_to_fill.push_back(pr.second.second);
    1421             : }
    1422             : 
    1423             : 
    1424             : 
    1425    73738406 : unsigned int BoundaryInfo::n_edge_boundary_ids (const Elem * const elem,
    1426             :                                                 const unsigned short int edge) const
    1427             : {
    1428     1170932 :   std::vector<boundary_id_type> ids;
    1429    73738406 :   this->edge_boundary_ids(elem, edge, ids);
    1430    74323920 :   return cast_int<unsigned int>(ids.size());
    1431             : }
    1432             : 
    1433             : 
    1434             : 
    1435    45424473 : void BoundaryInfo::raw_edge_boundary_ids (const Elem * const elem,
    1436             :                                           const unsigned short int edge,
    1437             :                                           std::vector<boundary_id_type> & vec_to_fill) const
    1438             : {
    1439    24134378 :   libmesh_assert(elem);
    1440             : 
    1441             :   // Only query BCs for edges that exist.
    1442    24134378 :   libmesh_assert_less (edge, elem->n_edges());
    1443             : 
    1444             :   // Clear out any previous contents
    1445    24134378 :   vec_to_fill.clear();
    1446             : 
    1447             :   // Only level-0 elements store BCs.
    1448    46135191 :   if (elem->parent())
    1449    10374712 :     return;
    1450             : 
    1451             :   // Check each element in the range to see if its edge matches the requested edge.
    1452    34295824 :   for (const auto & pr : as_range(_boundary_edge_id.equal_range(elem)))
    1453        3384 :     if (pr.second.first == edge)
    1454         282 :       vec_to_fill.push_back(pr.second.second);
    1455             : }
    1456             : 
    1457             : 
    1458             : 
    1459    56665780 : void BoundaryInfo::shellface_boundary_ids (const Elem * const elem,
    1460             :                                            const unsigned short int shellface,
    1461             :                                            std::vector<boundary_id_type> & vec_to_fill) const
    1462             : {
    1463     9153182 :   libmesh_assert(elem);
    1464             : 
    1465             :   // Shells only have 2 faces
    1466     9153182 :   libmesh_assert_less(shellface, 2);
    1467             : 
    1468             :   // Clear out any previous contents
    1469     9153182 :   vec_to_fill.clear();
    1470             : 
    1471             :   // Only level-0 elements store BCs.  If this is not a level-0
    1472             :   // element get its level-0 parent and infer the BCs.
    1473    56665780 :   const Elem * searched_elem = elem;
    1474             : #ifdef LIBMESH_ENABLE_AMR
    1475    56665780 :   if (elem->level() != 0)
    1476             :     {
    1477    26815168 :       while (searched_elem->parent() != nullptr)
    1478             :         {
    1479    16449840 :           const Elem * parent = searched_elem->parent();
    1480    20558824 :           searched_elem = parent;
    1481             :         }
    1482             :     }
    1483             : #endif
    1484             : 
    1485             :   // Check each element in the range to see if its shellface matches the requested shellface.
    1486    57209980 :   for (const auto & pr : as_range(_boundary_shellface_id.equal_range(searched_elem)))
    1487      544200 :     if (pr.second.first == shellface)
    1488      272196 :       vec_to_fill.push_back(pr.second.second);
    1489    56665780 : }
    1490             : 
    1491             : 
    1492             : 
    1493    22853878 : unsigned int BoundaryInfo::n_shellface_boundary_ids (const Elem * const elem,
    1494             :                                                      const unsigned short int shellface) const
    1495             : {
    1496      384144 :   std::vector<boundary_id_type> ids;
    1497    22853878 :   this->shellface_boundary_ids(elem, shellface, ids);
    1498    23049278 :   return cast_int<unsigned int>(ids.size());
    1499             : }
    1500             : 
    1501             : 
    1502             : 
    1503    15938122 : void BoundaryInfo::raw_shellface_boundary_ids (const Elem * const elem,
    1504             :                                                const unsigned short int shellface,
    1505             :                                                std::vector<boundary_id_type> & vec_to_fill) const
    1506             : {
    1507     8907786 :   libmesh_assert(elem);
    1508             : 
    1509             :   // Shells only have 2 faces
    1510     8907786 :   libmesh_assert_less(shellface, 2);
    1511             : 
    1512             :   // Clear out any previous contents
    1513     8907786 :   vec_to_fill.clear();
    1514             : 
    1515             :   // Only level-0 elements store BCs.
    1516    16186270 :   if (elem->parent())
    1517     4495864 :     return;
    1518             : 
    1519             :   // Check each element in the range to see if its shellface matches the requested shellface.
    1520    11094348 :   for (const auto & pr : as_range(_boundary_shellface_id.equal_range(elem)))
    1521       66584 :     if (pr.second.first == shellface)
    1522       33292 :       vec_to_fill.push_back(pr.second.second);
    1523             : }
    1524             : 
    1525             : 
    1526             : 
    1527     7052859 : bool BoundaryInfo::has_boundary_id(const Elem * const elem,
    1528             :                                    const unsigned short int side,
    1529             :                                    const boundary_id_type id) const
    1530             : {
    1531      533443 :   std::vector<boundary_id_type> ids;
    1532     7052859 :   this->boundary_ids(elem, side, ids);
    1533     7844147 :   return (std::find(ids.begin(), ids.end(), id) != ids.end());
    1534             : }
    1535             : 
    1536             : 
    1537          82 : void BoundaryInfo::side_boundary_ids (const Elem * const elem,
    1538             :                                       std::vector<std::vector<boundary_id_type>> & vec_to_fill) const
    1539             : {
    1540           5 :   libmesh_assert(elem);
    1541             : 
    1542             :   // Clear out any previous contents
    1543           5 :   vec_to_fill.clear();
    1544             :   // We are going to gather boundary ids for each side
    1545          82 :   vec_to_fill.resize(elem->n_sides());
    1546             : 
    1547             :   // In most cases only level-0 elements store BCs.
    1548             :   // In certain applications (such as time-dependent domains), however, children
    1549             :   // need to store BCs too. This case is covered with the _children_on_boundary
    1550             :   // flag.
    1551          82 :   const Elem * searched_elem = elem;
    1552             : 
    1553             : #ifdef LIBMESH_ENABLE_AMR
    1554          82 :   if (elem->level() != 0)
    1555             :   {
    1556             :     // If we have children on the boundaries, we need to search for boundary IDs on the
    1557             :     // child and its ancestors too if they share the side.
    1558          70 :     if (_children_on_boundary)
    1559             :     {
    1560             :       // Loop over ancestors to check if they have boundary ids on the same side
    1561          35 :       std::vector<bool> search_on_side(elem->n_sides(), true);
    1562           2 :       bool keep_searching = true;
    1563         105 :       while (searched_elem && keep_searching)
    1564             :       {
    1565         245 :         for (const auto & pr : as_range(_boundary_side_id.equal_range(searched_elem)))
    1566             :         {
    1567         875 :           for (const auto side : make_range(elem->n_sides()))
    1568             :             // Here we need to check if the boundary id already exists
    1569         752 :             if (search_on_side[side] && pr.second.first == side &&
    1570         672 :                 std::find(vec_to_fill[side].begin(), vec_to_fill[side].end(), pr.second.second) ==
    1571          52 :                           vec_to_fill[side].end())
    1572         111 :               vec_to_fill[side].push_back(pr.second.second);
    1573             :         }
    1574             : 
    1575           8 :         const Elem * parent = searched_elem->parent();
    1576          70 :         const auto child_index = parent ? parent->which_child_am_i(searched_elem) : libMesh::invalid_uint;
    1577         350 :         for (const auto side : make_range(elem->n_sides()))
    1578             :           // If the parent doesn't exist or if the child is not on the correct side of the
    1579             :           // parent we are done checking the ancestors
    1580         320 :           if (search_on_side[side] &&
    1581         156 :                 (!parent || parent->is_child_on_side(child_index, side) == false))
    1582          16 :             search_on_side[side] = false;
    1583             : 
    1584          70 :         searched_elem = parent;
    1585             :         // if found what we needed on all sides, exit
    1586          70 :         keep_searching = *std::max_element(search_on_side.begin(), search_on_side.end());
    1587             :       }
    1588             : 
    1589           2 :       return;
    1590             :     }
    1591             : 
    1592             :     // Children not on boundaries case.
    1593             :     // It could be that a children is interior to the parent (search_on_side = false will handle that)
    1594             :     // However, since no children on boundaries, we know that it's either the top parent or nothing
    1595          35 :     std::vector<bool> search_on_side(elem->n_sides(), true);
    1596         175 :     for (const auto side : make_range(elem->n_sides()))
    1597             :     {
    1598             :       // If we don't have children on boundaries and we are on an external boundary,
    1599             :       // we will just use the top parent. search_on_side[side] = true works
    1600         148 :       if (elem->neighbor_ptr(side) == nullptr)
    1601          66 :         continue;
    1602             :       // Otherwise we loop over the ancestors and check if they have a different BC for us
    1603             :       else
    1604         111 :         while (searched_elem->parent() != nullptr)
    1605             :         {
    1606           2 :           const Elem * parent = searched_elem->parent();
    1607          37 :           if (search_on_side[side] && parent->is_child_on_side(parent->which_child_am_i(searched_elem), side) == false)
    1608           4 :             search_on_side[side] = false;
    1609          35 :           searched_elem = parent;
    1610             :         }
    1611             :     }
    1612             :     // Now search on the top parent, only if we need to (element is not deep inside the top parent)
    1613          37 :     if (*std::max_element(search_on_side.begin(), search_on_side.end()))
    1614         175 :       for (const auto & pr : as_range(_boundary_side_id.equal_range(elem->top_parent())))
    1615         148 :         if (search_on_side[pr.second.first])
    1616         111 :           vec_to_fill[pr.second.first].push_back(pr.second.second);
    1617           2 :     return;
    1618             :   }
    1619             : #endif
    1620             : 
    1621             :   // Check each element in the range to see if its side matches the requested side.
    1622          72 :   for (const auto & pr : as_range(_boundary_side_id.equal_range(searched_elem)))
    1623          65 :     vec_to_fill[pr.second.first].push_back(pr.second.second);
    1624             : }
    1625             : 
    1626    35856423 : void BoundaryInfo::boundary_ids (const Elem * const elem,
    1627             :                                  const unsigned short int side,
    1628             :                                  std::vector<boundary_id_type> & vec_to_fill) const
    1629             : {
    1630    18739763 :   libmesh_assert(elem);
    1631             : 
    1632             :   // Only query BCs for sides that exist.
    1633    18739763 :   libmesh_assert_less (side, elem->n_sides());
    1634             : 
    1635             :   // Clear out any previous contents
    1636    18739763 :   vec_to_fill.clear();
    1637             : 
    1638             :   // In most cases only level-0 elements store BCs.
    1639             :   // In certain applications (such as time-dependent domains), however, children
    1640             :   // need to store BCs too. This case is covered with the _children_on_boundary
    1641             :   // flag.
    1642    35856423 :   const Elem * searched_elem = elem;
    1643             : 
    1644             : #ifdef LIBMESH_ENABLE_AMR
    1645             : 
    1646    35856423 :   if (elem->level() != 0)
    1647             :   {
    1648             :     // If we have children on the boundaries, we need to search for boundary IDs on the
    1649             :     // child and its ancestors too if they share the side.
    1650    12732720 :     if (_children_on_boundary)
    1651             :     {
    1652             :       // Loop over ancestors to check if they have boundary ids on the same side
    1653        2756 :       while (searched_elem)
    1654             :       {
    1655        6567 :         for (const auto & pr : as_range(_boundary_side_id.equal_range(searched_elem)))
    1656             :           // Here we need to check if the boundary id already exists
    1657        4247 :           if (pr.second.first == side &&
    1658        2565 :               std::find(vec_to_fill.begin(), vec_to_fill.end(), pr.second.second) ==
    1659        2100 :               vec_to_fill.end())
    1660         767 :             vec_to_fill.push_back(pr.second.second);
    1661             : 
    1662             : 
    1663        1368 :         const Elem * parent = searched_elem->parent();
    1664             :         // If the parent doesn't exist or if the child is not on the correct side of the
    1665             :         // parent we are done checking the ancestors
    1666        2756 :         if (!parent || parent->is_child_on_side(parent->which_child_am_i(searched_elem), side) == false)
    1667    10624814 :           return;
    1668             : 
    1669        1037 :         searched_elem = parent;
    1670             :       }
    1671             : 
    1672           0 :       return;
    1673             :     }
    1674             : 
    1675             :     // If we don't have children on boundaries and we are on an external boundary,
    1676             :     // we just look for the top parent
    1677    13064353 :     if (elem->neighbor_ptr(side) == nullptr)
    1678      625021 :       searched_elem = elem->top_parent();
    1679             :     // Otherwise we loop over the ancestors and check if they have a different BC for us
    1680             :     else
    1681    22056769 :       while (searched_elem->parent() != nullptr)
    1682             :       {
    1683    14542816 :         const Elem * parent = searched_elem->parent();
    1684    20073423 :         if (parent->is_child_on_side(parent->which_child_am_i(searched_elem), side) == false)
    1685     7672834 :           return;
    1686             : 
    1687     9450328 :         searched_elem = parent;
    1688             :       }
    1689             :   }
    1690             : 
    1691             : #endif
    1692             : 
    1693             :   // Check each element in the range to see if its side matches the requested side.
    1694    44600669 :   for (const auto & pr : as_range(_boundary_side_id.equal_range(searched_elem)))
    1695    19369060 :     if (pr.second.first == side)
    1696     6808793 :       vec_to_fill.push_back(pr.second.second);
    1697             : }
    1698             : 
    1699             : 
    1700             : 
    1701             : 
    1702      178898 : unsigned int BoundaryInfo::n_boundary_ids (const Elem * const elem,
    1703             :                                            const unsigned short int side) const
    1704             : {
    1705       10000 :   std::vector<boundary_id_type> ids;
    1706      178898 :   this->boundary_ids(elem, side, ids);
    1707      184762 :   return cast_int<unsigned int>(ids.size());
    1708             : }
    1709             : 
    1710             : 
    1711   183075574 : unsigned int BoundaryInfo::n_raw_boundary_ids (const Elem * const elem,
    1712             :                                                const unsigned short int side) const
    1713             : {
    1714     1275418 :   std::vector<boundary_id_type> ids;
    1715   183075574 :   this->raw_boundary_ids(elem, side, ids);
    1716   183729109 :   return cast_int<unsigned int>(ids.size());
    1717             : }
    1718             : 
    1719             : 
    1720             : 
    1721   236205152 : void BoundaryInfo::raw_boundary_ids (const Elem * const elem,
    1722             :                                      const unsigned short int side,
    1723             :                                      std::vector<boundary_id_type> & vec_to_fill) const
    1724             : {
    1725    18575503 :   libmesh_assert(elem);
    1726             : 
    1727             :   // Only query BCs for sides that exist.
    1728    18575503 :   libmesh_assert_less (side, elem->n_sides());
    1729             : 
    1730             :   // Clear out any previous contents
    1731    18575503 :   vec_to_fill.clear();
    1732             : 
    1733             :   // Only level-0 elements store BCs.
    1734   237147301 :   if (elem->parent() && !_children_on_boundary)
    1735     8829384 :     return;
    1736             : 
    1737             :   // Check each element in the range to see if its side matches the requested side.
    1738   205729290 :   for (const auto & pr : as_range(_boundary_side_id.equal_range(elem)))
    1739    72148765 :     if (pr.second.first == side)
    1740    22773962 :       vec_to_fill.push_back(pr.second.second);
    1741             : }
    1742             : 
    1743             : 
    1744             : 
    1745     3639218 : void BoundaryInfo::copy_boundary_ids (const BoundaryInfo & old_boundary_info,
    1746             :                                       const Elem * const old_elem,
    1747             :                                       const Elem * const new_elem)
    1748             : {
    1749      124050 :   libmesh_assert_equal_to (old_elem->n_sides(), new_elem->n_sides());
    1750      124050 :   libmesh_assert_equal_to (old_elem->n_edges(), new_elem->n_edges());
    1751             : 
    1752      248100 :   std::vector<boundary_id_type> bndry_ids;
    1753             : 
    1754    18477723 :   for (auto s : old_elem->side_index_range())
    1755             :     {
    1756    14838505 :       old_boundary_info.raw_boundary_ids (old_elem, s, bndry_ids);
    1757    14838505 :       this->add_side (new_elem, s, bndry_ids);
    1758             :     }
    1759             : 
    1760    25640031 :   for (auto e : old_elem->edge_index_range())
    1761             :     {
    1762    22000813 :       old_boundary_info.raw_edge_boundary_ids (old_elem, e, bndry_ids);
    1763    22000813 :       this->add_edge (new_elem, e, bndry_ids);
    1764             :     }
    1765             : 
    1766    10917654 :   for (unsigned short sf=0; sf != 2; sf++)
    1767             :     {
    1768     7278436 :       old_boundary_info.raw_shellface_boundary_ids (old_elem, sf, bndry_ids);
    1769     7278436 :       this->add_shellface (new_elem, sf, bndry_ids);
    1770             :     }
    1771     3639218 : }
    1772             : 
    1773             : 
    1774             : 
    1775    51842112 : void BoundaryInfo::remove (const Node * node)
    1776             : {
    1777      224898 :   libmesh_assert(node);
    1778             : 
    1779             :   // Erase everything associated with node
    1780      224898 :   _boundary_node_id.erase (node);
    1781    51842112 : }
    1782             : 
    1783             : 
    1784             : 
    1785      106809 : void BoundaryInfo::remove_node (const Node * node,
    1786             :                                 const boundary_id_type id)
    1787             : {
    1788        3102 :   libmesh_assert(node);
    1789             : 
    1790             :   // Erase (node, id) entry from map.
    1791      106809 :   erase_if(_boundary_node_id, node,
    1792      181917 :            [id](decltype(_boundary_node_id)::mapped_type & val)
    1793      176583 :            {return val == id;});
    1794      106809 : }
    1795             : 
    1796             : 
    1797             : 
    1798    41409539 : void BoundaryInfo::remove (const Elem * elem)
    1799             : {
    1800      240221 :   libmesh_assert(elem);
    1801             : 
    1802             :   // Erase everything associated with elem
    1803      240221 :   _boundary_edge_id.erase (elem);
    1804      240221 :   _boundary_side_id.erase (elem);
    1805      240221 :   _boundary_shellface_id.erase (elem);
    1806    41409539 : }
    1807             : 
    1808             : 
    1809             : 
    1810      737560 : void BoundaryInfo::remove_edge (const Elem * elem,
    1811             :                                 const unsigned short int edge)
    1812             : {
    1813       37852 :   libmesh_assert(elem);
    1814             : 
    1815             :   // Only touch BCs for edges that exist.
    1816       37852 :   libmesh_assert_less (edge, elem->n_edges());
    1817             : 
    1818             :   // Only level 0 elements are stored in BoundaryInfo.
    1819       37852 :   libmesh_assert_equal_to (elem->level(), 0);
    1820             : 
    1821             :   // Erase (elem, edge, *) entries from map.
    1822      737560 :   erase_if(_boundary_edge_id, elem,
    1823           0 :            [edge](decltype(_boundary_edge_id)::mapped_type & pr)
    1824           0 :            {return pr.first == edge;});
    1825      737560 : }
    1826             : 
    1827             : 
    1828             : 
    1829         110 : void BoundaryInfo::remove_edge (const Elem * elem,
    1830             :                                 const unsigned short int edge,
    1831             :                                 const boundary_id_type id)
    1832             : {
    1833           4 :   libmesh_assert(elem);
    1834             : 
    1835             :   // Only touch BCs for edges that exist.
    1836           4 :   libmesh_assert_less (edge, elem->n_edges());
    1837             : 
    1838             :   // Only level 0 elements are stored in BoundaryInfo.
    1839           4 :   libmesh_assert_equal_to (elem->level(), 0);
    1840             : 
    1841             :   // Erase (elem, edge, id) entries from map.
    1842         110 :   erase_if(_boundary_edge_id, elem,
    1843         330 :            [edge, id](decltype(_boundary_edge_id)::mapped_type & pr)
    1844         220 :            {return pr.first == edge && pr.second == id;});
    1845         110 : }
    1846             : 
    1847             : 
    1848           0 : void BoundaryInfo::remove_shellface (const Elem * elem,
    1849             :                                      const unsigned short int shellface)
    1850             : {
    1851           0 :   libmesh_assert(elem);
    1852             : 
    1853             :   // Only level 0 elements are stored in BoundaryInfo.
    1854           0 :   libmesh_assert_equal_to (elem->level(), 0);
    1855             : 
    1856             :   // Shells only have 2 faces
    1857           0 :   libmesh_assert_less(shellface, 2);
    1858             : 
    1859             :   // Erase (elem, shellface, *) entries from map.
    1860           0 :   erase_if(_boundary_shellface_id, elem,
    1861           0 :            [shellface](decltype(_boundary_shellface_id)::mapped_type & pr)
    1862           0 :            {return pr.first == shellface;});
    1863           0 : }
    1864             : 
    1865             : 
    1866             : 
    1867           0 : void BoundaryInfo::remove_shellface (const Elem * elem,
    1868             :                                      const unsigned short int shellface,
    1869             :                                      const boundary_id_type id)
    1870             : {
    1871           0 :   libmesh_assert(elem);
    1872             : 
    1873             :   // Only level 0 elements are stored in BoundaryInfo.
    1874           0 :   libmesh_assert_equal_to (elem->level(), 0);
    1875             : 
    1876             :   // Shells only have 2 faces
    1877           0 :   libmesh_assert_less(shellface, 2);
    1878             : 
    1879             :   // Erase (elem, shellface, id) entries from map.
    1880           0 :   erase_if(_boundary_shellface_id, elem,
    1881           0 :            [shellface, id](decltype(_boundary_shellface_id)::mapped_type & pr)
    1882           0 :            {return pr.first == shellface && pr.second == id;});
    1883           0 : }
    1884             : 
    1885      373748 : void BoundaryInfo::remove_side (const Elem * elem,
    1886             :                                 const unsigned short int side)
    1887             : {
    1888       18848 :   libmesh_assert(elem);
    1889             : 
    1890             :   // Only touch BCs for sides that exist.
    1891       18848 :   libmesh_assert_less (side, elem->n_sides());
    1892             : 
    1893             :   // Erase (elem, side, *) entries from map.
    1894      373748 :   erase_if(_boundary_side_id, elem,
    1895       68806 :            [side](decltype(_boundary_side_id)::mapped_type & pr)
    1896       63727 :            {return pr.first == side;});
    1897      373748 : }
    1898             : 
    1899             : 
    1900             : 
    1901        6441 : void BoundaryInfo::remove_side (const Elem * elem,
    1902             :                                 const unsigned short int side,
    1903             :                                 const boundary_id_type id)
    1904             : {
    1905         228 :   libmesh_assert(elem);
    1906             : 
    1907             :   // Only touch BCs for sides that exist.
    1908         228 :   libmesh_assert_less (side, elem->n_sides());
    1909             : 
    1910             : #ifdef LIBMESH_ENABLE_AMR
    1911             :   // Here we have to stop and check if somebody tries to remove an ancestor's boundary ID
    1912             :   // through a child
    1913        6441 :   if (elem->level())
    1914             :   {
    1915           4 :     std::vector<boundary_id_type> bd_ids;
    1916          35 :     this->boundary_ids(elem,side,bd_ids);
    1917          35 :     if(std::find(bd_ids.begin(), bd_ids.end(), id) != bd_ids.end())
    1918             :     {
    1919           4 :       std::vector<boundary_id_type> raw_bd_ids;
    1920          35 :       this->raw_boundary_ids(elem, side, raw_bd_ids);
    1921          35 :       if(std::find(raw_bd_ids.begin(), raw_bd_ids.end(), id) == raw_bd_ids.end())
    1922         167 :         libmesh_not_implemented_msg("We cannot delete boundary ID "
    1923             :                                     + std::to_string(id) +
    1924             :                                     " using a child because it is inherited from an ancestor.");
    1925             :     }
    1926             :   }
    1927             : #endif
    1928             : 
    1929             :   // Erase (elem, side, id) entries from map.
    1930        6406 :   erase_if(_boundary_side_id, elem,
    1931       25366 :            [side, id](decltype(_boundary_side_id)::mapped_type & pr)
    1932       18960 :            {return pr.first == side && pr.second == id;});
    1933        6406 : }
    1934             : 
    1935             : 
    1936             : 
    1937         568 : void BoundaryInfo::remove_id (boundary_id_type id, const bool global)
    1938             : {
    1939             :   // Pass global==false to the sub-methods here, so we can avoid
    1940             :   // unnecessary communications
    1941         568 :   this->remove_side_id(id, false);
    1942         568 :   this->remove_edge_id(id, false);
    1943         568 :   this->remove_shellface_id(id, false);
    1944         568 :   this->remove_node_id(id, false);
    1945             : 
    1946         568 :   if (global)
    1947           0 :     _global_boundary_ids.erase(id);
    1948         568 : }
    1949             : 
    1950             : 
    1951             : 
    1952         852 : void BoundaryInfo::remove_side_id (boundary_id_type id, const bool global)
    1953             : {
    1954             :   // Erase id from ids containers
    1955          24 :   _side_boundary_ids.erase(id);
    1956             : 
    1957          24 :   if (!_edge_boundary_ids.count(id) &&
    1958          48 :       !_shellface_boundary_ids.count(id) &&
    1959          24 :       !_node_boundary_ids.count(id))
    1960           2 :     _boundary_ids.erase(id);
    1961             : 
    1962          24 :   _ss_id_to_name.erase(id);
    1963         852 :   if (global)
    1964             :     {
    1965           0 :       bool someone_has_it = _boundary_ids.count(id);
    1966           0 :       this->comm().max(someone_has_it);
    1967           0 :       if (!someone_has_it)
    1968           0 :         _global_boundary_ids.erase(id);
    1969             :     }
    1970             : 
    1971             :   // Erase (*, *, id) entries from map.
    1972         852 :   erase_if(_boundary_side_id,
    1973        2240 :            [id](decltype(_boundary_side_id)::mapped_type & pr)
    1974        2112 :            {return pr.second == id;});
    1975         852 : }
    1976             : 
    1977             : 
    1978             : 
    1979         568 : void BoundaryInfo::remove_edge_id (boundary_id_type id, const bool global)
    1980             : {
    1981             :   // Erase id from ids containers
    1982          16 :   _edge_boundary_ids.erase(id);
    1983             : 
    1984          16 :   if (!_side_boundary_ids.count(id) &&
    1985          32 :       !_shellface_boundary_ids.count(id) &&
    1986          16 :       !_node_boundary_ids.count(id))
    1987           2 :     _boundary_ids.erase(id);
    1988             : 
    1989          16 :   _es_id_to_name.erase(id);
    1990         568 :   if (global)
    1991             :     {
    1992           0 :       bool someone_has_it = _boundary_ids.count(id);
    1993           0 :       this->comm().max(someone_has_it);
    1994           0 :       if (!someone_has_it)
    1995           0 :         _global_boundary_ids.erase(id);
    1996             :     }
    1997             : 
    1998             :   // Erase (*, *, id) entries from map.
    1999         568 :   erase_if(_boundary_edge_id,
    2000           0 :            [id](decltype(_boundary_edge_id)::mapped_type & pr)
    2001           0 :            {return pr.second == id;});
    2002         568 : }
    2003             : 
    2004             : 
    2005             : 
    2006         568 : void BoundaryInfo::remove_shellface_id (boundary_id_type id, const bool global)
    2007             : {
    2008             :   // Erase id from ids containers
    2009          16 :   _shellface_boundary_ids.erase(id);
    2010             : 
    2011          16 :   if (!_side_boundary_ids.count(id) &&
    2012          32 :       !_edge_boundary_ids.count(id) &&
    2013          16 :       !_node_boundary_ids.count(id))
    2014           2 :     _boundary_ids.erase(id);
    2015             : 
    2016         568 :   if (global)
    2017             :     {
    2018           0 :       bool someone_has_it = _boundary_ids.count(id);
    2019           0 :       this->comm().max(someone_has_it);
    2020           0 :       if (!someone_has_it)
    2021           0 :         _global_boundary_ids.erase(id);
    2022             :     }
    2023             : 
    2024             :   // Erase (*, *, id) entries from map.
    2025         568 :   erase_if(_boundary_shellface_id,
    2026           0 :            [id](decltype(_boundary_shellface_id)::mapped_type & pr)
    2027           0 :            {return pr.second == id;});
    2028         568 : }
    2029             : 
    2030             : 
    2031             : 
    2032         852 : void BoundaryInfo::remove_node_id (boundary_id_type id, const bool global)
    2033             : {
    2034             :   // Erase id from ids containers
    2035          24 :   _node_boundary_ids.erase(id);
    2036             : 
    2037          24 :   if (!_side_boundary_ids.count(id) &&
    2038          40 :       !_edge_boundary_ids.count(id) &&
    2039          16 :       !_shellface_boundary_ids.count(id))
    2040          16 :     _boundary_ids.erase(id);
    2041             : 
    2042         852 :   if (global)
    2043             :     {
    2044           0 :       bool someone_has_it = _boundary_ids.count(id);
    2045           0 :       this->comm().max(someone_has_it);
    2046           0 :       if (!someone_has_it)
    2047           0 :         _global_boundary_ids.erase(id);
    2048             :     }
    2049             : 
    2050             :   // Erase (*, id) entries from map.
    2051         852 :   erase_if(_boundary_node_id,
    2052        3255 :            [id](decltype(_boundary_node_id)::mapped_type & val)
    2053        3069 :            {return val == id;});
    2054         852 : }
    2055             : 
    2056             : 
    2057             : 
    2058        3692 : void BoundaryInfo::renumber_id (boundary_id_type old_id,
    2059             :                                 boundary_id_type new_id)
    2060             : {
    2061        3692 :   if (old_id == new_id)
    2062             :     {
    2063             :       // If the IDs are the same, this is a no-op.
    2064          48 :       return;
    2065             :     }
    2066             : 
    2067          56 :   bool found_node = false;
    2068       80132 :   for (auto & p : _boundary_node_id)
    2069       78144 :     if (p.second == old_id)
    2070             :       {
    2071             :         // If we already have this id on this node, we don't want to
    2072             :         // create a duplicate in our multimap
    2073       13164 :         this->remove_node(p.first, new_id);
    2074       13164 :         p.second = new_id;
    2075         456 :         found_node = true;
    2076             :       }
    2077        1988 :   if (found_node)
    2078             :     {
    2079          56 :       _node_boundary_ids.erase(old_id);
    2080        1500 :       _node_boundary_ids.insert(new_id);
    2081             :     }
    2082             : 
    2083          56 :   bool found_edge = false;
    2084        1988 :   for (auto & p : _boundary_edge_id)
    2085           0 :     if (p.second.second == old_id)
    2086             :       {
    2087             :         // If we already have this id on this edge, we don't want to
    2088             :         // create a duplicate in our multimap
    2089           0 :         this->remove_edge(p.first, p.second.first, new_id);
    2090           0 :         p.second.second = new_id;
    2091           0 :         found_edge = true;
    2092             :       }
    2093        1988 :   if (found_edge)
    2094             :     {
    2095           0 :       _edge_boundary_ids.erase(old_id);
    2096           0 :       _edge_boundary_ids.insert(new_id);
    2097             :     }
    2098             : 
    2099          56 :   bool found_shellface = false;
    2100        1988 :   for (auto & p : _boundary_shellface_id)
    2101           0 :     if (p.second.second == old_id)
    2102             :       {
    2103             :         // If we already have this id on this shellface, we don't want
    2104             :         // to create a duplicate in our multimap
    2105           0 :         this->remove_shellface(p.first, p.second.first, new_id);
    2106           0 :         p.second.second = new_id;
    2107           0 :         found_shellface = true;
    2108             :       }
    2109        1988 :   if (found_shellface)
    2110             :     {
    2111           0 :       _shellface_boundary_ids.erase(old_id);
    2112           0 :       _shellface_boundary_ids.insert(new_id);
    2113             :     }
    2114             : 
    2115          56 :   bool found_side = false;
    2116       37092 :   for (auto & p : _boundary_side_id)
    2117       35104 :     if (p.second.second == old_id)
    2118             :       {
    2119             :         // If we already have this id on this side, we don't want to
    2120             :         // create a duplicate in our multimap
    2121        5944 :         this->remove_side(p.first, p.second.first, new_id);
    2122        5944 :         p.second.second = new_id;
    2123         208 :         found_side = true;
    2124             :       }
    2125        1988 :   if (found_side)
    2126             :     {
    2127          56 :       _side_boundary_ids.erase(old_id);
    2128        1500 :       _side_boundary_ids.insert(new_id);
    2129             :     }
    2130             : 
    2131        1988 :   if (found_node || found_edge || found_shellface || found_side)
    2132             :     {
    2133          56 :       _boundary_ids.erase(old_id);
    2134        1500 :       _boundary_ids.insert(new_id);
    2135             :     }
    2136             : 
    2137        1988 :   renumber_name(_ss_id_to_name, old_id, new_id);
    2138        1988 :   renumber_name(_ns_id_to_name, old_id, new_id);
    2139        1988 :   renumber_name(_es_id_to_name, old_id, new_id);
    2140             : 
    2141        1988 :   this->libmesh_assert_valid_multimaps();
    2142             : }
    2143             : 
    2144             : 
    2145             : 
    2146         142 : void BoundaryInfo::renumber_side_id (boundary_id_type old_id,
    2147             :                                      boundary_id_type new_id)
    2148             : {
    2149             :   // If the IDs are the same, this is a no-op.
    2150         142 :   if (old_id == new_id)
    2151           0 :     return;
    2152             : 
    2153           4 :   bool found_side = false;
    2154        2782 :   for (auto & p : _boundary_side_id)
    2155        2640 :     if (p.second.second == old_id)
    2156             :       {
    2157             :         // If we already have this id on this side, we don't want to
    2158             :         // create a duplicate in our multimap
    2159         440 :         this->remove_side(p.first, p.second.first, new_id);
    2160         440 :         p.second.second = new_id;
    2161          16 :         found_side = true;
    2162             :       }
    2163           4 :   _side_boundary_ids.erase(old_id);
    2164             : 
    2165         142 :   if (found_side)
    2166             :     {
    2167         106 :       _side_boundary_ids.insert(new_id);
    2168             : 
    2169           4 :       if (!_shellface_boundary_ids.count(old_id) &&
    2170           8 :           !_edge_boundary_ids.count(old_id) &&
    2171           4 :           !_node_boundary_ids.count(old_id))
    2172             :         {
    2173           0 :           _boundary_ids.erase(old_id);
    2174             :         }
    2175         106 :       _boundary_ids.insert(new_id);
    2176             :     }
    2177             : 
    2178         142 :   renumber_name(_ss_id_to_name, old_id, new_id);
    2179             : 
    2180         142 :   this->libmesh_assert_valid_multimaps();
    2181             : }
    2182             : 
    2183             : 
    2184             : 
    2185         110 : void BoundaryInfo::renumber_edge_id (boundary_id_type old_id,
    2186             :                                      boundary_id_type new_id)
    2187             : {
    2188             :   // If the IDs are the same, this is a no-op.
    2189         110 :   if (old_id == new_id)
    2190           0 :     return;
    2191             : 
    2192           4 :   bool found_edge = false;
    2193         330 :   for (auto & p : _boundary_edge_id)
    2194         220 :     if (p.second.second == old_id)
    2195             :       {
    2196             :         // If we already have this id on this edge, we don't want to
    2197             :         // create a duplicate in our multimap
    2198         110 :         this->remove_edge(p.first, p.second.first, new_id);
    2199         110 :         p.second.second = new_id;
    2200           4 :         found_edge = true;
    2201             :       }
    2202           4 :   _edge_boundary_ids.erase(old_id);
    2203             : 
    2204         110 :   if (found_edge)
    2205             :     {
    2206         106 :       _edge_boundary_ids.insert(new_id);
    2207             : 
    2208           4 :       if (!_shellface_boundary_ids.count(old_id) &&
    2209           4 :           !_side_boundary_ids.count(old_id) &&
    2210           0 :           !_node_boundary_ids.count(old_id))
    2211             :         {
    2212           0 :           _boundary_ids.erase(old_id);
    2213             :         }
    2214         106 :       _boundary_ids.insert(new_id);
    2215             :     }
    2216             : 
    2217         110 :   renumber_name(_es_id_to_name, old_id, new_id);
    2218             : 
    2219         110 :   this->libmesh_assert_valid_multimaps();
    2220             : }
    2221             : 
    2222             : 
    2223             : 
    2224           0 : void BoundaryInfo::renumber_shellface_id (boundary_id_type old_id,
    2225             :                                           boundary_id_type new_id)
    2226             : {
    2227             :   // If the IDs are the same, this is a no-op.
    2228           0 :   if (old_id == new_id)
    2229           0 :     return;
    2230             : 
    2231           0 :   bool found_shellface = false;
    2232           0 :   for (auto & p : _boundary_shellface_id)
    2233           0 :     if (p.second.second == old_id)
    2234             :       {
    2235             :         // If we already have this id on this shellface, we don't want
    2236             :         // to create a duplicate in our multimap
    2237           0 :         this->remove_shellface(p.first, p.second.first, new_id);
    2238           0 :         p.second.second = new_id;
    2239           0 :         found_shellface = true;
    2240             :       }
    2241           0 :   _shellface_boundary_ids.erase(old_id);
    2242             : 
    2243           0 :   if (found_shellface)
    2244             :     {
    2245           0 :       _shellface_boundary_ids.insert(new_id);
    2246             : 
    2247           0 :       if (!_edge_boundary_ids.count(old_id) &&
    2248           0 :           !_side_boundary_ids.count(old_id) &&
    2249           0 :           !_node_boundary_ids.count(old_id))
    2250             :         {
    2251           0 :           _boundary_ids.erase(old_id);
    2252             :         }
    2253           0 :       _boundary_ids.insert(new_id);
    2254             :     }
    2255             : 
    2256           0 :   this->libmesh_assert_valid_multimaps();
    2257             : }
    2258             : 
    2259             : 
    2260             : 
    2261         142 : void BoundaryInfo::renumber_node_id (boundary_id_type old_id,
    2262             :                                      boundary_id_type new_id)
    2263             : {
    2264             :   // If the IDs are the same, this is a no-op.
    2265         142 :   if (old_id == new_id)
    2266           0 :     return;
    2267             : 
    2268           4 :   bool found_node = false;
    2269        6082 :   for (auto & p : _boundary_node_id)
    2270        5940 :     if (p.second == old_id)
    2271             :       {
    2272             :         // If we already have this id on this node, we don't want to
    2273             :         // create a duplicate in our multimap
    2274         990 :         this->remove_node(p.first, new_id);
    2275         990 :         p.second = new_id;
    2276          36 :         found_node = true;
    2277             :       }
    2278           4 :   _node_boundary_ids.erase(old_id);
    2279             : 
    2280         142 :   if (found_node)
    2281             :     {
    2282         106 :       _node_boundary_ids.insert(new_id);
    2283             : 
    2284           4 :       if (!_shellface_boundary_ids.count(old_id) &&
    2285           8 :           !_side_boundary_ids.count(old_id) &&
    2286           4 :           !_edge_boundary_ids.count(old_id))
    2287             :         {
    2288           4 :           _boundary_ids.erase(old_id);
    2289             :         }
    2290         106 :       _boundary_ids.insert(new_id);
    2291             :     }
    2292             : 
    2293         142 :   renumber_name(_ns_id_to_name, old_id, new_id);
    2294             : 
    2295         142 :   this->libmesh_assert_valid_multimaps();
    2296             : }
    2297             : 
    2298             : 
    2299             : 
    2300          78 : unsigned int BoundaryInfo::side_with_boundary_id(const Elem * const elem,
    2301             :                                                  const boundary_id_type boundary_id_in) const
    2302             : {
    2303          78 :   const Elem * searched_elem = elem;
    2304             : 
    2305             :   // If we don't have a time-dependent domain, we can just go ahead and use the top parent
    2306             :   // (since only those contain boundary conditions). Otherwise, we keep the element
    2307          78 :   if (elem->level() != 0 && !_children_on_boundary)
    2308           0 :       searched_elem = elem->top_parent();
    2309             : 
    2310             :   // elem may have zero or multiple occurrences
    2311         117 :   for (const auto & pr : as_range(_boundary_side_id.equal_range(searched_elem)))
    2312             :   {
    2313             :       // if this is true we found the requested boundary_id
    2314             :       // of the element and want to return the side
    2315          78 :       if (pr.second.second == boundary_id_in)
    2316             :       {
    2317          39 :         unsigned int side = pr.second.first;
    2318             : 
    2319             :         // Here we branch out. If we don't allow time-dependent boundary domains,
    2320             :         // we need to check if our parents are consistent.
    2321          39 :         if (!_children_on_boundary)
    2322             :         {
    2323             :           // If we're on this external boundary then we share this
    2324             :           // external boundary id
    2325           0 :           if (elem->neighbor_ptr(side) == nullptr)
    2326           2 :             return side;
    2327             : 
    2328             :           // If we're on an internal boundary then we need to be sure
    2329             :           // it's the same internal boundary as our top_parent
    2330           0 :           const Elem * p = elem;
    2331             : 
    2332             : #ifdef LIBMESH_ENABLE_AMR
    2333             : 
    2334           0 :           while (p != nullptr)
    2335             :           {
    2336           0 :             const Elem * parent = p->parent();
    2337           0 :             if (parent && !parent->is_child_on_side(parent->which_child_am_i(p), side))
    2338           0 :               break;
    2339           0 :             p = parent;
    2340             :           }
    2341             : #endif
    2342             :           // We're on that side of our top_parent; return it
    2343           0 :           if (!p)
    2344           0 :             return side;
    2345             :         }
    2346             :         // Otherwise we need to check if the child's ancestors have something on
    2347             :         // the side of the child
    2348             :         else
    2349          39 :           return side;
    2350             :       }
    2351             :   }
    2352             : 
    2353             : #ifdef LIBMESH_ENABLE_AMR
    2354             :   // We might have instances (especially with moving boundary domains) when we
    2355             :   // query the paren't boundary ID on a child. We only do this till we find the
    2356             :   // the first side, for multiple sides see above.
    2357          39 :   if (_children_on_boundary && elem->level() != 0)
    2358             :   {
    2359          78 :     for (auto side : make_range(elem->n_sides()))
    2360             :     {
    2361           4 :       const Elem * p = elem;
    2362         164 :       while (p->parent() != nullptr)
    2363             :       {
    2364         117 :         const Elem * parent = p->parent();
    2365             : 
    2366             :         // First we make sure the parent shares this side
    2367         117 :         if (parent->is_child_on_side(parent->which_child_am_i(p), side))
    2368             :         {
    2369             :           // parent may have multiple boundary ids
    2370         351 :           for (const auto & pr : as_range(_boundary_side_id.equal_range(parent)))
    2371             :             // if this is true we found the requested boundary_id
    2372             :             // of the element and want to return the side
    2373         273 :             if (pr.second.first == side && pr.second.second == boundary_id_in)
    2374           2 :               return side;
    2375             : 
    2376           8 :           p = parent;
    2377             :         }
    2378             :         // If the parent is not on the same side, other ancestors won't be on the same side either
    2379             :         else
    2380           0 :           break;
    2381             :       }
    2382             :     }
    2383             :   }
    2384             : #endif
    2385             : 
    2386             :   // if we get here, we found elem in the data structure but not
    2387             :   // the requested boundary id, so return the default value
    2388           0 :   return libMesh::invalid_uint;
    2389             : }
    2390             : 
    2391             : 
    2392             : std::vector<unsigned int>
    2393       63117 : BoundaryInfo::sides_with_boundary_id(const Elem * const elem,
    2394             :                                      const boundary_id_type boundary_id_in) const
    2395             : {
    2396       25630 :   std::vector<unsigned int> returnval;
    2397             : 
    2398       63117 :   const Elem * searched_elem = elem;
    2399       63117 :   if (elem->level() != 0 && !_children_on_boundary)
    2400       49065 :     searched_elem = elem->top_parent();
    2401             : 
    2402             :   // elem may have zero or multiple occurrences
    2403      177178 :   for (const auto & pr : as_range(_boundary_side_id.equal_range(searched_elem)))
    2404             :   {
    2405             :       // if this is true we found the requested boundary_id
    2406             :       // of the element and want to return the side
    2407      114061 :       if (pr.second.second == boundary_id_in)
    2408             :       {
    2409       61741 :         unsigned int side = pr.second.first;
    2410             : 
    2411             :         // Here we branch out. If we don't allow time-dependent boundary domains,
    2412             :         // we need to check if our parents are consistent.
    2413       61741 :         if (!_children_on_boundary)
    2414             :         {
    2415             :           // If we're on this external boundary then we share this
    2416             :           // external boundary id
    2417       80731 :           if (elem->neighbor_ptr(side) == nullptr)
    2418             :             {
    2419       61741 :               returnval.push_back(side);
    2420       61741 :               continue;
    2421             :             }
    2422             : 
    2423             :           // If we're on an internal boundary then we need to be sure
    2424             :           // it's the same internal boundary as our top_parent
    2425           0 :           const Elem * p = elem;
    2426             : 
    2427             : #ifdef LIBMESH_ENABLE_AMR
    2428             : 
    2429           0 :           while (p != nullptr)
    2430             :           {
    2431           0 :             const Elem * parent = p->parent();
    2432           0 :             if (parent && !parent->is_child_on_side(parent->which_child_am_i(p), side))
    2433           0 :               break;
    2434           0 :             p = parent;
    2435             :           }
    2436             : #endif
    2437             :           // We're on that side of our top_parent; return it
    2438           0 :           if (!p)
    2439           0 :             returnval.push_back(side);
    2440             :         }
    2441             :         // Otherwise we trust what we got and return the side
    2442             :         else
    2443           0 :           returnval.push_back(side);
    2444             :       }
    2445             :   }
    2446             : 
    2447             : #ifdef LIBMESH_ENABLE_AMR
    2448             :   // We might have instances (especially with moving boundary domains) when we
    2449             :   // query the parent boundary ID on a child.
    2450       63117 :   if (_children_on_boundary && elem->level() != 0)
    2451             :   {
    2452         255 :     for (auto side : make_range(elem->n_sides()))
    2453             :     {
    2454           8 :       const Elem * p = elem;
    2455         318 :       while (p->parent() != nullptr)
    2456             :       {
    2457         306 :         const Elem * parent = p->parent();
    2458             :         // First we make sure the parent shares this side
    2459         306 :         if (parent->is_child_on_side(parent->which_child_am_i(p), side))
    2460             :         {
    2461             :           // parent may have multiple boundary ids
    2462         306 :           for (const auto & pr : as_range(_boundary_side_id.equal_range(parent)))
    2463             :           {
    2464             :             // if this is true we found the requested boundary_id
    2465             :             // of the element and want to add the side to the vector. We
    2466             :             // also need to check if the side is already in the vector. This might
    2467             :             // happen if the child inherits the boundary from the parent.
    2468         212 :             if (pr.second.first == side && pr.second.second == boundary_id_in &&
    2469         208 :                 std::find(returnval.begin(), returnval.end(), side) == returnval.end())
    2470         102 :               returnval.push_back(side);
    2471             :           }
    2472             :         }
    2473             :         // If the parent is not on the same side, other ancestors won't be on the same side either
    2474             :         else
    2475           8 :           break;
    2476           8 :         p = parent;
    2477             :       }
    2478             :     }
    2479             :   }
    2480             : #endif
    2481             : 
    2482       88747 :   return returnval;
    2483             : }
    2484             : 
    2485             : void
    2486        6870 : BoundaryInfo::build_node_boundary_ids(std::vector<boundary_id_type> & b_ids) const
    2487             : {
    2488         624 :   b_ids.clear();
    2489             : 
    2490     1694264 :   for (const auto & pr : _boundary_node_id)
    2491             :     {
    2492     1687394 :       boundary_id_type id = pr.second;
    2493             : 
    2494     1687394 :       if (std::find(b_ids.begin(),b_ids.end(),id) == b_ids.end())
    2495       26914 :         b_ids.push_back(id);
    2496             :     }
    2497        6870 : }
    2498             : 
    2499             : void
    2500        6870 : BoundaryInfo::build_side_boundary_ids(std::vector<boundary_id_type> & b_ids) const
    2501             : {
    2502         624 :   b_ids.clear();
    2503             : 
    2504      993094 :   for (const auto & pr : _boundary_side_id)
    2505             :     {
    2506      986224 :       boundary_id_type id = pr.second.second;
    2507             : 
    2508      986224 :       if (std::find(b_ids.begin(),b_ids.end(),id) == b_ids.end())
    2509       27864 :         b_ids.push_back(id);
    2510             :     }
    2511        6870 : }
    2512             : 
    2513             : void
    2514        6870 : BoundaryInfo::build_shellface_boundary_ids(std::vector<boundary_id_type> & b_ids) const
    2515             : {
    2516         624 :   b_ids.clear();
    2517             : 
    2518       86742 :   for (const auto & pr :_boundary_shellface_id)
    2519             :     {
    2520       79872 :       boundary_id_type id = pr.second.second;
    2521             : 
    2522       79872 :       if (std::find(b_ids.begin(),b_ids.end(),id) == b_ids.end())
    2523         192 :         b_ids.push_back(id);
    2524             :     }
    2525        6870 : }
    2526             : 
    2527             : #ifdef LIBMESH_ENABLE_AMR
    2528             : void
    2529    15966365 : BoundaryInfo::transfer_boundary_ids_from_children(const Elem * const parent)
    2530             : {
    2531             :   // this is only needed when we allow boundary to be associated with children elements
    2532             :   // also, we only transfer the parent's boundary ids when we are actually coarsen the child element
    2533    15966389 :   if (!_children_on_boundary ||
    2534         386 :       !(!parent->active() && parent->refinement_flag() == Elem::COARSEN_INACTIVE))
    2535      839424 :     return;
    2536             : 
    2537             :   // We assume that edges can be divided ito two pieces, while triangles and
    2538             :   // quads can be divided into four smaller areas. This is double because we'll need
    2539             :   // to convert the ratio of the children with given boundary id to a double.
    2540          82 :   const double number_of_sides_on_children = std::pow(2, parent->dim()-1);
    2541             : 
    2542             :   // In this case the input argument elem is the parent element. We need to check all of its sides
    2543             :   // to grab any potential boundary ids.
    2544         410 :   for (unsigned int side_i = 0; side_i < parent->n_sides(); ++side_i)
    2545             :   {
    2546             :     // An temporary storage to count how many times the children's boundaries occur. the general
    2547             :     // consensus is that if the boundary occurs more than once we propagate upon coarsening. Otherwise,
    2548             :     // it will get deleted.
    2549          32 :     std::map<unsigned short int, unsigned short int> boundary_counts;
    2550             : 
    2551        1640 :     for (const auto & child_i : make_range(parent->n_children()))
    2552             :     {
    2553             :       // We only need to check the children which share the side
    2554        1312 :       if (parent->is_child_on_side(child_i, side_i))
    2555             :       {
    2556             :         // Fetching the boundary tags on the child's side
    2557         902 :         for (const auto & pr : as_range(_boundary_side_id.equal_range(parent->child_ptr(child_i))))
    2558             :         {
    2559             :           // Making sure we are on the same boundary
    2560         246 :           if (pr.second.first == side_i)
    2561         123 :             ++boundary_counts[pr.second.second];
    2562             :         }
    2563             :       }
    2564             :     }
    2565             : 
    2566             :     // This is where the decision is made. If 50% of the children have the tags,
    2567             :     // we propagate them upwards upon coarsening. Otherwise, they are deleted.
    2568         410 :     for (const auto & boundary : boundary_counts)
    2569          82 :       if (boundary.second / number_of_sides_on_children > 0.5)
    2570          41 :         this->add_side(parent, side_i, boundary.first);
    2571             :   }
    2572             : 
    2573         410 :   for (const auto & child_i : make_range(parent->n_children()))
    2574         328 :     this->remove(parent->child_ptr(child_i));
    2575             : }
    2576             : #endif
    2577             : 
    2578        8552 : std::size_t BoundaryInfo::n_boundary_conds () const
    2579             : {
    2580             :   // in serial we know the number of bcs from the
    2581             :   // size of the container
    2582        8552 :   if (_mesh->is_serial())
    2583        4949 :     return _boundary_side_id.size();
    2584             : 
    2585             :   // in parallel we need to sum the number of local bcs
    2586          42 :   parallel_object_only();
    2587             : 
    2588        3603 :   std::size_t nbcs=0;
    2589             : 
    2590       14722 :   for (const auto & pr : _boundary_side_id)
    2591       11159 :     if (pr.first->processor_id() == this->processor_id())
    2592        5272 :       nbcs++;
    2593             : 
    2594        3603 :   this->comm().sum (nbcs);
    2595             : 
    2596        3603 :   return nbcs;
    2597             : }
    2598             : 
    2599       32258 : std::size_t BoundaryInfo::n_edge_conds () const
    2600             : {
    2601             :   // in serial we know the number of nodesets from the
    2602             :   // size of the container
    2603       32258 :   if (_mesh->is_serial())
    2604        6568 :     return _boundary_edge_id.size();
    2605             : 
    2606             :   // in parallel we need to sum the number of local nodesets
    2607          26 :   parallel_object_only();
    2608             : 
    2609       25690 :   std::size_t n_edge_bcs=0;
    2610             : 
    2611       27510 :   for (const auto & pr : _boundary_edge_id)
    2612        1820 :     if (pr.first->processor_id() == this->processor_id())
    2613         792 :       n_edge_bcs++;
    2614             : 
    2615       25690 :   this->comm().sum (n_edge_bcs);
    2616             : 
    2617       25690 :   return n_edge_bcs;
    2618             : }
    2619             : 
    2620             : 
    2621        1496 : std::size_t BoundaryInfo::n_shellface_conds () const
    2622             : {
    2623             :   // in serial we know the number of nodesets from the
    2624             :   // size of the container
    2625        1496 :   if (_mesh->is_serial())
    2626         394 :     return _boundary_shellface_id.size();
    2627             : 
    2628             :   // in parallel we need to sum the number of local nodesets
    2629           4 :   parallel_object_only();
    2630             : 
    2631        1102 :   std::size_t n_shellface_bcs=0;
    2632             : 
    2633       42736 :   for (const auto & pr : _boundary_shellface_id)
    2634       41634 :     if (pr.first->processor_id() == this->processor_id())
    2635       26640 :       n_shellface_bcs++;
    2636             : 
    2637        1102 :   this->comm().sum (n_shellface_bcs);
    2638             : 
    2639        1102 :   return n_shellface_bcs;
    2640             : }
    2641             : 
    2642             : 
    2643        4265 : std::size_t BoundaryInfo::n_nodeset_conds () const
    2644             : {
    2645             :   // in serial we know the number of nodesets from the
    2646             :   // size of the container
    2647        4265 :   if (_mesh->is_serial())
    2648        3227 :     return _boundary_node_id.size();
    2649             : 
    2650             :   // in parallel we need to sum the number of local nodesets
    2651           4 :   parallel_object_only();
    2652             : 
    2653        1038 :   std::size_t n_nodesets=0;
    2654             : 
    2655       15235 :   for (const auto & pr : _boundary_node_id)
    2656       14253 :     if (pr.first->processor_id() == this->processor_id())
    2657        5600 :       n_nodesets++;
    2658             : 
    2659        1038 :   this->comm().sum (n_nodesets);
    2660             : 
    2661        1038 :   return n_nodesets;
    2662             : }
    2663             : 
    2664             : 
    2665             : 
    2666             : std::vector<BoundaryInfo::NodeBCTuple>
    2667       27401 : BoundaryInfo::build_node_list(NodeBCTupleSortBy sort_by) const
    2668             : {
    2669         986 :   std::vector<NodeBCTuple> bc_tuples;
    2670       27401 :   bc_tuples.reserve(_boundary_node_id.size());
    2671             : 
    2672     2153236 :   for (const auto & [node, bid] : _boundary_node_id)
    2673     2125835 :     bc_tuples.emplace_back(node->id(), bid);
    2674             : 
    2675             :   // This list is currently in memory address (arbitrary) order, so
    2676             :   // sort, using the specified ordering, to make it consistent on all procs.
    2677       27401 :   if (sort_by == NodeBCTupleSortBy::NODE_ID)
    2678       27401 :     std::sort(bc_tuples.begin(), bc_tuples.end());
    2679           0 :   else if (sort_by == NodeBCTupleSortBy::BOUNDARY_ID)
    2680           0 :     std::sort(bc_tuples.begin(), bc_tuples.end(),
    2681           0 :               [](const NodeBCTuple & left, const NodeBCTuple & right)
    2682           0 :               {return std::get<1>(left) < std::get<1>(right);});
    2683             : 
    2684       27401 :   return bc_tuples;
    2685             : }
    2686             : 
    2687             : 
    2688             : void
    2689         142 : BoundaryInfo::build_node_list_from_side_list(const std::set<boundary_id_type> & sideset_list)
    2690             : {
    2691             :   // If we're on a distributed mesh, even the owner of a node is not
    2692             :   // guaranteed to be able to properly assign its new boundary id(s)!
    2693             :   // Nodal neighbors are not always ghosted, and a nodal neighbor
    2694             :   // might have a boundary side.
    2695         142 :   const bool mesh_is_serial = _mesh->is_serial();
    2696             : 
    2697             :   typedef std::set<std::pair<dof_id_type, boundary_id_type>> set_type;
    2698             :   typedef std::vector<std::pair<dof_id_type, boundary_id_type>> vec_type;
    2699             : 
    2700           8 :   const processor_id_type my_proc_id = this->processor_id();
    2701           4 :   std::unordered_map<processor_id_type, set_type> nodes_to_push;
    2702           4 :   std::unordered_map<processor_id_type, vec_type> node_vecs_to_push;
    2703             : 
    2704             :   // For avoiding extraneous element side construction
    2705           4 :   ElemSideBuilder side_builder;
    2706             :   // Pull objects out of the loop to reduce heap operations
    2707             :   const Elem * side;
    2708             : 
    2709             :   // Loop over the side list
    2710         937 :   for (const auto & [elem, id_pair] : _boundary_side_id)
    2711             :     {
    2712             :       // Don't add remote sides
    2713         795 :       if (elem->is_remote())
    2714         140 :         continue;
    2715             : 
    2716         795 :       auto [sidenum, bcid] = id_pair;
    2717             : 
    2718         795 :       if (!sideset_list.empty() && !sideset_list.count(bcid))
    2719         132 :         continue;
    2720             : 
    2721             :       // Need to loop over the sides of any possible children
    2722          96 :       std::vector<const Elem *> family;
    2723             : #ifdef LIBMESH_ENABLE_AMR
    2724         655 :       elem->active_family_tree_by_side (family, sidenum);
    2725             : #else
    2726             :       family.push_back(elem);
    2727             : #endif
    2728             : 
    2729        1310 :       for (const auto & cur_elem : family)
    2730             :         {
    2731         655 :           side = &side_builder(*cur_elem, sidenum);
    2732             : 
    2733             :           // Add each node node on the side with the side's boundary id
    2734        1965 :           for (auto i : side->node_index_range())
    2735             :             {
    2736        1406 :               this->add_node(side->node_ptr(i), bcid);
    2737        1310 :               if (!mesh_is_serial)
    2738             :                 {
    2739             :                   const processor_id_type proc_id =
    2740         974 :                     side->node_ptr(i)->processor_id();
    2741         974 :                   if (proc_id != my_proc_id)
    2742         590 :                     nodes_to_push[proc_id].emplace(side->node_id(i), bcid);
    2743             :                 }
    2744             :             }
    2745             :         }
    2746             :     }
    2747             : 
    2748             :   // If we're on a serial mesh then we're done.
    2749         142 :   if (mesh_is_serial)
    2750           4 :     return;
    2751             : 
    2752             :   // Otherwise we need to push ghost node bcids to their owners, then
    2753             :   // pull ghost node bcids from their owners.
    2754             : 
    2755         372 :   for (auto & [proc_id, s] : nodes_to_push)
    2756             :     {
    2757         244 :       node_vecs_to_push[proc_id].assign(s.begin(), s.end());
    2758           0 :       s.clear();
    2759             :     }
    2760             : 
    2761             :   auto nodes_action_functor =
    2762         244 :     [this]
    2763             :     (processor_id_type,
    2764         423 :      const vec_type & received_nodes)
    2765             :     {
    2766         667 :       for (const auto & [dof_id, bndry_id] : received_nodes)
    2767         423 :         this->add_node(_mesh->node_ptr(dof_id), bndry_id);
    2768         128 :     };
    2769             : 
    2770             :   Parallel::push_parallel_vector_data
    2771         128 :     (this->comm(), node_vecs_to_push, nodes_action_functor);
    2772             : 
    2773             :   // At this point we should know all the BCs for our own nodes; now
    2774             :   // we need BCs for ghost nodes.
    2775             :   std::unordered_map<processor_id_type, std::vector<dof_id_type>>
    2776           0 :     node_ids_requested;
    2777             : 
    2778             :   // Determine what nodes we need to request
    2779        2902 :   for (const auto & node : _mesh->node_ptr_range())
    2780             :     {
    2781        1323 :       const processor_id_type pid = node->processor_id();
    2782        1323 :       if (pid != my_proc_id)
    2783         963 :         node_ids_requested[pid].push_back(node->id());
    2784         128 :     }
    2785             : 
    2786             :   typedef std::vector<boundary_id_type> datum_type;
    2787             : 
    2788             :   auto node_bcid_gather_functor =
    2789         378 :     [this]
    2790             :     (processor_id_type,
    2791             :      const std::vector<dof_id_type> & ids,
    2792         963 :      std::vector<datum_type> & data)
    2793             :     {
    2794           0 :       const std::size_t query_size = ids.size();
    2795         378 :       data.resize(query_size);
    2796             : 
    2797        1341 :       for (std::size_t i=0; i != query_size; ++i)
    2798         963 :         this->boundary_ids(_mesh->node_ptr(ids[i]), data[i]);
    2799         506 :     };
    2800             : 
    2801             :   auto node_bcid_action_functor =
    2802         378 :     [this]
    2803             :     (processor_id_type,
    2804             :      const std::vector<dof_id_type> & ids,
    2805         963 :      const std::vector<datum_type> & data)
    2806             :     {
    2807        1341 :       for (auto i : index_range(ids))
    2808         963 :         this->add_node(_mesh->node_ptr(ids[i]), data[i]);
    2809         128 :     };
    2810             : 
    2811           0 :   datum_type * datum_type_ex = nullptr;
    2812             :   Parallel::pull_parallel_vector_data
    2813         128 :     (this->comm(), node_ids_requested, node_bcid_gather_functor,
    2814             :      node_bcid_action_functor, datum_type_ex);
    2815             : }
    2816             : 
    2817           0 : void BoundaryInfo::parallel_sync_side_ids()
    2818             : {
    2819             :   // we need BCs for ghost elements.
    2820             :   std::unordered_map<processor_id_type, std::vector<dof_id_type>>
    2821           0 :     elem_ids_requested;
    2822             : 
    2823             :   // Determine what elements we need to request
    2824           0 :   for (const auto & elem : _mesh->element_ptr_range())
    2825             :   {
    2826           0 :     const processor_id_type pid = elem->processor_id();
    2827           0 :     if (pid != this->processor_id())
    2828           0 :       elem_ids_requested[pid].push_back(elem->id());
    2829           0 :   }
    2830             : 
    2831             :   typedef std::vector<std::pair<unsigned short int, boundary_id_type>> datum_type;
    2832             : 
    2833             :   // gather the element ID, side, and boundary_id_type for the ghost elements
    2834             :   auto elem_id_gather_functor =
    2835           0 :     [this]
    2836             :     (processor_id_type,
    2837             :      const std::vector<dof_id_type> & ids,
    2838           0 :      std::vector<datum_type> & data)
    2839             :   {
    2840           0 :     data.resize(ids.size());
    2841           0 :     for (auto i : index_range(ids))
    2842             :     {
    2843           0 :       Elem * elem = _mesh->elem_ptr(ids[i]);
    2844           0 :       for (const auto & pr : as_range(_boundary_side_id.equal_range(elem)))
    2845           0 :         data[i].push_back(std::make_pair(pr.second.first, pr.second.second));
    2846             :     }
    2847           0 :   };
    2848             :   // update the _boundary_side_id on this processor
    2849             :   auto elem_id_action_functor =
    2850           0 :     [this]
    2851             :     (processor_id_type,
    2852             :      const std::vector<dof_id_type> & ids,
    2853           0 :      std::vector<datum_type> & data)
    2854             :   {
    2855           0 :       for (auto i : index_range(ids))
    2856             :       {
    2857           0 :         Elem * elem = _mesh->elem_ptr(ids[i]);
    2858             :         //clear boundary sides for this element
    2859           0 :         _boundary_side_id.erase(elem);
    2860             :         // update boundary sides for it
    2861           0 :         for (const auto & [side_id, bndry_id] : data[i])
    2862           0 :           _boundary_side_id.insert(std::make_pair(elem, std::make_pair(side_id, bndry_id)));
    2863             :       }
    2864           0 :   };
    2865             : 
    2866             : 
    2867           0 :   datum_type * datum_type_ex = nullptr;
    2868             :   Parallel::pull_parallel_vector_data
    2869           0 :     (this->comm(), elem_ids_requested, elem_id_gather_functor,
    2870             :      elem_id_action_functor, datum_type_ex);
    2871           0 : }
    2872             : 
    2873           0 : void BoundaryInfo::parallel_sync_node_ids()
    2874             : {
    2875             :   // we need BCs for ghost nodes.
    2876             :   std::unordered_map<processor_id_type, std::vector<dof_id_type>>
    2877           0 :     node_ids_requested;
    2878             : 
    2879             :   // Determine what nodes we need to request
    2880           0 :   for (const auto & node : _mesh->node_ptr_range())
    2881             :   {
    2882           0 :     const processor_id_type pid = node->processor_id();
    2883           0 :     if (pid != this->processor_id())
    2884           0 :       node_ids_requested[pid].push_back(node->id());
    2885           0 :   }
    2886             : 
    2887             :   typedef std::vector<boundary_id_type> datum_type;
    2888             : 
    2889             :   // gather the node ID and boundary_id_type for the ghost nodes
    2890             :   auto node_id_gather_functor =
    2891           0 :   [this]
    2892             :   (processor_id_type,
    2893             :     const std::vector<dof_id_type> & ids,
    2894           0 :     std::vector<datum_type> & data)
    2895             :     {
    2896           0 :       data.resize(ids.size());
    2897           0 :       for (auto i : index_range(ids))
    2898             :       {
    2899           0 :         Node * node = _mesh->node_ptr(ids[i]);
    2900           0 :         for (const auto & pr : as_range(_boundary_node_id.equal_range(node)))
    2901           0 :         data[i].push_back(pr.second);
    2902             :       }
    2903           0 :     };
    2904             : 
    2905             :     // update the _boundary_node_id on this processor
    2906             :     auto node_id_action_functor =
    2907           0 :       [this]
    2908             :       (processor_id_type,
    2909             :        const std::vector<dof_id_type> & ids,
    2910           0 :        std::vector<datum_type> & data)
    2911             :     {
    2912           0 :         for (auto i : index_range(ids))
    2913             :         {
    2914           0 :           Node * node = _mesh->node_ptr(ids[i]);
    2915             :           //clear boundary node
    2916           0 :           _boundary_node_id.erase(node);
    2917             :           // update boundary node
    2918           0 :           for (const auto & pr : data[i])
    2919           0 :             _boundary_node_id.insert(std::make_pair(node, pr));
    2920             :         }
    2921           0 :     };
    2922             : 
    2923             : 
    2924           0 :     datum_type * datum_type_ex = nullptr;
    2925             :     Parallel::pull_parallel_vector_data
    2926           0 :       (this->comm(), node_ids_requested, node_id_gather_functor,
    2927             :        node_id_action_functor, datum_type_ex);
    2928           0 : }
    2929             : 
    2930          71 : void BoundaryInfo::build_side_list_from_node_list(const std::set<boundary_id_type> & nodeset_list)
    2931             : {
    2932             :   // Check for early return
    2933          71 :   if (_boundary_node_id.empty())
    2934             :     {
    2935           0 :       libMesh::out << "No boundary node IDs have been added: cannot build side list!" << std::endl;
    2936          36 :       return;
    2937             :     }
    2938             : 
    2939             :   // For avoiding extraneous element side construction
    2940           4 :   ElemSideBuilder side_builder;
    2941             :   // Pull objects out of the loop to reduce heap operations
    2942             :   const Elem * side_elem;
    2943             : 
    2944         332 :   for (const auto & elem : _mesh->active_element_ptr_range())
    2945         708 :     for (auto side : elem->side_index_range())
    2946             :       {
    2947         560 :         side_elem = &side_builder(*elem, side);
    2948             : 
    2949             :         // map from nodeset_id to count for that ID
    2950          64 :         std::map<boundary_id_type, unsigned> nodesets_node_count;
    2951             : 
    2952             :         // For each nodeset that this node is a member of, increment the associated
    2953             :         // nodeset ID count
    2954        1680 :         for (const auto & node : side_elem->node_ref_range())
    2955        2240 :           for (const auto & pr : as_range(_boundary_node_id.equal_range(&node)))
    2956        1120 :             if (nodeset_list.empty() || nodeset_list.count(pr.second))
    2957         560 :               nodesets_node_count[pr.second]++;
    2958             : 
    2959             :         // Now check to see what nodeset_counts have the correct
    2960             :         // number of nodes in them.  For any that do, add this side to
    2961             :         // the sideset, making sure the sideset inherits the
    2962             :         // nodeset's name, if there is one.
    2963         980 :         for (const auto & pr : nodesets_node_count)
    2964         420 :           if (pr.second == side_elem->n_nodes())
    2965             :             {
    2966         140 :               add_side(elem, side, pr.first);
    2967             : 
    2968             :               // Let the sideset inherit any non-empty name from the nodeset
    2969         140 :               std::string & nset_name = nodeset_name(pr.first);
    2970             : 
    2971         140 :               if (nset_name != "")
    2972         140 :                 sideset_name(pr.first) = nset_name;
    2973             :             }
    2974          31 :       } // end for side
    2975             : }
    2976             : 
    2977             : 
    2978             : 
    2979             : 
    2980             : std::vector<BoundaryInfo::BCTuple>
    2981       22246 : BoundaryInfo::build_side_list(BCTupleSortBy sort_by) const
    2982             : {
    2983         842 :   std::vector<BCTuple> bc_triples;
    2984       22246 :   bc_triples.reserve(_boundary_side_id.size());
    2985             : 
    2986      632742 :   for (const auto & [elem, id_pair] : _boundary_side_id)
    2987      610496 :     bc_triples.emplace_back(elem->id(), id_pair.first, id_pair.second);
    2988             : 
    2989             :   // bc_triples is currently in whatever order the Elem pointers in
    2990             :   // the _boundary_side_id multimap are in, and in particular might be
    2991             :   // in different orders on different processors. To avoid this
    2992             :   // inconsistency, we'll sort using the default operator< for tuples.
    2993       22246 :   if (sort_by == BCTupleSortBy::ELEM_ID)
    2994       22246 :     std::sort(bc_triples.begin(), bc_triples.end());
    2995           0 :   else if (sort_by == BCTupleSortBy::SIDE_ID)
    2996           0 :     std::sort(bc_triples.begin(), bc_triples.end(),
    2997           0 :               [](const BCTuple & left, const BCTuple & right)
    2998           0 :               {return std::get<1>(left) < std::get<1>(right);});
    2999           0 :   else if (sort_by == BCTupleSortBy::BOUNDARY_ID)
    3000           0 :     std::sort(bc_triples.begin(), bc_triples.end(),
    3001           0 :               [](const BCTuple & left, const BCTuple & right)
    3002           0 :               {return std::get<2>(left) < std::get<2>(right);});
    3003             : 
    3004       22246 :   return bc_triples;
    3005             : }
    3006             : 
    3007             : 
    3008             : 
    3009             : std::vector<BoundaryInfo::BCTuple>
    3010           0 : BoundaryInfo::build_active_side_list () const
    3011             : {
    3012           0 :   std::vector<BCTuple> bc_triples;
    3013           0 :   bc_triples.reserve(_boundary_side_id.size());
    3014             : 
    3015           0 :   for (const auto & [elem, id_pair] : _boundary_side_id)
    3016             :     {
    3017             :       // Don't add remote sides
    3018           0 :       if (elem->is_remote())
    3019           0 :         continue;
    3020             : 
    3021             :       // Loop over the sides of possible children
    3022           0 :       std::vector<const Elem *> family;
    3023             : #ifdef LIBMESH_ENABLE_AMR
    3024           0 :       elem->active_family_tree_by_side(family, id_pair.first);
    3025             : #else
    3026             :       family.push_back(elem);
    3027             : #endif
    3028             : 
    3029             :       // Populate the list items
    3030           0 :       for (const auto & f : family)
    3031           0 :         bc_triples.emplace_back(f->id(), id_pair.first, id_pair.second);
    3032             :     }
    3033             : 
    3034             :   // This list is currently in memory address (arbitrary) order, so
    3035             :   // sort to make it consistent on all procs.
    3036           0 :   std::sort(bc_triples.begin(), bc_triples.end());
    3037             : 
    3038           0 :   return bc_triples;
    3039             : }
    3040             : 
    3041             : 
    3042             : std::vector<BoundaryInfo::BCTuple>
    3043        4862 : BoundaryInfo::build_edge_list() const
    3044             : {
    3045         354 :   std::vector<BCTuple> bc_triples;
    3046        4862 :   bc_triples.reserve(_boundary_edge_id.size());
    3047             : 
    3048       22778 :   for (const auto & [elem, id_pair] : _boundary_edge_id)
    3049       17916 :     bc_triples.emplace_back(elem->id(), id_pair.first, id_pair.second);
    3050             : 
    3051             :   // This list is currently in memory address (arbitrary) order, so
    3052             :   // sort to make it consistent on all procs.
    3053        4862 :   std::sort(bc_triples.begin(), bc_triples.end());
    3054             : 
    3055        4862 :   return bc_triples;
    3056             : }
    3057             : 
    3058             : 
    3059             : std::vector<BoundaryInfo::BCTuple>
    3060        4784 : BoundaryInfo::build_shellface_list() const
    3061             : {
    3062         350 :   std::vector<BCTuple> bc_triples;
    3063        4784 :   bc_triples.reserve(_boundary_shellface_id.size());
    3064             : 
    3065       44720 :   for (const auto & [elem, id_pair] : _boundary_shellface_id)
    3066       39936 :     bc_triples.emplace_back(elem->id(), id_pair.first, id_pair.second);
    3067             : 
    3068             :   // This list is currently in memory address (arbitrary) order, so
    3069             :   // sort to make it consistent on all procs.
    3070        4784 :   std::sort(bc_triples.begin(), bc_triples.end());
    3071             : 
    3072        4784 :   return bc_triples;
    3073             : }
    3074             : 
    3075             : 
    3076           0 : void BoundaryInfo::print_info(std::ostream & out_stream) const
    3077             : {
    3078             :   // Print out the nodal BCs
    3079           0 :   if (!_boundary_node_id.empty())
    3080             :     {
    3081           0 :       out_stream << "Nodal Boundary conditions:" << std::endl
    3082           0 :                  << "--------------------------" << std::endl
    3083           0 :                  << "  (Node No., ID)               " << std::endl;
    3084             : 
    3085           0 :       for (const auto & [node, bndry_id] : _boundary_node_id)
    3086           0 :         out_stream << "  (" << node->id()
    3087           0 :                    << ", "  << bndry_id
    3088           0 :                    << ")"  << std::endl;
    3089             :     }
    3090             : 
    3091             :   // Print out the element edge BCs
    3092           0 :   if (!_boundary_edge_id.empty())
    3093             :     {
    3094           0 :       out_stream << std::endl
    3095           0 :                  << "Edge Boundary conditions:" << std::endl
    3096           0 :                  << "-------------------------" << std::endl
    3097           0 :                  << "  (Elem No., Edge No., ID)      " << std::endl;
    3098             : 
    3099           0 :       for (const auto & [elem, id_pair] : _boundary_edge_id)
    3100           0 :         out_stream << "  (" << elem->id()
    3101           0 :                    << ", "  << id_pair.first
    3102           0 :                    << ", "  << id_pair.second
    3103           0 :                    << ")"   << std::endl;
    3104             :     }
    3105             : 
    3106             :   // Print out the element shellface BCs
    3107           0 :   if (!_boundary_shellface_id.empty())
    3108             :     {
    3109           0 :       out_stream << std::endl
    3110           0 :                  << "Shell-face Boundary conditions:" << std::endl
    3111           0 :                  << "-------------------------" << std::endl
    3112           0 :                  << "  (Elem No., Shell-face No., ID)      " << std::endl;
    3113             : 
    3114           0 :       for (const auto & [elem, id_pair] : _boundary_shellface_id)
    3115           0 :         out_stream << "  (" << elem->id()
    3116           0 :                    << ", "  << id_pair.first
    3117           0 :                    << ", "  << id_pair.second
    3118           0 :                    << ")"   << std::endl;
    3119             :     }
    3120             : 
    3121             :   // Print out the element side BCs
    3122           0 :   if (!_boundary_side_id.empty())
    3123             :     {
    3124           0 :       out_stream << std::endl
    3125           0 :                  << "Side Boundary conditions:" << std::endl
    3126           0 :                  << "-------------------------" << std::endl
    3127           0 :                  << "  (Elem No., Side No., ID)      " << std::endl;
    3128             : 
    3129           0 :       for (const auto & [elem, id_pair] : _boundary_side_id)
    3130           0 :         out_stream << "  (" << elem->id()
    3131           0 :                    << ", "  << id_pair.first
    3132           0 :                    << ", "  << id_pair.second
    3133           0 :                    << ")"   << std::endl;
    3134             :     }
    3135           0 : }
    3136             : 
    3137             : 
    3138             : 
    3139           0 : void BoundaryInfo::print_summary(std::ostream & out_stream) const
    3140             : {
    3141             :   // Print out the nodal BCs
    3142           0 :   if (!_boundary_node_id.empty())
    3143             :     {
    3144           0 :       out_stream << "Nodal Boundary conditions:" << std::endl
    3145           0 :                  << "--------------------------" << std::endl
    3146           0 :                  << "  (ID, number of nodes)   " << std::endl;
    3147             : 
    3148           0 :       std::map<boundary_id_type, std::size_t> ID_counts;
    3149             : 
    3150           0 :       for (const auto & pr : _boundary_node_id)
    3151           0 :         ID_counts[pr.second]++;
    3152             : 
    3153           0 :       for (const auto & [bndry_id, cnt] : ID_counts)
    3154           0 :         out_stream << "  (" << bndry_id
    3155           0 :                    << ", "  << cnt
    3156           0 :                    << ")"  << std::endl;
    3157             :     }
    3158             : 
    3159             :   // Print out the element edge BCs
    3160           0 :   if (!_boundary_edge_id.empty())
    3161             :     {
    3162           0 :       out_stream << std::endl
    3163           0 :                  << "Edge Boundary conditions:" << std::endl
    3164           0 :                  << "-------------------------" << std::endl
    3165           0 :                  << "  (ID, number of edges)   " << std::endl;
    3166             : 
    3167           0 :       std::map<boundary_id_type, std::size_t> ID_counts;
    3168             : 
    3169           0 :       for (const auto & pr : _boundary_edge_id)
    3170           0 :         ID_counts[pr.second.second]++;
    3171             : 
    3172           0 :       for (const auto & [bndry_id, cnt] : ID_counts)
    3173           0 :         out_stream << "  (" << bndry_id
    3174           0 :                    << ", "  << cnt
    3175           0 :                    << ")"  << std::endl;
    3176             :     }
    3177             : 
    3178             : 
    3179             :   // Print out the element edge BCs
    3180           0 :   if (!_boundary_shellface_id.empty())
    3181             :     {
    3182           0 :       out_stream << std::endl
    3183           0 :                  << "Shell-face Boundary conditions:" << std::endl
    3184           0 :                  << "-------------------------" << std::endl
    3185           0 :                  << "  (ID, number of shellfaces)   " << std::endl;
    3186             : 
    3187           0 :       std::map<boundary_id_type, std::size_t> ID_counts;
    3188             : 
    3189           0 :       for (const auto & pr : _boundary_shellface_id)
    3190           0 :         ID_counts[pr.second.second]++;
    3191             : 
    3192           0 :       for (const auto & [bndry_id, cnt] : ID_counts)
    3193           0 :         out_stream << "  (" << bndry_id
    3194           0 :                    << ", "  << cnt
    3195           0 :                    << ")"  << std::endl;
    3196             :     }
    3197             : 
    3198             :   // Print out the element side BCs
    3199           0 :   if (!_boundary_side_id.empty())
    3200             :     {
    3201           0 :       out_stream << std::endl
    3202           0 :                  << "Side Boundary conditions:" << std::endl
    3203           0 :                  << "-------------------------" << std::endl
    3204           0 :                  << "  (ID, number of sides)   " << std::endl;
    3205             : 
    3206           0 :       std::map<boundary_id_type, std::size_t> ID_counts;
    3207             : 
    3208           0 :       for (const auto & pr : _boundary_side_id)
    3209           0 :         ID_counts[pr.second.second]++;
    3210             : 
    3211           0 :       for (const auto & [bndry_id, cnt] : ID_counts)
    3212           0 :         out_stream << "  (" << bndry_id
    3213           0 :                    << ", "  << cnt
    3214           0 :                    << ")"  << std::endl;
    3215             :     }
    3216           0 : }
    3217             : 
    3218             : 
    3219       31587 : const std::string & BoundaryInfo::get_sideset_name(boundary_id_type id) const
    3220             : {
    3221       31587 :   static const std::string empty_string;
    3222       31587 :   if (const auto it = _ss_id_to_name.find(id);
    3223        1754 :       it == _ss_id_to_name.end())
    3224         241 :     return empty_string;
    3225             :   else
    3226       26496 :     return it->second;
    3227             : }
    3228             : 
    3229             : 
    3230     1267757 : std::string & BoundaryInfo::sideset_name(boundary_id_type id)
    3231             : {
    3232     1267757 :   return _ss_id_to_name[id];
    3233             : }
    3234             : 
    3235       25691 : const std::string & BoundaryInfo::get_nodeset_name(boundary_id_type id) const
    3236             : {
    3237       25691 :   static const std::string empty_string;
    3238       25691 :   if (const auto it = _ns_id_to_name.find(id);
    3239        1517 :       it == _ns_id_to_name.end())
    3240          40 :     return empty_string;
    3241             :   else
    3242       25218 :     return it->second;
    3243             : }
    3244             : 
    3245     1267524 : std::string & BoundaryInfo::nodeset_name(boundary_id_type id)
    3246             : {
    3247     1267524 :   return _ns_id_to_name[id];
    3248             : }
    3249             : 
    3250         269 : const std::string & BoundaryInfo::get_edgeset_name(boundary_id_type id) const
    3251             : {
    3252         269 :   static const std::string empty_string;
    3253         269 :   if (const auto it = _es_id_to_name.find(id);
    3254          23 :       it == _es_id_to_name.end())
    3255          21 :     return empty_string;
    3256             :   else
    3257          24 :     return it->second;
    3258             : }
    3259             : 
    3260             : 
    3261         934 : std::string & BoundaryInfo::edgeset_name(boundary_id_type id)
    3262             : {
    3263         934 :   return _es_id_to_name[id];
    3264             : }
    3265             : 
    3266        2240 : boundary_id_type BoundaryInfo::get_id_by_name(std::string_view name) const
    3267             : {
    3268             :   // Search sidesets
    3269        5600 :   for (const auto & [ss_id, ss_name] : _ss_id_to_name)
    3270        5536 :     if (ss_name == name)
    3271        2240 :       return ss_id;
    3272             : 
    3273             :   // Search nodesets
    3274           0 :   for (const auto & [ns_id, ns_name] : _ns_id_to_name)
    3275           0 :     if (ns_name == name)
    3276           0 :       return ns_id;
    3277             : 
    3278             :   // Search edgesets
    3279           0 :   for (const auto & [es_id, es_name] : _es_id_to_name)
    3280           0 :     if (es_name == name)
    3281           0 :       return es_id;
    3282             : 
    3283             :   // If we made it here without returning, we don't have a sideset,
    3284             :   // nodeset, or edgeset by the requested name, so return invalid_id
    3285           0 :   return invalid_id;
    3286             : }
    3287             : 
    3288             : 
    3289             : 
    3290        4402 : void BoundaryInfo::_find_id_maps(const std::set<boundary_id_type> & requested_boundary_ids,
    3291             :                                  dof_id_type first_free_node_id,
    3292             :                                  std::map<dof_id_type, dof_id_type> * node_id_map,
    3293             :                                  dof_id_type first_free_elem_id,
    3294             :                                  std::map<std::pair<dof_id_type, unsigned char>, dof_id_type> * side_id_map,
    3295             :                                  const std::set<subdomain_id_type> & subdomains_relative_to)
    3296             : {
    3297             :   // We'll do the same modulus trick that DistributedMesh uses to avoid
    3298             :   // id conflicts between different processors
    3299             :   dof_id_type
    3300        4402 :     next_node_id = first_free_node_id + this->processor_id(),
    3301        4402 :     next_elem_id = first_free_elem_id + this->processor_id();
    3302             : 
    3303             :   // For avoiding extraneous element side construction
    3304         248 :   ElemSideBuilder side_builder;
    3305             :   // Pull objects out of the loop to reduce heap operations
    3306             :   const Elem * side;
    3307             : 
    3308             :   // We'll pass through the mesh once first to build
    3309             :   // the maps and count boundary nodes and elements.
    3310             :   // To find local boundary nodes, we have to examine all elements
    3311             :   // here rather than just local elements, because it's possible to
    3312             :   // have a local boundary node that's not on a local boundary
    3313             :   // element, e.g. at the tip of a triangle.
    3314             : 
    3315             :   // We'll loop through two different ranges here: first all elements,
    3316             :   // looking for local nodes, and second through unpartitioned
    3317             :   // elements, looking for all remaining nodes.
    3318        4526 :   const MeshBase::const_element_iterator end_el = _mesh->elements_end();
    3319         124 :   bool hit_end_el = false;
    3320             :   const MeshBase::const_element_iterator end_unpartitioned_el =
    3321        4526 :     _mesh->pid_elements_end(DofObject::invalid_processor_id);
    3322             : 
    3323       13670 :   for (MeshBase::const_element_iterator el = _mesh->elements_begin();
    3324       97548 :        !hit_end_el || (el != end_unpartitioned_el); ++el)
    3325             :     {
    3326       92806 :       if ((el == end_el) && !hit_end_el)
    3327             :         {
    3328             :           // Note that we're done with local nodes and just looking
    3329             :           // for remaining unpartitioned nodes
    3330         124 :           hit_end_el = true;
    3331             : 
    3332             :           // Join up the local results from other processors
    3333        4402 :           if (side_id_map)
    3334        4402 :             this->comm().set_union(*side_id_map);
    3335        4402 :           if (node_id_map)
    3336        1917 :             this->comm().set_union(*node_id_map);
    3337             : 
    3338             :           // Finally we'll pass through any unpartitioned elements to add them
    3339             :           // to the maps and counts.
    3340        4402 :           next_node_id = first_free_node_id + this->n_processors();
    3341        4402 :           next_elem_id = first_free_elem_id + this->n_processors();
    3342             : 
    3343        8680 :           el = _mesh->pid_elements_begin(DofObject::invalid_processor_id);
    3344        4402 :           if (el == end_unpartitioned_el)
    3345         124 :             break;
    3346             :         }
    3347             : 
    3348       88404 :       const Elem * elem = *el;
    3349             : 
    3350             :       // If the subdomains_relative_to container has the
    3351             :       // invalid_subdomain_id, we fall back on the "old" behavior of
    3352             :       // adding sides regardless of this Elem's subdomain. Otherwise,
    3353             :       // if the subdomains_relative_to container doesn't contain the
    3354             :       // current Elem's subdomain_id(), we won't add any sides from
    3355             :       // it.
    3356       15390 :       if (!subdomains_relative_to.count(Elem::invalid_subdomain_id) &&
    3357       88518 :           !subdomains_relative_to.count(elem->subdomain_id()))
    3358       10424 :         continue;
    3359             : 
    3360      370038 :       for (auto s : elem->side_index_range())
    3361             :         {
    3362       15220 :           bool add_this_side = false;
    3363             : 
    3364             :           // Find all the boundary side ids for this Elem side.
    3365       30440 :           std::vector<boundary_id_type> bcids;
    3366      292874 :           this->boundary_ids(elem, s, bcids);
    3367             : 
    3368      341224 :           for (const boundary_id_type bcid : bcids)
    3369             :             {
    3370             :               // if the user wants this id, we want this side
    3371        3854 :               if (requested_boundary_ids.count(bcid))
    3372             :                 {
    3373        1642 :                   add_this_side = true;
    3374        1642 :                   break;
    3375             :                 }
    3376             :             }
    3377             : 
    3378             :           // We may still want to add this side if the user called
    3379             :           // sync() with no requested_boundary_ids. This corresponds
    3380             :           // to the "old" style of calling sync() in which the entire
    3381             :           // boundary was copied to the BoundaryMesh, and handles the
    3382             :           // case where elements on the geometric boundary are not in
    3383             :           // any sidesets.
    3384       15220 :           if (requested_boundary_ids.count(invalid_id) &&
    3385           0 :               elem->neighbor_ptr(s) == nullptr)
    3386           0 :             add_this_side = true;
    3387             : 
    3388      292874 :           if (add_this_side)
    3389             :             {
    3390             :               // We only assign ids for our own and for
    3391             :               // unpartitioned elements
    3392       29506 :               if (side_id_map &&
    3393       19654 :                   ((elem->processor_id() == this->processor_id()) ||
    3394         821 :                    (elem->processor_id() ==
    3395             :                     DofObject::invalid_processor_id)))
    3396             :                 {
    3397        1642 :                   std::pair<dof_id_type, unsigned char> side_pair(elem->id(), s);
    3398         821 :                   libmesh_assert (!side_id_map->count(side_pair));
    3399        9852 :                   (*side_id_map)[side_pair] = next_elem_id;
    3400       10673 :                   next_elem_id += this->n_processors() + 1;
    3401             :                 }
    3402             : 
    3403       27864 :               side = &side_builder(*elem, s);
    3404      110580 :               for (auto n : side->node_index_range())
    3405             :                 {
    3406        4882 :                   const Node & node = side->node_ref(n);
    3407             : 
    3408             :                   // In parallel we don't know enough to number
    3409             :                   // others' nodes ourselves.
    3410       87598 :                   if (!hit_end_el &&
    3411        9764 :                       (node.processor_id() != this->processor_id()))
    3412       53424 :                     continue;
    3413             : 
    3414       29292 :                   dof_id_type node_id = node.id();
    3415       29292 :                   if (node_id_map && !node_id_map->count(node_id))
    3416             :                     {
    3417        7452 :                       (*node_id_map)[node_id] = next_node_id;
    3418        8073 :                       next_node_id += this->n_processors() + 1;
    3419             :                     }
    3420             :                 }
    3421             :             }
    3422             :         }
    3423             :     }
    3424             : 
    3425             :   // FIXME: ought to renumber side/node_id_map image to be contiguous
    3426             :   // to save memory, also ought to reserve memory
    3427        4402 : }
    3428             : 
    3429        1349 : void BoundaryInfo::clear_stitched_boundary_side_ids (const boundary_id_type sideset_id,
    3430             :                                                      const boundary_id_type other_sideset_id,
    3431             :                                                      const bool clear_nodeset_data)
    3432             : {
    3433          38 :   auto end_it = _boundary_side_id.end();
    3434          38 :   auto it = _boundary_side_id.begin();
    3435             : 
    3436             :   // This predicate checks to see whether the pred_pr triplet's boundary ID matches sideset_id
    3437             :   // (other_sideset_id) *and* whether there is a boundary information triplet on the other side of
    3438             :   // the face whose boundary ID matches the other_sideset_id (sideset_id). We return a pair where
    3439             :   // first is a boolean indicating our condition is true or false, and second is an iterator to the
    3440             :   // neighboring triplet if our condition is true
    3441             :   auto predicate =
    3442       70216 :       [sideset_id, other_sideset_id](
    3443             :           const std::pair<const Elem *, std::pair<unsigned short int, boundary_id_type>> & pred_pr,
    3444             :           const std::multimap<const Elem *, std::pair<unsigned short int, boundary_id_type>> &
    3445        7256 :               pred_container) {
    3446       74408 :         const Elem & elem = *pred_pr.first;
    3447       74408 :         const auto elem_side = pred_pr.second.first;
    3448       74408 :         const Elem * const other_elem = elem.neighbor_ptr(elem_side);
    3449       74408 :         if (!other_elem)
    3450        1880 :           return std::make_pair(false, pred_container.end());
    3451             : 
    3452        7668 :         const auto elem_side_bnd_id = pred_pr.second.second;
    3453         216 :         auto other_elem_side_bnd_id = BoundaryInfo::invalid_id;
    3454        7668 :         if (elem_side_bnd_id == sideset_id)
    3455         165 :           other_elem_side_bnd_id = other_sideset_id;
    3456        2826 :         else if (elem_side_bnd_id == other_sideset_id)
    3457          51 :           other_elem_side_bnd_id = sideset_id;
    3458             :         else
    3459           0 :           return std::make_pair(false, pred_container.end());
    3460             : 
    3461        7668 :         const auto other_elem_side = other_elem->which_neighbor_am_i(&elem);
    3462             :         const typename std::decay<decltype(pred_container)>::type::value_type other_sideset_info(
    3463         432 :             other_elem, std::make_pair(other_elem_side, other_elem_side_bnd_id));
    3464         432 :         auto other_range = pred_container.equal_range(other_elem);
    3465         216 :         libmesh_assert_msg(
    3466             :             other_range.first != other_range.second,
    3467             :             "No matching sideset information for other element in boundary information");
    3468        7668 :         auto other_it = std::find(other_range.first, other_range.second, other_sideset_info);
    3469         216 :         libmesh_assert_msg(
    3470             :             other_it != pred_container.end(),
    3471             :             "No matching sideset information for other element in boundary information");
    3472         216 :         return std::make_pair(true, other_it);
    3473        1349 :       };
    3474             : 
    3475       75757 :   for (; it != end_it;)
    3476             :   {
    3477       76504 :     auto pred_result = predicate(*it, _boundary_side_id);
    3478       74408 :     if (pred_result.first)
    3479             :     {
    3480             :       // First erase associated nodeset information.  Do it from both
    3481             :       // sides, so we get any higher-order nodes if we're looking at
    3482             :       // them from a lower-order side, and so we only remove the two
    3483             :       // boundary ids used for stitching.
    3484        7668 :       if (clear_nodeset_data)
    3485             :       {
    3486        7668 :         const Elem & elem = *it->first;
    3487        7668 :         const Elem & neigh = *pred_result.second->first;
    3488        7668 :         const auto elem_side = it->second.first;
    3489        7668 :         const boundary_id_type neigh_side = pred_result.second->second.first;
    3490        7668 :         const auto elem_bcid = it->second.second;
    3491        7668 :         const boundary_id_type neigh_bcid = pred_result.second->second.second;
    3492             : 
    3493       54215 :         for (const auto local_node_num : elem.nodes_on_side(elem_side))
    3494       47634 :           this->remove_node(elem.node_ptr(local_node_num), elem_bcid);
    3495             : 
    3496       54208 :         for (const auto local_node_num : neigh.nodes_on_side(neigh_side))
    3497       47631 :           this->remove_node(neigh.node_ptr(local_node_num), neigh_bcid);
    3498             :       }
    3499             : 
    3500             :       // Now erase the sideset information for our element and its
    3501             :       // neighbor, together.  This is safe since a multimap doesn't
    3502             :       // invalidate iterators.
    3503        7452 :       _boundary_side_id.erase(pred_result.second);
    3504        7452 :       it = _boundary_side_id.erase(it);
    3505             :     }
    3506             :     else
    3507        1880 :       ++it;
    3508             :   }
    3509             : 
    3510             :   // Removing stitched-away boundary ids might have removed an id
    3511             :   // *entirely*, so we need to recompute boundary id sets to check
    3512             :   // for that.
    3513        1349 :   this->regenerate_id_sets();
    3514        1349 :   this->libmesh_assert_valid_multimaps();
    3515        1349 : }
    3516             : 
    3517             : const std::set<boundary_id_type> &
    3518         568 : BoundaryInfo::get_global_boundary_ids() const
    3519             : {
    3520          16 :   libmesh_assert(_mesh && _mesh->is_prepared());
    3521         568 :   return _global_boundary_ids;
    3522             : }
    3523             : 
    3524             : void
    3525        3731 : BoundaryInfo::libmesh_assert_valid_multimaps() const
    3526             : {
    3527             : #ifndef NDEBUG
    3528         424 :   auto verify_multimap = [](const auto & themap) {
    3529       13708 :     for (const auto & [key, val] : themap)
    3530             :       {
    3531       13284 :         auto range = themap.equal_range(key);
    3532             : 
    3533       13284 :         int count = 0;
    3534       41356 :         for (auto it = range.first; it != range.second; ++it)
    3535       28072 :           if (it->second == val)
    3536       13284 :             ++count;
    3537             : 
    3538       13284 :         libmesh_assert(count == 1);
    3539             :       }
    3540         424 :   };
    3541             : 
    3542         106 :   verify_multimap(this->_boundary_node_id);
    3543         106 :   verify_multimap(this->_boundary_edge_id);
    3544         106 :   verify_multimap(this->_boundary_shellface_id);
    3545         106 :   verify_multimap(this->_boundary_side_id);
    3546             : #else
    3547        3625 :   return;
    3548             : #endif
    3549         106 : }
    3550             : 
    3551             : } // namespace libMesh
 |