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