libMesh
Loading...
Searching...
No Matches
unstructured_mesh.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/boundary_info.h"
22#include "libmesh/ghosting_functor.h"
23#include "libmesh/ghost_point_neighbors.h"
24#include "libmesh/unstructured_mesh.h"
25#include "libmesh/libmesh_logging.h"
26#include "libmesh/elem.h"
27#include "libmesh/elem_range.h"
28#include "libmesh/mesh_tools.h" // For n_levels
29#include "libmesh/parallel.h"
30#include "libmesh/remote_elem.h"
31#include "libmesh/namebased_io.h"
32#include "libmesh/partitioner.h"
33#include "libmesh/enum_order.h"
34#include "libmesh/mesh_communication.h"
35#include "libmesh/enum_to_string.h"
36#include "libmesh/mesh_serializer.h"
37#include "libmesh/utility.h"
38
39#ifdef LIBMESH_HAVE_NANOFLANN
40#include "libmesh/nanoflann.hpp"
41#endif
42
43// C++ includes
44#include <algorithm> // std::all_of
45#include <atomic>
46#include <fstream>
47#include <iomanip>
48#include <map>
49#include <sstream>
50#include <unordered_map>
51
52// for disjoint neighbors
53#include "libmesh/periodic_boundaries.h"
54#include "libmesh/periodic_boundary.h"
55
56namespace {
57
58using namespace libMesh;
59
60// Helper functions for all_second_order, all_complete_order
61
62std::map<std::vector<dof_id_type>, Node *>::iterator
63map_hi_order_node(unsigned int hon,
64 const Elem & hi_elem,
65 std::map<std::vector<dof_id_type>, Node *> & adj_vertices_to_ho_nodes)
66{
67 /*
68 * form a vector that will hold the node id's of
69 * the vertices that are adjacent to the nth
70 * higher-order node.
71 */
72 const unsigned int n_adjacent_vertices =
74
75 std::vector<dof_id_type> adjacent_vertices_ids(n_adjacent_vertices);
76
77 for (unsigned int v=0; v<n_adjacent_vertices; v++)
78 adjacent_vertices_ids[v] =
79 hi_elem.node_id( hi_elem.second_order_adjacent_vertex(hon,v) );
80
81 /*
82 * \p adjacent_vertices_ids is now in order of the current
83 * side. sort it, so that comparisons with the
84 * \p adjacent_vertices_ids created through other elements'
85 * sides can match
86 */
87 std::sort(adjacent_vertices_ids.begin(),
88 adjacent_vertices_ids.end());
89
90 // Does this set of vertices already have a mid-node added? If not
91 // we'll want to add it.
92 return adj_vertices_to_ho_nodes.try_emplace(adjacent_vertices_ids, nullptr).first;
93}
94
95void transfer_elem(Elem & lo_elem,
96 std::unique_ptr<Elem> hi_elem,
97#ifdef LIBMESH_ENABLE_UNIQUE_ID
98 unique_id_type max_unique_id,
99 unique_id_type max_new_nodes_per_elem,
100#endif
102 std::map<std::vector<dof_id_type>, Node *> & adj_vertices_to_ho_nodes,
103 std::unordered_map<Elem *, std::vector<Elem *>> & exterior_children_of)
104{
105 libmesh_assert_equal_to (lo_elem.n_vertices(), hi_elem->n_vertices());
106
107 const processor_id_type my_pid = mesh.processor_id();
108 const processor_id_type lo_pid = lo_elem.processor_id();
109
110 /*
111 * Now handle the additional higher-order nodes. This
112 * is simply handled through a map that remembers
113 * the already-added nodes. This map maps the global
114 * ids of the vertices (that uniquely define this
115 * higher-order node) to the new node.
116 * Notation: hon = high-order node
117 */
118 const unsigned int hon_begin = lo_elem.n_nodes();
119 const unsigned int hon_end = hi_elem->n_nodes();
120
121 libmesh_assert_less (hon_begin, hon_end);
122 libmesh_assert_less_equal
123 (hon_end-hon_begin, max_new_nodes_per_elem);
124
125 for (unsigned int hon=hon_begin; hon<hon_end; hon++)
126 {
127 auto pos = map_hi_order_node(hon, *hi_elem, adj_vertices_to_ho_nodes);
128
129 // no, not added yet
130 if (!pos->second)
131 {
132 const auto & adjacent_vertices_ids = pos->first;
133
134 /*
135 * for this set of vertices, there is no
136 * second_order node yet. Add it.
137 *
138 * compute the location of the new node as
139 * the average over the adjacent vertices.
140 */
141 Point new_location = 0;
142 for (dof_id_type vertex_id : adjacent_vertices_ids)
143 new_location += mesh.point(vertex_id);
144
145 new_location /= static_cast<Real>(adjacent_vertices_ids.size());
146
147 /* Add the new point to the mesh.
148 *
149 * If we are on a serialized mesh, then we're doing this
150 * all in sync, and the node processor_id will be
151 * consistent between processors.
152 *
153 * If we are on a distributed mesh, we can fix
154 * inconsistent processor ids later, but only if every
155 * processor gives new nodes a *locally* consistent
156 * processor id, so we'll give the new node the
157 * processor id of an adjacent element for now and then
158 * we'll update that later if appropriate.
159 */
160 Node * hi_node = mesh.add_point
161 (new_location, DofObject::invalid_id, lo_pid);
162
163 /* Come up with a unique unique_id for a potentially new
164 * node. On a distributed mesh we don't yet know what
165 * processor_id will definitely own it, so we can't let
166 * the pid determine the unique_id. But we're not
167 * adding unpartitioned nodes in sync, so we can't let
168 * the mesh autodetermine a unique_id for a new
169 * unpartitioned node either. So we have to pick unique
170 * unique_id values manually.
171 *
172 * We don't have to pick the *same* unique_id value as
173 * will be picked on other processors, though; we'll
174 * sync up each node later. We just need to make sure
175 * we don't duplicate any unique_id that might be chosen
176 * by the same process elsewhere.
177 */
178#ifdef LIBMESH_ENABLE_UNIQUE_ID
179 unique_id_type new_unique_id = max_unique_id +
180 max_new_nodes_per_elem * lo_elem.id() +
181 hon - hon_begin;
182
183 hi_node->set_unique_id(new_unique_id);
184#endif
185
186 /*
187 * insert the new node with its defining vertex
188 * set into the map, and relocate pos to this
189 * new entry, so that the hi_elem can use
190 * \p pos for inserting the node
191 */
192 pos->second = hi_node;
193
194 hi_elem->set_node(hon, hi_node);
195 }
196 // yes, already added.
197 else
198 {
199 Node * hi_node = pos->second;
200 libmesh_assert(hi_node);
201 libmesh_assert_equal_to(mesh.node_ptr(hi_node->id()), hi_node);
202
203 hi_elem->set_node(hon, hi_node);
204
205 // We need to ensure that the processor who should own a
206 // node *knows* they own the node. And because
207 // Node::choose_processor_id() may depend on Node id,
208 // which may not yet be authoritative, we still have to
209 // use a dumb-but-id-independent partitioning heuristic.
210 processor_id_type chosen_pid =
211 std::min (hi_node->processor_id(), lo_pid);
212
213 // Plus, if we just discovered that we own this node,
214 // then on a distributed mesh we need to make sure to
215 // give it a valid id, not just a placeholder id!
216 if (!mesh.is_replicated() &&
217 hi_node->processor_id() != my_pid &&
218 chosen_pid == my_pid)
219 mesh.own_node(*hi_node);
220
221 hi_node->processor_id() = chosen_pid;
222 }
223 }
224
225 /*
226 * find_neighbors relies on remote_elem neighbor links being
227 * properly maintained. Our own code here relies on ordinary
228 * neighbor links being properly maintained, so let's just keep
229 * everything up to date.
230 */
231 for (auto s : lo_elem.side_index_range())
232 {
233 Elem * neigh = lo_elem.neighbor_ptr(s);
234 if (!neigh)
235 continue;
236
237 if (neigh != remote_elem)
238 {
239 // We don't support AMR even outside our own range yet.
240 libmesh_assert_equal_to (neigh->level(), 0);
241
242 const unsigned int ns = neigh->which_neighbor_am_i(&lo_elem);
243 libmesh_assert_not_equal_to(ns, libMesh::invalid_uint);
244
245 neigh->set_neighbor(ns, hi_elem.get());
246 }
247
248 hi_elem->set_neighbor(s, neigh);
249 }
250
257 Elem * interior_p = lo_elem.interior_parent();
258 if (interior_p)
259 hi_elem->set_interior_parent(interior_p);
260
261 if (auto parent_exterior_it = exterior_children_of.find(interior_p);
262 parent_exterior_it != exterior_children_of.end())
263 {
264 auto & exteriors = parent_exterior_it->second;
265 for (std::size_t i : index_range(exteriors))
266 if (exteriors[i] == &lo_elem)
267 {
268 exteriors[i] = hi_elem.get();
269 break;
270 }
271 }
272
277 if (auto exterior_it = exterior_children_of.find(&lo_elem);
278 exterior_it != exterior_children_of.end())
279 {
280 for (Elem * exterior_elem : exterior_it->second)
281 {
282 libmesh_assert(exterior_elem->interior_parent() == &lo_elem);
283 exterior_elem->set_interior_parent(hi_elem.get());
284 }
285 }
286
299 (mesh.get_boundary_info(), &lo_elem, hi_elem.get());
300
301 /*
302 * The new second-order element is ready.
303 * Inserting it into the mesh will replace and delete
304 * the first-order element.
305 */
306 hi_elem->set_id(lo_elem.id());
307#ifdef LIBMESH_ENABLE_UNIQUE_ID
308 hi_elem->set_unique_id(lo_elem.unique_id());
309#endif
310
311 const unsigned int nei = lo_elem.n_extra_integers();
312 hi_elem->add_extra_integers(nei);
313 for (unsigned int i=0; i != nei; ++i)
314 hi_elem->set_extra_integer(i, lo_elem.get_extra_integer(i));
315
316 hi_elem->inherit_data_from(lo_elem);
317
318 mesh.insert_elem(std::move(hi_elem));
319}
320
321
322template <typename ElemTypeConverter>
323void
324all_increased_order_range (UnstructuredMesh & mesh,
326 const unsigned int max_new_nodes_per_elem,
327 const ElemTypeConverter & elem_type_converter)
328{
329 // This function must be run on all processors at once
330 timpi_parallel_only(mesh.comm());
331
332 /*
333 * The maximum number of new higher-order nodes we might be adding,
334 * for use when picking unique unique_id values later. This variable
335 * is not used unless unique ids are enabled, so libmesh_ignore() it
336 * to avoid warnings in that case.
337 */
338 libmesh_ignore(max_new_nodes_per_elem);
339
340 /*
341 * The mesh should at least be consistent enough for us to add new
342 * nodes consistently.
343 */
345
346 /*
347 * If the mesh is empty then we have nothing to do
348 */
349 if (!mesh.n_elem())
350 return;
351
352 // If every element in the range _on every proc_ is already of the
353 // requested higher order then we have nothing to do. However, if
354 // any proc has some lower-order elements in the range, then _all_
355 // processors need to continue this function because it is
356 // parallel_only().
357 //
358 // Note: std::all_of() returns true for an empty range, which can
359 // happen for example in the DistributedMesh case when there are
360 // more processors than elements. In the case of an empty range we
361 // therefore set already_second_order to true on that proc.
362 auto is_higher_order = [&elem_type_converter](const Elem * elem) {
363 ElemType old_type = elem->type();
364 ElemType new_type = elem_type_converter(old_type);
365 return old_type == new_type;
366 };
367
368 bool already_higher_order =
369 std::all_of(range.begin(), range.end(), is_higher_order);
370
371 // Check with other processors and possibly return early
372 mesh.comm().min(already_higher_order);
373 if (already_higher_order)
374 return;
375
376 /*
377 * this map helps in identifying higher order
378 * nodes. Namely, a higher-order node:
379 * - edge node
380 * - face node
381 * - bubble node
382 * is uniquely defined through a set of adjacent
383 * vertices. This set of adjacent vertices is
384 * used to identify already added higher-order
385 * nodes. We are safe to use node id's since we
386 * make sure that these are correctly numbered.
387 *
388 * We lazily use an ordered map here to avoid having to implement a
389 * good hash for vector<dof_id_type>
390 */
391 std::map<std::vector<dof_id_type>, Node *> adj_vertices_to_ho_nodes;
392
393 /*
394 * This map helps us reset any interior_parent() values from the
395 * lower order element to its higher order replacement. Unlike with
396 * neighbor pointers, we don't have backlinks here, so we have to
397 * iterate over the mesh to track forward links.
398 */
399 std::unordered_map<Elem *, std::vector<Elem *>> exterior_children_of;
400
401 /*
402 * max_new_nodes_per_elem is the maximum number of new higher order
403 * nodes we might be adding, for use when picking unique unique_id
404 * values later. This variable is not used unless unique ids are
405 * enabled.
406 */
407#ifdef LIBMESH_ENABLE_UNIQUE_ID
409#endif
410
417 dof_id_type n_unpartitioned_elem = 0;
418
433 auto track_if_necessary = [&adj_vertices_to_ho_nodes,
434 &exterior_children_of,
435 &elem_type_converter](Elem * elem) {
436 if (elem && elem != remote_elem)
437 {
438 if (elem->default_order() != FIRST)
439 for (unsigned int hon : make_range(elem->n_vertices(), elem->n_nodes()))
440 {
441 auto pos = map_hi_order_node(hon, *elem, adj_vertices_to_ho_nodes);
442 pos->second = elem->node_ptr(hon);
443 }
444
445 const ElemType old_type = elem->type();
446 const ElemType new_type = elem_type_converter(old_type);
447 if (old_type != new_type)
448 exterior_children_of.emplace(elem, std::vector<Elem *>());
449 }
450 };
451
452 // If we're in the common case then just track everything; otherwise
453 // find point neighbors to track
454 if (range.begin() == mesh.elements_begin() &&
455 range.end() == mesh.elements_end())
456 {
457 for (auto & elem : range)
458 track_if_necessary(elem);
459 }
460 else
461 {
462 GhostingFunctor::map_type point_neighbor_elements;
463
464 GhostPointNeighbors point_neighbor_finder(mesh);
465 point_neighbor_finder(range.begin(), range.end(),
467 point_neighbor_elements);
468
469 for (auto & [elem, coupling_map] : point_neighbor_elements)
470 {
471 libmesh_ignore(coupling_map);
472 track_if_necessary(const_cast<Elem *>(elem));
473 }
474 }
475
480 for (auto & elem : mesh.element_ptr_range())
481 if (auto exterior_map_it = exterior_children_of.find(elem->interior_parent());
482 exterior_map_it != exterior_children_of.end())
483 exterior_map_it->second.push_back(elem);
484
491 for (auto & lo_elem : range)
492 {
493 // Now we can skip the elements in the range that are already
494 // higher-order.
495 const ElemType old_type = lo_elem->type();
496 const ElemType new_type = elem_type_converter(old_type);
497
498 if (old_type == new_type)
499 continue;
500
501 // this does _not_ work for refined elements
502 libmesh_assert_equal_to (lo_elem->level(), 0);
503
505 ++n_unpartitioned_elem;
506
507 /*
508 * Build the higher-order equivalent; add to
509 * the new_elements list.
510 */
511 auto ho_elem = Elem::build (new_type);
512
513 libmesh_assert_equal_to (lo_elem->n_vertices(), ho_elem->n_vertices());
514
515 /*
516 * By definition the initial nodes of the lower and higher order
517 * element are identically numbered. Transfer these.
518 */
519 for (unsigned int v=0, lnn=lo_elem->n_nodes(); v < lnn; v++)
520 ho_elem->set_node(v, lo_elem->node_ptr(v));
521
522 transfer_elem(*lo_elem, std::move(ho_elem),
523#ifdef LIBMESH_ENABLE_UNIQUE_ID
524 max_unique_id, max_new_nodes_per_elem,
525#endif
526 mesh, adj_vertices_to_ho_nodes,
527 exterior_children_of);
528 } // end for (auto & lo_elem : range)
529
530 // we can clear the map at this point.
531 adj_vertices_to_ho_nodes.clear();
532
533#ifdef LIBMESH_ENABLE_UNIQUE_ID
534 const unique_id_type new_max_unique_id = max_unique_id +
535 max_new_nodes_per_elem * mesh.n_elem();
536 mesh.set_next_unique_id(new_max_unique_id);
537#endif
538
539 // On a DistributedMesh our ghost node processor ids may be bad,
540 // the ids of nodes touching remote elements may be inconsistent,
541 // unique_ids of newly added non-local nodes remain unset, and our
542 // partitioning of new nodes may not be well balanced.
543 //
544 // make_nodes_parallel_consistent() will fix all this.
545 if (!mesh.is_replicated())
546 {
547 dof_id_type max_unpartitioned_elem = n_unpartitioned_elem;
548 mesh.comm().max(max_unpartitioned_elem);
549 if (max_unpartitioned_elem)
550 {
551 // We'd better be effectively serialized here. In theory we
552 // could support more complicated cases but for now we
553 // only support "completely partitioned" and/or "serialized"
554 if (mesh.is_serial())
555 libmesh_assert(mesh.comm().verify(n_unpartitioned_elem));
556 else
557 libmesh_not_implemented();
558 }
559 else
560 {
562 }
563 }
564
565 // renumber nodes, repartition nodes, etc. We may no longer need a
566 // find_neighbors() here since we're keeping neighbor links intact
567 // ourselves, *except* that if we're not already prepared we may
568 // have user code that was expecting this call to prepare neighbors.
569 const bool old_find_neighbors = mesh.allow_find_neighbors();
570 if (mesh.is_prepared())
573 mesh.allow_find_neighbors(old_find_neighbors);
574}
575
576
577} // anonymous namespace
578
579
580namespace libMesh
581{
582
583// This class adapts a vector of Nodes (represented by a pair of a Point and a dof_id_type)
584// for use in a nanoflann KD-Tree
585
587{
588private:
589 const std::vector<std::pair<Point, dof_id_type>> _nodes;
590
591public:
592 VectorOfNodesAdaptor(const std::vector<std::pair<Point, dof_id_type>> & nodes) :
593 _nodes(nodes)
594 {}
595
599 inline size_t kdtree_get_point_count() const { return _nodes.size(); }
600
606 inline Real kdtree_get_pt(const size_t idx, int dim) const
607 {
608 libmesh_assert_less (idx, _nodes.size());
609 libmesh_assert_less (dim, 3);
610
611 const Point & p(_nodes[idx].first);
612
613 if (dim==0) return p(0);
614 if (dim==1) return p(1);
615 return p(2);
616 }
617
618 /*
619 * Optional bounding-box computation
620 */
621 template <class BBOX>
622 bool kdtree_get_bbox(BBOX & /* bb */) const { return false; }
623};
624
625
626// ------------------------------------------------------------
627// UnstructuredMesh class member functions
629 unsigned char d) :
630 MeshBase (comm_in,d)
631{
633}
634
635
636
638 MeshBase (other_mesh)
639{
641}
642
643
644
646 const bool skip_find_neighbors,
647 dof_id_type element_id_offset,
648 dof_id_type node_id_offset,
650#ifdef LIBMESH_ENABLE_UNIQUE_ID
651 unique_id_offset
652#endif
653 ,
654 std::unordered_map<subdomain_id_type, subdomain_id_type> *
655 id_remapping,
656 const bool skip_preparation)
657{
658 LOG_SCOPE("copy_nodes_and_elements()", "UnstructuredMesh");
659
660 // If we're asked to skip all preparation, we should be skipping
661 // find_neighbors specifically.
662 libmesh_assert(!skip_preparation || skip_find_neighbors);
663
664 std::pair<std::vector<unsigned int>, std::vector<unsigned int>>
665 extra_int_maps = this->merge_extra_integer_names(other_mesh);
666
667 const unsigned int n_old_node_ints = extra_int_maps.second.size(),
668 n_new_node_ints = _node_integer_names.size(),
669 n_old_elem_ints = extra_int_maps.first.size(),
670 n_new_elem_ints = _elem_integer_names.size();
671
672 // If we are partitioned into fewer parts than the incoming mesh has
673 // processors to handle, then we need to "wrap" the other Mesh's
674 // processor ids to fit within our range. This can happen, for
675 // example, while stitching meshes with small numbers of elements in
676 // parallel...
677 bool wrap_proc_ids = (this->n_processors() <
678 other_mesh.n_partitions());
679
680 // We're assuming the other mesh has proper element number ordering,
681 // so that we add parents before their children, and that the other
682 // mesh is consistently partitioned. We're not assuming that node
683 // proc ids are topologically consistent, so we don't just
684 // libmesh_assert_valid_procids.
685#ifdef DEBUG
688#endif
689
690 //Copy in Nodes
691 {
692 //Preallocate Memory if necessary
693 this->reserve_nodes(other_mesh.n_nodes());
694
695 for (const auto & oldn : other_mesh.node_ptr_range())
696 {
697 processor_id_type added_pid = cast_int<processor_id_type>
698 (wrap_proc_ids ? oldn->processor_id() % this->n_processors() : oldn->processor_id());
699
700 // Add new nodes in old node Point locations
701 Node * newn =
702 this->add_point(*oldn,
703 oldn->id() + node_id_offset,
704 added_pid);
705
706 newn->add_extra_integers(n_new_node_ints);
707 for (unsigned int i = 0; i != n_old_node_ints; ++i)
708 newn->set_extra_integer(extra_int_maps.second[i],
709 oldn->get_extra_integer(i));
710
711#ifdef LIBMESH_ENABLE_UNIQUE_ID
712 newn->set_unique_id(oldn->unique_id() + unique_id_offset);
713#endif
714 }
715 }
716
717 //Copy in Elements
718 {
719 //Preallocate Memory if necessary
720 this->reserve_elem(other_mesh.n_elem());
721
722 // Declare a map linking old and new elements, needed to copy the neighbor lists
723 typedef std::unordered_map<const Elem *, Elem *> map_type;
724 map_type old_elems_to_new_elems, ip_map;
725
726 // Loop over the elements
727 for (const auto & old : other_mesh.element_ptr_range())
728 {
729 // Build a new element
730 Elem * newparent = old->parent() ?
731 this->elem_ptr(old->parent()->id() + element_id_offset) :
732 nullptr;
733 auto el = old->disconnected_clone();
734 el->set_parent(newparent);
735
736 subdomain_id_type sbd_id = old->subdomain_id();
737 if (id_remapping)
738 {
739 auto remapping_it = id_remapping->find(sbd_id);
740 if (remapping_it != id_remapping->end())
741 sbd_id = remapping_it->second;
742 }
743 el->subdomain_id() = sbd_id;
744
745 // Hold off on trying to set the interior parent because we may actually
746 // add lower dimensional elements before their interior parents
747 if (old->interior_parent())
748 ip_map[old] = el.get();
749
750#ifdef LIBMESH_ENABLE_AMR
751 if (old->has_children())
752 for (unsigned int c = 0, nc = old->n_children(); c != nc; ++c)
753 if (old->child_ptr(c) == remote_elem)
754 el->add_child(const_cast<RemoteElem *>(remote_elem), c);
755
756 //Create the parent's child pointers if necessary
757 if (newparent)
758 {
759 unsigned int oldc = old->parent()->which_child_am_i(old);
760 newparent->add_child(el.get(), oldc);
761 }
762
763 // Copy the refinement flags
764 el->set_refinement_flag(old->refinement_flag());
765
766 // Use hack_p_level since we may not have sibling elements
767 // added yet
768 el->hack_p_level(old->p_level());
769
770 el->set_p_refinement_flag(old->p_refinement_flag());
771#endif // #ifdef LIBMESH_ENABLE_AMR
772
773 //Assign all the nodes
774 for (auto i : el->node_index_range())
775 el->set_node(i,
776 this->node_ptr(old->node_id(i) + node_id_offset));
777
778 // And start it off with the same processor id (mod _n_parts).
779 el->processor_id() = cast_int<processor_id_type>
780 (wrap_proc_ids ? old->processor_id() % this->n_processors() : old->processor_id());
781
782 // Give it the same element and unique ids
783 el->set_id(old->id() + element_id_offset);
784
785 el->add_extra_integers(n_new_elem_ints);
786 for (unsigned int i = 0; i != n_old_elem_ints; ++i)
787 el->set_extra_integer(extra_int_maps.first[i],
788 old->get_extra_integer(i));
789
790#ifdef LIBMESH_ENABLE_UNIQUE_ID
791 el->set_unique_id(old->unique_id() + unique_id_offset);
792#endif
793
794 //Hold onto it
795 if (!skip_find_neighbors)
796 {
797 for (auto s : old->side_index_range())
798 if (old->neighbor_ptr(s) == remote_elem)
799 el->set_neighbor(s, const_cast<RemoteElem *>(remote_elem));
800 this->add_elem(std::move(el));
801 }
802 else
803 {
804 Elem * new_el = this->add_elem(std::move(el));
805 old_elems_to_new_elems[old] = new_el;
806 }
807 }
808
809 // If the other_mesh had some interior parents, we may need to
810 // copy those pointers (if they're to elements in a third mesh),
811 // or create new equivalent pointers (if they're to elements we
812 // just copied), or scream and die (if the other mesh had interior
813 // parents from a third mesh but we already have interior parents
814 // that aren't to that same third mesh.
815 if (!ip_map.empty())
816 {
817 std::atomic<bool> existing_interior_parents{false};
818
820 (this->element_stored_range(),
821 [&existing_interior_parents](const ElemRange & range)
822 {
823 for (Elem * elem : range)
824 if (elem->interior_parent())
825 {
826 existing_interior_parents = true;
827 break;
828 }
829 });
830
831 MeshBase * other_interior_mesh =
832 const_cast<MeshBase *>(&other_mesh.interior_mesh());
833
834 // If we don't already have interior parents, then we can just
835 // use whatever interior_mesh we need for the incoming
836 // elements.
837 if (!existing_interior_parents)
838 {
839 if (other_interior_mesh == &other_mesh)
840 this->set_interior_mesh(*this);
841 else
842 this->set_interior_mesh(*other_interior_mesh);
843 }
844
845 if (other_interior_mesh == &other_mesh &&
846 _interior_mesh == this)
847 for (auto & elem_pair : ip_map)
848 elem_pair.second->set_interior_parent(
849 this->elem_ptr(elem_pair.first->interior_parent()->id() + element_id_offset));
850 else if (other_interior_mesh == _interior_mesh)
851 for (auto & elem_pair : ip_map)
852 {
853 Elem * ip = const_cast<Elem *>(elem_pair.first->interior_parent());
855 ip == other_interior_mesh->elem_ptr(ip->id()));
856 elem_pair.second->set_interior_parent(ip);
857 }
858 else
859 libmesh_error_msg("Cannot copy boundary elements between meshes with different interior meshes");
860 }
861
862 // Loop (again) over the elements to fill in the neighbors
863 if (skip_find_neighbors)
864 {
865 old_elems_to_new_elems[remote_elem] = const_cast<RemoteElem*>(remote_elem);
866
867 for (const auto & old_elem : other_mesh.element_ptr_range())
868 {
869 Elem * new_elem = old_elems_to_new_elems[old_elem];
870 for (auto s : old_elem->side_index_range())
871 {
872 const Elem * old_neighbor = old_elem->neighbor_ptr(s);
873 Elem * new_neighbor = old_elems_to_new_elems[old_neighbor];
874 new_elem->set_neighbor(s, new_neighbor);
875 }
876 }
877 }
878 }
879
880#ifdef LIBMESH_ENABLE_UNIQUE_ID
881 // We set the unique ids of nodes after adding them to the mesh such that our value of
882 // _next_unique_id may be wrong. So we amend that here
883 this->set_next_unique_id(other_mesh.parallel_max_unique_id() + unique_id_offset + 1);
884#endif
885
886 // Finally, partially prepare the new Mesh for use, if that isn't
887 // being skipped.
888 // Even the default behavior here is for backwards compatibility,
889 // and we don't want to prepare everything.
890
891 if (!skip_preparation)
892 {
893 // Keep the same numbering and partitioning and distribution
894 // status for now, but save our original policies to restore
895 // later.
896 const bool allowed_renumbering = this->allow_renumbering();
897 const bool allowed_find_neighbors = this->allow_find_neighbors();
898 const bool allowed_elem_removal = this->allow_remote_element_removal();
899 const bool allowed_detect_detect_interior_parents = this->allow_detect_interior_parents();
900 this->allow_renumbering(false);
901 this->allow_remote_element_removal(false);
902 this->allow_find_neighbors(!skip_find_neighbors);
904
905 // We should generally be able to skip *all* partitioning here
906 // because we're only adding one already-consistent mesh to
907 // another.
908 const bool skipped_partitioning = this->skip_partitioning();
909 this->skip_partitioning(true);
910
911 const bool was_prepared = this->is_prepared();
912 this->prepare_for_use();
913
914 //But in the long term, don't change our policies.
915 this->allow_find_neighbors(allowed_find_neighbors);
916 this->allow_renumbering(allowed_renumbering);
917 this->allow_remote_element_removal(allowed_elem_removal);
918 this->skip_partitioning(skipped_partitioning);
919 this->allow_detect_interior_parents(allowed_detect_detect_interior_parents);
920
921 // That prepare_for_use() call marked us as prepared, but we
922 // specifically avoided some important preparation, so we might not
923 // actually be prepared now.
924 if (skip_find_neighbors ||
925 !was_prepared || !other_mesh.is_prepared())
926 this->unset_is_prepared();
927 }
928
929 // In general we've just invalidated just about everything, and we'd
930 // like to unset_is_prepared(), but specific use cases might know a
931 // priori that they're still partitioned well, or that they've
932 // copied in a disjoint mesh component and don't need new neighbor
933 // pointers, or that they're not adding anything that would change
934 // cached subdomain/element/boundary sets, etc., so we'll rely on
935 // users of the "advanced" skip_preparation option to also set what
936 // preparation they still need.
937
938 // else
939 // this->unset_is_prepared();
940}
941
942
943
945{
946 // this->clear (); // Nothing to clear at this level
947
948 libmesh_exceptionless_assert (!libMesh::closed());
949}
950
951
952
953
954
955void UnstructuredMesh::find_neighbors (const bool reset_remote_elements,
956 const bool reset_current_list,
957 const bool assert_valid)
958{
959 // We might actually want to run this on an empty mesh
960 // (e.g. the boundary mesh for a nonexistent bcid!)
961 // libmesh_assert_not_equal_to (this->n_nodes(), 0);
962 // libmesh_assert_not_equal_to (this->n_elem(), 0);
963
964 // This function must be run on all processors at once
965 parallel_object_only();
966
967 LOG_SCOPE("find_neighbors()", "Mesh");
968
969 //TODO:[BSK] This should be removed later?!
970 if (reset_current_list)
972 (this->element_stored_range(),
973 [reset_remote_elements](const ElemRange & range)
974 {
975 for (Elem * e : range)
976 for (auto s : e->side_index_range())
977 if (e->neighbor_ptr(s) != remote_elem || reset_remote_elements)
978 e->set_neighbor(s, nullptr);
979 });
980
981 // Find neighboring elements by first finding elements
982 // with identical side keys and then check to see if they
983 // are neighbors
984 {
985 // data structures -- Use the hash_multimap if available
986 typedef dof_id_type key_type;
987 typedef std::pair<Elem *, unsigned char> val_type;
988 typedef std::unordered_multimap<key_type, val_type> map_type;
989
990 // A map from side keys to corresponding elements & side numbers
991 map_type side_to_elem_map;
992
993 // Pull objects out of the loop to reduce heap operations
994 std::unique_ptr<Elem> my_side, their_side;
995
996 for (const auto & element : this->element_ptr_range())
997 {
998 for (auto ms : element->side_index_range())
999 {
1000 next_side:
1001 // If we haven't yet found a neighbor on this side, try.
1002 // Even if we think our neighbor is remote, that
1003 // information may be out of date.
1004 if (element->neighbor_ptr(ms) == nullptr ||
1005 element->neighbor_ptr(ms) == remote_elem)
1006 {
1007 // Get the key for the side of this element. Use the
1008 // low_order_key so we can find neighbors in
1009 // mixed-order meshes if necessary.
1010 const dof_id_type key = element->low_order_key(ms);
1011
1012 // Look for elements that have an identical side key
1013 auto bounds = side_to_elem_map.equal_range(key);
1014
1015 // May be multiple keys, check all the possible
1016 // elements which _might_ be neighbors.
1017 if (bounds.first != bounds.second)
1018 {
1019 // Get the side for this element
1020 element->side_ptr(my_side, ms);
1021
1022 // Look at all the entries with an equivalent key
1023 while (bounds.first != bounds.second)
1024 {
1025 // Get the potential element
1026 Elem * neighbor = bounds.first->second.first;
1027
1028 // Get the side for the neighboring element
1029 const unsigned int ns = bounds.first->second.second;
1030 neighbor->side_ptr(their_side, ns);
1031 //libmesh_assert(my_side.get());
1032 //libmesh_assert(their_side.get());
1033
1034 // If found a match with my side
1035 //
1036 // In 1D, since parents and children have an
1037 // equal side (i.e. a node) we need to check
1038 // for matching level() to avoid setting our
1039 // neighbor pointer to any of our neighbor's
1040 // descendants.
1041 if ((*my_side == *their_side) &&
1042 (element->level() == neighbor->level()))
1043 {
1044 // So share a side. Is this a mixed pair
1045 // of subactive and active/ancestor
1046 // elements?
1047 // If not, then we're neighbors.
1048 // If so, then the subactive's neighbor is
1049
1050 if (element->subactive() ==
1051 neighbor->subactive())
1052 {
1053 // an element is only subactive if it has
1054 // been coarsened but not deleted
1055 element->set_neighbor (ms,neighbor);
1056 neighbor->set_neighbor(ns,element);
1057 }
1058 else if (element->subactive())
1059 {
1060 element->set_neighbor(ms,neighbor);
1061 }
1062 else if (neighbor->subactive())
1063 {
1064 neighbor->set_neighbor(ns,element);
1065 }
1066 side_to_elem_map.erase (bounds.first);
1067
1068 // get out of this nested crap
1069 goto next_side;
1070 }
1071
1072 ++bounds.first;
1073 }
1074 }
1075
1076 // didn't find a match...
1077 // Build the map entry for this element
1078 side_to_elem_map.emplace
1079 (key, std::make_pair(element, cast_int<unsigned char>(ms)));
1080 }
1081 }
1082 }
1083 }
1084
1085#ifdef LIBMESH_ENABLE_PERIODIC
1086 // Get the disjoint neighbor boundary pairs object (from periodic BCs)
1087 auto * db = this->get_disjoint_neighbor_boundary_pairs();
1088
1089 if (db)
1090 {
1091 // Obtain a point locator
1092 std::unique_ptr<PointLocatorBase> point_locator = this->sub_point_locator();
1093
1094 for (const auto & element : this->element_ptr_range())
1095 {
1096 for (auto ms : element->side_index_range())
1097 {
1098 // Skip if this side already has a valid neighbor (including remote neighbors)
1099 if (element->neighbor_ptr(ms) != nullptr &&
1100 element->neighbor_ptr(ms) != remote_elem)
1101 continue;
1102
1103 for (const auto & [id, boundary_ptr] : *db)
1104 {
1105 if (!this->get_boundary_info().has_boundary_id(element, ms, id))
1106 continue;
1107
1108 unsigned int neigh_side;
1109 const Elem * neigh =
1110 db->neighbor(id, *point_locator, element, ms, &neigh_side);
1111
1112 if (neigh && neigh != remote_elem && neigh != element)
1113 {
1114 auto neigh_changeable = this->elem_ptr(neigh->id());
1115 element->set_neighbor(ms, neigh_changeable);
1116 neigh_changeable->set_neighbor(neigh_side, element);
1117 }
1118 }
1119 }
1120 }
1121 }
1122#endif // LIBMESH_ENABLE_PERIODIC
1123
1124#ifdef LIBMESH_ENABLE_AMR
1125
1153 const unsigned int n_levels = MeshTools::n_levels(*this);
1154 for (unsigned int level = 1; level < n_levels; ++level)
1155 {
1156 for (auto & current_elem : as_range(level_elements_begin(level),
1157 level_elements_end(level)))
1158 {
1159 libmesh_assert(current_elem);
1160 Elem * parent = current_elem->parent();
1161 libmesh_assert(parent);
1162 const unsigned int my_child_num = parent->which_child_am_i(current_elem);
1163
1164 for (auto s : current_elem->side_index_range())
1165 {
1166 if (current_elem->neighbor_ptr(s) == nullptr ||
1167 (current_elem->neighbor_ptr(s) == remote_elem &&
1168 parent->is_child_on_side(my_child_num, s)))
1169 {
1170 Elem * neigh = parent->neighbor_ptr(s);
1171
1172 // If neigh was refined and had non-subactive children
1173 // made remote earlier, then our current elem should
1174 // actually have one of those remote children as a
1175 // neighbor
1176 if (neigh &&
1177 (neigh->ancestor() ||
1178 // If neigh has subactive children which should have
1179 // matched as neighbors of the current element but
1180 // did not, then those likewise must be remote
1181 // children.
1182 (current_elem->subactive() && neigh->has_children() &&
1183 (neigh->level()+1) == current_elem->level())))
1184 {
1185#ifdef DEBUG
1186 // Let's make sure that "had children made remote"
1187 // situation is actually the case
1188 libmesh_assert(neigh->has_children());
1189 bool neigh_has_remote_children = false;
1190 for (auto & child : neigh->child_ref_range())
1191 if (&child == remote_elem)
1192 neigh_has_remote_children = true;
1193 libmesh_assert(neigh_has_remote_children);
1194
1195 // And let's double-check that we don't have
1196 // a remote_elem neighboring an active local element
1197 if (current_elem->active())
1198 libmesh_assert_not_equal_to (current_elem->processor_id(),
1199 this->processor_id());
1200#endif // DEBUG
1201 neigh = const_cast<RemoteElem *>(remote_elem);
1202 }
1203 // If neigh and current_elem are more than one level
1204 // apart, figuring out whether we have a remote
1205 // neighbor here becomes much harder.
1206 else if (neigh && (current_elem->subactive() &&
1207 neigh->has_children()))
1208 {
1209 // Find the deepest descendant of neigh which
1210 // we could consider for a neighbor. If we run
1211 // out of neigh children, then that's our
1212 // neighbor. If we find a potential neighbor
1213 // with remote_children and we don't find any
1214 // potential neighbors among its non-remote
1215 // children, then our neighbor must be remote.
1216 while (neigh != remote_elem &&
1217 neigh->has_children())
1218 {
1219 bool found_neigh = false;
1220 for (unsigned int c = 0, nc = neigh->n_children();
1221 !found_neigh && c != nc; ++c)
1222 {
1223 Elem * child = neigh->child_ptr(c);
1224 if (child == remote_elem)
1225 continue;
1226 for (auto ncn : child->neighbor_ptr_range())
1227 {
1228 if (ncn != remote_elem &&
1229 ncn->is_ancestor_of(current_elem))
1230 {
1231 neigh = ncn;
1232 found_neigh = true;
1233 break;
1234 }
1235 }
1236 }
1237 if (!found_neigh)
1238 neigh = const_cast<RemoteElem *>(remote_elem);
1239 }
1240 }
1241 current_elem->set_neighbor(s, neigh);
1242#ifdef DEBUG
1243 if (neigh != nullptr && neigh != remote_elem)
1244 // We ignore subactive elements here because
1245 // we don't care about neighbors of subactive element.
1246 if ((!neigh->active()) && (!current_elem->subactive()))
1247 {
1248 libMesh::err << "On processor " << this->processor_id()
1249 << std::endl;
1250 libMesh::err << "Bad element ID = " << current_elem->id()
1251 << ", Side " << s << ", Bad neighbor ID = " << neigh->id() << std::endl;
1252 libMesh::err << "Bad element proc_ID = " << current_elem->processor_id()
1253 << ", Bad neighbor proc_ID = " << neigh->processor_id() << std::endl;
1254 libMesh::err << "Bad element size = " << current_elem->hmin()
1255 << ", Bad neighbor size = " << neigh->hmin() << std::endl;
1256 libMesh::err << "Bad element center = " << current_elem->vertex_average()
1257 << ", Bad neighbor center = " << neigh->vertex_average() << std::endl;
1258 libMesh::err << "ERROR: "
1259 << (current_elem->active()?"Active":"Ancestor")
1260 << " Element at level "
1261 << current_elem->level() << std::endl;
1262 libMesh::err << "with "
1263 << (parent->active()?"active":
1264 (parent->subactive()?"subactive":"ancestor"))
1265 << " parent share "
1266 << (neigh->subactive()?"subactive":"ancestor")
1267 << " neighbor at level " << neigh->level()
1268 << std::endl;
1269 NameBasedIO(*this).write ("bad_mesh.gmv");
1270 libmesh_error_msg("Problematic mesh written to bad_mesh.gmv.");
1271 }
1272#endif // DEBUG
1273 }
1274 }
1275
1276 // We can skip to the next element if we're full-dimension
1277 // and therefore don't have any interior parents
1278 if (current_elem->dim() >= LIBMESH_DIM)
1279 continue;
1280
1281 // We have no interior parents unless we can find one later
1282 current_elem->set_interior_parent(nullptr);
1283
1284 Elem * pip = parent->interior_parent();
1285
1286 if (!pip)
1287 continue;
1288
1289 // If there's no interior_parent children, whether due to a
1290 // remote element or a non-conformity, then there's no
1291 // children to search.
1292 if (pip == remote_elem || pip->active())
1293 {
1294 current_elem->set_interior_parent(pip);
1295 continue;
1296 }
1297
1298 // For node comparisons we'll need a sensible tolerance
1299 Real node_tolerance = current_elem->hmin() * TOLERANCE;
1300
1301 // Otherwise our interior_parent should be a child of our
1302 // parent's interior_parent.
1303 for (auto & child : pip->child_ref_range())
1304 {
1305 // If we have a remote_elem, that might be our
1306 // interior_parent. We'll set it provisionally now and
1307 // keep trying to find something better.
1308 if (&child == remote_elem)
1309 {
1310 current_elem->set_interior_parent
1311 (const_cast<RemoteElem *>(remote_elem));
1312 continue;
1313 }
1314
1315 bool child_contains_our_nodes = true;
1316 for (auto & n : current_elem->node_ref_range())
1317 {
1318 bool child_contains_this_node = false;
1319 for (auto & cn : child.node_ref_range())
1320 if (cn.absolute_fuzzy_equals
1321 (n, node_tolerance))
1322 {
1323 child_contains_this_node = true;
1324 break;
1325 }
1326 if (!child_contains_this_node)
1327 {
1328 child_contains_our_nodes = false;
1329 break;
1330 }
1331 }
1332 if (child_contains_our_nodes)
1333 {
1334 current_elem->set_interior_parent(&child);
1335 break;
1336 }
1337 }
1338
1339 // We should have found *some* interior_parent at this
1340 // point, whether semilocal or remote.
1341 libmesh_assert(current_elem->interior_parent());
1342 }
1343 }
1344#endif // AMR
1345
1346#ifdef DEBUG
1347 if (assert_valid)
1348 {
1350 !reset_remote_elements);
1352 }
1353#else
1354 libmesh_ignore(assert_valid);
1355#endif
1356
1357 this->_preparation.has_neighbor_ptrs = true;
1358}
1359
1360
1361
1362void UnstructuredMesh::read (const std::string & name,
1363 void *,
1364 bool skip_renumber_nodes_and_elements,
1365 bool skip_find_neighbors,
1366 bool skip_detect_interior_parents)
1367{
1368 // Set the skip_renumber_nodes_and_elements flag on all processors
1369 // if necessary.
1370 // This ensures that renumber_nodes_and_elements is *not* called
1371 // during prepare_for_use() for certain types of mesh files.
1372 // This is required in cases where there is an associated solution
1373 // file which expects a certain ordering of the nodes.
1374 if (Utility::ends_with(name, ".gmv"))
1375 this->allow_renumbering(false);
1376
1377 NameBasedIO(*this).read(name);
1378
1379 if (skip_renumber_nodes_and_elements)
1380 {
1381 // Use MeshBase::allow_renumbering() yourself instead.
1382 libmesh_deprecated();
1383 this->allow_renumbering(false);
1384 }
1385
1386 // Done reading the mesh. Now prepare it for use.
1387 const bool old_allow_find_neighbors = this->allow_find_neighbors();
1388 const bool old_allow_detect_interior_parents = this->allow_detect_interior_parents();
1389
1390 this->allow_find_neighbors(!skip_find_neighbors);
1391 this->allow_detect_interior_parents(!skip_detect_interior_parents);
1392
1393 this->prepare_for_use();
1394
1395 this->allow_find_neighbors(old_allow_find_neighbors);
1396 this->allow_detect_interior_parents(old_allow_detect_interior_parents);
1397}
1398
1399
1400
1401void UnstructuredMesh::write (const std::string & name) const
1402{
1403 LOG_SCOPE("write()", "Mesh");
1404
1405 NameBasedIO(*this).write(name);
1406}
1407
1408
1409
1410void UnstructuredMesh::write (const std::string & name,
1411 const std::vector<Number> & v,
1412 const std::vector<std::string> & vn) const
1413{
1414 LOG_SCOPE("write()", "Mesh");
1415
1416 NameBasedIO(*this).write_nodal_data(name, v, vn);
1417}
1418
1419
1420
1421
1422
1424 const processor_id_type pid) const
1425{
1426
1427 // Issue a warning if the number the number of processors
1428 // currently available is less that that requested for
1429 // partitioning. This is not necessarily an error since
1430 // you may run on one processor and still partition the
1431 // mesh into several partitions.
1432#ifdef DEBUG
1433 if (this->n_processors() < pid)
1434 {
1435 libMesh::out << "WARNING: You are creating a "
1436 << "mesh for a processor id (="
1437 << pid
1438 << ") greater than "
1439 << "the number of processors available for "
1440 << "the calculation. (="
1441 << this->n_processors()
1442 << ")."
1443 << std::endl;
1444 }
1445#endif
1446
1447 this->create_submesh (pid_mesh,
1448 this->active_pid_elements_begin(pid),
1449 this->active_pid_elements_end(pid));
1450}
1451
1452
1453
1454
1455
1456
1457
1459 const const_element_iterator & it,
1460 const const_element_iterator & it_end) const
1461{
1462 // Just in case the subdomain_mesh already has some information
1463 // in it, get rid of it.
1464 new_mesh.clear();
1465
1466 // If we're not serial, our submesh isn't either.
1467 // There are no remote elements to delete on an empty mesh, but
1468 // calling the method to do so marks the mesh as parallel.
1469 if (!this->is_serial())
1470 new_mesh.delete_remote_elements();
1471
1472 // Fail if (*this == new_mesh), we cannot create a submesh inside ourself!
1473 // This may happen if the user accidentally passes the original mesh into
1474 // this function! We will check this by making sure we did not just
1475 // clear ourself.
1476 libmesh_assert_not_equal_to (this->n_nodes(), 0);
1477 libmesh_assert_not_equal_to (this->n_elem(), 0);
1478
1479 // Container to catch boundary IDs handed back by BoundaryInfo
1480 std::vector<boundary_id_type> bc_ids;
1481
1482 // Put any extra integers on the new mesh too
1483 new_mesh.merge_extra_integer_names(*this);
1484 const unsigned int n_node_ints = _node_integer_names.size();
1485
1486 for (const auto & old_elem : as_range(it, it_end))
1487 {
1488 // Add an equivalent element type to the new_mesh.
1489 // disconnected_clone() copies ids, extra element integers, etc.
1490 auto uelem = old_elem->disconnected_clone();
1491 Elem * new_elem = new_mesh.add_elem(std::move(uelem));
1492 libmesh_assert(new_elem);
1493
1494 // Loop over the nodes on this element.
1495 for (auto n : old_elem->node_index_range())
1496 {
1497 const dof_id_type this_node_id = old_elem->node_id(n);
1498
1499 // Add this node to the new mesh if it's not there already
1500 if (!new_mesh.query_node_ptr(this_node_id))
1501 {
1502 Node * newn =
1503 new_mesh.add_point (old_elem->point(n),
1504 this_node_id,
1505 old_elem->node_ptr(n)->processor_id());
1506
1507 newn->add_extra_integers(n_node_ints);
1508 for (unsigned int i = 0; i != n_node_ints; ++i)
1509 newn->set_extra_integer(i, old_elem->node_ptr(n)->get_extra_integer(i));
1510
1511#ifdef LIBMESH_ENABLE_UNIQUE_ID
1512 newn->set_unique_id(old_elem->node_ptr(n)->unique_id());
1513#endif
1514 }
1515
1516 // Define this element's connectivity on the new mesh
1517 new_elem->set_node(n, new_mesh.node_ptr(this_node_id));
1518 }
1519
1520 // Maybe add boundary conditions for this element
1521 for (auto s : old_elem->side_index_range())
1522 {
1523 this->get_boundary_info().boundary_ids(old_elem, s, bc_ids);
1524 new_mesh.get_boundary_info().add_side (new_elem, s, bc_ids);
1525 }
1526 } // end loop over elements
1527
1528 // Prepare the new_mesh for use
1529 new_mesh.prepare_for_use();
1530}
1531
1532
1533
1534#ifdef LIBMESH_ENABLE_AMR
1536{
1537 LOG_SCOPE ("contract()", "Mesh");
1538
1539 // Flag indicating if this call actually changes the mesh
1540 bool mesh_changed = false;
1541
1542#ifdef DEBUG
1543 for (const auto & elem : this->element_ptr_range())
1544 libmesh_assert(elem->active() || elem->subactive() || elem->ancestor());
1545#endif
1546
1547 // Loop over the elements.
1548 for (auto & elem : this->element_ptr_range())
1549 {
1550 // Delete all the subactive ones
1551 if (elem->subactive())
1552 {
1553 // No level-0 element should be subactive.
1554 // Note that we CAN'T test elem->level(), as that
1555 // touches elem->parent()->dim(), and elem->parent()
1556 // might have already been deleted!
1557 libmesh_assert(elem->parent());
1558
1559 // Delete the element
1560 // This just sets a pointer to nullptr, and doesn't
1561 // invalidate any iterators
1562 this->delete_elem(elem);
1563
1564 // the mesh has certainly changed
1565 mesh_changed = true;
1566 }
1567 else
1568 {
1569 // Compress all the active ones
1570 if (elem->active())
1571 elem->contract();
1572 else
1573 libmesh_assert (elem->ancestor());
1574 }
1575 }
1576
1577 // Strip any newly-created nullptr voids out of the element array
1579
1580 // FIXME: Need to understand why deleting subactive children
1581 // invalidates the point locator. For now we will clear it explicitly
1582 this->clear_point_locator();
1583
1584 // Allow our GhostingFunctor objects to reinit if necessary.
1585 for (auto & gf : as_range(this->ghosting_functors_begin(),
1586 this->ghosting_functors_end()))
1587 {
1588 libmesh_assert(gf);
1589 gf->mesh_reinit();
1590 }
1591
1592 return mesh_changed;
1593}
1594#endif // #ifdef LIBMESH_ENABLE_AMR
1595
1596
1597
1599{
1600 LOG_SCOPE("all_first_order()", "Mesh");
1601
1605 std::vector<bool> node_touched_by_me(this->max_node_id(), false);
1606
1607 // Loop over the high-ordered elements.
1608 // First make sure they _are_ indeed high-order, and then replace
1609 // them with an equivalent first-order element.
1610 for (auto & so_elem : element_ptr_range())
1611 {
1612 libmesh_assert(so_elem);
1613
1614 /*
1615 * build the first-order equivalent, add to
1616 * the new_elements list.
1617 */
1618 auto lo_elem = Elem::build
1620 (so_elem->type()), so_elem->parent());
1621
1622 const unsigned short n_sides = so_elem->n_sides();
1623
1624 for (unsigned short s=0; s != n_sides; ++s)
1625 if (so_elem->neighbor_ptr(s) == remote_elem)
1626 lo_elem->set_neighbor(s, const_cast<RemoteElem *>(remote_elem));
1627
1628#ifdef LIBMESH_ENABLE_AMR
1629 /*
1630 * Reset the parent links of any child elements
1631 */
1632 if (so_elem->has_children())
1633 for (unsigned int c = 0, nc = so_elem->n_children(); c != nc; ++c)
1634 {
1635 Elem * child = so_elem->child_ptr(c);
1636 if (child != remote_elem)
1637 child->set_parent(lo_elem.get());
1638 lo_elem->add_child(child, c);
1639 }
1640
1641 /*
1642 * Reset the child link of any parent element
1643 */
1644 if (so_elem->parent())
1645 {
1646 unsigned int c =
1647 so_elem->parent()->which_child_am_i(so_elem);
1648 lo_elem->parent()->replace_child(lo_elem.get(), c);
1649 }
1650
1651 /*
1652 * Copy as much data to the new element as makes sense
1653 */
1654 lo_elem->set_p_level(so_elem->p_level());
1655 lo_elem->set_refinement_flag(so_elem->refinement_flag());
1656 lo_elem->set_p_refinement_flag(so_elem->p_refinement_flag());
1657#endif
1658
1659 libmesh_assert_equal_to (lo_elem->n_vertices(), so_elem->n_vertices());
1660
1661 /*
1662 * By definition the vertices of the linear and
1663 * second order element are identically numbered.
1664 * transfer these.
1665 */
1666 for (unsigned int v=0, snv=so_elem->n_vertices(); v < snv; v++)
1667 {
1668 lo_elem->set_node(v, so_elem->node_ptr(v));
1669 node_touched_by_me[lo_elem->node_id(v)] = true;
1670 }
1671
1672 /*
1673 * find_neighbors relies on remote_elem neighbor links being
1674 * properly maintained.
1675 */
1676 for (unsigned short s=0; s != n_sides; s++)
1677 {
1678 if (so_elem->neighbor_ptr(s) == remote_elem)
1679 lo_elem->set_neighbor(s, const_cast<RemoteElem*>(remote_elem));
1680 }
1681
1689 (this->get_boundary_info(), so_elem, lo_elem.get());
1690
1691 /*
1692 * The new first-order element is ready.
1693 * Inserting it into the mesh will replace and delete
1694 * the second-order element.
1695 */
1696 lo_elem->set_id(so_elem->id());
1697#ifdef LIBMESH_ENABLE_UNIQUE_ID
1698 lo_elem->set_unique_id(so_elem->unique_id());
1699#endif
1700
1701 const unsigned int nei = so_elem->n_extra_integers();
1702 lo_elem->add_extra_integers(nei);
1703 for (unsigned int i=0; i != nei; ++i)
1704 lo_elem->set_extra_integer(i, so_elem->get_extra_integer(i));
1705
1706 lo_elem->inherit_data_from(*so_elem);
1707
1708 this->insert_elem(std::move(lo_elem));
1709 }
1710
1711 // Deleting nodes does not invalidate iterators, so this is safe.
1712 for (const auto & node : this->node_ptr_range())
1713 if (!node_touched_by_me[node->id()])
1714 this->delete_node(node);
1715
1716 // If crazy people applied boundary info to non-vertices and then
1717 // deleted those non-vertices, we should make sure their boundary id
1718 // caches are correct.
1720
1721 // On hanging nodes that used to also be second order nodes, we
1722 // might now have an invalid nodal processor_id()
1724
1725 // delete or renumber nodes if desired
1726 this->prepare_for_use();
1727}
1728
1729
1730
1731void
1733 const bool full_ordered)
1734{
1735 LOG_SCOPE("all_second_order_range()", "Mesh");
1736
1737 /*
1738 * The maximum number of new second order nodes we might be adding,
1739 * for use when picking unique unique_id values later. This variable
1740 * is not used unless unique ids are enabled.
1741 */
1742 unsigned int max_new_nodes_per_elem;
1743
1744 /*
1745 * For speed-up of the \p add_point() method, we
1746 * can reserve memory. Guess the number of additional
1747 * nodes based on the element spatial dimensions and the
1748 * total number of nodes in the mesh as an upper bound.
1749 */
1750 switch (this->mesh_dimension())
1751 {
1752 case 1:
1753 /*
1754 * in 1D, there can only be order-increase from Edge2
1755 * to Edge3. Something like 1/2 of n_nodes() have
1756 * to be added
1757 */
1758 max_new_nodes_per_elem = 3 - 2;
1759 this->reserve_nodes(static_cast<unsigned int>
1760 (1.5*static_cast<double>(this->n_nodes())));
1761 break;
1762
1763 case 2:
1764 /*
1765 * in 2D, either refine from Tri3 to Tri6 (double the nodes)
1766 * or from Quad4 to Quad8 (again, double) or Quad9 (2.25 that much)
1767 */
1768 max_new_nodes_per_elem = 9 - 4;
1769 this->reserve_nodes(static_cast<unsigned int>
1770 (2*static_cast<double>(this->n_nodes())));
1771 break;
1772
1773
1774 case 3:
1775 /*
1776 * in 3D, either refine from Tet4 to Tet10 (factor = 2.5) up to
1777 * Hex8 to Hex27 (something > 3). Since in 3D there _are_ already
1778 * quite some nodes, and since we do not want to overburden the memory by
1779 * a too conservative guess, use the lower bound
1780 */
1781 max_new_nodes_per_elem = 27 - 8;
1782 this->reserve_nodes(static_cast<unsigned int>
1783 (2.5*static_cast<double>(this->n_nodes())));
1784 break;
1785
1786 default:
1787 // Hm?
1788 libmesh_error_msg("Unknown mesh dimension " << this->mesh_dimension());
1789 }
1790
1791 // All the real work is done in the helper function
1792 all_increased_order_range(*this, range, max_new_nodes_per_elem,
1793 [full_ordered](ElemType t) {
1794 return Elem::second_order_equivalent_type(t, full_ordered);
1795 });
1796}
1797
1798
1799
1801{
1802 LOG_SCOPE("all_complete_order()", "Mesh");
1803
1804 /*
1805 * The maximum number of new higher-order nodes we might be adding,
1806 * for use when picking unique unique_id values later. This variable
1807 * is not used unless unique ids are enabled.
1808 */
1809 unsigned int max_new_nodes_per_elem;
1810
1811 /*
1812 * for speed-up of the \p add_point() method, we
1813 * can reserve memory. Guess the number of additional
1814 * nodes based on the element spatial dimensions and the
1815 * total number of nodes in the mesh as an upper bound.
1816 */
1817 switch (this->mesh_dimension())
1818 {
1819 case 1:
1820 /*
1821 * in 1D, there can only be order-increase from Edge2
1822 * to Edge3. Something like 1/2 of n_nodes() have
1823 * to be added
1824 */
1825 max_new_nodes_per_elem = 3 - 2;
1826 this->reserve_nodes(static_cast<unsigned int>
1827 (1.5*static_cast<double>(this->n_nodes())));
1828 break;
1829
1830 case 2:
1831 /*
1832 * in 2D, we typically refine from Tri3 or Tri6 to Tri7 (2.3333
1833 * or 1.1667 times the nodes) but might refine from Quad4 to
1834 * Quad9 (2.25 times the nodes)
1835 */
1836 max_new_nodes_per_elem = 9 - 4;
1837 this->reserve_nodes(static_cast<unsigned int>
1838 (2*static_cast<double>(this->n_nodes())));
1839 break;
1840
1841
1842 case 3:
1843 /*
1844 * in 3D, we typically refine from Tet10 to Tet14 (factor = 1.4)
1845 * but may go Hex8 to Hex27 or Tet4 to Tet14 (something > 3).
1846 * Since in 3D there _are_ already quite some nodes, and since
1847 * we do not want to overburden the memory by a too-conservative
1848 * guess, use a moderate bound
1849 */
1850 max_new_nodes_per_elem = 27 - 8;
1851 this->reserve_nodes(static_cast<unsigned int>
1852 (2.5*static_cast<double>(this->n_nodes())));
1853 break;
1854
1855 default:
1856 // Hm?
1857 libmesh_error_msg("Unknown mesh dimension " << this->mesh_dimension());
1858 }
1859
1860 // All the real work is done in the helper function
1861 all_increased_order_range(*this, range, max_new_nodes_per_elem,
1862 [](ElemType t) {
1864 });
1865}
1866
1867
1868std::size_t
1870 boundary_id_type this_mesh_boundary_id,
1871 boundary_id_type other_mesh_boundary_id,
1872 Real tol,
1873 bool clear_stitched_boundary_ids,
1874 bool verbose,
1875 bool use_binary_search,
1876 bool enforce_all_nodes_match_on_boundaries,
1877 bool merge_boundary_nodes_all_or_nothing,
1878 bool remap_subdomain_ids,
1879 bool prepare_after_stitching)
1880{
1881 LOG_SCOPE("stitch_meshes()", "UnstructuredMesh");
1882 return stitching_helper(&other_mesh,
1883 this_mesh_boundary_id,
1884 other_mesh_boundary_id,
1885 tol,
1886 clear_stitched_boundary_ids,
1887 verbose,
1888 use_binary_search,
1889 enforce_all_nodes_match_on_boundaries,
1890 true,
1891 merge_boundary_nodes_all_or_nothing,
1892 remap_subdomain_ids,
1893 prepare_after_stitching);
1894}
1895
1896
1897std::size_t
1899 boundary_id_type boundary_id_2,
1900 Real tol,
1901 bool clear_stitched_boundary_ids,
1902 bool verbose,
1903 bool use_binary_search,
1904 bool enforce_all_nodes_match_on_boundaries,
1905 bool merge_boundary_nodes_all_or_nothing,
1906 bool prepare_after_stitching)
1907
1908{
1909 return stitching_helper(nullptr,
1910 boundary_id_1,
1911 boundary_id_2,
1912 tol,
1913 clear_stitched_boundary_ids,
1914 verbose,
1915 use_binary_search,
1916 enforce_all_nodes_match_on_boundaries,
1917 /* skip_find_neighbors = */ true,
1918 merge_boundary_nodes_all_or_nothing,
1919 /* remap_subdomain_ids = */ false,
1920 prepare_after_stitching);
1921}
1922
1923
1924std::size_t
1926 boundary_id_type this_mesh_boundary_id,
1927 boundary_id_type other_mesh_boundary_id,
1928 Real tol,
1929 bool clear_stitched_boundary_ids,
1930 bool verbose,
1931 bool use_binary_search,
1932 bool enforce_all_nodes_match_on_boundaries,
1933 bool skip_find_neighbors,
1934 bool merge_boundary_nodes_all_or_nothing,
1935 bool remap_subdomain_ids,
1936 bool prepare_after_stitching)
1937{
1938#ifdef DEBUG
1939 // We rely on neighbor links here
1941#endif
1942
1943 bool is_valid_disjoint_pair_to_stitch = false;
1944
1945#ifdef LIBMESH_ENABLE_PERIODIC
1946 auto * this_db = this->get_disjoint_neighbor_boundary_pairs();
1947 auto * other_db = (other_mesh ? other_mesh->get_disjoint_neighbor_boundary_pairs() : nullptr);
1948 const bool have_disc_bdys =
1949 (this_db && !this_db->empty()) || (other_db && !other_db->empty());
1950
1951 if (have_disc_bdys)
1952 {
1953 const boundary_id_type a = this_mesh_boundary_id;
1954 const boundary_id_type b = other_mesh_boundary_id;
1955
1956 auto get_pb = [](const PeriodicBoundaries * db, boundary_id_type id)
1957 {
1958 return db ? db->boundary(id) : nullptr;
1959 };
1960
1961 // this mesh
1962 const auto * pb_this_a = get_pb(this_db, a);
1963 const auto * pb_this_b = get_pb(this_db, b);
1964 const bool in_this =
1965 (pb_this_a && pb_this_a->pairedboundary == b) ||
1966 (pb_this_b && pb_this_b->pairedboundary == a);
1967
1968 // other mesh
1969 const auto * pb_other_b = get_pb(other_db, b);
1970 const auto * pb_other_a = get_pb(other_db, a);
1971 const bool in_other =
1972 (pb_other_b && pb_other_b->pairedboundary == a) ||
1973 (pb_other_a && pb_other_a->pairedboundary == b);
1974
1975 // Conflict conditions:
1976 // Case 1: On "this" mesh, a or b exist but are not paired,
1977 // while the other mesh pairs them.
1978 if (!in_this && (pb_this_a || pb_this_b) && in_other)
1979 libmesh_error_msg("Disjoint neighbor boundary pairing mismatch: on 'this' mesh, "
1980 "boundary (" << a << " or " << b
1981 << ") exists but is not paired; on 'other' mesh the pair is present.");
1982
1983 // Case 2: On "other" mesh, a or b exist but are not paired,
1984 // while this mesh pairs them.
1985 if (!in_other && (pb_other_a || pb_other_b) && in_this)
1986 libmesh_error_msg("Disjoint neighbor boundary pairing mismatch: on 'other' mesh, "
1987 "boundary (" << a << " or " << b
1988 << ") exists but is not paired; on 'this' mesh the pair is present.");
1989
1990 // Legal conditions: either side has a correct pairing
1991 if (in_this || in_other)
1992 is_valid_disjoint_pair_to_stitch = true;
1993 }
1994#endif // LIBMESH_ENABLE_PERIODIC
1995
1996 // We can't even afford any unset neighbor links here.
1997 if (!this->is_prepared())
1998 this->find_neighbors();
1999
2000 // FIXME: make distributed mesh support efficient.
2001 // Yes, we currently suck.
2002 MeshSerializer serialize(*this);
2003
2004 // *Badly*.
2005 std::unique_ptr<MeshSerializer> serialize_other;
2006 if (other_mesh)
2007 serialize_other = std::make_unique<MeshSerializer>
2008 (*const_cast<MeshBase *>(other_mesh));
2009
2010 std::map<dof_id_type, dof_id_type> node_to_node_map, other_to_this_node_map; // The second is the inverse map of the first
2011 std::map<dof_id_type, std::vector<dof_id_type>> node_to_elems_map;
2012
2013 typedef dof_id_type key_type;
2014 typedef std::pair<const Elem *, unsigned char> val_type;
2015 typedef std::pair<key_type, val_type> key_val_pair;
2016 typedef std::unordered_multimap<key_type, val_type> map_type;
2017 // Mapping between all side keys in this mesh and elements+side numbers relevant to the boundary in this mesh as well.
2018 map_type side_to_elem_map;
2019
2020 // If there is only one mesh (i.e. other_mesh == nullptr), then loop over this mesh twice
2021 if (!other_mesh)
2022 {
2023 other_mesh = this;
2024 }
2025
2026 if ((this_mesh_boundary_id != BoundaryInfo::invalid_id) &&
2027 (other_mesh_boundary_id != BoundaryInfo::invalid_id))
2028 {
2029 LOG_SCOPE("stitch_meshes node merging", "UnstructuredMesh");
2030
2031 // While finding nodes on the boundary, also find the minimum edge length
2032 // of all faces on both boundaries. This will later be used in relative
2033 // distance checks when stitching nodes.
2034 Real h_min = std::numeric_limits<Real>::max();
2035 bool h_min_updated = false;
2036
2037 // Loop below fills in these sets for the two meshes.
2038 std::set<dof_id_type> this_boundary_node_ids, other_boundary_node_ids;
2039
2040 // Pull objects out of the loop to reduce heap operations
2041 std::unique_ptr<const Elem> side;
2042
2043 {
2044 // Make temporary fixed-size arrays for loop
2045 boundary_id_type id_array[2] = {this_mesh_boundary_id, other_mesh_boundary_id};
2046 std::set<dof_id_type> * set_array[2] = {&this_boundary_node_ids, &other_boundary_node_ids};
2047 const MeshBase * mesh_array[2] = {this, other_mesh};
2048
2049 for (unsigned i=0; i<2; ++i)
2050 {
2051 // First we deal with node boundary IDs. We only enter
2052 // this loop if we have at least one nodeset. Note that we
2053 // do not attempt to make an h_min determination here.
2054 // The h_min determination is done while looping over the
2055 // Elems and checking their sides and edges for boundary
2056 // information, below.
2057 if (mesh_array[i]->get_boundary_info().n_nodeset_conds() > 0)
2058 {
2059 // build_node_list() returns a vector of (node-id, bc-id) tuples
2060 for (const auto & t : mesh_array[i]->get_boundary_info().build_node_list())
2061 {
2062 boundary_id_type node_bc_id = std::get<1>(t);
2063 if (node_bc_id == id_array[i])
2064 {
2065 dof_id_type this_node_id = std::get<0>(t);
2066 set_array[i]->insert( this_node_id );
2067 }
2068 }
2069 }
2070
2071 // Container to catch boundary IDs passed back from BoundaryInfo.
2072 std::vector<boundary_id_type> bc_ids;
2073
2074 // Pointers to boundary NodeElems encountered while looping over the entire Mesh
2075 // and checking side and edge boundary ids. The Nodes associated with NodeElems
2076 // may be in a boundary nodeset, but not connected to any other Elems. In this
2077 // case, we also consider the "minimum node separation distance" amongst all
2078 // NodeElems when determining the relevant h_min value for this mesh.
2079 std::vector<const Elem *> boundary_node_elems;
2080
2081 for (auto & el : mesh_array[i]->element_ptr_range())
2082 {
2083 // Now check whether elem has a face on the specified boundary
2084 for (auto side_id : el->side_index_range())
2085 {
2086 bool should_stitch_this_side =
2087 (el->neighbor_ptr(side_id) == nullptr) ||
2088 (is_valid_disjoint_pair_to_stitch &&
2089 mesh_array[i]->get_boundary_info().has_boundary_id(el, side_id, id_array[i]));
2090
2091 if (should_stitch_this_side)
2092 {
2093 // Get *all* boundary IDs on this side, not just the first one!
2094 mesh_array[i]->get_boundary_info().boundary_ids (el, side_id, bc_ids);
2095
2096 if (std::find(bc_ids.begin(), bc_ids.end(), id_array[i]) != bc_ids.end())
2097 {
2098 el->build_side_ptr(side, side_id);
2099 for (auto & n : side->node_ref_range())
2100 set_array[i]->insert(n.id());
2101
2102 h_min = std::min(h_min, side->hmin());
2103 h_min_updated = true;
2104
2105 // This side is on the boundary, add its information to side_to_elem
2106 if (skip_find_neighbors && (i==0))
2107 {
2108 key_type key = el->low_order_key(side_id);
2109 val_type val;
2110 val.first = el;
2111 val.second = cast_int<unsigned char>(side_id);
2112
2113 key_val_pair kvp;
2114 kvp.first = key;
2115 kvp.second = val;
2116 side_to_elem_map.insert (kvp);
2117 }
2118 }
2119
2120 // Also, check the edges on this side. We don't have to worry about
2121 // updating neighbor info in this case since elements don't store
2122 // neighbor info on edges.
2123 for (auto edge_id : el->edge_index_range())
2124 {
2125 if (el->is_edge_on_side(edge_id, side_id))
2126 {
2127 // Get *all* boundary IDs on this edge, not just the first one!
2128 mesh_array[i]->get_boundary_info().edge_boundary_ids (el, edge_id, bc_ids);
2129
2130 if (std::find(bc_ids.begin(), bc_ids.end(), id_array[i]) != bc_ids.end())
2131 {
2132 std::unique_ptr<const Elem> edge (el->build_edge_ptr(edge_id));
2133 for (auto & n : edge->node_ref_range())
2134 set_array[i]->insert( n.id() );
2135
2136 h_min = std::min(h_min, edge->hmin());
2137 h_min_updated = true;
2138 }
2139 }
2140 } // end for (edge_id)
2141 } // end if (should_stitch_this_side)
2142 } // end for (side_id)
2143
2144 // Alternatively, is this a boundary NodeElem? If so,
2145 // add it to a list of NodeElems that will later be
2146 // used to set h_min based on the minimum node
2147 // separation distance between all pairs of boundary
2148 // NodeElems.
2149 if (el->type() == NODEELEM)
2150 {
2151 mesh_array[i]->get_boundary_info().boundary_ids(el->node_ptr(0), bc_ids);
2152 if (std::find(bc_ids.begin(), bc_ids.end(), id_array[i]) != bc_ids.end())
2153 {
2154 boundary_node_elems.push_back(el);
2155
2156 // Debugging:
2157 // libMesh::out << "Elem " << el->id() << " is a NodeElem on boundary " << id_array[i] << std::endl;
2158 }
2159 } // end if (el->type() == NODEELEM)
2160 } // end for (el)
2161
2162 // Compute the minimum node separation distance amongst
2163 // all boundary NodeElem pairs.
2164 {
2165 const auto N = boundary_node_elems.size();
2166 for (auto node_elem_i : make_range(N))
2167 for (auto node_elem_j : make_range(node_elem_i+1, N))
2168 {
2169 Real node_sep =
2170 (boundary_node_elems[node_elem_i]->point(0) - boundary_node_elems[node_elem_j]->point(0)).norm();
2171
2172 // We only want to consider non-coincident
2173 // boundary NodeElem pairs when determining the
2174 // minimum node separation distance.
2175 if (node_sep > 0.)
2176 {
2177 h_min = std::min(h_min, node_sep);
2178 h_min_updated = true;
2179 }
2180 } // end for (node_elem_j)
2181 } // end minimum NodeElem separation scope
2182 } // end for (i)
2183 } // end scope
2184
2185 if (verbose)
2186 {
2187 libMesh::out << "In UnstructuredMesh::stitch_meshes:\n"
2188 << "This mesh has " << this_boundary_node_ids.size()
2189 << " nodes on boundary `"
2190 << this->get_boundary_info().get_sideset_name(this_mesh_boundary_id)
2191 << "' (" << this_mesh_boundary_id << ").\n"
2192 << "Other mesh has " << other_boundary_node_ids.size()
2193 << " nodes on boundary `"
2194 << other_mesh->get_boundary_info().get_sideset_name(other_mesh_boundary_id)
2195 << "' (" << other_mesh_boundary_id << ").\n";
2196
2197 if (h_min_updated)
2198 {
2199 libMesh::out << "Minimum edge length on both surfaces is " << h_min << ".\n";
2200 }
2201 else
2202 {
2203 libMesh::out << "No minimum edge length determined on specified surfaces." << std::endl;
2204 }
2205 }
2206
2207 // At this point, if h_min==0 it means that there were at least two coincident
2208 // nodes on the surfaces being stitched, and we don't currently support that case.
2209 // (It might be possible to support, but getting it exactly right would be tricky
2210 // and probably not worth the extra complications to the "normal" case.)
2211 libmesh_error_msg_if(h_min < std::numeric_limits<Real>::epsilon(),
2212 "Coincident nodes detected on source and/or target "
2213 "surface, stitching meshes is not possible.");
2214
2215 // We require nanoflann for the "binary search" (really kd-tree)
2216 // option to work. If it's not available, turn that option off,
2217 // warn the user, and fall back on the N^2 search algorithm.
2218 if (use_binary_search)
2219 {
2220#ifndef LIBMESH_HAVE_NANOFLANN
2221 use_binary_search = false;
2222 libmesh_warning("The use_binary_search option in the "
2223 "UnstructuredMesh stitching algorithms requires nanoflann "
2224 "support. Falling back on N^2 search algorithm.");
2225#endif
2226 }
2227
2228 if (!this_boundary_node_ids.empty())
2229 {
2230 if (use_binary_search)
2231 {
2232#ifdef LIBMESH_HAVE_NANOFLANN
2233 typedef nanoflann::KDTreeSingleIndexAdaptor<nanoflann::L2_Simple_Adaptor<Real, VectorOfNodesAdaptor>,
2234 VectorOfNodesAdaptor, 3, std::size_t> kd_tree_t;
2235
2236 // Create the dataset needed to build the kd tree with nanoflann
2237 std::vector<std::pair<Point, dof_id_type>> this_mesh_nodes(this_boundary_node_ids.size());
2238
2239 for (auto [it, ctr] = std::make_tuple(this_boundary_node_ids.begin(), 0u);
2240 it != this_boundary_node_ids.end(); ++it, ++ctr)
2241 {
2242 this_mesh_nodes[ctr].first = this->point(*it);
2243 this_mesh_nodes[ctr].second = *it;
2244 }
2245
2246 VectorOfNodesAdaptor vec_nodes_adaptor(this_mesh_nodes);
2247
2248 kd_tree_t this_kd_tree(3, vec_nodes_adaptor, 10);
2249 this_kd_tree.buildIndex();
2250
2251 // Storage for nearest neighbor in the loop below
2252 std::size_t ret_index;
2253 Real ret_dist_sqr;
2254
2255 // Loop over other mesh. For each node, find its nearest neighbor in this mesh, and fill in the maps.
2256 for (const auto & node_id : other_boundary_node_ids)
2257 {
2258 const auto & p = other_mesh->point(node_id);
2259 const Real query_pt[] = {p(0), p(1), p(2)};
2260 this_kd_tree.knnSearch(&query_pt[0], 1, &ret_index, &ret_dist_sqr);
2261
2262 // TODO: here we should use the user's specified tolerance
2263 // and the previously determined value of h_min in the
2264 // distance comparison, not just TOLERANCE^2.
2265 if (ret_dist_sqr < TOLERANCE*TOLERANCE)
2266 {
2267 node_to_node_map[this_mesh_nodes[ret_index].second] = node_id;
2268 other_to_this_node_map[node_id] = this_mesh_nodes[ret_index].second;
2269 }
2270 }
2271
2272 // If the two maps don't have the same size, it means one
2273 // node in this mesh is the nearest neighbor of several
2274 // nodes in other mesh. Since the stitching is ambiguous in
2275 // this case, we throw an error.
2276 libmesh_error_msg_if(node_to_node_map.size() != other_to_this_node_map.size(),
2277 "Error: Found multiple matching nodes in stitch_meshes");
2278#endif
2279 }
2280 else // !use_binary_search
2281 {
2282 // In the unlikely event that two meshes composed entirely of
2283 // NodeElems are being stitched together, we will not have
2284 // selected a valid h_min value yet, and the distance
2285 // comparison below will be true for essentially any two
2286 // nodes. In this case we simply fall back on an absolute
2287 // distance check.
2288 if (!h_min_updated)
2289 {
2290 libmesh_warning("No valid h_min value was found, falling back on "
2291 "absolute distance check in the N^2 search algorithm.");
2292 h_min = 1.;
2293 }
2294
2295 // Otherwise, use a simple N^2 search to find the closest matching points. This can be helpful
2296 // in the case that we have tolerance issues which cause mismatch between the two surfaces
2297 // that are being stitched.
2298 for (const auto & this_node_id : this_boundary_node_ids)
2299 {
2300 Node & this_node = this->node_ref(this_node_id);
2301
2302 bool found_matching_nodes = false;
2303
2304 for (const auto & other_node_id : other_boundary_node_ids)
2305 {
2306 const Node & other_node = other_mesh->node_ref(other_node_id);
2307
2308 Real node_distance = (this_node - other_node).norm();
2309
2310 if (node_distance < tol*h_min)
2311 {
2312 // Make sure we didn't already find a matching node!
2313 libmesh_error_msg_if(found_matching_nodes,
2314 "Error: Found multiple matching nodes in stitch_meshes");
2315
2316 node_to_node_map[this_node_id] = other_node_id;
2317 other_to_this_node_map[other_node_id] = this_node_id;
2318
2319 found_matching_nodes = true;
2320 }
2321 }
2322 }
2323 }
2324 }
2325
2326 // Build up the node_to_elems_map, using only one loop over other_mesh
2327 for (auto & el : other_mesh->element_ptr_range())
2328 {
2329 // For each node on the element, find the corresponding node
2330 // on "this" Mesh, 'this_node_id', if it exists, and push
2331 // the current element ID back onto node_to_elems_map[this_node_id].
2332 // For that we will use the reverse mapping we created at
2333 // the same time as the forward mapping.
2334 for (auto & n : el->node_ref_range())
2335 if (const auto it = other_to_this_node_map.find(/*other_node_id=*/n.id());
2336 it != other_to_this_node_map.end())
2337 node_to_elems_map[/*this_node_id=*/it->second].push_back( el->id() );
2338 }
2339
2340 if (verbose)
2341 {
2342 libMesh::out << "In UnstructuredMesh::stitch_meshes:\n"
2343 << "Found " << node_to_node_map.size()
2344 << " matching nodes.\n"
2345 << std::endl;
2346 }
2347
2348 if (enforce_all_nodes_match_on_boundaries)
2349 {
2350 std::size_t n_matching_nodes = node_to_node_map.size();
2351 std::size_t this_mesh_n_nodes = this_boundary_node_ids.size();
2352 std::size_t other_mesh_n_nodes = other_boundary_node_ids.size();
2353 libmesh_error_msg_if((n_matching_nodes != this_mesh_n_nodes) || (n_matching_nodes != other_mesh_n_nodes),
2354 "Error: We expected the number of nodes to match.");
2355 }
2356
2357 if (merge_boundary_nodes_all_or_nothing)
2358 {
2359 std::size_t n_matching_nodes = node_to_node_map.size();
2360 std::size_t this_mesh_n_nodes = this_boundary_node_ids.size();
2361 std::size_t other_mesh_n_nodes = other_boundary_node_ids.size();
2362 if ((n_matching_nodes != this_mesh_n_nodes) || (n_matching_nodes != other_mesh_n_nodes))
2363 {
2364 if (verbose)
2365 {
2366 libMesh::out << "Skipping node merging in "
2367 "UnstructuredMesh::stitch_meshes because not "
2368 "all boundary nodes were matched."
2369 << std::endl;
2370 }
2371 node_to_node_map.clear();
2372 other_to_this_node_map.clear();
2373 node_to_elems_map.clear();
2374 }
2375 }
2376 }
2377 else
2378 {
2379 if (verbose)
2380 {
2381 libMesh::out << "Skip node merging in UnstructuredMesh::stitch_meshes:" << std::endl;
2382 }
2383 }
2384
2385 dof_id_type node_delta = this->max_node_id();
2386 dof_id_type elem_delta = this->max_elem_id();
2387
2388 unique_id_type unique_delta =
2389#ifdef LIBMESH_ENABLE_UNIQUE_ID
2390 this->parallel_max_unique_id();
2391#else
2392 0;
2393#endif
2394
2395 // If other_mesh != nullptr, then we have to do a bunch of work
2396 // in order to copy it to this mesh
2397 if (this!=other_mesh)
2398 {
2399 LOG_SCOPE("stitch_meshes copying", "UnstructuredMesh");
2400
2401#ifdef LIBMESH_ENABLE_PERIODIC
2402 // Copy disjoint neighbor boundary pairs (PeriodicBoundary objects)
2403 // from `other_mesh` to `this` mesh
2404 if (other_db && !other_db->empty())
2405 {
2406 for (const auto & [bdy_id, pb_ptr] : *other_db)
2407 {
2408 const auto & pb = *pb_ptr;
2409 const boundary_id_type a = pb.myboundary;
2410 const boundary_id_type b = pb.pairedboundary;
2411
2412 if (this_db)
2413 {
2414 // Skip if identical pair already exists
2415 if (const auto * existing_pb = this_db->boundary(a))
2416 if ((existing_pb->myboundary == a && existing_pb->pairedboundary == b) ||
2417 (existing_pb->myboundary == b && existing_pb->pairedboundary == a))
2418 continue;
2419
2420 // If both boundary ids exist on this mesh but aren't paired here, refuse to create a new pair
2421 const auto & bdy_ids = this->get_boundary_info().get_boundary_ids();
2422 const bool a_exists = bdy_ids.count(a);
2423 const bool b_exists = bdy_ids.count(b);
2424 // If a and b already exist on `this`, we should be screaming and dying
2425 // unless they already have PeriodicBoundary objects connecting them too
2426 if (a_exists && b_exists && !this_db->boundary(a))
2427 libmesh_error_msg("Conflict: boundaries " << a << " and " << b
2428 << " already exist on this mesh but are not paired.");
2429 }
2430
2431 this->add_disjoint_neighbor_boundary_pairs(a, b, pb.get_corresponding_pos(Point(0.0,0.0,0.0)));
2432 }
2433 }
2434#endif // LIBMESH_ENABLE_PERIODIC
2435
2436
2437 // Increment the node_to_node_map and node_to_elems_map
2438 // to account for id offsets
2439 for (auto & pr : node_to_node_map)
2440 pr.second += node_delta;
2441
2442 for (auto & pr : node_to_elems_map)
2443 for (auto & entry : pr.second)
2444 entry += elem_delta;
2445
2446 // We run into problems when the libMesh subdomain standard (the
2447 // id defines the subdomain; the name was an afterthought) and
2448 // the MOOSE standard (the name defines the subdomain; the id
2449 // might be autogenerated) clash.
2450 //
2451 // Subdomain ids with the same name in both meshes are surely
2452 // meant to represent the same subdomain. We can just merge
2453 // them.
2454 //
2455 // Subdomain ids which don't have a name in either mesh are
2456 // almost surely meant to represent the same subdomain. We'll
2457 // just merge them.
2458 //
2459 // Subdomain ids with different names in different meshes, or
2460 // names with different ids in different meshes, are trickier.
2461 // For backwards compatibility we default to the old "just copy
2462 // all the subdomain ids over" behavior, but if requested we'll
2463 // remap any ids that appear to be clear conflicts, and we'll
2464 // scream and die if we see any ids that are ambiguous due to
2465 // being named in one mesh but not the other.
2466 std::unordered_map<subdomain_id_type, subdomain_id_type> id_remapping;
2467 if (remap_subdomain_ids)
2468 {
2469 const auto & this_map = this->get_subdomain_name_map();
2470 const auto & other_map = other_mesh->get_subdomain_name_map();
2471 std::unordered_map<std::string, subdomain_id_type> other_map_reversed;
2472 for (auto & [sid, sname] : other_map)
2473 other_map_reversed.emplace(sname, sid);
2474
2475 std::unordered_map<std::string, subdomain_id_type> this_map_reversed;
2476 for (auto & [sid, sname] : this_map)
2477 this_map_reversed.emplace(sname, sid);
2478
2479 // We don't require either mesh to be prepared, but that
2480 // means we need to check for subdomains manually.
2481 auto get_subdomains = [](const MeshBase & mesh) {
2482 std::set<subdomain_id_type> all_subdomains;
2483 for (auto & el : mesh.element_ptr_range())
2484 all_subdomains.insert(el->subdomain_id());
2485 return all_subdomains;
2486 };
2487
2488 const auto this_subdomains = get_subdomains(*this);
2489 const auto other_subdomains = get_subdomains(*other_mesh);
2490
2491 for (auto & [sid, sname] : this_map)
2492 {
2493 // The same name with the same id means we're fine. The
2494 // same name with another id means we remap their id to
2495 // ours
2496 if (const auto other_reverse_it = other_map_reversed.find(sname);
2497 other_reverse_it != other_map_reversed.end() && other_reverse_it->second != sid)
2498 id_remapping[other_reverse_it->second] = sid;
2499
2500 // The same id with a different name, we'll get to
2501 // later. The same id without any name means we don't
2502 // know what the user wants.
2503 if (other_subdomains.count(sid) && !other_map.count(sid))
2504 libmesh_error_msg("Can't safely stitch with a mesh sharing subdomain id "
2505 << sid << " but not subdomain name " << sname);
2506 }
2507
2508 subdomain_id_type next_free_id = 0;
2509 // We might try to stitch empty meshes ...
2510 if (!this_subdomains.empty())
2511 next_free_id = *this_subdomains.rbegin() + 1;
2512 if (!other_subdomains.empty())
2513 next_free_id =
2514 std::max(next_free_id,
2515 cast_int<subdomain_id_type>
2516 (*other_subdomains.rbegin() + 1));
2517
2518 for (auto & [sid, sname] : other_map)
2519 {
2520 // At this point we've figured out any remapping
2521 // necessary for an sname that we share. And we don't
2522 // need to remap any sid we don't share.
2523 if (!this_map_reversed.count(sname))
2524 {
2525 // But if we don't have this sname and we do have this
2526 // sid then we can't just merge into that.
2527 if (this_subdomains.count(sid))
2528 {
2529 // If we have this sid with no name, we don't
2530 // know what the user wants.
2531 if (!this_map.count(sid))
2532 libmesh_error_msg("Can't safely stitch with a mesh sharing subdomain id "
2533 << sid << " but under subdomain name " << sname);
2534
2535 // We have this sid under a different name, so
2536 // we just need to give the other elements a new
2537 // id.
2538
2539 // Users might have done crazy things with id
2540 // choice so let's make sure they didn't get too
2541 // crazy.
2542 libmesh_error_msg_if ((!this_subdomains.empty() &&
2543 next_free_id < *this_subdomains.rbegin()) ||
2544 (!other_subdomains.empty() &&
2545 next_free_id < *other_subdomains.rbegin()),
2546 "Subdomain id overflow");
2547
2548 id_remapping[sid] = next_free_id++;
2549 this->set_subdomain_name(next_free_id, sname);
2550 }
2551 // If we don't have this subdomain id, well, we're
2552 // about to, so we should have its name too.
2553 else
2554 this->set_subdomain_name(sid, sname);
2555 }
2556 }
2557 }
2558
2559 // Copy mesh data. If we skip the call to find_neighbors(), the lists
2560 // of neighbors will be copied verbatim from the other mesh
2561 this->copy_nodes_and_elements(*other_mesh, skip_find_neighbors,
2562 elem_delta, node_delta,
2563 unique_delta, &id_remapping);
2564
2565 // Copy BoundaryInfo from other_mesh too. We do this via the
2566 // list APIs rather than element-by-element for speed.
2567 BoundaryInfo & boundary = this->get_boundary_info();
2568 const BoundaryInfo & other_boundary = other_mesh->get_boundary_info();
2569
2570 for (const auto & t : other_boundary.build_node_list())
2571 boundary.add_node(std::get<0>(t) + node_delta,
2572 std::get<1>(t));
2573
2574 for (const auto & t : other_boundary.build_side_list())
2575 boundary.add_side(std::get<0>(t) + elem_delta,
2576 std::get<1>(t),
2577 std::get<2>(t));
2578
2579 for (const auto & t : other_boundary.build_edge_list())
2580 boundary.add_edge(std::get<0>(t) + elem_delta,
2581 std::get<1>(t),
2582 std::get<2>(t));
2583
2584 for (const auto & t : other_boundary.build_shellface_list())
2585 boundary.add_shellface(std::get<0>(t) + elem_delta,
2586 std::get<1>(t),
2587 std::get<2>(t));
2588
2589 const auto & other_ns_id_to_name = other_boundary.get_nodeset_name_map();
2590 auto & ns_id_to_name = boundary.set_nodeset_name_map();
2591 ns_id_to_name.insert(other_ns_id_to_name.begin(), other_ns_id_to_name.end());
2592
2593 const auto & other_ss_id_to_name = other_boundary.get_sideset_name_map();
2594 auto & ss_id_to_name = boundary.set_sideset_name_map();
2595 ss_id_to_name.insert(other_ss_id_to_name.begin(), other_ss_id_to_name.end());
2596
2597 const auto & other_es_id_to_name = other_boundary.get_edgeset_name_map();
2598 auto & es_id_to_name = boundary.set_edgeset_name_map();
2599 es_id_to_name.insert(other_es_id_to_name.begin(), other_es_id_to_name.end());
2600
2601 // Merge other_mesh's elemset information with ours. Throw an
2602 // error if this and other_mesh have overlapping elemset codes
2603 // that refer to different elemset ids.
2604 std::vector<dof_id_type> this_elemset_codes = this->get_elemset_codes();
2605 MeshBase::elemset_type this_id_set_to_fill, other_id_set_to_fill;
2606 for (const auto & elemset_code : other_mesh->get_elemset_codes())
2607 {
2608 // Get the elemset ids for this elemset_code on other_mesh
2609 other_mesh->get_elemsets(elemset_code, other_id_set_to_fill);
2610
2611 // Check that this elemset code does not already exist
2612 // in this mesh, or if it does, that it has the same elemset
2613 // ids associated with it.
2614 //
2615 // Note: get_elemset_codes() is guaranteed to return a
2616 // sorted vector, so we can binary search in it.
2617 auto it = Utility::binary_find(this_elemset_codes.begin(),
2618 this_elemset_codes.end(),
2619 elemset_code);
2620
2621 if (it != this_elemset_codes.end())
2622 {
2623 // This mesh has the same elemset code. Does it refer to
2624 // the same elemset ids?
2625 this->get_elemsets(elemset_code, this_id_set_to_fill);
2626
2627 // Throw an error if they don't match, otherwise we
2628 // don't need to do anything
2629 libmesh_error_msg_if(other_id_set_to_fill != this_id_set_to_fill,
2630 "Attempted to stitch together meshes with conflicting elemset codes.");
2631 }
2632 else
2633 {
2634 // Add other_mesh's elemset code to this mesh
2635 this->add_elemset_code(elemset_code, other_id_set_to_fill);
2636 }
2637 }
2638
2639 } // end if (other_mesh)
2640
2641 // Finally, we need to "merge" the overlapping nodes
2642 // We do this by iterating over node_to_elems_map and updating
2643 // the elements so that they "point" to the nodes that came
2644 // from this mesh, rather than from other_mesh.
2645 // Then we iterate over node_to_node_map and delete the
2646 // duplicate nodes that came from other_mesh.
2647
2648 {
2649 LOG_SCOPE("stitch_meshes node updates", "UnstructuredMesh");
2650
2651 // Container to catch boundary IDs passed back from BoundaryInfo.
2652 std::vector<boundary_id_type> bc_ids;
2653
2654 for (const auto & [target_node_id, elem_vec] : node_to_elems_map)
2655 {
2656 dof_id_type other_node_id = node_to_node_map[target_node_id];
2657 Node & target_node = this->node_ref(target_node_id);
2658
2659 std::size_t n_elems = elem_vec.size();
2660 for (std::size_t i=0; i<n_elems; i++)
2661 {
2662 dof_id_type elem_id = elem_vec[i];
2663 Elem * el = this->elem_ptr(elem_id);
2664
2665 // find the local node index that we want to update
2666 unsigned int local_node_index = el->local_node(other_node_id);
2667 libmesh_assert_not_equal_to(local_node_index, libMesh::invalid_uint);
2668
2669 // We also need to copy over the nodeset info here,
2670 // because the node will get deleted below
2671 this->get_boundary_info().boundary_ids(el->node_ptr(local_node_index), bc_ids);
2672 el->set_node(local_node_index, &target_node);
2673 this->get_boundary_info().add_node(&target_node, bc_ids);
2674 }
2675 }
2676 }
2677
2678 {
2679 LOG_SCOPE("stitch_meshes node deletion", "UnstructuredMesh");
2680 for (const auto & [other_node_id, this_node_id] : node_to_node_map)
2681 {
2682 // In the case that this==other_mesh, the two nodes might be the same (e.g. if
2683 // we're stitching a "sliver"), hence we need to skip node deletion in that case.
2684 if ((this == other_mesh) && (this_node_id == other_node_id))
2685 continue;
2686
2687 this->delete_node( this->node_ptr(this_node_id) );
2688 }
2689 }
2690
2691 // If find_neighbors() wasn't called in prepare_for_use(), we need to
2692 // manually loop once more over all elements adjacent to the stitched boundary
2693 // and fix their lists of neighbors.
2694 // This is done according to the following steps:
2695 // 1. Loop over all copied elements adjacent to the boundary using node_to_elems_map (trying to avoid duplicates)
2696 // 2. Look at all their sides with a nullptr neighbor and update them using side_to_elem_map if necessary
2697 // 3. Update the corresponding side in side_to_elem_map as well
2698 if (skip_find_neighbors)
2699 {
2700 LOG_SCOPE("stitch_meshes neighbor fixes", "UnstructuredMesh");
2701
2702 // Pull objects out of the loop to reduce heap operations
2703 std::unique_ptr<const Elem> my_side, their_side;
2704
2705 std::set<dof_id_type> fixed_elems;
2706 for (const auto & pr : node_to_elems_map)
2707 {
2708 std::size_t n_elems = pr.second.size();
2709 for (std::size_t i=0; i<n_elems; i++)
2710 {
2711 dof_id_type elem_id = pr.second[i];
2712 if (!fixed_elems.count(elem_id))
2713 {
2714 Elem * el = this->elem_ptr(elem_id);
2715 fixed_elems.insert(elem_id);
2716 for (auto s : el->side_index_range())
2717 {
2718 bool has_real_neighbor = (el->neighbor_ptr(s) != nullptr);
2719 bool has_disdjoint_neighbor = is_valid_disjoint_pair_to_stitch &&
2720 (this->get_boundary_info().has_boundary_id(el, s, this_mesh_boundary_id)
2721 || this->get_boundary_info().has_boundary_id(el, s, other_mesh_boundary_id));
2722
2723 if (!has_real_neighbor || has_disdjoint_neighbor)
2724 {
2725 key_type key = el->low_order_key(s);
2726 auto bounds = side_to_elem_map.equal_range(key);
2727
2728 if (bounds.first != bounds.second)
2729 {
2730 // Get the side for this element
2731 el->side_ptr(my_side, s);
2732
2733 // Look at all the entries with an equivalent key
2734 while (bounds.first != bounds.second)
2735 {
2736 // Get the potential element
2737 Elem * neighbor = const_cast<Elem *>(bounds.first->second.first);
2738
2739 // Get the side for the neighboring element
2740 const unsigned int ns = bounds.first->second.second;
2741 neighbor->side_ptr(their_side, ns);
2742 //libmesh_assert(my_side.get());
2743 //libmesh_assert(their_side.get());
2744
2745 // If found a match with my side
2746 //
2747 // We need special tests here for 1D:
2748 // since parents and children have an equal
2749 // side (i.e. a node), we need to check
2750 // ns != ms, and we also check level() to
2751 // avoid setting our neighbor pointer to
2752 // any of our neighbor's descendants
2753 if ((*my_side == *their_side) &&
2754 (el->level() == neighbor->level()) &&
2755 ((el->dim() != 1) || (ns != s)))
2756 {
2757 // So share a side. Is this a mixed pair
2758 // of subactive and active/ancestor
2759 // elements?
2760 // If not, then we're neighbors.
2761 // If so, then the subactive's neighbor is
2762
2763 if (el->subactive() ==
2764 neighbor->subactive())
2765 {
2766 // an element is only subactive if it has
2767 // been coarsened but not deleted
2768 el->set_neighbor (s,neighbor);
2769 neighbor->set_neighbor(ns,el);
2770 }
2771 else if (el->subactive())
2772 {
2773 el->set_neighbor(s,neighbor);
2774 }
2775 else if (neighbor->subactive())
2776 {
2777 neighbor->set_neighbor(ns,el);
2778 }
2779 // It's OK to invalidate the
2780 // bounds.first iterator here,
2781 // as we are immediately going
2782 // to break out of this while
2783 // loop. bounds.first will
2784 // therefore not be used for
2785 // anything else.
2786 side_to_elem_map.erase (bounds.first);
2787 break;
2788 }
2789
2790 ++bounds.first;
2791 }
2792 }
2793 }
2794 }
2795 }
2796 }
2797 }
2798 }
2799
2800#ifdef LIBMESH_ENABLE_PERIODIC
2801 // Remove only the disjoint pair that was actually stitched.
2802 // Safe because `is_valid_disjoint_pair_to_stitch` is true
2803 // only if this exact (a,b) pair exists in the registry.
2804 // Other disjoint pairs remain untouched.
2805 if (is_valid_disjoint_pair_to_stitch)
2806 this->remove_disjoint_boundary_pair(this_mesh_boundary_id, other_mesh_boundary_id);
2807#endif
2808
2809 if (prepare_after_stitching)
2810 {
2811 // We set our new neighbor pointers already
2812 const bool old_allow_find_neighbors = this->allow_find_neighbors();
2813 this->allow_find_neighbors(!skip_find_neighbors);
2814
2815 // We haven't newly remoted any elements
2816 const bool old_allow_remote_element_removal = this->allow_remote_element_removal();
2817 this->allow_remote_element_removal(false);
2818
2819 this->prepare_for_use();
2820
2821 this->allow_find_neighbors(old_allow_find_neighbors);
2822 this->allow_remote_element_removal(old_allow_remote_element_removal);
2823 }
2824
2825 // After the stitching, we may want to clear boundary IDs from element
2826 // faces that are now internal to the mesh
2827 if (clear_stitched_boundary_ids)
2828 {
2829 LOG_SCOPE("stitch_meshes clear bcids", "UnstructuredMesh");
2830
2832 this_mesh_boundary_id, other_mesh_boundary_id, /*clear_nodeset_data=*/true);
2833 }
2834
2835 // Return the number of nodes which were merged.
2836 return node_to_node_map.size();
2837}
2838
2839
2840} // namespace libMesh
unsigned int dim
void max(const T &r, T &o, Request &req) const
void min(const T &r, T &o, Request &req) const
timpi_pure bool verify(const T &r) const
The BoundaryInfo class contains information relevant to boundary conditions including storing faces,...
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 ...
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::vector< BCTuple > build_side_list(BCTupleSortBy sort_by=BCTupleSortBy::ELEM_ID) const
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
void edge_boundary_ids(const Elem *const elem, const unsigned short int edge, std::vector< boundary_id_type > &vec_to_fill) const
void boundary_ids(const Node *node, std::vector< boundary_id_type > &vec_to_fill) const
Fills a user-provided std::vector with the boundary ids associated with Node node.
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::map< boundary_id_type, std::string > & set_sideset_name_map()
std::vector< BCTuple > build_shellface_list() const
Create a list of (element_id, shellface_id, boundary_id) tuples for all relevant shellfaces.
void copy_boundary_ids(const BoundaryInfo &old_boundary_info, const Elem *const old_elem, const Elem *const new_elem)
const std::set< boundary_id_type > & get_boundary_ids() const
const std::string & get_sideset_name(boundary_id_type id) 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::map< boundary_id_type, std::string > & set_edgeset_name_map()
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.
std::vector< BCTuple > build_edge_list() const
Create a list of (element_id, edge_id, boundary_id) tuples for all relevant edges.
const std::map< boundary_id_type, std::string > & get_edgeset_name_map() const
const std::map< boundary_id_type, std::string > & get_nodeset_name_map() const
std::map< boundary_id_type, std::string > & set_nodeset_name_map()
const std::map< boundary_id_type, std::string > & get_sideset_name_map() const
dof_id_type get_extra_integer(const unsigned int index) const
Gets the value on this object of the extra integer associated with index, which should have been obta...
unsigned int n_extra_integers() const
Returns how many extra integers are associated to the DofObject.
processor_id_type processor_id() const
Definition dof_object.h:881
dof_id_type & set_id()
Definition dof_object.h:827
void add_extra_integers(const unsigned int n_integers)
Assigns a set of extra integers to this DofObject.
Definition dof_object.C:482
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition dof_object.h:473
unique_id_type unique_id() const
Definition dof_object.h:835
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_unique_id(unique_id_type new_id)
Sets the unique_id for this DofObject.
Definition dof_object.h:848
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...
This is the base class from which all geometric element types are derived.
Definition elem.h:96
virtual Node *& set_node(const unsigned int i)
Definition elem.h:2567
void set_p_refinement_flag(const RefinementState pflag)
Sets the value of the p-refinement flag for the element.
Definition elem.h:3251
SimpleRange< NeighborPtrIter > neighbor_ptr_range()
Returns a range with all neighbors of an element, usable in range-based for loops.
Definition elem.h:3520
virtual bool is_child_on_side(const unsigned int c, const unsigned int s) const =0
bool active() const
Definition elem.h:2958
bool is_ancestor_of(const Elem *descendant) const
Definition elem.h:3026
virtual unsigned int n_vertices() const =0
bool has_children() const
Definition elem.h:2996
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
virtual unsigned int n_nodes() const =0
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
static ElemType complete_order_equivalent_type(const ElemType et)
Definition elem.C:3352
const Elem * child_ptr(unsigned int i) const
Definition elem.h:3180
bool ancestor() const
Definition elem.C:2019
unsigned int which_neighbor_am_i(const Elem *e) const
This function tells you which neighbor e is.
Definition elem.h:2936
virtual dof_id_type low_order_key(const unsigned int s) const =0
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition elem.C:442
void set_interior_parent(Elem *p)
Sets the pointer to the element's interior_parent.
Definition elem.C:1222
static ElemType first_order_equivalent_type(const ElemType et)
Definition elem.C:3099
virtual unsigned int n_second_order_adjacent_vertices(const unsigned int n) const
Definition elem.C:3073
unsigned int local_node(const dof_id_type i) const
Definition elem.h:2496
unsigned int which_child_am_i(const Elem *e) const
Definition elem.h:3209
virtual Real hmin() const
Definition elem.C:683
static ElemType second_order_equivalent_type(const ElemType et, const bool full_ordered=true)
Definition elem.C:3168
virtual unsigned short dim() const =0
SimpleRange< ChildRefIter > child_ref_range()
Returns a range with all children of a parent element, usable in range-based for loops.
Definition elem.h:2355
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
void replace_child(Elem *elem, unsigned int c)
Replaces the child pointer at the specified index in the child array.
Definition elem.C:2099
virtual unsigned int n_children() const =0
virtual ElemType type() const =0
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
dof_id_type node_id(const unsigned int i) const
Definition elem.h:2484
bool subactive() const
Definition elem.h:2976
virtual unsigned short int second_order_adjacent_vertex(const unsigned int n, const unsigned int v) const
Definition elem.C:3081
void set_p_level(const unsigned int p)
Sets the value of the p-refinement level for the element.
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
void inherit_data_from(const Elem &src)
A helper function for copying generic element data (mapping, subdomain, processor) from an element to...
Definition elem.h:3356
This class implements the original default geometry ghosting requirements in libMesh: point neighbors...
std::map< const Elem *, const CouplingMatrix *, CompareDofObjectsByPIDAndThenID > map_type
What elements do we care about and what variables do we care about on each element?
This is the MeshBase class.
Definition mesh_base.h:81
void set_subdomain_name(subdomain_id_type id, const std::string &name, bool synchronous=false)
Sets the name for the provided id.
Definition mesh_base.C:1914
virtual const Node & node_ref(const dof_id_type i) const
Definition mesh_base.h:745
virtual const Point & point(const dof_id_type i) const =0
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
bool is_prepared() const
Definition mesh_base.C:1064
bool allow_renumbering() const
Definition mesh_base.h:1356
virtual const Node * node_ptr(const dof_id_type i) const =0
unsigned int mesh_dimension() const
Definition mesh_base.C:430
virtual dof_id_type n_elem() const =0
virtual bool is_replicated() const
Definition mesh_base.h:379
virtual void own_node(Node &)
Takes ownership of node n on this partition of a distributed mesh, by setting n.processor_id() to thi...
Definition mesh_base.h:869
virtual Elem * insert_elem(Elem *e)=0
Insert elem e to the element array, preserving its id and replacing/deleting any existing element wit...
bool skip_partitioning() const
Definition mesh_base.h:1431
virtual void renumber_nodes_and_elements()=0
After partitioning a mesh it is useful to renumber the nodes and elements so that they lie in contigu...
bool allow_detect_interior_parents() const
Definition mesh_base.h:1370
void allow_find_neighbors(bool allow)
If false is passed then this mesh will no longer work to find element neighbors when being prepared f...
Definition mesh_base.h:1362
PeriodicBoundaries * get_disjoint_neighbor_boundary_pairs()
Definition mesh_base.C:2296
const std::map< subdomain_id_type, std::string > & get_subdomain_name_map() const
Definition mesh_base.h:1926
virtual void delete_node(Node *n)=0
Removes the Node n from the mesh.
const ElemRange & element_stored_range()
Definition mesh_base.C:1939
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
std::vector< dof_id_type > get_elemset_codes() const
Return a vector of all elemset codes defined on the mesh.
Definition mesh_base.C:503
virtual dof_id_type n_nodes() const =0
virtual void delete_elem(Elem *e)=0
Removes element e from the mesh.
virtual dof_id_type max_node_id() const =0
virtual const Node * query_node_ptr(const dof_id_type i) const =0
GhostingFunctorIterator ghosting_functors_begin() const
Beginning of range of ghosting functors.
Definition mesh_base.h:1472
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
std::vector< std::string > _elem_integer_names
The array of names for integer data associated with each element in the mesh.
Definition mesh_base.h:2371
void prepare_for_use()
Definition mesh_base.C:860
unsigned int n_partitions() const
Definition mesh_base.h:1526
virtual void set_next_unique_id(unique_id_type id)=0
Sets the next available unique id to be used.
virtual dof_id_type max_elem_id() const =0
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
MeshBase * _interior_mesh
Defaulting to this, a pointer to the mesh used to generate boundary elements on this.
Definition mesh_base.h:2252
virtual void clear()
Deletes all the element and node data that is currently stored.
Definition mesh_base.C:1036
std::pair< std::vector< unsigned int >, std::vector< unsigned int > > merge_extra_integer_names(const MeshBase &other)
Merge extra-integer arrays from an other mesh.
Definition mesh_base.C:2385
const MeshBase & interior_mesh() const
Definition mesh_base.h:2040
void add_elemset_code(dof_id_type code, MeshBase::elemset_type id_set)
Tabulate a user-defined "code" for elements which belong to the element sets specified in id_set.
Definition mesh_base.C:456
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
bool allow_remote_element_removal() const
Definition mesh_base.h:1379
bool allow_find_neighbors() const
Definition mesh_base.h:1363
void get_elemsets(dof_id_type elemset_code, MeshBase::elemset_type &id_set_to_fill) const
Look up the element sets for a given elemset code and vice-versa.
Definition mesh_base.C:487
void remove_disjoint_boundary_pair(const boundary_id_type b1, const boundary_id_type b2)
Definition mesh_base.C:2306
void clear_point_locator()
Releases the current PointLocator object.
Definition mesh_base.C:1866
virtual void reserve_elem(const dof_id_type ne)=0
Reserves space for a known number of elements.
GhostingFunctorIterator ghosting_functors_end() const
End of range of ghosting functors.
Definition mesh_base.h:1478
std::set< elemset_id_type > elemset_type
Typedef for the "set" container used to store elemset ids.
Definition mesh_base.h:466
std::vector< std::string > _node_integer_names
The array of names for integer data associated with each node in the mesh.
Definition mesh_base.h:2383
std::unique_ptr< PointLocatorBase > sub_point_locator() const
Definition mesh_base.C:1833
void add_disjoint_neighbor_boundary_pairs(const boundary_id_type b1, const boundary_id_type b2, const RealVectorValue &translation)
Register a pair of boundaries as disjoint neighbor boundary pairs.
Definition mesh_base.C:2272
virtual void update_parallel_id_counts()=0
Updates parallel caches so that methods like n_elem() accurately reflect changes on other processors.
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
void allow_detect_interior_parents(bool allow)
If false is passed then this mesh will no longer work to detect interior parents when being prepared ...
Definition mesh_base.h:1369
virtual void reserve_nodes(const dof_id_type nn)=0
Reserves space for a known number of nodes.
void unset_is_prepared()
Tells this we have done some operation where we should no longer consider ourself prepared.
Definition mesh_base.C:1070
virtual unique_id_type parallel_max_unique_id() const =0
This is the MeshCommunication class.
void make_nodes_parallel_consistent(MeshBase &)
Copy processor_ids and ids on ghost nodes from their local processors.
Temporarily serialize a DistributedMesh for non-distributed-mesh capable code paths.
This class supports simple reads and writes in any libMesh-supported format, by dispatching to one of...
virtual void read(const std::string &mesh_file) override
This method implements reading a mesh from a specified file.
virtual void write_nodal_data(const std::string &, const std::vector< Number > &, const std::vector< std::string > &) override
This method implements writing a mesh with nodal data to a specified file where the nodal data and va...
virtual void write(const std::string &mesh_file) override
This method implements writing a mesh to a specified file.
A Node is like a Point, but with more information.
Definition node.h:55
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.
We're using a class instead of a typedef to allow forward declarations and future flexibility.
PeriodicBoundaryBase * boundary(boundary_id_type id)
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
In parallel meshes where a ghost element has neighbors which do not exist on the local processor,...
Definition remote_elem.h:61
The SimpleRange templated class is intended to make it easy to construct ranges from pairs of iterato...
IndexType end() const
IndexType begin() const
The StoredRange class defines a contiguous, divisible set of objects.
The UnstructuredMesh class is derived from the MeshBase class.
virtual void all_second_order_range(const SimpleRange< element_iterator > &range, const bool full_ordered=true) override
Converts a (conforming, non-refined) mesh with linear elements into a mesh with second-order elements...
virtual void all_first_order() override
Converts a mesh with higher-order elements into a mesh with linear elements.
void create_pid_mesh(UnstructuredMesh &pid_mesh, const processor_id_type pid) const
Generates a new mesh containing all the elements which are assigned to processor pid.
virtual void copy_nodes_and_elements(const MeshBase &other_mesh, const bool skip_find_neighbors=false, dof_id_type element_id_offset=0, dof_id_type node_id_offset=0, unique_id_type unique_id_offset=0, std::unordered_map< subdomain_id_type, subdomain_id_type > *id_remapping=nullptr, const bool skip_preparation=false)
Deep copy of nodes and elements from another mesh object (used by subclass copy constructors and by m...
virtual void write(const std::string &name) const override
Write the file specified by name.
std::size_t stitch_meshes(const MeshBase &other_mesh, boundary_id_type this_mesh_boundary, boundary_id_type other_mesh_boundary, Real tol=TOLERANCE, bool clear_stitched_boundary_ids=false, bool verbose=true, bool use_binary_search=true, bool enforce_all_nodes_match_on_boundaries=false, bool merge_boundary_nodes_all_or_nothing=false, bool remap_subdomain_ids=false, bool prepare_after_stitching=true)
Stitch other_mesh to this mesh so that this mesh is the union of the two meshes.
virtual void read(const std::string &name, void *mesh_data=nullptr, bool skip_renumber_nodes_and_elements=false, bool skip_find_neighbors=false, bool skip_detect_interior_parents=false) override
Reads the file specified by name.
virtual void all_complete_order_range(const SimpleRange< element_iterator > &range) override
Converts a (conforming, non-refined) mesh with linear elements into a mesh with "complete" order elem...
virtual bool contract() override
Delete subactive (i.e.
virtual void find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true, const bool assert_valid=true) override
Other functions from MeshBase requiring re-definition.
std::size_t stitching_helper(const MeshBase *other_mesh, boundary_id_type boundary_id_1, boundary_id_type boundary_id_2, Real tol, bool clear_stitched_boundary_ids, bool verbose, bool use_binary_search, bool enforce_all_nodes_match_on_boundaries, bool skip_find_neighbors, bool merge_boundary_nodes_all_or_nothing, bool remap_subdomain_ids, bool prepare_after_stitching)
Helper function for stitch_meshes and stitch_surfaces that does the mesh stitching.
void create_submesh(UnstructuredMesh &new_mesh, const const_element_iterator &it, const const_element_iterator &it_end) const
Constructs a mesh called "new_mesh" from the current mesh by iterating over the elements between it a...
UnstructuredMesh(const Parallel::Communicator &comm_in, unsigned char dim=1)
Constructor.
virtual ~UnstructuredMesh()
Destructor.
std::size_t stitch_surfaces(boundary_id_type boundary_id_1, boundary_id_type boundary_id_2, Real tol=TOLERANCE, bool clear_stitched_boundary_ids=false, bool verbose=true, bool use_binary_search=true, bool enforce_all_nodes_match_on_boundaries=false, bool merge_boundary_nodes_all_or_nothing=false, bool prepare_after_stitching=true)
Similar to stitch_meshes, except that we stitch two adjacent surfaces within this mesh.
Real kdtree_get_pt(const size_t idx, int dim) const
size_t kdtree_get_point_count() const
Must return the number of data points.
VectorOfNodesAdaptor(const std::vector< std::pair< Point, dof_id_type > > &nodes)
const std::vector< std::pair< Point, dof_id_type > > _nodes
static const Real b
MeshBase & mesh
void libmesh_assert_valid_neighbors(const MeshBase &mesh, bool assert_valid_remote_elems=true)
A function for verifying that neighbor connectivity is correct (each element is a neighbor of or desc...
void libmesh_assert_valid_amr_interior_parents(const MeshBase &mesh)
A function for verifying that any interior_parent pointers on elements are consistent with AMR (paren...
void libmesh_assert_valid_amr_elem_ids(const MeshBase &mesh)
A function for verifying that ids of elements are correctly sorted for AMR (parents have lower ids th...
unsigned int n_levels(const MeshBase &mesh)
Definition mesh_tools.C:826
void libmesh_assert_parallel_consistent_procids< Node >(const MeshBase &mesh)
void parallel_for(const Range &range, const Body &body, unsigned int n_threads=libMesh::n_threads())
Execute the provided function object in parallel on the specified range.
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
bool ends_with(std::string_view superstring, std::string_view suffix)
Look for a substring at the very end of a string.
Definition utility.C:213
The libMesh namespace provides an interface to certain functionality in the library.
OStreamProxy err
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
bool closed()
Checks that the library has been closed.
Definition libmesh.C:331
ElemType
Defines an enum for geometric element types.
int8_t boundary_id_type
Definition id_types.h:51
void libmesh_ignore(const Args &...)
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
bool initialized()
Checks that library initialization has been done.
Definition libmesh.C:324
static constexpr Real TOLERANCE
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