LCOV - code coverage report
Current view: top level - src/mesh - boundary_info.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4529 (d6a6ff) with base 9779e6 Lines: 1188 1634 72.7 %
Date: 2026-09-01 14:14:34 Functions: 116 158 73.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14