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