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