libMesh
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 
56 namespace {
57 
58 using namespace libMesh;
59 
60 // Helper functions for all_second_order, all_complete_order
61 
62 std::map<std::vector<dof_id_type>, Node *>::iterator
63 map_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 
95 void 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  for (unsigned int hon=hon_begin; hon<hon_end; hon++)
122  {
123  auto pos = map_hi_order_node(hon, *hi_elem, adj_vertices_to_ho_nodes);
124 
125  // no, not added yet
126  if (!pos->second)
127  {
128  const auto & adjacent_vertices_ids = pos->first;
129 
130  /*
131  * for this set of vertices, there is no
132  * second_order node yet. Add it.
133  *
134  * compute the location of the new node as
135  * the average over the adjacent vertices.
136  */
137  Point new_location = 0;
138  for (dof_id_type vertex_id : adjacent_vertices_ids)
139  new_location += mesh.point(vertex_id);
140 
141  new_location /= static_cast<Real>(adjacent_vertices_ids.size());
142 
143  /* Add the new point to the mesh.
144  *
145  * If we are on a serialized mesh, then we're doing this
146  * all in sync, and the node processor_id will be
147  * consistent between processors.
148  *
149  * If we are on a distributed mesh, we can fix
150  * inconsistent processor ids later, but only if every
151  * processor gives new nodes a *locally* consistent
152  * processor id, so we'll give the new node the
153  * processor id of an adjacent element for now and then
154  * we'll update that later if appropriate.
155  */
156  Node * hi_node = mesh.add_point
157  (new_location, DofObject::invalid_id, lo_pid);
158 
159  /* Come up with a unique unique_id for a potentially new
160  * node. On a distributed mesh we don't yet know what
161  * processor_id will definitely own it, so we can't let
162  * the pid determine the unique_id. But we're not
163  * adding unpartitioned nodes in sync, so we can't let
164  * the mesh autodetermine a unique_id for a new
165  * unpartitioned node either. So we have to pick unique
166  * unique_id values manually.
167  *
168  * We don't have to pick the *same* unique_id value as
169  * will be picked on other processors, though; we'll
170  * sync up each node later. We just need to make sure
171  * we don't duplicate any unique_id that might be chosen
172  * by the same process elsewhere.
173  */
174 #ifdef LIBMESH_ENABLE_UNIQUE_ID
175  unique_id_type new_unique_id = max_unique_id +
176  max_new_nodes_per_elem * lo_elem.id() +
177  hon - hon_begin;
178 
179  hi_node->set_unique_id(new_unique_id);
180 #endif
181 
182  /*
183  * insert the new node with its defining vertex
184  * set into the map, and relocate pos to this
185  * new entry, so that the hi_elem can use
186  * \p pos for inserting the node
187  */
188  pos->second = hi_node;
189 
190  hi_elem->set_node(hon, hi_node);
191  }
192  // yes, already added.
193  else
194  {
195  Node * hi_node = pos->second;
196  libmesh_assert(hi_node);
197  libmesh_assert_equal_to(mesh.node_ptr(hi_node->id()), hi_node);
198 
199  hi_elem->set_node(hon, hi_node);
200 
201  // We need to ensure that the processor who should own a
202  // node *knows* they own the node. And because
203  // Node::choose_processor_id() may depend on Node id,
204  // which may not yet be authoritative, we still have to
205  // use a dumb-but-id-independent partitioning heuristic.
206  processor_id_type chosen_pid =
207  std::min (hi_node->processor_id(), lo_pid);
208 
209  // Plus, if we just discovered that we own this node,
210  // then on a distributed mesh we need to make sure to
211  // give it a valid id, not just a placeholder id!
212  if (!mesh.is_replicated() &&
213  hi_node->processor_id() != my_pid &&
214  chosen_pid == my_pid)
215  mesh.own_node(*hi_node);
216 
217  hi_node->processor_id() = chosen_pid;
218  }
219  }
220 
221  /*
222  * find_neighbors relies on remote_elem neighbor links being
223  * properly maintained. Our own code here relies on ordinary
224  * neighbor links being properly maintained, so let's just keep
225  * everything up to date.
226  */
227  for (auto s : lo_elem.side_index_range())
228  {
229  Elem * neigh = lo_elem.neighbor_ptr(s);
230  if (!neigh)
231  continue;
232 
233  if (neigh != remote_elem)
234  {
235  // We don't support AMR even outside our own range yet.
236  libmesh_assert_equal_to (neigh->level(), 0);
237 
238  const unsigned int ns = neigh->which_neighbor_am_i(&lo_elem);
239  libmesh_assert_not_equal_to(ns, libMesh::invalid_uint);
240 
241  neigh->set_neighbor(ns, hi_elem.get());
242  }
243 
244  hi_elem->set_neighbor(s, neigh);
245  }
246 
253  Elem * interior_p = lo_elem.interior_parent();
254  if (interior_p)
255  hi_elem->set_interior_parent(interior_p);
256 
257  if (auto parent_exterior_it = exterior_children_of.find(interior_p);
258  parent_exterior_it != exterior_children_of.end())
259  {
260  auto & exteriors = parent_exterior_it->second;
261  for (std::size_t i : index_range(exteriors))
262  if (exteriors[i] == &lo_elem)
263  {
264  exteriors[i] = hi_elem.get();
265  break;
266  }
267  }
268 
273  if (auto exterior_it = exterior_children_of.find(&lo_elem);
274  exterior_it != exterior_children_of.end())
275  {
276  for (Elem * exterior_elem : exterior_it->second)
277  {
278  libmesh_assert(exterior_elem->interior_parent() == &lo_elem);
279  exterior_elem->set_interior_parent(hi_elem.get());
280  }
281  }
282 
295  (mesh.get_boundary_info(), &lo_elem, hi_elem.get());
296 
297  /*
298  * The new second-order element is ready.
299  * Inserting it into the mesh will replace and delete
300  * the first-order element.
301  */
302  hi_elem->set_id(lo_elem.id());
303 #ifdef LIBMESH_ENABLE_UNIQUE_ID
304  hi_elem->set_unique_id(lo_elem.unique_id());
305 #endif
306 
307  const unsigned int nei = lo_elem.n_extra_integers();
308  hi_elem->add_extra_integers(nei);
309  for (unsigned int i=0; i != nei; ++i)
310  hi_elem->set_extra_integer(i, lo_elem.get_extra_integer(i));
311 
312  hi_elem->inherit_data_from(lo_elem);
313 
314  mesh.insert_elem(std::move(hi_elem));
315 }
316 
317 
318 template <typename ElemTypeConverter>
319 void
320 all_increased_order_range (UnstructuredMesh & mesh,
322  const unsigned int max_new_nodes_per_elem,
323  const ElemTypeConverter & elem_type_converter)
324 {
325  // This function must be run on all processors at once
326  timpi_parallel_only(mesh.comm());
327 
328  /*
329  * The maximum number of new higher-order nodes we might be adding,
330  * for use when picking unique unique_id values later. This variable
331  * is not used unless unique ids are enabled, so libmesh_ignore() it
332  * to avoid warnings in that case.
333  */
334  libmesh_ignore(max_new_nodes_per_elem);
335 
336  /*
337  * The mesh should at least be consistent enough for us to add new
338  * nodes consistently.
339  */
342 
343  /*
344  * If the mesh is empty then we have nothing to do
345  */
346  if (!mesh.n_elem())
347  return;
348 
349  // If every element in the range _on every proc_ is already of the
350  // requested higher order then we have nothing to do. However, if
351  // any proc has some lower-order elements in the range, then _all_
352  // processors need to continue this function because it is
353  // parallel_only().
354  //
355  // Note: std::all_of() returns true for an empty range, which can
356  // happen for example in the DistributedMesh case when there are
357  // more processors than elements. In the case of an empty range we
358  // therefore set already_second_order to true on that proc.
359  auto is_higher_order = [&elem_type_converter](const Elem * elem) {
360  ElemType old_type = elem->type();
361  ElemType new_type = elem_type_converter(old_type);
362  return old_type == new_type;
363  };
364 
365  bool already_higher_order =
366  std::all_of(range.begin(), range.end(), is_higher_order);
367 
368  // Check with other processors and possibly return early
369  mesh.comm().min(already_higher_order);
370  if (already_higher_order)
371  return;
372 
373  /*
374  * this map helps in identifying higher order
375  * nodes. Namely, a higher-order node:
376  * - edge node
377  * - face node
378  * - bubble node
379  * is uniquely defined through a set of adjacent
380  * vertices. This set of adjacent vertices is
381  * used to identify already added higher-order
382  * nodes. We are safe to use node id's since we
383  * make sure that these are correctly numbered.
384  *
385  * We lazily use an ordered map here to avoid having to implement a
386  * good hash for vector<dof_id_type>
387  */
388  std::map<std::vector<dof_id_type>, Node *> adj_vertices_to_ho_nodes;
389 
390  /*
391  * This map helps us reset any interior_parent() values from the
392  * lower order element to its higher order replacement. Unlike with
393  * neighbor pointers, we don't have backlinks here, so we have to
394  * iterate over the mesh to track forward links.
395  */
396  std::unordered_map<Elem *, std::vector<Elem *>> exterior_children_of;
397 
398  /*
399  * max_new_nodes_per_elem is the maximum number of new higher order
400  * nodes we might be adding, for use when picking unique unique_id
401  * values later. This variable is not used unless unique ids are
402  * enabled.
403  */
404 #ifdef LIBMESH_ENABLE_UNIQUE_ID
405  unique_id_type max_unique_id = mesh.parallel_max_unique_id();
406 #endif
407 
414  dof_id_type n_unpartitioned_elem = 0,
415  n_partitioned_elem = 0;
416 
431  auto track_if_necessary = [&adj_vertices_to_ho_nodes,
432  &exterior_children_of,
433  &elem_type_converter](Elem * elem) {
434  if (elem && elem != remote_elem)
435  {
436  if (elem->default_order() != FIRST)
437  for (unsigned int hon : make_range(elem->n_vertices(), elem->n_nodes()))
438  {
439  auto pos = map_hi_order_node(hon, *elem, adj_vertices_to_ho_nodes);
440  pos->second = elem->node_ptr(hon);
441  }
442 
443  const ElemType old_type = elem->type();
444  const ElemType new_type = elem_type_converter(old_type);
445  if (old_type != new_type)
446  exterior_children_of.emplace(elem, std::vector<Elem *>());
447  }
448  };
449 
450  // If we're in the common case then just track everything; otherwise
451  // find point neighbors to track
452  if (range.begin() == mesh.elements_begin() &&
453  range.end() == mesh.elements_end())
454  {
455  for (auto & elem : range)
456  track_if_necessary(elem);
457  }
458  else
459  {
460  GhostingFunctor::map_type point_neighbor_elements;
461 
462  GhostPointNeighbors point_neighbor_finder(mesh);
463  point_neighbor_finder(range.begin(), range.end(),
464  mesh.n_processors(),
465  point_neighbor_elements);
466 
467  for (auto & [elem, coupling_map] : point_neighbor_elements)
468  {
469  libmesh_ignore(coupling_map);
470  track_if_necessary(const_cast<Elem *>(elem));
471  }
472  }
473 
478  for (auto & elem : mesh.element_ptr_range())
479  if (auto exterior_map_it = exterior_children_of.find(elem->interior_parent());
480  exterior_map_it != exterior_children_of.end())
481  exterior_map_it->second.push_back(elem);
482 
489  for (auto & lo_elem : range)
490  {
491  // Now we can skip the elements in the range that are already
492  // higher-order.
493  const ElemType old_type = lo_elem->type();
494  const ElemType new_type = elem_type_converter(old_type);
495 
496  if (old_type == new_type)
497  continue;
498 
499  // this does _not_ work for refined elements
500  libmesh_assert_equal_to (lo_elem->level(), 0);
501 
503  ++n_unpartitioned_elem;
504  else
505  ++n_partitioned_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 in practice we
553  // only support "completely partitioned" and/or "serialized"
554  if (!mesh.comm().verify(n_unpartitioned_elem) ||
555  !mesh.comm().verify(n_partitioned_elem) ||
556  !mesh.is_serial())
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())
571  mesh.allow_find_neighbors(false);
573  mesh.allow_find_neighbors(old_find_neighbors);
574 }
575 
576 
577 } // anonymous namespace
578 
579 
580 namespace 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 {
588 private:
589  const std::vector<std::pair<Point, dof_id_type>> _nodes;
590 
591 public:
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());
854  libmesh_assert(ip == remote_elem ||
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 
955 void 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 
1362 void 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 
1401 void UnstructuredMesh::write (const std::string & name) const
1402 {
1403  LOG_SCOPE("write()", "Mesh");
1404 
1405  NameBasedIO(*this).write(name);
1406 }
1407 
1408 
1409 
1410 void 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 
1731 void
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 Tri6 to Tri7 (1.1667 times
1833  * the nodes) but might refine from Quad4 to Quad9
1834  * (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 (something > 3). Since in 3D there
1846  * _are_ already quite some nodes, and since we do not want to
1847  * overburden the memory by a too conservative guess, use a
1848  * 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 
1868 std::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 
1897 std::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 
1924 std::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->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->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
std::string name(const ElemQuality q)
This function returns a string containing some name for q.
Definition: elem_quality.C:42
GhostingFunctorIterator ghosting_functors_begin() const
Beginning of range of ghosting functors.
Definition: mesh_base.h:1462
OStreamProxy err
IndexType begin() const
Definition: simple_range.h:41
void set_p_level(const unsigned int p)
Sets the value of the p-refinement level for the element.
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.
Definition: threads_none.h:73
bool closed()
Checks that the library has been closed.
Definition: libmesh.C:331
ElemType
Defines an enum for geometric element types.
The SimpleRange templated class is intended to make it easy to construct ranges from pairs of iterato...
Definition: simple_range.h:36
This class supports simple reads and writes in any libMesh-supported format, by dispatching to one of...
Definition: namebased_io.h:44
virtual void reserve_nodes(const dof_id_type nn)=0
Reserves space for a known number of nodes.
std::vector< BCTuple > build_shellface_list() const
Create a list of (element_id, shellface_id, boundary_id) tuples for all relevant shellfaces.
const Elem * parent() const
Definition: elem.h:3044
bool is_prepared() const
Definition: mesh_base.C:1057
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...
In parallel meshes where a ghost element has neighbors which do not exist on the local processor...
Definition: remote_elem.h:59
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
virtual Node *& set_node(const unsigned int i)
Definition: elem.h:2564
A Node is like a Point, but with more information.
Definition: node.h:52
bool is_ancestor_of(const Elem *descendant) const
Definition: elem.h:3023
const MeshBase & interior_mesh() const
Definition: mesh_base.h:2008
static ElemType complete_order_equivalent_type(const ElemType et)
Definition: elem.C:3352
virtual unique_id_type parallel_max_unique_id() const =0
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...
Definition: mesh_tools.C:1441
void set_interior_mesh(MeshBase &int_mesh)
Sets the interior mesh.
Definition: mesh_base.h:2018
bool has_boundary_id(const Node *const node, const boundary_id_type id) const
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
Definition: libmesh.h:303
void set_parent(Elem *p)
Sets the pointer to the element&#39;s parent.
Definition: elem.h:3060
std::vector< std::string > _elem_integer_names
The array of names for integer data associated with each element in the mesh.
Definition: mesh_base.h:2338
std::unique_ptr< PointLocatorBase > sub_point_locator() const
Definition: mesh_base.C:1826
const Elem * interior_parent() const
Definition: elem.C:1160
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 void read(const std::string &mesh_file) override
This method implements reading a mesh from a specified file.
Definition: namebased_io.C:69
IntRange< unsigned short > side_index_range() const
Definition: elem.h:2724
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 definition of the const_element_iterator struct.
Definition: mesh_base.h:2521
static constexpr Real TOLERANCE
unsigned int dim
MeshBase * _interior_mesh
Defaulting to this, a pointer to the mesh used to generate boundary elements on this.
Definition: mesh_base.h:2219
We&#39;re using a class instead of a typedef to allow forward declarations and future flexibility...
static void set_node_processor_ids(MeshBase &mesh)
This function is called after partitioning to set the processor IDs for the nodes.
Definition: partitioner.C:852
virtual bool is_child_on_side(const unsigned int c, const unsigned int s) const =0
bool allow_detect_interior_parents() const
Definition: mesh_base.h:1360
size_t kdtree_get_point_count() const
Must return the number of data points.
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
virtual dof_id_type low_order_key(const unsigned int s) const =0
const boundary_id_type side_id
VectorOfNodesAdaptor(const std::vector< std::pair< Point, dof_id_type >> &nodes)
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
const std::map< boundary_id_type, std::string > & get_sideset_name_map() const
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
MeshBase & mesh
PeriodicBoundaryBase * boundary(boundary_id_type id)
void add_child(Elem *elem)
Adds a child pointer to the array of children of this element.
Definition: elem.C:2053
void set_refinement_flag(const RefinementState rflag)
Sets the value of the refinement flag for the element.
Definition: elem.h:3232
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.
unique_id_type unique_id() const
Definition: dof_object.h:835
const Parallel::Communicator & comm() const
virtual unsigned int n_children() const =0
void boundary_ids(const Node *node, std::vector< boundary_id_type > &vec_to_fill) const
Fills a user-provided std::vector with the boundary ids associated with Node node.
std::map< const Elem *, const CouplingMatrix *, CompareDofObjectsByPIDAndThenID > map_type
What elements do we care about and what variables do we care about on each element?
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:859
virtual void all_first_order() override
Converts a mesh with higher-order elements into a mesh with linear elements.
The StoredRange class defines a contiguous, divisible set of objects.
Definition: stored_range.h:54
GhostingFunctorIterator ghosting_functors_end() const
End of range of ghosting functors.
Definition: mesh_base.h:1468
virtual void set_next_unique_id(unique_id_type id)=0
Sets the next available unique id to be used.
The libMesh namespace provides an interface to certain functionality in the library.
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.
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 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:1359
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition: mesh_base.h:170
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:2350
std::vector< BCTuple > build_side_list(BCTupleSortBy sort_by=BCTupleSortBy::ELEM_ID) const
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 Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
Add a new Node at Point p to the end of the vertex array, with processor_id procid.
void set_interior_parent(Elem *p)
Sets the pointer to the element&#39;s interior_parent.
Definition: elem.C:1222
uint8_t processor_id_type
Definition: id_types.h:104
This is the MeshBase class.
Definition: mesh_base.h:80
SimpleRange< ChildRefIter > child_ref_range()
Returns a range with all children of a parent element, usable in range-based for loops.
Definition: elem.h:2352
dof_id_type & set_id()
Definition: dof_object.h:827
bool ancestor() const
Definition: elem.C:2019
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
virtual bool contract() override
Delete subactive (i.e.
uint8_t processor_id_type
std::map< boundary_id_type, std::string > & set_sideset_name_map()
void replace_child(Elem *elem, unsigned int c)
Replaces the child pointer at the specified index in the child array.
Definition: elem.C:2099
processor_id_type n_processors() const
virtual bool is_serial() const
Definition: mesh_base.h:347
void libmesh_ignore(const Args &...)
void add_node(const Node *node, const boundary_id_type id)
Add Node node with boundary id id to the boundary information data structures.
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:2232
const std::map< boundary_id_type, std::string > & get_nodeset_name_map() const
int8_t boundary_id_type
Definition: id_types.h:51
static const boundary_id_type invalid_id
Number used for internal use.
virtual void delete_elem(Elem *e)=0
Removes element e from the mesh.
This is the MeshCommunication class.
dof_id_type id() const
Definition: dof_object.h:819
void min(const T &r, T &o, Request &req) const
const std::map< subdomain_id_type, std::string > & get_subdomain_name_map() const
Definition: mesh_base.h:1896
virtual Real hmin() const
Definition: elem.C:683
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition: dof_object.h:473
virtual unsigned int n_nodes() const =0
const ElemRange & element_stored_range()
Definition: mesh_base.C:1913
The UnstructuredMesh class is derived from the MeshBase class.
unsigned int which_neighbor_am_i(const Elem *e) const
This function tells you which neighbor e is.
Definition: elem.h:2933
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition: elem.C:442
PeriodicBoundaries * get_disjoint_neighbor_boundary_pairs()
Definition: mesh_base.C:2256
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
Definition: simple_range.h:57
unsigned int n_levels(const MeshBase &mesh)
Definition: mesh_tools.C:826
virtual const Node * query_node_ptr(const dof_id_type i) const =0
const std::map< boundary_id_type, std::string > & get_edgeset_name_map() const
virtual dof_id_type max_elem_id() const =0
void clear_point_locator()
Releases the current PointLocator object.
Definition: mesh_base.C:1859
bool allow_find_neighbors() const
Definition: mesh_base.h:1353
virtual void write(const std::string &mesh_file) override
This method implements writing a mesh to a specified file.
Definition: namebased_io.C:306
The BoundaryInfo class contains information relevant to boundary conditions including storing faces...
Definition: boundary_info.h:57
libmesh_assert(ctx)
std::vector< BCTuple > build_edge_list() const
Create a list of (element_id, edge_id, boundary_id) tuples for all relevant edges.
virtual void delete_node(Node *n)=0
Removes the Node n from the mesh.
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:2345
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...
Definition: mesh_tools.C:1421
std::string & subdomain_name(subdomain_id_type id)
Definition: mesh_base.C:1880
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:1352
virtual std::unique_ptr< Elem > disconnected_clone() const
Definition: elem.C:410
virtual unsigned int n_second_order_adjacent_vertices(const unsigned int n) const
Definition: elem.C:3073
bool allow_remote_element_removal() const
Definition: mesh_base.h:1369
void set_neighbor(const unsigned int i, Elem *n)
Assigns n as the neighbor.
Definition: elem.h:2632
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...
Definition: namebased_io.C:471
void regenerate_id_sets()
Clears and regenerates the cached sets of ids.
void prepare_for_use()
Definition: mesh_base.C:860
virtual void clear()
Deletes all the element and node data that is currently stored.
Definition: mesh_base.C:1029
static ElemType second_order_equivalent_type(const ElemType et, const bool full_ordered=true)
Definition: elem.C:3168
void libmesh_assert_parallel_consistent_procids< Node >(const MeshBase &mesh)
Definition: mesh_tools.C:2218
unsigned int which_child_am_i(const Elem *e) const
Definition: elem.h:3206
bool skip_partitioning() const
Definition: mesh_base.h:1421
const std::set< boundary_id_type > & get_boundary_ids() const
unsigned int n_partitions() const
Definition: mesh_base.h:1516
virtual const Elem * elem_ptr(const dof_id_type i) const =0
UnstructuredMesh(const Parallel::Communicator &comm_in, unsigned char dim=1)
Constructor.
Real kdtree_get_pt(const size_t idx, int dim) const
virtual Elem * insert_elem(Elem *e)=0
Insert elem e to the element array, preserving its id and replacing/deleting any existing element wit...
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2612
Preparation _preparation
Flags indicating in what ways this mesh has been prepared.
Definition: mesh_base.h:2162
std::set< elemset_id_type > elemset_type
Typedef for the "set" container used to store elemset ids.
Definition: mesh_base.h:456
unsigned int level() const
Definition: elem.h:3088
void set_unique_id(unique_id_type new_id)
Sets the unique_id for this DofObject.
Definition: dof_object.h:848
virtual unsigned int n_vertices() const =0
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const std::vector< std::pair< Point, dof_id_type > > _nodes
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
virtual ~UnstructuredMesh()
Destructor.
timpi_pure bool verify(const T &r) const
void copy_boundary_ids(const BoundaryInfo &old_boundary_info, const Elem *const old_elem, const Elem *const new_elem)
void max(const T &r, T &o, Request &req) const
auto norm(const T &a)
Definition: tensor_tools.h:74
virtual unsigned short dim() const =0
IndexType end() const
Definition: simple_range.h:43
Temporarily serialize a DistributedMesh for non-distributed-mesh capable code paths.
const Node * node_ptr(const unsigned int i) const
Definition: elem.h:2513
std::map< boundary_id_type, std::string > & set_nodeset_name_map()
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.
void make_nodes_parallel_consistent(MeshBase &)
Copy processor_ids and ids on ghost nodes from their local processors.
static const Real b
OStreamProxy out
virtual bool is_replicated() const
Definition: mesh_base.h:369
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
Add side side of element number elem with boundary id id to the boundary information data structure...
const std::string & get_sideset_name(boundary_id_type id) const
virtual unsigned short int second_order_adjacent_vertex(const unsigned int n, const unsigned int v) const
Definition: elem.C:3081
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 ...
bool subactive() const
Definition: elem.h:2973
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
void add_extra_integers(const unsigned int n_integers)
Assigns a set of extra integers to this DofObject.
Definition: dof_object.C:482
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
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 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.
unsigned int mesh_dimension() const
Definition: mesh_base.C:430
bool initialized()
Checks that library initialization has been done.
Definition: libmesh.C:324
unsigned int n_extra_integers() const
Returns how many extra integers are associated to the DofObject.
Definition: dof_object.h:1146
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...
virtual const Node & node_ref(const dof_id_type i) const
Definition: mesh_base.h:735
virtual const Point & point(const dof_id_type i) const =0
unsigned int local_node(const dof_id_type i) const
Definition: elem.h:2493
std::vector< NodeBCTuple > build_node_list(NodeBCTupleSortBy sort_by=NodeBCTupleSortBy::NODE_ID) const
void edge_boundary_ids(const Elem *const elem, const unsigned short int edge, std::vector< boundary_id_type > &vec_to_fill) const
SimpleRange< NeighborPtrIter > neighbor_ptr_range()
Returns a range with all neighbors of an element, usable in range-based for loops.
Definition: elem.h:3517
void set_p_refinement_flag(const RefinementState pflag)
Sets the value of the p-refinement flag for the element.
Definition: elem.h:3248
bool allow_renumbering() const
Definition: mesh_base.h:1346
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:3353
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...
Definition: mesh_tools.C:2320
virtual void delete_remote_elements()
When supported, deletes all nonlocal elements of the mesh except for "ghosts" which touch a local ele...
Definition: mesh_base.h:389
virtual dof_id_type max_node_id() const =0
void remove_disjoint_boundary_pair(const boundary_id_type b1, const boundary_id_type b2)
Definition: mesh_base.C:2266
virtual dof_id_type n_elem() const =0
virtual const Node * node_ptr(const dof_id_type i) const =0
processor_id_type processor_id() const
bool active() const
Definition: elem.h:2955
bool kdtree_get_bbox(BBOX &) const
static ElemType first_order_equivalent_type(const ElemType et)
Definition: elem.C:3099
processor_id_type processor_id() const
Definition: dof_object.h:881
virtual ElemType type() const =0
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
dof_id_type node_id(const unsigned int i) const
Definition: elem.h:2481
virtual void reserve_elem(const dof_id_type ne)=0
Reserves space for a known number of elements.
uint8_t unique_id_type
Definition: id_types.h:86
bool has_children() const
Definition: elem.h:2993
virtual void write(const std::string &name) const override
Write the file specified by name.
void unset_is_prepared()
Tells this we have done some operation where we should no longer consider ourself prepared...
Definition: mesh_base.C:1063
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:153
void set_extra_integer(const unsigned int index, const dof_id_type value)
Sets the value on this object of the extra integer associated with index, which should have been obta...
Definition: dof_object.h:1062
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...
virtual dof_id_type n_nodes() const =0
This class implements the original default geometry ghosting requirements in libMesh: point neighbors...
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.
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...
Point vertex_average() const
Definition: elem.C:669
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...
Definition: dof_object.h:1078
const Elem * child_ptr(unsigned int i) const
Definition: elem.h:3177
unsigned int idx(const ElemType type, const unsigned int nx, const unsigned int i, const unsigned int j)
A useful inline function which replaces the macros used previously.
uint8_t dof_id_type
Definition: id_types.h:67
std::map< boundary_id_type, std::string > & set_edgeset_name_map()
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...
const RemoteElem * remote_elem
Definition: remote_elem.C:57