LCOV - code coverage report
Current view: top level - src/mesh - boundary_info.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4490 (fb4270) with base e59def Lines: 1182 1624 72.8 %
Date: 2026-07-23 17:02:07 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      221890 : void erase_if(std::multimap<Key,T> & map, Key k, Pred pred)
      50             : {
      51       60248 :   auto rng = map.equal_range(k);
      52       60248 :   auto it = rng.first;
      53      262699 :   while (it != rng.second)
      54             :     {
      55       39801 :       if (pred(it->second))
      56        5880 :         it = map.erase(it);
      57             :       else
      58        8807 :         ++it;
      59             :     }
      60      221890 : }
      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       36434 : BoundaryInfo::BoundaryInfo(MeshBase & m) :
     105             :   ParallelObject(m.comm()),
     106       15294 :   _mesh (&m),
     107       57558 :   _children_on_boundary(false)
     108             : {
     109       36434 : }
     110             : 
     111        2639 : BoundaryInfo & BoundaryInfo::operator=(const BoundaryInfo & other_boundary_info)
     112             : {
     113             :   // Overwrite any preexisting boundary info
     114        2639 :   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       63232 :   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        2674 :   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        2653 :   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       65669 :   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        2639 :   _children_on_boundary = other_boundary_info._children_on_boundary;
     143             : 
     144         806 :   _boundary_ids = other_boundary_info._boundary_ids;
     145         806 :   _global_boundary_ids = other_boundary_info._global_boundary_ids;
     146         806 :   _side_boundary_ids = other_boundary_info._side_boundary_ids;
     147         806 :   _node_boundary_ids = other_boundary_info._node_boundary_ids;
     148         806 :   _edge_boundary_ids = other_boundary_info._edge_boundary_ids;
     149         806 :   _shellface_boundary_ids = other_boundary_info._shellface_boundary_ids;
     150             : 
     151         806 :   _ss_id_to_name = other_boundary_info._ss_id_to_name;
     152         806 :   _ns_id_to_name = other_boundary_info._ns_id_to_name;
     153         806 :   _es_id_to_name = other_boundary_info._es_id_to_name;
     154             : 
     155        2639 :   return *this;
     156             : }
     157             : 
     158             : 
     159        2102 : bool BoundaryInfo::operator==(const BoundaryInfo & other_boundary_info) const
     160             : {
     161       86625 :   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       86356 :   for (const auto & [node, bid] : this->_boundary_node_id)
     170             :     {
     171             :       const Node * other_node =
     172       84282 :         other_boundary_info._mesh->query_node_ptr(node->id());
     173       84282 :       if (!other_node)
     174           8 :         return false;
     175       84261 :       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        2074 :   };
     201             : 
     202        2119 :   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        2119 :   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        2074 :   };
     238             : 
     239       49534 :   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       49534 :   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        2074 :   };
     275             : 
     276        2092 :   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        2092 :   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        2074 :   if (_children_on_boundary != other_boundary_info._children_on_boundary)
     291           0 :     return false;
     292             : 
     293       12444 :   auto compare_sets = [](const auto & set1, const auto & set2)
     294             :   {
     295       12444 :     if (set1.size() != set2.size())
     296           0 :       return false;
     297       43250 :     for (boundary_id_type bid : set1)
     298       12502 :       if (!set2.count(bid))
     299           0 :         return false;
     300             : 
     301       10032 :     return true;
     302             :   };
     303             : 
     304        4186 :   if (!compare_sets(_boundary_ids,
     305        3092 :                     other_boundary_info._boundary_ids) ||
     306        2074 :       !compare_sets(_global_boundary_ids,
     307        2476 :                     other_boundary_info._global_boundary_ids) ||
     308        2074 :       !compare_sets(_edge_boundary_ids,
     309        2476 :                     other_boundary_info._edge_boundary_ids) ||
     310        2074 :       !compare_sets(_node_boundary_ids,
     311        2476 :                     other_boundary_info._node_boundary_ids) ||
     312        2074 :       !compare_sets(_shellface_boundary_ids,
     313        5606 :                     other_boundary_info._shellface_boundary_ids) ||
     314        2074 :       !compare_sets(_side_boundary_ids,
     315        2074 :                     other_boundary_info._side_boundary_ids))
     316           0 :     return false;
     317             : 
     318        6222 :   auto compare_maps = [](const auto & map1, const auto & map2)
     319             :   {
     320        6222 :     if (map1.size() != map2.size())
     321           0 :       return false;
     322       20848 :     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        3168 :     return true;
     328             :   };
     329             : 
     330        4186 :   if (!compare_maps(_ss_id_to_name,
     331        3092 :                     other_boundary_info._ss_id_to_name) ||
     332        2074 :       !compare_maps(_ns_id_to_name,
     333        4148 :                     other_boundary_info._ns_id_to_name) ||
     334        2074 :       !compare_maps(_es_id_to_name,
     335        2074 :                     other_boundary_info._es_id_to_name))
     336           0 :     return false;
     337             : 
     338        1056 :   return true;
     339             : }
     340             : 
     341             : 
     342       30591 : BoundaryInfo::~BoundaryInfo() = default;
     343             : 
     344             : 
     345             : 
     346      107581 : void BoundaryInfo::clear()
     347             : {
     348       31593 :   _boundary_node_id.clear();
     349       31593 :   _boundary_side_id.clear();
     350       31593 :   _boundary_edge_id.clear();
     351       31593 :   _boundary_shellface_id.clear();
     352       31593 :   _boundary_ids.clear();
     353       31593 :   _side_boundary_ids.clear();
     354       31593 :   _node_boundary_ids.clear();
     355       31593 :   _edge_boundary_ids.clear();
     356       31593 :   _shellface_boundary_ids.clear();
     357       31593 :   _ss_id_to_name.clear();
     358       31593 :   _ns_id_to_name.clear();
     359       31593 :   _es_id_to_name.clear();
     360      107581 : }
     361             : 
     362             : 
     363             : 
     364       46478 : void BoundaryInfo::regenerate_id_sets()
     365             : {
     366       27312 :   const auto old_ss_id_to_name = _ss_id_to_name;
     367       27312 :   const auto old_ns_id_to_name = _ns_id_to_name;
     368       27312 :   const auto old_es_id_to_name = _es_id_to_name;
     369             : 
     370             :   // Clear the old caches
     371       13656 :   _boundary_ids.clear();
     372       13656 :   _side_boundary_ids.clear();
     373       13656 :   _node_boundary_ids.clear();
     374       13656 :   _edge_boundary_ids.clear();
     375       13656 :   _shellface_boundary_ids.clear();
     376       27296 :   _ss_id_to_name.clear();
     377       27296 :   _ns_id_to_name.clear();
     378       27296 :   _es_id_to_name.clear();
     379             : 
     380             :   // Loop over id maps to regenerate each set.
     381     1831994 :   for (const auto & pr : _boundary_node_id)
     382             :     {
     383     1785516 :       const boundary_id_type id = pr.second;
     384     1275543 :       _boundary_ids.insert(id);
     385     1275543 :       _node_boundary_ids.insert(id);
     386     1785516 :       if (const auto it = old_ns_id_to_name.find(id);
     387      510021 :           it != old_ns_id_to_name.end())
     388     1630675 :         _ns_id_to_name.emplace(id, it->second);
     389             :     }
     390             : 
     391       46989 :   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      904942 :   for (const auto & pr : _boundary_side_id)
     402             :     {
     403      858464 :       const boundary_id_type id = pr.second.second;
     404      612718 :       _boundary_ids.insert(id);
     405      612718 :       _side_boundary_ids.insert(id);
     406      858464 :       if (const auto it = old_ss_id_to_name.find(id);
     407      246526 :           it != old_ss_id_to_name.end())
     408      665688 :         _ss_id_to_name.emplace(id, it->second);
     409             :     }
     410             : 
     411       93084 :   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       13656 :   libmesh_assert(_mesh);
     420       46478 :   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       46478 :   this->synchronize_global_id_set();
     428       46478 : }
     429             : 
     430             : 
     431             : 
     432       46485 : void BoundaryInfo::synchronize_global_id_set()
     433             : {
     434             :   // Handle global data
     435       13658 :   _global_boundary_ids = _boundary_ids;
     436       13658 :   libmesh_assert(_mesh);
     437       46485 :   if (!_mesh->is_serial())
     438        1422 :     _communicator.set_union(_global_boundary_ids);
     439             : 
     440       46485 :   _mesh->_preparation.has_boundary_id_sets = true;
     441       46485 : }
     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     1695216 : void BoundaryInfo::add_node(const Node * node,
     996             :                             const boundary_id_type id)
     997             : {
     998     1695216 :   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     2382510 :   for (const auto & pr : as_range(_boundary_node_id.equal_range(node)))
    1005      713681 :     if (pr.second == id)
    1006        8766 :       return;
    1007             : 
    1008      474369 :   _boundary_node_id.emplace(node, id);
    1009     1194460 :   _boundary_ids.insert(id);
    1010     1194460 :   _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      209594 :   _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     2498974 : void BoundaryInfo::add_edge(const Elem * elem,
    1106             :                             const unsigned short int edge,
    1107             :                             const std::vector<boundary_id_type> & ids)
    1108             : {
    1109     2498974 :   if (ids.empty())
    1110     2498974 :     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      854530 : void BoundaryInfo::add_shellface(const Elem * elem,
    1199             :                                  const unsigned short int shellface,
    1200             :                                  const std::vector<boundary_id_type> & ids)
    1201             : {
    1202      854530 :   if (ids.empty())
    1203      854530 :     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      684194 : void BoundaryInfo::add_side(const Elem * elem,
    1261             :                             const unsigned short int side,
    1262             :                             const boundary_id_type id)
    1263             : {
    1264      195443 :   libmesh_assert(elem);
    1265             : 
    1266             :   // Only add BCs for sides that exist.
    1267      195443 :   libmesh_assert_less (side, elem->n_sides());
    1268             : 
    1269      684194 :   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      995427 :   for (const auto & pr : as_range(_boundary_side_id.equal_range(elem)))
    1275      321757 :     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      673670 :   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      673663 :   _boundary_side_id.emplace(elem, std::make_pair(side, id));
    1300      481732 :   _boundary_ids.insert(id);
    1301      481732 :   _side_boundary_ids.insert(id); // Also add this ID to the set of side boundary IDs
    1302             : }
    1303             : 
    1304             : 
    1305             : 
    1306     1768503 : void BoundaryInfo::add_side(const Elem * elem,
    1307             :                             const unsigned short int side,
    1308             :                             const std::vector<boundary_id_type> & ids)
    1309             : {
    1310     1768503 :   if (ids.empty())
    1311     1611473 :     return;
    1312             : 
    1313       44494 :   libmesh_assert(elem);
    1314             : 
    1315             :   // Only add BCs for sides that exist.
    1316       44494 :   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      157030 :   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       44492 :   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      201515 :   std::vector<boundary_id_type> unique_ids(ids.begin(), ids.end());
    1348      157023 :   std::sort(unique_ids.begin(), unique_ids.end());
    1349             :   std::vector<boundary_id_type>::iterator new_end =
    1350      157023 :     std::unique(unique_ids.begin(), unique_ids.end());
    1351             : 
    1352      314046 :   for (auto & id : as_range(unique_ids.begin(), new_end))
    1353             :     {
    1354      157023 :       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       44492 :       bool already_inserted = false;
    1360      167707 :       for (const auto & pr : as_range(bounds))
    1361       10684 :         if (pr.second.first == side && pr.second.second == id)
    1362             :           {
    1363           0 :             already_inserted = true;
    1364           0 :             break;
    1365             :           }
    1366      157023 :       if (already_inserted)
    1367           0 :         continue;
    1368             : 
    1369      157023 :       _boundary_side_id.emplace(elem, std::make_pair(side, id));
    1370      112531 :       _boundary_ids.insert(id);
    1371      112531 :       _side_boundary_ids.insert(id); // Also add this ID to the set of side boundary IDs
    1372             :     }
    1373             : }
    1374             : 
    1375             : 
    1376             : 
    1377      169064 : bool BoundaryInfo::has_boundary_id(const Node * const node,
    1378             :                                    const boundary_id_type id) const
    1379             : {
    1380      245092 :   for (const auto & pr : as_range(_boundary_node_id.equal_range(node)))
    1381      244861 :     if (pr.second == id)
    1382       60997 :       return true;
    1383             : 
    1384          66 :   return false;
    1385             : }
    1386             : 
    1387             : 
    1388             : 
    1389    29894724 : 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    27836844 :   vec_to_fill.clear();
    1394             : 
    1395    34024340 :   for (const auto & pr : as_range(_boundary_node_id.equal_range(node)))
    1396     4129616 :     vec_to_fill.push_back(pr.second);
    1397    29894724 : }
    1398             : 
    1399             : 
    1400             : 
    1401      319072 : unsigned int BoundaryInfo::n_boundary_ids(const Node * node) const
    1402             : {
    1403      158890 :   auto pos = _boundary_node_id.equal_range(node);
    1404      477962 :   return cast_int<unsigned int>(std::distance(pos.first, pos.second));
    1405             : }
    1406             : 
    1407             : 
    1408             : 
    1409    24741531 : 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    21785870 :   libmesh_assert(elem);
    1414             : 
    1415             :   // Clear out any previous contents
    1416    21785870 :   vec_to_fill.clear();
    1417             : 
    1418             :   // Only query BCs for edges that exist.
    1419    21785870 :   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    24741531 :   const Elem * searched_elem = elem;
    1424             : #ifdef LIBMESH_ENABLE_AMR
    1425    24741531 :   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    11000125 :       bool found_boundary_edge = false;
    1431    59814176 :       for (auto side : elem->side_index_range())
    1432             :         {
    1433    48761267 :           if (elem->is_edge_on_side(edge,side))
    1434             :             {
    1435    16062547 :               if (elem->neighbor_ptr(side) == nullptr)
    1436             :                 {
    1437      709975 :                   searched_elem = elem->top_parent ();
    1438      652060 :                   found_boundary_edge = true;
    1439      652060 :                   break;
    1440             :                 }
    1441             :             }
    1442             :         }
    1443             : 
    1444    11000125 :       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    18635190 :           while (searched_elem->parent() != nullptr)
    1451             :             {
    1452    16294232 :               const Elem * parent = searched_elem->parent();
    1453    17376161 :               if (parent->is_child_on_edge(parent->which_child_am_i(searched_elem), edge) == false)
    1454    10229644 :                 return;
    1455     7146517 :               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    14520827 :   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     1224936 : unsigned int BoundaryInfo::n_edge_boundary_ids (const Elem * const elem,
    1470             :                                                 const unsigned short int edge) const
    1471             : {
    1472     1224240 :   std::vector<boundary_id_type> ids;
    1473     1224936 :   this->edge_boundary_ids(elem, edge, ids);
    1474     1837104 :   return cast_int<unsigned int>(ids.size());
    1475             : }
    1476             : 
    1477             : 
    1478             : 
    1479    22731360 : 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    20936294 :   libmesh_assert(elem);
    1484             : 
    1485             :   // Only query BCs for edges that exist.
    1486    20936294 :   libmesh_assert_less (edge, elem->n_edges());
    1487             : 
    1488             :   // Clear out any previous contents
    1489    20936294 :   vec_to_fill.clear();
    1490             : 
    1491             :   // Only level-0 elements store BCs.
    1492    23435268 :   if (elem->parent())
    1493    10800152 :     return;
    1494             : 
    1495             :   // Check each element in the range to see if its edge matches the requested edge.
    1496    11741464 :   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     9148100 : 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     8201632 :   libmesh_assert(elem);
    1508             : 
    1509             :   // Shells only have 2 faces
    1510     8201632 :   libmesh_assert_less(shellface, 2);
    1511             : 
    1512             :   // Clear out any previous contents
    1513     8201632 :   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     9148100 :   const Elem * searched_elem = elem;
    1518             : #ifdef LIBMESH_ENABLE_AMR
    1519     9148100 :   if (elem->level() != 0)
    1520             :     {
    1521    23922028 :       while (searched_elem->parent() != nullptr)
    1522             :         {
    1523    17320816 :           const Elem * parent = searched_elem->parent();
    1524    18317210 :           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     9327924 :   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     9148100 : }
    1534             : 
    1535             : 
    1536             : 
    1537      401076 : unsigned int BoundaryInfo::n_shellface_boundary_ids (const Elem * const elem,
    1538             :                                                      const unsigned short int shellface) const
    1539             : {
    1540      400184 :   std::vector<boundary_id_type> ids;
    1541      401076 :   this->shellface_boundary_ids(elem, shellface, ids);
    1542      604496 :   return cast_int<unsigned int>(ids.size());
    1543             : }
    1544             : 
    1545             : 
    1546             : 
    1547     8550608 : 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     7937762 :   libmesh_assert(elem);
    1552             : 
    1553             :   // Shells only have 2 faces
    1554     7937762 :   libmesh_assert_less(shellface, 2);
    1555             : 
    1556             :   // Clear out any previous contents
    1557     7937762 :   vec_to_fill.clear();
    1558             : 
    1559             :   // Only level-0 elements store BCs.
    1560     8792292 :   if (elem->parent())
    1561     4748184 :     return;
    1562             : 
    1563             :   // Check each element in the range to see if its shellface matches the requested shellface.
    1564     3760904 :   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     1933688 : bool BoundaryInfo::has_boundary_id(const Elem * const elem,
    1572             :                                    const unsigned short int side,
    1573             :                                    const boundary_id_type id) const
    1574             : {
    1575      534043 :   std::vector<boundary_id_type> ids;
    1576     1933688 :   this->boundary_ids(elem, side, ids);
    1577     2725912 :   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    20280730 : 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    16823152 :   libmesh_assert(elem);
    1683             : 
    1684             :   // Only query BCs for sides that exist.
    1685    16823152 :   libmesh_assert_less (side, elem->n_sides());
    1686             : 
    1687             :   // Clear out any previous contents
    1688    16823152 :   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    20280730 :   const Elem * searched_elem = elem;
    1695             : 
    1696             : #ifdef LIBMESH_ENABLE_AMR
    1697             : 
    1698    20280730 :   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    10334882 :     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     8737191 :           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    10681984 :     if (elem->neighbor_ptr(side) == nullptr)
    1730      486140 :       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    18206583 :       while (searched_elem->parent() != nullptr)
    1734             :       {
    1735    15354369 :         const Elem * parent = searched_elem->parent();
    1736    16571170 :         if (parent->is_child_on_side(parent->which_child_am_i(searched_elem), side) == false)
    1737     8083327 :           return;
    1738             : 
    1739     7834916 :         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    19018472 :   for (const auto & pr : as_range(_boundary_side_id.equal_range(searched_elem)))
    1747     7474933 :     if (pr.second.first == side)
    1748     2370235 :       vec_to_fill.push_back(pr.second.second);
    1749             : }
    1750             : 
    1751             : 
    1752             : 
    1753             : 
    1754       17754 : unsigned int BoundaryInfo::n_boundary_ids (const Elem * const elem,
    1755             :                                            const unsigned short int side) const
    1756             : {
    1757       10064 :   std::vector<boundary_id_type> ids;
    1758       17754 :   this->boundary_ids(elem, side, ids);
    1759       23666 :   return cast_int<unsigned int>(ids.size());
    1760             : }
    1761             : 
    1762             : 
    1763     1573112 : unsigned int BoundaryInfo::n_raw_boundary_ids (const Elem * const elem,
    1764             :                                                const unsigned short int side) const
    1765             : {
    1766     1362996 :   std::vector<boundary_id_type> ids;
    1767     1573112 :   this->raw_boundary_ids(elem, side, ids);
    1768     2274176 :   return cast_int<unsigned int>(ids.size());
    1769             : }
    1770             : 
    1771             : 
    1772             : 
    1773    18828783 : 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    16632068 :   libmesh_assert(elem);
    1778             : 
    1779             :   // Only query BCs for sides that exist.
    1780    16632068 :   libmesh_assert_less (side, elem->n_sides());
    1781             : 
    1782             :   // Clear out any previous contents
    1783    16632068 :   vec_to_fill.clear();
    1784             : 
    1785             :   // Only level-0 elements store BCs.
    1786    19795312 :   if (elem->parent() && !_children_on_boundary)
    1787     9277496 :     return;
    1788             : 
    1789             :   // Check each element in the range to see if its side matches the requested side.
    1790    12987836 :   for (const auto & pr : as_range(_boundary_side_id.equal_range(elem)))
    1791     3720173 :     if (pr.second.first == side)
    1792      839871 :       vec_to_fill.push_back(pr.second.second);
    1793             : }
    1794             : 
    1795             : 
    1796             : 
    1797      427265 : void BoundaryInfo::copy_boundary_ids (const BoundaryInfo & old_boundary_info,
    1798             :                                       const Elem * const old_elem,
    1799             :                                       const Elem * const new_elem)
    1800             : {
    1801      120842 :   libmesh_assert_equal_to (old_elem->n_sides(), new_elem->n_sides());
    1802      120842 :   libmesh_assert_equal_to (old_elem->n_edges(), new_elem->n_edges());
    1803             : 
    1804      241684 :   std::vector<boundary_id_type> bndry_ids;
    1805             : 
    1806     2173253 :   for (auto s : old_elem->side_index_range())
    1807             :     {
    1808     1745988 :       old_boundary_info.raw_boundary_ids (old_elem, s, bndry_ids);
    1809     1745988 :       this->add_side (new_elem, s, bndry_ids);
    1810             :     }
    1811             : 
    1812     2926239 :   for (auto e : old_elem->edge_index_range())
    1813             :     {
    1814     2498974 :       old_boundary_info.raw_edge_boundary_ids (old_elem, e, bndry_ids);
    1815     2498974 :       this->add_edge (new_elem, e, bndry_ids);
    1816             :     }
    1817             : 
    1818     1281795 :   for (unsigned short sf=0; sf != 2; sf++)
    1819             :     {
    1820      854530 :       old_boundary_info.raw_shellface_boundary_ids (old_elem, sf, bndry_ids);
    1821      854530 :       this->add_shellface (new_elem, sf, bndry_ids);
    1822             :     }
    1823      427265 : }
    1824             : 
    1825             : 
    1826             : 
    1827      804855 : void BoundaryInfo::remove (const Node * node)
    1828             : {
    1829      239086 :   libmesh_assert(node);
    1830             : 
    1831             :   // Erase everything associated with node
    1832      239086 :   _boundary_node_id.erase (node);
    1833      804855 : }
    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      860101 : void BoundaryInfo::remove (const Elem * elem)
    1851             : {
    1852      248933 :   libmesh_assert(elem);
    1853             : 
    1854             :   // Erase everything associated with elem
    1855      248933 :   _boundary_edge_id.erase (elem);
    1856      248933 :   _boundary_side_id.erase (elem);
    1857      248933 :   _boundary_shellface_id.erase (elem);
    1858      860101 : }
    1859             : 
    1860             : 
    1861             : 
    1862      140420 : void BoundaryInfo::remove_edge (const Elem * elem,
    1863             :                                 const unsigned short int edge)
    1864             : {
    1865       37948 :   libmesh_assert(elem);
    1866             : 
    1867             :   // Only touch BCs for edges that exist.
    1868       37948 :   libmesh_assert_less (edge, elem->n_edges());
    1869             : 
    1870             :   // Only level 0 elements are stored in BoundaryInfo.
    1871       37948 :   libmesh_assert_equal_to (elem->level(), 0);
    1872             : 
    1873             :   // Erase (elem, edge, *) entries from map.
    1874      140420 :   erase_if(_boundary_edge_id, elem,
    1875           0 :            [edge](decltype(_boundary_edge_id)::mapped_type & pr)
    1876           0 :            {return pr.first == edge;});
    1877      140420 : }
    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       69696 : void BoundaryInfo::remove_side (const Elem * elem,
    1938             :                                 const unsigned short int side)
    1939             : {
    1940       18936 :   libmesh_assert(elem);
    1941             : 
    1942             :   // Only touch BCs for sides that exist.
    1943       18936 :   libmesh_assert_less (side, elem->n_sides());
    1944             : 
    1945             :   // Erase (elem, side, *) entries from map.
    1946       69696 :   erase_if(_boundary_side_id, elem,
    1947       19788 :            [side](decltype(_boundary_side_id)::mapped_type & pr)
    1948       14709 :            {return pr.first == side;});
    1949       69696 : }
    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             :     }
    2189             : 
    2190         196 :   renumber_name(_ss_id_to_name, old_id, new_id);
    2191         196 :   renumber_name(_ns_id_to_name, old_id, new_id);
    2192         196 :   renumber_name(_es_id_to_name, old_id, new_id);
    2193             : 
    2194         196 :   this->libmesh_assert_valid_multimaps();
    2195             : }
    2196             : 
    2197             : 
    2198             : 
    2199          14 : void BoundaryInfo::renumber_side_id (boundary_id_type old_id,
    2200             :                                      boundary_id_type new_id)
    2201             : {
    2202             :   // If the IDs are the same, this is a no-op.
    2203          14 :   if (old_id == new_id)
    2204           0 :     return;
    2205             : 
    2206           4 :   bool found_side = false;
    2207         350 :   for (auto & p : _boundary_side_id)
    2208         336 :     if (p.second.second == old_id)
    2209             :       {
    2210             :         // If we already have this id on this side, we don't want to
    2211             :         // create a duplicate in our multimap
    2212          56 :         this->remove_side(p.first, p.second.first, new_id);
    2213          56 :         p.second.second = new_id;
    2214          16 :         found_side = true;
    2215             :       }
    2216           4 :   _side_boundary_ids.erase(old_id);
    2217             : 
    2218          14 :   if (found_side)
    2219             :     {
    2220          10 :       _side_boundary_ids.insert(new_id);
    2221             : 
    2222           4 :       if (!_shellface_boundary_ids.count(old_id) &&
    2223           8 :           !_edge_boundary_ids.count(old_id) &&
    2224           4 :           !_node_boundary_ids.count(old_id))
    2225             :         {
    2226           0 :           _boundary_ids.erase(old_id);
    2227             :         }
    2228          10 :       _boundary_ids.insert(new_id);
    2229             :     }
    2230             : 
    2231          14 :   renumber_name(_ss_id_to_name, old_id, new_id);
    2232             : 
    2233          14 :   this->libmesh_assert_valid_multimaps();
    2234             : }
    2235             : 
    2236             : 
    2237             : 
    2238          14 : void BoundaryInfo::renumber_edge_id (boundary_id_type old_id,
    2239             :                                      boundary_id_type new_id)
    2240             : {
    2241             :   // If the IDs are the same, this is a no-op.
    2242          14 :   if (old_id == new_id)
    2243           0 :     return;
    2244             : 
    2245           4 :   bool found_edge = false;
    2246          42 :   for (auto & p : _boundary_edge_id)
    2247          28 :     if (p.second.second == old_id)
    2248             :       {
    2249             :         // If we already have this id on this edge, we don't want to
    2250             :         // create a duplicate in our multimap
    2251          14 :         this->remove_edge(p.first, p.second.first, new_id);
    2252          14 :         p.second.second = new_id;
    2253           4 :         found_edge = true;
    2254             :       }
    2255           4 :   _edge_boundary_ids.erase(old_id);
    2256             : 
    2257          14 :   if (found_edge)
    2258             :     {
    2259          10 :       _edge_boundary_ids.insert(new_id);
    2260             : 
    2261           4 :       if (!_shellface_boundary_ids.count(old_id) &&
    2262           4 :           !_side_boundary_ids.count(old_id) &&
    2263           0 :           !_node_boundary_ids.count(old_id))
    2264             :         {
    2265           0 :           _boundary_ids.erase(old_id);
    2266             :         }
    2267          10 :       _boundary_ids.insert(new_id);
    2268             :     }
    2269             : 
    2270          14 :   renumber_name(_es_id_to_name, old_id, new_id);
    2271             : 
    2272          14 :   this->libmesh_assert_valid_multimaps();
    2273             : }
    2274             : 
    2275             : 
    2276             : 
    2277           0 : void BoundaryInfo::renumber_shellface_id (boundary_id_type old_id,
    2278             :                                           boundary_id_type new_id)
    2279             : {
    2280             :   // If the IDs are the same, this is a no-op.
    2281           0 :   if (old_id == new_id)
    2282           0 :     return;
    2283             : 
    2284           0 :   bool found_shellface = false;
    2285           0 :   for (auto & p : _boundary_shellface_id)
    2286           0 :     if (p.second.second == old_id)
    2287             :       {
    2288             :         // If we already have this id on this shellface, we don't want
    2289             :         // to create a duplicate in our multimap
    2290           0 :         this->remove_shellface(p.first, p.second.first, new_id);
    2291           0 :         p.second.second = new_id;
    2292           0 :         found_shellface = true;
    2293             :       }
    2294           0 :   _shellface_boundary_ids.erase(old_id);
    2295             : 
    2296           0 :   if (found_shellface)
    2297             :     {
    2298           0 :       _shellface_boundary_ids.insert(new_id);
    2299             : 
    2300           0 :       if (!_edge_boundary_ids.count(old_id) &&
    2301           0 :           !_side_boundary_ids.count(old_id) &&
    2302           0 :           !_node_boundary_ids.count(old_id))
    2303             :         {
    2304           0 :           _boundary_ids.erase(old_id);
    2305             :         }
    2306           0 :       _boundary_ids.insert(new_id);
    2307             :     }
    2308             : 
    2309           0 :   this->libmesh_assert_valid_multimaps();
    2310             : }
    2311             : 
    2312             : 
    2313             : 
    2314          14 : void BoundaryInfo::renumber_node_id (boundary_id_type old_id,
    2315             :                                      boundary_id_type new_id)
    2316             : {
    2317             :   // If the IDs are the same, this is a no-op.
    2318          14 :   if (old_id == new_id)
    2319           0 :     return;
    2320             : 
    2321           4 :   bool found_node = false;
    2322         770 :   for (auto & p : _boundary_node_id)
    2323         756 :     if (p.second == old_id)
    2324             :       {
    2325             :         // If we already have this id on this node, we don't want to
    2326             :         // create a duplicate in our multimap
    2327         126 :         this->remove_node(p.first, new_id);
    2328         126 :         p.second = new_id;
    2329          36 :         found_node = true;
    2330             :       }
    2331           4 :   _node_boundary_ids.erase(old_id);
    2332             : 
    2333          14 :   if (found_node)
    2334             :     {
    2335          10 :       _node_boundary_ids.insert(new_id);
    2336             : 
    2337           4 :       if (!_shellface_boundary_ids.count(old_id) &&
    2338           8 :           !_side_boundary_ids.count(old_id) &&
    2339           4 :           !_edge_boundary_ids.count(old_id))
    2340             :         {
    2341           4 :           _boundary_ids.erase(old_id);
    2342             :         }
    2343          10 :       _boundary_ids.insert(new_id);
    2344             :     }
    2345             : 
    2346          14 :   renumber_name(_ns_id_to_name, old_id, new_id);
    2347             : 
    2348          14 :   this->libmesh_assert_valid_multimaps();
    2349             : }
    2350             : 
    2351             : 
    2352             : 
    2353          66 : unsigned int BoundaryInfo::side_with_boundary_id(const Elem * const elem,
    2354             :                                                  const boundary_id_type boundary_id_in) const
    2355             : {
    2356          66 :   const Elem * searched_elem = elem;
    2357             : 
    2358             :   // If we don't have a time-dependent domain, we can just go ahead and use the top parent
    2359             :   // (since only those contain boundary conditions). Otherwise, we keep the element
    2360          66 :   if (elem->level() != 0 && !_children_on_boundary)
    2361           0 :       searched_elem = elem->top_parent();
    2362             : 
    2363             :   // elem may have zero or multiple occurrences
    2364         145 :   for (const auto & pr : as_range(_boundary_side_id.equal_range(searched_elem)))
    2365             :   {
    2366             :       // if this is true we found the requested boundary_id
    2367             :       // of the element and want to return the side
    2368         114 :       if (pr.second.second == boundary_id_in)
    2369             :       {
    2370          35 :         unsigned int side = pr.second.first;
    2371             : 
    2372             :         // Here we branch out. If we don't allow time-dependent boundary domains,
    2373             :         // we need to check if our parents are consistent.
    2374          35 :         if (!_children_on_boundary)
    2375             :         {
    2376             : #ifdef LIBMESH_ENABLE_AMR
    2377             :           // If we're on this external boundary then we share this
    2378             :           // external boundary id
    2379          35 :           if (elem->neighbor_ptr(side) == nullptr)
    2380           9 :             return side;
    2381             : 
    2382             :           // Internal boundary case
    2383           7 :           const Elem * p = elem;
    2384             : 
    2385             :           // If we're on an internal boundary then we need to be sure
    2386             :           // it's the same internal boundary as our top_parent
    2387          56 :           while (p != nullptr)
    2388             :           {
    2389          14 :             const Elem * parent = p->parent();
    2390          42 :             if (parent && !parent->is_child_on_side(parent->which_child_am_i(p), side))
    2391           0 :               break;
    2392           7 :             p = parent;
    2393             :           }
    2394             : 
    2395             :           // We're on that side of our top_parent; return it
    2396          14 :           if (!p)
    2397          28 :             return side;
    2398             : #else
    2399             :           // do not forget to return the internal boundary when AMR is disabled
    2400             :           return side;
    2401             : #endif
    2402             :         }
    2403             :         // Otherwise we need to check if the child's ancestors have something on
    2404             :         // the side of the child
    2405             :         else
    2406           7 :           return side;
    2407             :       }
    2408             :   }
    2409             : 
    2410             : #ifdef LIBMESH_ENABLE_AMR
    2411             :   // We might have instances (especially with moving boundary domains) when we
    2412             :   // query the paren't boundary ID on a child. We only do this till we find the
    2413             :   // the first side, for multiple sides see above.
    2414          31 :   if (_children_on_boundary && elem->level() != 0)
    2415             :   {
    2416          14 :     for (auto side : make_range(elem->n_sides()))
    2417             :     {
    2418           4 :       const Elem * p = elem;
    2419          36 :       while (p->parent() != nullptr)
    2420             :       {
    2421          21 :         const Elem * parent = p->parent();
    2422             : 
    2423             :         // First we make sure the parent shares this side
    2424          21 :         if (parent->is_child_on_side(parent->which_child_am_i(p), side))
    2425             :         {
    2426             :           // parent may have multiple boundary ids
    2427          63 :           for (const auto & pr : as_range(_boundary_side_id.equal_range(parent)))
    2428             :             // if this is true we found the requested boundary_id
    2429             :             // of the element and want to return the side
    2430          49 :             if (pr.second.first == side && pr.second.second == boundary_id_in)
    2431           2 :               return side;
    2432             : 
    2433           8 :           p = parent;
    2434             :         }
    2435             :         // If the parent is not on the same side, other ancestors won't be on the same side either
    2436             :         else
    2437           0 :           break;
    2438             :       }
    2439             :     }
    2440             :   }
    2441             : #endif
    2442             : 
    2443             :   // if we get here, we found elem in the data structure but not
    2444             :   // the requested boundary id, so return the default value
    2445           6 :   return libMesh::invalid_uint;
    2446             : }
    2447             : 
    2448             : 
    2449             : std::vector<unsigned int>
    2450       53775 : BoundaryInfo::sides_with_boundary_id(const Elem * const elem,
    2451             :                                      const boundary_id_type boundary_id_in) const
    2452             : {
    2453       25665 :   std::vector<unsigned int> returnval;
    2454             : 
    2455       53775 :   const Elem * searched_elem = elem;
    2456       53775 :   if (elem->level() != 0 && !_children_on_boundary)
    2457       49065 :     searched_elem = elem->top_parent();
    2458             : 
    2459             :   // elem may have zero or multiple occurrences
    2460      157376 :   for (const auto & pr : as_range(_boundary_side_id.equal_range(searched_elem)))
    2461             :   {
    2462             :       // if this is true we found the requested boundary_id
    2463             :       // of the element and want to return the side
    2464      103601 :       if (pr.second.second == boundary_id_in)
    2465             :       {
    2466       53045 :         unsigned int side = pr.second.first;
    2467             : 
    2468             :         // Here we branch out. If we don't allow time-dependent boundary domains,
    2469             :         // we need to check if our parents are consistent.
    2470       53045 :         if (!_children_on_boundary)
    2471             :         {
    2472             :           // If we're on this external boundary then we share this
    2473             :           // external boundary id
    2474       72063 :           if (elem->neighbor_ptr(side) == nullptr)
    2475             :             {
    2476       53045 :               returnval.push_back(side);
    2477       53045 :               continue;
    2478             :             }
    2479             : 
    2480             :           // If we're on an internal boundary then we need to be sure
    2481             :           // it's the same internal boundary as our top_parent
    2482           0 :           const Elem * p = elem;
    2483             : 
    2484             : #ifdef LIBMESH_ENABLE_AMR
    2485             : 
    2486           0 :           while (p != nullptr)
    2487             :           {
    2488           0 :             const Elem * parent = p->parent();
    2489           0 :             if (parent && !parent->is_child_on_side(parent->which_child_am_i(p), side))
    2490           0 :               break;
    2491           0 :             p = parent;
    2492             :           }
    2493             : #endif
    2494             :           // We're on that side of our top_parent; return it
    2495           0 :           if (!p)
    2496           0 :             returnval.push_back(side);
    2497             :         }
    2498             :         // Otherwise we trust what we got and return the side
    2499             :         else
    2500           0 :           returnval.push_back(side);
    2501             :       }
    2502             :   }
    2503             : 
    2504             : #ifdef LIBMESH_ENABLE_AMR
    2505             :   // We might have instances (especially with moving boundary domains) when we
    2506             :   // query the parent boundary ID on a child.
    2507       53775 :   if (_children_on_boundary && elem->level() != 0)
    2508             :   {
    2509          35 :     for (auto side : make_range(elem->n_sides()))
    2510             :     {
    2511           8 :       const Elem * p = elem;
    2512          54 :       while (p->parent() != nullptr)
    2513             :       {
    2514          42 :         const Elem * parent = p->parent();
    2515             :         // First we make sure the parent shares this side
    2516          42 :         if (parent->is_child_on_side(parent->which_child_am_i(p), side))
    2517             :         {
    2518             :           // parent may have multiple boundary ids
    2519          42 :           for (const auto & pr : as_range(_boundary_side_id.equal_range(parent)))
    2520             :           {
    2521             :             // if this is true we found the requested boundary_id
    2522             :             // of the element and want to add the side to the vector. We
    2523             :             // also need to check if the side is already in the vector. This might
    2524             :             // happen if the child inherits the boundary from the parent.
    2525          36 :             if (pr.second.first == side && pr.second.second == boundary_id_in &&
    2526          32 :                 std::find(returnval.begin(), returnval.end(), side) == returnval.end())
    2527          14 :               returnval.push_back(side);
    2528             :           }
    2529             :         }
    2530             :         // If the parent is not on the same side, other ancestors won't be on the same side either
    2531             :         else
    2532           8 :           break;
    2533           8 :         p = parent;
    2534             :       }
    2535             :     }
    2536             :   }
    2537             : #endif
    2538             : 
    2539       79440 :   return returnval;
    2540             : }
    2541             : 
    2542             : void
    2543        2498 : BoundaryInfo::build_node_boundary_ids(std::vector<boundary_id_type> & b_ids) const
    2544             : {
    2545         642 :   b_ids.clear();
    2546             : 
    2547      518706 :   for (const auto & pr : _boundary_node_id)
    2548             :     {
    2549      516208 :       boundary_id_type id = pr.second;
    2550             : 
    2551      516208 :       if (std::find(b_ids.begin(),b_ids.end(),id) == b_ids.end())
    2552        8740 :         b_ids.push_back(id);
    2553             :     }
    2554        2498 : }
    2555             : 
    2556             : void
    2557        2498 : BoundaryInfo::build_side_boundary_ids(std::vector<boundary_id_type> & b_ids) const
    2558             : {
    2559         642 :   b_ids.clear();
    2560             : 
    2561      320054 :   for (const auto & pr : _boundary_side_id)
    2562             :     {
    2563      317556 :       boundary_id_type id = pr.second.second;
    2564             : 
    2565      317556 :       if (std::find(b_ids.begin(),b_ids.end(),id) == b_ids.end())
    2566        9372 :         b_ids.push_back(id);
    2567             :     }
    2568        2498 : }
    2569             : 
    2570             : void
    2571        2498 : BoundaryInfo::build_shellface_boundary_ids(std::vector<boundary_id_type> & b_ids) const
    2572             : {
    2573         642 :   b_ids.clear();
    2574             : 
    2575       29122 :   for (const auto & pr :_boundary_shellface_id)
    2576             :     {
    2577       26624 :       boundary_id_type id = pr.second.second;
    2578             : 
    2579       26624 :       if (std::find(b_ids.begin(),b_ids.end(),id) == b_ids.end())
    2580          64 :         b_ids.push_back(id);
    2581             :     }
    2582        2498 : }
    2583             : 
    2584             : #ifdef LIBMESH_ENABLE_AMR
    2585             : void
    2586     2931479 : BoundaryInfo::transfer_boundary_ids_from_children(const Elem * const parent)
    2587             : {
    2588             :   // this is only needed when we allow boundary to be associated with children elements
    2589             :   // also, we only transfer the parent's boundary ids when we are actually coarsen the child element
    2590     2931503 :   if (!_children_on_boundary ||
    2591          74 :       !(!parent->active() && parent->refinement_flag() == Elem::COARSEN_INACTIVE))
    2592      921908 :     return;
    2593             : 
    2594             :   // We assume that edges can be divided ito two pieces, while triangles and
    2595             :   // quads can be divided into four smaller areas. This is double because we'll need
    2596             :   // to convert the ratio of the children with given boundary id to a double.
    2597          14 :   const double number_of_sides_on_children = std::pow(2, parent->dim()-1);
    2598             : 
    2599             :   // In this case the input argument elem is the parent element. We need to check all of its sides
    2600             :   // to grab any potential boundary ids.
    2601          70 :   for (unsigned int side_i = 0; side_i < parent->n_sides(); ++side_i)
    2602             :   {
    2603             :     // An temporary storage to count how many times the children's boundaries occur. the general
    2604             :     // consensus is that if the boundary occurs more than once we propagate upon coarsening. Otherwise,
    2605             :     // it will get deleted.
    2606          32 :     std::map<unsigned short int, unsigned short int> boundary_counts;
    2607             : 
    2608         280 :     for (const auto & child_i : make_range(parent->n_children()))
    2609             :     {
    2610             :       // We only need to check the children which share the side
    2611         224 :       if (parent->is_child_on_side(child_i, side_i))
    2612             :       {
    2613             :         // Fetching the boundary tags on the child's side
    2614         154 :         for (const auto & pr : as_range(_boundary_side_id.equal_range(parent->child_ptr(child_i))))
    2615             :         {
    2616             :           // Making sure we are on the same boundary
    2617          42 :           if (pr.second.first == side_i)
    2618          21 :             ++boundary_counts[pr.second.second];
    2619             :         }
    2620             :       }
    2621             :     }
    2622             : 
    2623             :     // This is where the decision is made. If 50% of the children have the tags,
    2624             :     // we propagate them upwards upon coarsening. Otherwise, they are deleted.
    2625          70 :     for (const auto & boundary : boundary_counts)
    2626          14 :       if (boundary.second / number_of_sides_on_children > 0.5)
    2627           7 :         this->add_side(parent, side_i, boundary.first);
    2628             :   }
    2629             : 
    2630          70 :   for (const auto & child_i : make_range(parent->n_children()))
    2631          56 :     this->remove(parent->child_ptr(child_i));
    2632             : }
    2633             : #endif
    2634             : 
    2635        1739 : std::size_t BoundaryInfo::n_boundary_conds () const
    2636             : {
    2637             :   // in serial we know the number of bcs from the
    2638             :   // size of the container
    2639        1739 :   if (_mesh->is_serial())
    2640        1592 :     return _boundary_side_id.size();
    2641             : 
    2642             :   // in parallel we need to sum the number of local bcs
    2643          42 :   parallel_object_only();
    2644             : 
    2645         147 :   std::size_t nbcs=0;
    2646             : 
    2647         293 :   for (const auto & pr : _boundary_side_id)
    2648         186 :     if (pr.first->processor_id() == this->processor_id())
    2649         104 :       nbcs++;
    2650             : 
    2651         147 :   this->comm().sum (nbcs);
    2652             : 
    2653         147 :   return nbcs;
    2654             : }
    2655             : 
    2656        3897 : std::size_t BoundaryInfo::n_edge_conds () const
    2657             : {
    2658             :   // in serial we know the number of nodesets from the
    2659             :   // size of the container
    2660        3897 :   if (_mesh->is_serial())
    2661        3809 :     return _boundary_edge_id.size();
    2662             : 
    2663             :   // in parallel we need to sum the number of local nodesets
    2664          26 :   parallel_object_only();
    2665             : 
    2666          88 :   std::size_t n_edge_bcs=0;
    2667             : 
    2668          88 :   for (const auto & pr : _boundary_edge_id)
    2669           0 :     if (pr.first->processor_id() == this->processor_id())
    2670           0 :       n_edge_bcs++;
    2671             : 
    2672          88 :   this->comm().sum (n_edge_bcs);
    2673             : 
    2674          88 :   return n_edge_bcs;
    2675             : }
    2676             : 
    2677             : 
    2678         550 : std::size_t BoundaryInfo::n_shellface_conds () const
    2679             : {
    2680             :   // in serial we know the number of nodesets from the
    2681             :   // size of the container
    2682         550 :   if (_mesh->is_serial())
    2683         536 :     return _boundary_shellface_id.size();
    2684             : 
    2685             :   // in parallel we need to sum the number of local nodesets
    2686           4 :   parallel_object_only();
    2687             : 
    2688          14 :   std::size_t n_shellface_bcs=0;
    2689             : 
    2690          14 :   for (const auto & pr : _boundary_shellface_id)
    2691           0 :     if (pr.first->processor_id() == this->processor_id())
    2692           0 :       n_shellface_bcs++;
    2693             : 
    2694          14 :   this->comm().sum (n_shellface_bcs);
    2695             : 
    2696          14 :   return n_shellface_bcs;
    2697             : }
    2698             : 
    2699             : 
    2700         893 : std::size_t BoundaryInfo::n_nodeset_conds () const
    2701             : {
    2702             :   // in serial we know the number of nodesets from the
    2703             :   // size of the container
    2704         893 :   if (_mesh->is_serial())
    2705         879 :     return _boundary_node_id.size();
    2706             : 
    2707             :   // in parallel we need to sum the number of local nodesets
    2708           4 :   parallel_object_only();
    2709             : 
    2710          14 :   std::size_t n_nodesets=0;
    2711             : 
    2712         210 :   for (const auto & pr : _boundary_node_id)
    2713         252 :     if (pr.first->processor_id() == this->processor_id())
    2714         112 :       n_nodesets++;
    2715             : 
    2716          14 :   this->comm().sum (n_nodesets);
    2717             : 
    2718          14 :   return n_nodesets;
    2719             : }
    2720             : 
    2721             : 
    2722             : 
    2723             : std::vector<BoundaryInfo::NodeBCTuple>
    2724        3621 : BoundaryInfo::build_node_list(NodeBCTupleSortBy sort_by) const
    2725             : {
    2726        1003 :   std::vector<NodeBCTuple> bc_tuples;
    2727        3621 :   bc_tuples.reserve(_boundary_node_id.size());
    2728             : 
    2729      528847 :   for (const auto & [node, bid] : _boundary_node_id)
    2730      525226 :     bc_tuples.emplace_back(node->id(), bid);
    2731             : 
    2732             :   // This list is currently in memory address (arbitrary) order, so
    2733             :   // sort, using the specified ordering, to make it consistent on all procs.
    2734        3621 :   if (sort_by == NodeBCTupleSortBy::NODE_ID)
    2735        3621 :     std::sort(bc_tuples.begin(), bc_tuples.end());
    2736           0 :   else if (sort_by == NodeBCTupleSortBy::BOUNDARY_ID)
    2737           0 :     std::sort(bc_tuples.begin(), bc_tuples.end(),
    2738           0 :               [](const NodeBCTuple & left, const NodeBCTuple & right)
    2739           0 :               {return std::get<1>(left) < std::get<1>(right);});
    2740             : 
    2741        3621 :   return bc_tuples;
    2742             : }
    2743             : 
    2744             : 
    2745             : void
    2746          14 : BoundaryInfo::build_node_list_from_side_list(const std::set<boundary_id_type> & sideset_list)
    2747             : {
    2748             :   // If we're on a distributed mesh, even the owner of a node is not
    2749             :   // guaranteed to be able to properly assign its new boundary id(s)!
    2750             :   // Nodal neighbors are not always ghosted, and a nodal neighbor
    2751             :   // might have a boundary side.
    2752          14 :   const bool mesh_is_serial = _mesh->is_serial();
    2753             : 
    2754             :   typedef std::set<std::pair<dof_id_type, boundary_id_type>> set_type;
    2755             :   typedef std::vector<std::pair<dof_id_type, boundary_id_type>> vec_type;
    2756             : 
    2757           8 :   const processor_id_type my_proc_id = this->processor_id();
    2758           4 :   std::unordered_map<processor_id_type, set_type> nodes_to_push;
    2759           4 :   std::unordered_map<processor_id_type, vec_type> node_vecs_to_push;
    2760             : 
    2761             :   // For avoiding extraneous element side construction
    2762           4 :   ElemSideBuilder side_builder;
    2763             :   // Pull objects out of the loop to reduce heap operations
    2764             :   const Elem * side;
    2765             : 
    2766             :   // Loop over the side list
    2767         210 :   for (const auto & [elem, id_pair] : _boundary_side_id)
    2768             :     {
    2769             :       // Don't add remote sides
    2770         196 :       if (elem->is_remote())
    2771          28 :         continue;
    2772             : 
    2773         196 :       auto [sidenum, bcid] = id_pair;
    2774             : 
    2775         196 :       if (!sideset_list.empty() && !sideset_list.count(bcid))
    2776          20 :         continue;
    2777             : 
    2778             :       // Need to loop over the sides of any possible children
    2779          96 :       std::vector<const Elem *> family;
    2780             : #ifdef LIBMESH_ENABLE_AMR
    2781         168 :       elem->active_family_tree_by_side (family, sidenum);
    2782             : #else
    2783             :       family.push_back(elem);
    2784             : #endif
    2785             : 
    2786         336 :       for (const auto & cur_elem : family)
    2787             :         {
    2788         168 :           side = &side_builder(*cur_elem, sidenum);
    2789             : 
    2790             :           // Add each node node on the side with the side's boundary id
    2791         504 :           for (auto i : side->node_index_range())
    2792             :             {
    2793         432 :               this->add_node(side->node_ptr(i), bcid);
    2794         336 :               if (!mesh_is_serial)
    2795             :                 {
    2796             :                   const processor_id_type proc_id =
    2797           0 :                     side->node_ptr(i)->processor_id();
    2798           0 :                   if (proc_id != my_proc_id)
    2799           0 :                     nodes_to_push[proc_id].emplace(side->node_id(i), bcid);
    2800             :                 }
    2801             :             }
    2802             :         }
    2803             :     }
    2804             : 
    2805             :   // If we're on a serial mesh then we're done.
    2806          14 :   if (mesh_is_serial)
    2807           4 :     return;
    2808             : 
    2809             :   // Otherwise we need to push ghost node bcids to their owners, then
    2810             :   // pull ghost node bcids from their owners.
    2811             : 
    2812           0 :   for (auto & [proc_id, s] : nodes_to_push)
    2813             :     {
    2814           0 :       node_vecs_to_push[proc_id].assign(s.begin(), s.end());
    2815           0 :       s.clear();
    2816             :     }
    2817             : 
    2818             :   auto nodes_action_functor =
    2819           0 :     [this]
    2820             :     (processor_id_type,
    2821           0 :      const vec_type & received_nodes)
    2822             :     {
    2823           0 :       for (const auto & [dof_id, bndry_id] : received_nodes)
    2824           0 :         this->add_node(_mesh->node_ptr(dof_id), bndry_id);
    2825           0 :     };
    2826             : 
    2827             :   Parallel::push_parallel_vector_data
    2828           0 :     (this->comm(), node_vecs_to_push, nodes_action_functor);
    2829             : 
    2830             :   // At this point we should know all the BCs for our own nodes; now
    2831             :   // we need BCs for ghost nodes.
    2832             :   std::unordered_map<processor_id_type, std::vector<dof_id_type>>
    2833           0 :     node_ids_requested;
    2834             : 
    2835             :   // Determine what nodes we need to request
    2836           0 :   for (const auto & node : _mesh->node_ptr_range())
    2837             :     {
    2838           0 :       const processor_id_type pid = node->processor_id();
    2839           0 :       if (pid != my_proc_id)
    2840           0 :         node_ids_requested[pid].push_back(node->id());
    2841           0 :     }
    2842             : 
    2843             :   typedef std::vector<boundary_id_type> datum_type;
    2844             : 
    2845             :   auto node_bcid_gather_functor =
    2846           0 :     [this]
    2847             :     (processor_id_type,
    2848             :      const std::vector<dof_id_type> & ids,
    2849           0 :      std::vector<datum_type> & data)
    2850             :     {
    2851           0 :       const std::size_t query_size = ids.size();
    2852           0 :       data.resize(query_size);
    2853             : 
    2854           0 :       for (std::size_t i=0; i != query_size; ++i)
    2855           0 :         this->boundary_ids(_mesh->node_ptr(ids[i]), data[i]);
    2856           0 :     };
    2857             : 
    2858             :   auto node_bcid_action_functor =
    2859           0 :     [this]
    2860             :     (processor_id_type,
    2861             :      const std::vector<dof_id_type> & ids,
    2862           0 :      const std::vector<datum_type> & data)
    2863             :     {
    2864           0 :       for (auto i : index_range(ids))
    2865           0 :         this->add_node(_mesh->node_ptr(ids[i]), data[i]);
    2866           0 :     };
    2867             : 
    2868           0 :   datum_type * datum_type_ex = nullptr;
    2869             :   Parallel::pull_parallel_vector_data
    2870           0 :     (this->comm(), node_ids_requested, node_bcid_gather_functor,
    2871             :      node_bcid_action_functor, datum_type_ex);
    2872             : }
    2873             : 
    2874           0 : void BoundaryInfo::parallel_sync_side_ids()
    2875             : {
    2876             :   // we need BCs for ghost elements.
    2877             :   std::unordered_map<processor_id_type, std::vector<dof_id_type>>
    2878           0 :     elem_ids_requested;
    2879             : 
    2880             :   // Determine what elements we need to request
    2881           0 :   for (const auto & elem : _mesh->element_ptr_range())
    2882             :   {
    2883           0 :     const processor_id_type pid = elem->processor_id();
    2884           0 :     if (pid != this->processor_id())
    2885           0 :       elem_ids_requested[pid].push_back(elem->id());
    2886           0 :   }
    2887             : 
    2888             :   typedef std::vector<std::pair<unsigned short int, boundary_id_type>> datum_type;
    2889             : 
    2890             :   // gather the element ID, side, and boundary_id_type for the ghost elements
    2891             :   auto elem_id_gather_functor =
    2892           0 :     [this]
    2893             :     (processor_id_type,
    2894             :      const std::vector<dof_id_type> & ids,
    2895           0 :      std::vector<datum_type> & data)
    2896             :   {
    2897           0 :     data.resize(ids.size());
    2898           0 :     for (auto i : index_range(ids))
    2899             :     {
    2900           0 :       Elem * elem = _mesh->elem_ptr(ids[i]);
    2901           0 :       for (const auto & pr : as_range(_boundary_side_id.equal_range(elem)))
    2902           0 :         data[i].push_back(std::make_pair(pr.second.first, pr.second.second));
    2903             :     }
    2904           0 :   };
    2905             :   // update the _boundary_side_id on this processor
    2906             :   auto elem_id_action_functor =
    2907           0 :     [this]
    2908             :     (processor_id_type,
    2909             :      const std::vector<dof_id_type> & ids,
    2910           0 :      std::vector<datum_type> & data)
    2911             :   {
    2912           0 :       for (auto i : index_range(ids))
    2913             :       {
    2914           0 :         Elem * elem = _mesh->elem_ptr(ids[i]);
    2915             :         //clear boundary sides for this element
    2916           0 :         _boundary_side_id.erase(elem);
    2917             :         // update boundary sides for it
    2918           0 :         for (const auto & [side_id, bndry_id] : data[i])
    2919           0 :           _boundary_side_id.insert(std::make_pair(elem, std::make_pair(side_id, bndry_id)));
    2920             :       }
    2921           0 :   };
    2922             : 
    2923             : 
    2924           0 :   datum_type * datum_type_ex = nullptr;
    2925             :   Parallel::pull_parallel_vector_data
    2926           0 :     (this->comm(), elem_ids_requested, elem_id_gather_functor,
    2927             :      elem_id_action_functor, datum_type_ex);
    2928           0 : }
    2929             : 
    2930           0 : void BoundaryInfo::parallel_sync_node_ids()
    2931             : {
    2932             :   // we need BCs for ghost nodes.
    2933             :   std::unordered_map<processor_id_type, std::vector<dof_id_type>>
    2934           0 :     node_ids_requested;
    2935             : 
    2936             :   // Determine what nodes we need to request
    2937           0 :   for (const auto & node : _mesh->node_ptr_range())
    2938             :   {
    2939           0 :     const processor_id_type pid = node->processor_id();
    2940           0 :     if (pid != this->processor_id())
    2941           0 :       node_ids_requested[pid].push_back(node->id());
    2942           0 :   }
    2943             : 
    2944             :   typedef std::vector<boundary_id_type> datum_type;
    2945             : 
    2946             :   // gather the node ID and boundary_id_type for the ghost nodes
    2947             :   auto node_id_gather_functor =
    2948           0 :   [this]
    2949             :   (processor_id_type,
    2950             :     const std::vector<dof_id_type> & ids,
    2951           0 :     std::vector<datum_type> & data)
    2952             :     {
    2953           0 :       data.resize(ids.size());
    2954           0 :       for (auto i : index_range(ids))
    2955             :       {
    2956           0 :         Node * node = _mesh->node_ptr(ids[i]);
    2957           0 :         for (const auto & pr : as_range(_boundary_node_id.equal_range(node)))
    2958           0 :         data[i].push_back(pr.second);
    2959             :       }
    2960           0 :     };
    2961             : 
    2962             :     // update the _boundary_node_id on this processor
    2963             :     auto node_id_action_functor =
    2964           0 :       [this]
    2965             :       (processor_id_type,
    2966             :        const std::vector<dof_id_type> & ids,
    2967           0 :        std::vector<datum_type> & data)
    2968             :     {
    2969           0 :         for (auto i : index_range(ids))
    2970             :         {
    2971           0 :           Node * node = _mesh->node_ptr(ids[i]);
    2972             :           //clear boundary node
    2973           0 :           _boundary_node_id.erase(node);
    2974             :           // update boundary node
    2975           0 :           for (const auto & pr : data[i])
    2976           0 :             _boundary_node_id.insert(std::make_pair(node, pr));
    2977             :         }
    2978           0 :     };
    2979             : 
    2980             : 
    2981           0 :     datum_type * datum_type_ex = nullptr;
    2982             :     Parallel::pull_parallel_vector_data
    2983           0 :       (this->comm(), node_ids_requested, node_id_gather_functor,
    2984             :        node_id_action_functor, datum_type_ex);
    2985           0 : }
    2986             : 
    2987           7 : void BoundaryInfo::build_side_list_from_node_list(const std::set<boundary_id_type> & nodeset_list)
    2988             : {
    2989             :   // Check for early return
    2990           7 :   if (_boundary_node_id.empty())
    2991             :     {
    2992           0 :       libMesh::out << "No boundary node IDs have been added: cannot build side list!" << std::endl;
    2993           0 :       return;
    2994             :     }
    2995             : 
    2996             :   // For avoiding extraneous element side construction
    2997           4 :   ElemSideBuilder side_builder;
    2998             :   // Pull objects out of the loop to reduce heap operations
    2999             :   const Elem * side_elem;
    3000             : 
    3001          52 :   for (const auto & elem : _mesh->active_element_ptr_range())
    3002         148 :     for (auto side : elem->side_index_range())
    3003             :       {
    3004         112 :         side_elem = &side_builder(*elem, side);
    3005             : 
    3006             :         // map from nodeset_id to count for that ID
    3007          64 :         std::map<boundary_id_type, unsigned> nodesets_node_count;
    3008             : 
    3009             :         // For each nodeset that this node is a member of, increment the associated
    3010             :         // nodeset ID count
    3011         336 :         for (const auto & node : side_elem->node_ref_range())
    3012         448 :           for (const auto & pr : as_range(_boundary_node_id.equal_range(&node)))
    3013         224 :             if (nodeset_list.empty() || nodeset_list.count(pr.second))
    3014         112 :               nodesets_node_count[pr.second]++;
    3015             : 
    3016             :         // Now check to see what nodeset_counts have the correct
    3017             :         // number of nodes in them.  For any that do, add this side to
    3018             :         // the sideset, making sure the sideset inherits the
    3019             :         // nodeset's name, if there is one.
    3020         196 :         for (const auto & pr : nodesets_node_count)
    3021          84 :           if (pr.second == side_elem->n_nodes())
    3022             :             {
    3023          28 :               add_side(elem, side, pr.first);
    3024             : 
    3025             :               // Let the sideset inherit any non-empty name from the nodeset
    3026          28 :               std::string & nset_name = nodeset_name(pr.first);
    3027             : 
    3028          28 :               if (nset_name != "")
    3029          28 :                 sideset_name(pr.first) = nset_name;
    3030             :             }
    3031           3 :       } // end for side
    3032             : }
    3033             : 
    3034             : 
    3035             : 
    3036             : 
    3037             : std::vector<BoundaryInfo::BCTuple>
    3038        3114 : BoundaryInfo::build_side_list(BCTupleSortBy sort_by) const
    3039             : {
    3040         855 :   std::vector<BCTuple> bc_triples;
    3041        3114 :   bc_triples.reserve(_boundary_side_id.size());
    3042             : 
    3043      177711 :   for (const auto & [elem, id_pair] : _boundary_side_id)
    3044      174597 :     bc_triples.emplace_back(elem->id(), id_pair.first, id_pair.second);
    3045             : 
    3046             :   // bc_triples is currently in whatever order the Elem pointers in
    3047             :   // the _boundary_side_id multimap are in, and in particular might be
    3048             :   // in different orders on different processors. To avoid this
    3049             :   // inconsistency, we'll sort using the default operator< for tuples.
    3050        3114 :   if (sort_by == BCTupleSortBy::ELEM_ID)
    3051        3114 :     std::sort(bc_triples.begin(), bc_triples.end());
    3052           0 :   else if (sort_by == BCTupleSortBy::SIDE_ID)
    3053           0 :     std::sort(bc_triples.begin(), bc_triples.end(),
    3054           0 :               [](const BCTuple & left, const BCTuple & right)
    3055           0 :               {return std::get<1>(left) < std::get<1>(right);});
    3056           0 :   else if (sort_by == BCTupleSortBy::BOUNDARY_ID)
    3057           0 :     std::sort(bc_triples.begin(), bc_triples.end(),
    3058           0 :               [](const BCTuple & left, const BCTuple & right)
    3059           0 :               {return std::get<2>(left) < std::get<2>(right);});
    3060             : 
    3061        3114 :   return bc_triples;
    3062             : }
    3063             : 
    3064             : 
    3065             : 
    3066             : std::vector<BoundaryInfo::BCTuple>
    3067           0 : BoundaryInfo::build_active_side_list () const
    3068             : {
    3069           0 :   std::vector<BCTuple> bc_triples;
    3070           0 :   bc_triples.reserve(_boundary_side_id.size());
    3071             : 
    3072           0 :   for (const auto & [elem, id_pair] : _boundary_side_id)
    3073             :     {
    3074             :       // Don't add remote sides
    3075           0 :       if (elem->is_remote())
    3076           0 :         continue;
    3077             : 
    3078             :       // Loop over the sides of possible children
    3079           0 :       std::vector<const Elem *> family;
    3080             : #ifdef LIBMESH_ENABLE_AMR
    3081           0 :       elem->active_family_tree_by_side(family, id_pair.first);
    3082             : #else
    3083             :       family.push_back(elem);
    3084             : #endif
    3085             : 
    3086             :       // Populate the list items
    3087           0 :       for (const auto & f : family)
    3088           0 :         bc_triples.emplace_back(f->id(), id_pair.first, id_pair.second);
    3089             :     }
    3090             : 
    3091             :   // This list is currently in memory address (arbitrary) order, so
    3092             :   // sort to make it consistent on all procs.
    3093           0 :   std::sort(bc_triples.begin(), bc_triples.end());
    3094             : 
    3095           0 :   return bc_triples;
    3096             : }
    3097             : 
    3098             : 
    3099             : std::vector<BoundaryInfo::BCTuple>
    3100        1410 : BoundaryInfo::build_edge_list() const
    3101             : {
    3102         367 :   std::vector<BCTuple> bc_triples;
    3103        1410 :   bc_triples.reserve(_boundary_edge_id.size());
    3104             : 
    3105        4282 :   for (const auto & [elem, id_pair] : _boundary_edge_id)
    3106        2872 :     bc_triples.emplace_back(elem->id(), id_pair.first, id_pair.second);
    3107             : 
    3108             :   // This list is currently in memory address (arbitrary) order, so
    3109             :   // sort to make it consistent on all procs.
    3110        1410 :   std::sort(bc_triples.begin(), bc_triples.end());
    3111             : 
    3112        1410 :   return bc_triples;
    3113             : }
    3114             : 
    3115             : 
    3116             : std::vector<BoundaryInfo::BCTuple>
    3117        1396 : BoundaryInfo::build_shellface_list() const
    3118             : {
    3119         363 :   std::vector<BCTuple> bc_triples;
    3120        1396 :   bc_triples.reserve(_boundary_shellface_id.size());
    3121             : 
    3122       14708 :   for (const auto & [elem, id_pair] : _boundary_shellface_id)
    3123       13312 :     bc_triples.emplace_back(elem->id(), id_pair.first, id_pair.second);
    3124             : 
    3125             :   // This list is currently in memory address (arbitrary) order, so
    3126             :   // sort to make it consistent on all procs.
    3127        1396 :   std::sort(bc_triples.begin(), bc_triples.end());
    3128             : 
    3129        1396 :   return bc_triples;
    3130             : }
    3131             : 
    3132             : 
    3133           0 : void BoundaryInfo::print_info(std::ostream & out_stream) const
    3134             : {
    3135             :   // Print out the nodal BCs
    3136           0 :   if (!_boundary_node_id.empty())
    3137             :     {
    3138           0 :       out_stream << "Nodal Boundary conditions:" << std::endl
    3139           0 :                  << "--------------------------" << std::endl
    3140           0 :                  << "  (Node No., ID)               " << std::endl;
    3141             : 
    3142           0 :       for (const auto & [node, bndry_id] : _boundary_node_id)
    3143           0 :         out_stream << "  (" << node->id()
    3144           0 :                    << ", "  << bndry_id
    3145           0 :                    << ")"  << std::endl;
    3146             :     }
    3147             : 
    3148             :   // Print out the element edge BCs
    3149           0 :   if (!_boundary_edge_id.empty())
    3150             :     {
    3151           0 :       out_stream << std::endl
    3152           0 :                  << "Edge Boundary conditions:" << std::endl
    3153           0 :                  << "-------------------------" << std::endl
    3154           0 :                  << "  (Elem No., Edge No., ID)      " << std::endl;
    3155             : 
    3156           0 :       for (const auto & [elem, id_pair] : _boundary_edge_id)
    3157           0 :         out_stream << "  (" << elem->id()
    3158           0 :                    << ", "  << id_pair.first
    3159           0 :                    << ", "  << id_pair.second
    3160           0 :                    << ")"   << std::endl;
    3161             :     }
    3162             : 
    3163             :   // Print out the element shellface BCs
    3164           0 :   if (!_boundary_shellface_id.empty())
    3165             :     {
    3166           0 :       out_stream << std::endl
    3167           0 :                  << "Shell-face Boundary conditions:" << std::endl
    3168           0 :                  << "-------------------------" << std::endl
    3169           0 :                  << "  (Elem No., Shell-face No., ID)      " << std::endl;
    3170             : 
    3171           0 :       for (const auto & [elem, id_pair] : _boundary_shellface_id)
    3172           0 :         out_stream << "  (" << elem->id()
    3173           0 :                    << ", "  << id_pair.first
    3174           0 :                    << ", "  << id_pair.second
    3175           0 :                    << ")"   << std::endl;
    3176             :     }
    3177             : 
    3178             :   // Print out the element side BCs
    3179           0 :   if (!_boundary_side_id.empty())
    3180             :     {
    3181           0 :       out_stream << std::endl
    3182           0 :                  << "Side Boundary conditions:" << std::endl
    3183           0 :                  << "-------------------------" << std::endl
    3184           0 :                  << "  (Elem No., Side No., ID)      " << std::endl;
    3185             : 
    3186           0 :       for (const auto & [elem, id_pair] : _boundary_side_id)
    3187           0 :         out_stream << "  (" << elem->id()
    3188           0 :                    << ", "  << id_pair.first
    3189           0 :                    << ", "  << id_pair.second
    3190           0 :                    << ")"   << std::endl;
    3191             :     }
    3192           0 : }
    3193             : 
    3194             : 
    3195             : 
    3196           0 : void BoundaryInfo::print_summary(std::ostream & out_stream) const
    3197             : {
    3198             :   // Print out the nodal BCs
    3199           0 :   if (!_boundary_node_id.empty())
    3200             :     {
    3201           0 :       out_stream << "Nodal Boundary conditions:" << std::endl
    3202           0 :                  << "--------------------------" << std::endl
    3203           0 :                  << "  (ID, number of nodes)   " << std::endl;
    3204             : 
    3205           0 :       std::map<boundary_id_type, std::size_t> ID_counts;
    3206             : 
    3207           0 :       for (const auto & pr : _boundary_node_id)
    3208           0 :         ID_counts[pr.second]++;
    3209             : 
    3210           0 :       for (const auto & [bndry_id, cnt] : ID_counts)
    3211           0 :         out_stream << "  (" << bndry_id
    3212           0 :                    << ", "  << cnt
    3213           0 :                    << ")"  << std::endl;
    3214             :     }
    3215             : 
    3216             :   // Print out the element edge BCs
    3217           0 :   if (!_boundary_edge_id.empty())
    3218             :     {
    3219           0 :       out_stream << std::endl
    3220           0 :                  << "Edge Boundary conditions:" << std::endl
    3221           0 :                  << "-------------------------" << std::endl
    3222           0 :                  << "  (ID, number of edges)   " << std::endl;
    3223             : 
    3224           0 :       std::map<boundary_id_type, std::size_t> ID_counts;
    3225             : 
    3226           0 :       for (const auto & pr : _boundary_edge_id)
    3227           0 :         ID_counts[pr.second.second]++;
    3228             : 
    3229           0 :       for (const auto & [bndry_id, cnt] : ID_counts)
    3230           0 :         out_stream << "  (" << bndry_id
    3231           0 :                    << ", "  << cnt
    3232           0 :                    << ")"  << std::endl;
    3233             :     }
    3234             : 
    3235             : 
    3236             :   // Print out the element edge BCs
    3237           0 :   if (!_boundary_shellface_id.empty())
    3238             :     {
    3239           0 :       out_stream << std::endl
    3240           0 :                  << "Shell-face Boundary conditions:" << std::endl
    3241           0 :                  << "-------------------------" << std::endl
    3242           0 :                  << "  (ID, number of shellfaces)   " << std::endl;
    3243             : 
    3244           0 :       std::map<boundary_id_type, std::size_t> ID_counts;
    3245             : 
    3246           0 :       for (const auto & pr : _boundary_shellface_id)
    3247           0 :         ID_counts[pr.second.second]++;
    3248             : 
    3249           0 :       for (const auto & [bndry_id, cnt] : ID_counts)
    3250           0 :         out_stream << "  (" << bndry_id
    3251           0 :                    << ", "  << cnt
    3252           0 :                    << ")"  << std::endl;
    3253             :     }
    3254             : 
    3255             :   // Print out the element side BCs
    3256           0 :   if (!_boundary_side_id.empty())
    3257             :     {
    3258           0 :       out_stream << std::endl
    3259           0 :                  << "Side Boundary conditions:" << std::endl
    3260           0 :                  << "-------------------------" << std::endl
    3261           0 :                  << "  (ID, number of sides)   " << std::endl;
    3262             : 
    3263           0 :       std::map<boundary_id_type, std::size_t> ID_counts;
    3264             : 
    3265           0 :       for (const auto & pr : _boundary_side_id)
    3266           0 :         ID_counts[pr.second.second]++;
    3267             : 
    3268           0 :       for (const auto & [bndry_id, cnt] : ID_counts)
    3269           0 :         out_stream << "  (" << bndry_id
    3270           0 :                    << ", "  << cnt
    3271           0 :                    << ")"  << std::endl;
    3272             :     }
    3273           0 : }
    3274             : 
    3275             : 
    3276        6443 : const std::string & BoundaryInfo::get_sideset_name(boundary_id_type id) const
    3277             : {
    3278        6443 :   static const std::string empty_string;
    3279        6443 :   if (const auto it = _ss_id_to_name.find(id);
    3280        1704 :       it == _ss_id_to_name.end())
    3281         241 :     return empty_string;
    3282             :   else
    3283        5536 :     return it->second;
    3284             : }
    3285             : 
    3286             : 
    3287      131787 : std::string & BoundaryInfo::sideset_name(boundary_id_type id)
    3288             : {
    3289      131787 :   return _ss_id_to_name[id];
    3290             : }
    3291             : 
    3292        5570 : const std::string & BoundaryInfo::get_nodeset_name(boundary_id_type id) const
    3293             : {
    3294        5570 :   static const std::string empty_string;
    3295        5570 :   if (const auto it = _ns_id_to_name.find(id);
    3296        1467 :       it == _ns_id_to_name.end())
    3297          40 :     return empty_string;
    3298             :   else
    3299        5410 :     return it->second;
    3300             : }
    3301             : 
    3302      131446 : std::string & BoundaryInfo::nodeset_name(boundary_id_type id)
    3303             : {
    3304      131446 :   return _ns_id_to_name[id];
    3305             : }
    3306             : 
    3307          92 : const std::string & BoundaryInfo::get_edgeset_name(boundary_id_type id) const
    3308             : {
    3309          92 :   static const std::string empty_string;
    3310          92 :   if (const auto it = _es_id_to_name.find(id);
    3311          23 :       it == _es_id_to_name.end())
    3312          21 :     return empty_string;
    3313             :   else
    3314           8 :     return it->second;
    3315             : }
    3316             : 
    3317             : 
    3318         166 : std::string & BoundaryInfo::edgeset_name(boundary_id_type id)
    3319             : {
    3320         166 :   return _es_id_to_name[id];
    3321             : }
    3322             : 
    3323         206 : boundary_id_type BoundaryInfo::get_id_by_name(std::string_view name) const
    3324             : {
    3325             :   // Search sidesets
    3326         515 :   for (const auto & [ss_id, ss_name] : _ss_id_to_name)
    3327         447 :     if (ss_name == name)
    3328         206 :       return ss_id;
    3329             : 
    3330             :   // Search nodesets
    3331           0 :   for (const auto & [ns_id, ns_name] : _ns_id_to_name)
    3332           0 :     if (ns_name == name)
    3333           0 :       return ns_id;
    3334             : 
    3335             :   // Search edgesets
    3336           0 :   for (const auto & [es_id, es_name] : _es_id_to_name)
    3337           0 :     if (es_name == name)
    3338           0 :       return es_id;
    3339             : 
    3340             :   // If we made it here without returning, we don't have a sideset,
    3341             :   // nodeset, or edgeset by the requested name, so return invalid_id
    3342           0 :   return invalid_id;
    3343             : }
    3344             : 
    3345             : 
    3346             : 
    3347         581 : void BoundaryInfo::_find_id_maps(const std::set<boundary_id_type> & requested_boundary_ids,
    3348             :                                  dof_id_type first_free_node_id,
    3349             :                                  std::map<dof_id_type, dof_id_type> * node_id_map,
    3350             :                                  dof_id_type first_free_elem_id,
    3351             :                                  std::map<std::pair<dof_id_type, unsigned char>, dof_id_type> * side_id_map,
    3352             :                                  const std::set<subdomain_id_type> & subdomains_relative_to)
    3353             : {
    3354             :   // We'll use the standard DistributedMesh trick of dividing up id
    3355             :   // ranges by processor_id to avoid picking duplicate node ids.
    3356             :   dof_id_type
    3357         747 :     next_node_id = first_free_node_id + this->processor_id();
    3358             : 
    3359             :   // We have to be careful how we select different element ids on
    3360             :   // different processors, because we may be adding sides of refined
    3361             :   // elements and sides of their parents (which are thereby also
    3362             :   // parents of their sides), but libMesh convention requires (and
    3363             :   // some libMesh communication code assumes) that parent elements
    3364             :   // have lower ids than their children.
    3365             :   //
    3366             :   // We'll start with temporary ids in a temporary map stratefied by
    3367             :   // element level, so we can sort by element level after.
    3368             :   // Make sure we're sorting by ids here, not anything that might
    3369             :   // differ from processor to processor, so we can do an
    3370             :   // embarrassingly parallel sort.  This is a serialized data
    3371             :   // structure, but it's at worst O(N^2/3) so we'll hold off on doing
    3372             :   // this distributed until someone's benchmark screams at us.
    3373             :   std::vector
    3374             :     <std::set<std::pair<dof_id_type, unsigned char>>>
    3375         498 :     side_id_set_by_level;
    3376             : 
    3377             :   // For avoiding extraneous element side construction
    3378         332 :   ElemSideBuilder side_builder;
    3379             :   // Pull objects out of the loop to reduce heap operations
    3380             :   const Elem * side;
    3381             : 
    3382             :   // We'll pass through the mesh once first to build
    3383             :   // the maps and count boundary nodes and elements.
    3384             :   // To find local boundary nodes, we have to examine all elements
    3385             :   // here rather than just local elements, because it's possible to
    3386             :   // have a local boundary node that's not on a local boundary
    3387             :   // element, e.g. at the tip of a triangle.
    3388             : 
    3389             :   // We'll loop through two different ranges here: first all elements,
    3390             :   // looking for local nodes, and second through unpartitioned
    3391             :   // elements, looking for all remaining nodes.
    3392         747 :   const MeshBase::const_element_iterator end_el = _mesh->elements_end();
    3393         166 :   bool hit_end_el = false;
    3394             :   const MeshBase::const_element_iterator end_unpartitioned_el =
    3395         747 :     _mesh->pid_elements_end(DofObject::invalid_processor_id);
    3396             : 
    3397       10632 :   for (MeshBase::const_element_iterator el = _mesh->elements_begin();
    3398       42449 :        !hit_end_el || (el != end_unpartitioned_el); ++el)
    3399             :     {
    3400       33145 :       if ((el == end_el) && !hit_end_el)
    3401             :         {
    3402             :           // Note that we're done with local nodes and just looking
    3403             :           // for remaining unpartitioned nodes
    3404         166 :           hit_end_el = true;
    3405             : 
    3406             :           // Join up the local results from other processors
    3407         581 :           if (side_id_map)
    3408             :           {
    3409         329 :             std::size_t n_levels = side_id_set_by_level.size();
    3410         329 :             this->comm().max(n_levels);
    3411         329 :             side_id_set_by_level.resize(n_levels);
    3412         728 :             for (auto l : make_range(n_levels))
    3413         513 :               this->comm().set_union(side_id_set_by_level[l]);
    3414             :           }
    3415         581 :           if (node_id_map)
    3416         252 :             this->comm().set_union(*node_id_map);
    3417             : 
    3418             :           // Finally we'll pass through any unpartitioned elements to add them
    3419             :           // to the maps and counts.
    3420         581 :           next_node_id = first_free_node_id + this->n_processors();
    3421             : 
    3422         996 :           el = _mesh->pid_elements_begin(DofObject::invalid_processor_id);
    3423         581 :           if (el == end_unpartitioned_el)
    3424         166 :             break;
    3425             :         }
    3426             : 
    3427       32564 :       const Elem * elem = *el;
    3428             : 
    3429             :       // If the subdomains_relative_to container has the
    3430             :       // invalid_subdomain_id, we fall back on the "old" behavior of
    3431             :       // adding sides regardless of this Elem's subdomain. Otherwise,
    3432             :       // if the subdomains_relative_to container doesn't contain the
    3433             :       // current Elem's subdomain_id(), we won't add any sides from
    3434             :       // it.
    3435       11568 :       if (!subdomains_relative_to.count(Elem::invalid_subdomain_id) &&
    3436       25676 :           !subdomains_relative_to.count(elem->subdomain_id()))
    3437        2040 :         continue;
    3438             : 
    3439      175462 :       for (auto s : elem->side_index_range())
    3440             :         {
    3441       41644 :           bool add_this_side = false;
    3442             : 
    3443             :           // Find all the boundary side ids for this Elem side.
    3444       83288 :           std::vector<boundary_id_type> bcids;
    3445      145754 :           this->boundary_ids(elem, s, bcids);
    3446             : 
    3447      153538 :           for (const boundary_id_type bcid : bcids)
    3448             :             {
    3449             :               // if the user wants this id, we want this side
    3450        5046 :               if (requested_boundary_ids.count(bcid))
    3451             :                 {
    3452        2822 :                   add_this_side = true;
    3453        2822 :                   break;
    3454             :                 }
    3455             :             }
    3456             : 
    3457             :           // We may still want to add this side if the user called
    3458             :           // sync() with no requested_boundary_ids. This corresponds
    3459             :           // to the "old" style of calling sync() in which the entire
    3460             :           // boundary was copied to the BoundaryMesh, and handles the
    3461             :           // case where elements on the geometric boundary are not in
    3462             :           // any sidesets.
    3463      133372 :           if (requested_boundary_ids.count(invalid_id) &&
    3464       91728 :               elem->neighbor_ptr(s) == nullptr)
    3465        1120 :             add_this_side = true;
    3466             : 
    3467      142954 :           if (add_this_side)
    3468             :             {
    3469             :               // We only assign ids for our own and for
    3470             :               // unpartitioned elements
    3471       11389 :               if (side_id_map &&
    3472        3780 :                   ((elem->processor_id() == this->processor_id()) ||
    3473         756 :                    (elem->processor_id() ==
    3474             :                     DofObject::invalid_processor_id)))
    3475             :                 {
    3476        1512 :                   std::pair<dof_id_type, unsigned char> side_pair(elem->id(), s);
    3477        3024 :                   auto level = elem->level();
    3478        3780 :                   if (side_id_set_by_level.size() <= level)
    3479         369 :                     side_id_set_by_level.resize(level+1);
    3480        1512 :                   auto & level_side_id_set = side_id_set_by_level[level];
    3481         756 :                   libmesh_assert (!level_side_id_set.count(side_pair));
    3482        2268 :                   level_side_id_set.insert(side_pair);
    3483             :                 }
    3484             : 
    3485        9877 :               side = &side_builder(*elem, s);
    3486       60312 :               for (auto n : side->node_index_range())
    3487             :                 {
    3488       14410 :                   const Node & node = side->node_ref(n);
    3489             : 
    3490             :                   // In parallel we don't know enough to number
    3491             :                   // others' nodes ourselves.
    3492       64845 :                   if (!hit_end_el &&
    3493       28820 :                       (node.processor_id() != this->processor_id()))
    3494       21615 :                     continue;
    3495             : 
    3496       28820 :                   dof_id_type node_id = node.id();
    3497       28820 :                   if (node_id_map && !node_id_map->count(node_id))
    3498             :                     {
    3499        6788 :                       (*node_id_map)[node_id] = next_node_id;
    3500        8485 :                       next_node_id += this->n_processors() + 1;
    3501             :                     }
    3502             :                 }
    3503             :             }
    3504             :         }
    3505             :     }
    3506             : 
    3507             :   // FIXME: should we renumber node_id_map's image to be contiguous
    3508             :   // here, rather than waiting for renumbering in mesh preparation to
    3509             :   // do it later?
    3510             : 
    3511             :   // We do need to build up side_id_map here, to handle proper sorting
    3512             :   // by level.
    3513         581 :   if (side_id_map)
    3514             :     {
    3515          94 :       dof_id_type next_elem_id = first_free_elem_id;
    3516         728 :       for (auto level : make_range(side_id_set_by_level.size()))
    3517             :         {
    3518        5691 :           for (auto side_pair : side_id_set_by_level[level])
    3519        5292 :             (*side_id_map)[side_pair] = next_elem_id++;
    3520             :         }
    3521             :     }
    3522         581 : }
    3523             : 
    3524         161 : void BoundaryInfo::clear_stitched_boundary_side_ids (const boundary_id_type sideset_id,
    3525             :                                                      const boundary_id_type other_sideset_id,
    3526             :                                                      const bool clear_nodeset_data)
    3527             : {
    3528          46 :   auto end_it = _boundary_side_id.end();
    3529          46 :   auto it = _boundary_side_id.begin();
    3530             : 
    3531             :   // This predicate checks to see whether the pred_pr triplet's boundary ID matches sideset_id
    3532             :   // (other_sideset_id) *and* whether there is a boundary information triplet on the other side of
    3533             :   // the face whose boundary ID matches the other_sideset_id (sideset_id). We return a pair where
    3534             :   // first is a boolean indicating our condition is true or false, and second is an iterator to the
    3535             :   // neighboring triplet if our condition is true
    3536             :   auto predicate =
    3537        3204 :       [sideset_id, other_sideset_id](
    3538             :           const std::pair<const Elem *, std::pair<unsigned short int, boundary_id_type>> & pred_pr,
    3539             :           const std::multimap<const Elem *, std::pair<unsigned short int, boundary_id_type>> &
    3540        3028 :               pred_container) {
    3541        7476 :         const Elem & elem = *pred_pr.first;
    3542        7476 :         const auto elem_side = pred_pr.second.first;
    3543        7476 :         const Elem * const other_elem = elem.neighbor_ptr(elem_side);
    3544        7476 :         if (!other_elem)
    3545        1896 :           return std::make_pair(false, pred_container.end());
    3546             : 
    3547         840 :         const auto elem_side_bnd_id = pred_pr.second.second;
    3548         240 :         auto other_elem_side_bnd_id = BoundaryInfo::invalid_id;
    3549         840 :         if (elem_side_bnd_id == sideset_id)
    3550         162 :           other_elem_side_bnd_id = other_sideset_id;
    3551         328 :         else if (elem_side_bnd_id == other_sideset_id)
    3552          62 :           other_elem_side_bnd_id = sideset_id;
    3553             :         else
    3554          16 :           return std::make_pair(false, pred_container.end());
    3555             : 
    3556         784 :         const auto other_elem_side = other_elem->which_neighbor_am_i(&elem);
    3557             :         const typename std::decay<decltype(pred_container)>::type::value_type other_sideset_info(
    3558         448 :             other_elem, std::make_pair(other_elem_side, other_elem_side_bnd_id));
    3559         448 :         auto other_range = pred_container.equal_range(other_elem);
    3560         224 :         libmesh_assert_msg(
    3561             :             other_range.first != other_range.second,
    3562             :             "No matching sideset information for other element in boundary information");
    3563         784 :         auto other_it = std::find(other_range.first, other_range.second, other_sideset_info);
    3564         224 :         libmesh_assert_msg(
    3565             :             other_it != pred_container.end(),
    3566             :             "No matching sideset information for other element in boundary information");
    3567         224 :         return std::make_pair(true, other_it);
    3568         161 :       };
    3569             : 
    3570        7637 :   for (; it != end_it;)
    3571             :   {
    3572        9612 :     auto pred_result = predicate(*it, _boundary_side_id);
    3573        7476 :     if (pred_result.first)
    3574             :     {
    3575             :       // First erase associated nodeset information.  Do it from both
    3576             :       // sides, so we get any higher-order nodes if we're looking at
    3577             :       // them from a lower-order side, and so we only remove the two
    3578             :       // boundary ids used for stitching.
    3579         784 :       if (clear_nodeset_data)
    3580             :       {
    3581         784 :         const Elem & elem = *it->first;
    3582         784 :         const Elem & neigh = *pred_result.second->first;
    3583         784 :         const auto elem_side = it->second.first;
    3584         784 :         const boundary_id_type neigh_side = pred_result.second->second.first;
    3585         784 :         const auto elem_bcid = it->second.second;
    3586         784 :         const boundary_id_type neigh_bcid = pred_result.second->second.second;
    3587             : 
    3588        5632 :         for (const auto local_node_num : elem.nodes_on_side(elem_side))
    3589        5945 :           this->remove_node(elem.node_ptr(local_node_num), elem_bcid);
    3590             : 
    3591        5631 :         for (const auto local_node_num : neigh.nodes_on_side(neigh_side))
    3592        5944 :           this->remove_node(neigh.node_ptr(local_node_num), neigh_bcid);
    3593             :       }
    3594             : 
    3595             :       // Now erase the sideset information for our element and its
    3596             :       // neighbor, together.  This is safe since a multimap doesn't
    3597             :       // invalidate iterators.
    3598         560 :       _boundary_side_id.erase(pred_result.second);
    3599         560 :       it = _boundary_side_id.erase(it);
    3600             :     }
    3601             :     else
    3602        1912 :       ++it;
    3603             :   }
    3604             : 
    3605             :   // Removing stitched-away boundary ids might have removed an id
    3606             :   // *entirely*, so we need to recompute boundary id sets to check
    3607             :   // for that.
    3608         161 :   this->regenerate_id_sets();
    3609         161 :   this->libmesh_assert_valid_multimaps();
    3610         161 : }
    3611             : 
    3612             : const std::set<boundary_id_type> &
    3613          56 : BoundaryInfo::get_global_boundary_ids() const
    3614             : {
    3615          16 :   libmesh_assert(_mesh && _mesh->_preparation.has_boundary_id_sets);
    3616          56 :   return _global_boundary_ids;
    3617             : }
    3618             : 
    3619             : void
    3620         399 : BoundaryInfo::libmesh_assert_valid_multimaps() const
    3621             : {
    3622             : #ifndef NDEBUG
    3623         456 :   auto verify_multimap = [](const auto & themap) {
    3624       13772 :     for (const auto & [key, val] : themap)
    3625             :       {
    3626       13316 :         auto range = themap.equal_range(key);
    3627             : 
    3628       13316 :         int count = 0;
    3629       41436 :         for (auto it = range.first; it != range.second; ++it)
    3630       28120 :           if (it->second == val)
    3631       13316 :             ++count;
    3632             : 
    3633       13316 :         libmesh_assert(count == 1);
    3634             :       }
    3635         456 :   };
    3636             : 
    3637         114 :   verify_multimap(this->_boundary_node_id);
    3638         114 :   verify_multimap(this->_boundary_edge_id);
    3639         114 :   verify_multimap(this->_boundary_shellface_id);
    3640         114 :   verify_multimap(this->_boundary_side_id);
    3641             : #else
    3642         285 :   return;
    3643             : #endif
    3644         114 : }
    3645             : 
    3646             : } // namespace libMesh

Generated by: LCOV version 1.14