LCOV - code coverage report
Current view: top level - src/mesh - boundary_info.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4256 (26f7e2) with base d735cc Lines: 1090 1523 71.6 %
Date: 2025-10-01 13:47:18 Functions: 108 159 67.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14