libMesh
replicated_mesh.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2019 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/elem.h"
23 #include "libmesh/libmesh_logging.h"
24 #include "libmesh/metis_partitioner.h"
25 #include "libmesh/replicated_mesh.h"
26 #include "libmesh/utility.h"
27 #include "libmesh/parallel.h"
28 #include "libmesh/point.h"
29 #ifdef LIBMESH_HAVE_NANOFLANN
30 #include "libmesh/nanoflann.hpp"
31 #endif
32 
33 // C++ includes
34 #include <unordered_map>
35 #include <unordered_set>
36 
37 namespace libMesh
38 {
39 
40 // This class adapts a vector of Nodes (represented by a pair of a Point and a dof_id_type)
41 // for use in a nanoflann KD-Tree
42 
44 {
45 private:
46  const std::vector<std::pair<Point, dof_id_type>> _nodes;
47 
48 public:
49  VectorOfNodesAdaptor(const std::vector<std::pair<Point, dof_id_type>> & nodes) :
50  _nodes(nodes)
51  {}
52 
56  inline size_t kdtree_get_point_count() const { return _nodes.size(); }
57 
63  inline Real kdtree_get_pt(const size_t idx, int dim) const
64  {
65  libmesh_assert_less (idx, _nodes.size());
66  libmesh_assert_less (dim, 3);
67 
68  const Point & p(_nodes[idx].first);
69 
70  if (dim==0) return p(0);
71  if (dim==1) return p(1);
72  return p(2);
73  }
74 
75  /*
76  * Optional bounding-box computation
77  */
78  template <class BBOX>
79  bool kdtree_get_bbox(BBOX & /* bb */) const { return false; }
80 };
81 
82 
83 // ------------------------------------------------------------
84 // ReplicatedMesh class member functions
85 ReplicatedMesh::ReplicatedMesh (const Parallel::Communicator & comm_in,
86  unsigned char d) :
87  UnstructuredMesh (comm_in,d)
88 {
89 #ifdef LIBMESH_ENABLE_UNIQUE_ID
90  // In serial we just need to reset the next unique id to zero
91  // here in the constructor.
92  _next_unique_id = 0;
93 #endif
94  _partitioner = libmesh_make_unique<MetisPartitioner>();
95 }
96 
97 
98 
100 {
101  this->clear(); // Free nodes and elements
102 }
103 
104 
105 // This might be specialized later, but right now it's just here to
106 // make sure the compiler doesn't give us a default (non-deep) copy
107 // constructor instead.
109  UnstructuredMesh (other_mesh)
110 {
111  this->copy_nodes_and_elements(other_mesh, true);
112 
113  auto & this_boundary_info = this->get_boundary_info();
114  const auto & other_boundary_info = other_mesh.get_boundary_info();
115 
116  this_boundary_info = other_boundary_info;
117 
118  this->set_subdomain_name_map() = other_mesh.get_subdomain_name_map();
119 
120  // Use the first BoundaryInfo object to build the list of side boundary ids
121  std::vector<boundary_id_type> side_boundaries;
122  other_boundary_info.build_side_boundary_ids(side_boundaries);
123 
124  // Assign those boundary ids in our BoundaryInfo object
125  for (const auto & side_bnd_id : side_boundaries)
126  this_boundary_info.sideset_name(side_bnd_id) =
127  other_boundary_info.get_sideset_name(side_bnd_id);
128 
129  // Do the same thing for node boundary ids
130  std::vector<boundary_id_type> node_boundaries;
131  other_boundary_info.build_node_boundary_ids(node_boundaries);
132 
133  for (const auto & node_bnd_id : node_boundaries)
134  this_boundary_info.nodeset_name(node_bnd_id) =
135  other_boundary_info.get_nodeset_name(node_bnd_id);
136 
137 #ifdef LIBMESH_ENABLE_UNIQUE_ID
138  this->_next_unique_id = other_mesh._next_unique_id;
139 #endif
140 }
141 
142 
144  UnstructuredMesh (other_mesh)
145 {
146  this->copy_nodes_and_elements(other_mesh, true);
147 
148  auto & this_boundary_info = this->get_boundary_info();
149  const auto & other_boundary_info = other_mesh.get_boundary_info();
150 
151  this_boundary_info = other_boundary_info;
152 
153  this->set_subdomain_name_map() = other_mesh.get_subdomain_name_map();
154 
155  // Use the first BoundaryInfo object to build the list of side boundary ids
156  std::vector<boundary_id_type> side_boundaries;
157  other_boundary_info.build_side_boundary_ids(side_boundaries);
158 
159  // Assign those boundary ids in our BoundaryInfo object
160  for (const auto & side_bnd_id : side_boundaries)
161  this_boundary_info.sideset_name(side_bnd_id) =
162  other_boundary_info.get_sideset_name(side_bnd_id);
163 
164  // Do the same thing for node boundary ids
165  std::vector<boundary_id_type> node_boundaries;
166  other_boundary_info.build_node_boundary_ids(node_boundaries);
167 
168  for (const auto & node_bnd_id : node_boundaries)
169  this_boundary_info.nodeset_name(node_bnd_id) =
170  other_boundary_info.get_nodeset_name(node_bnd_id);
171 }
172 
173 
174 const Point & ReplicatedMesh::point (const dof_id_type i) const
175 {
176  return this->node_ref(i);
177 }
178 
179 
180 
181 
183 {
184  libmesh_assert_less (i, this->n_nodes());
186  libmesh_assert_equal_to (_nodes[i]->id(), i); // This will change soon
187 
188  return _nodes[i];
189 }
190 
191 
192 
193 
195 {
196  libmesh_assert_less (i, this->n_nodes());
198  libmesh_assert_equal_to (_nodes[i]->id(), i); // This will change soon
199 
200  return _nodes[i];
201 }
202 
203 
204 
205 
207 {
208  if (i >= this->n_nodes())
209  return nullptr;
210  libmesh_assert (_nodes[i] == nullptr ||
211  _nodes[i]->id() == i); // This will change soon
212 
213  return _nodes[i];
214 }
215 
216 
217 
218 
220 {
221  if (i >= this->n_nodes())
222  return nullptr;
223  libmesh_assert (_nodes[i] == nullptr ||
224  _nodes[i]->id() == i); // This will change soon
225 
226  return _nodes[i];
227 }
228 
229 
230 
231 
233 {
234  libmesh_assert_less (i, this->n_elem());
236  libmesh_assert_equal_to (_elements[i]->id(), i); // This will change soon
237 
238  return _elements[i];
239 }
240 
241 
242 
243 
245 {
246  libmesh_assert_less (i, this->n_elem());
248  libmesh_assert_equal_to (_elements[i]->id(), i); // This will change soon
249 
250  return _elements[i];
251 }
252 
253 
254 
255 
257 {
258  if (i >= this->n_elem())
259  return nullptr;
260  libmesh_assert (_elements[i] == nullptr ||
261  _elements[i]->id() == i); // This will change soon
262 
263  return _elements[i];
264 }
265 
266 
267 
268 
270 {
271  if (i >= this->n_elem())
272  return nullptr;
273  libmesh_assert (_elements[i] == nullptr ||
274  _elements[i]->id() == i); // This will change soon
275 
276  return _elements[i];
277 }
278 
279 
280 
281 
283 {
284  libmesh_assert(e);
285 
286  // We no longer merely append elements with ReplicatedMesh
287 
288  // If the user requests a valid id that doesn't correspond to an
289  // existing element, let's give them that id, resizing the elements
290  // container if necessary.
291  if (!e->valid_id())
292  e->set_id (cast_int<dof_id_type>(_elements.size()));
293 
294 #ifdef LIBMESH_ENABLE_UNIQUE_ID
295  if (!e->valid_unique_id())
297  else
298  _next_unique_id = std::max(_next_unique_id, e->unique_id()+1);
299 #endif
300 
301  const dof_id_type id = e->id();
302 
303  if (id < _elements.size())
304  {
305  // Overwriting existing elements is still probably a mistake.
307  }
308  else
309  {
310  _elements.resize(id+1, nullptr);
311  }
312 
313  _elements[id] = e;
314 
315  // Make sure any new element is given space for any extra integers
316  // we've requested
318 
319  // And set mapping type and data on any new element
322 
323  return e;
324 }
325 
326 
327 
329 {
330 #ifdef LIBMESH_ENABLE_UNIQUE_ID
331  if (!e->valid_unique_id())
333 #endif
334 
335  dof_id_type eid = e->id();
336  libmesh_assert_less (eid, _elements.size());
337  Elem * oldelem = _elements[eid];
338 
339  if (oldelem)
340  {
341  libmesh_assert_equal_to (oldelem->id(), eid);
342  this->delete_elem(oldelem);
343  }
344 
345  _elements[e->id()] = e;
346 
347  // Make sure any new element is given space for any extra integers
348  // we've requested
350 
351  // And set mapping type and data on any new element
354 
355  return e;
356 }
357 
358 
359 
361 {
362  libmesh_assert(e);
363 
364  // Initialize an iterator to eventually point to the element we want to delete
365  std::vector<Elem *>::iterator pos = _elements.end();
366 
367  // In many cases, e->id() gives us a clue as to where e
368  // is located in the _elements vector. Try that first
369  // before trying the O(n_elem) search.
370  libmesh_assert_less (e->id(), _elements.size());
371 
372  if (_elements[e->id()] == e)
373  {
374  // We found it!
375  pos = _elements.begin();
376  std::advance(pos, e->id());
377  }
378 
379  else
380  {
381  // This search is O(n_elem)
382  pos = std::find (_elements.begin(),
383  _elements.end(),
384  e);
385  }
386 
387  // Huh? Element not in the vector?
388  libmesh_assert (pos != _elements.end());
389 
390  // Remove the element from the BoundaryInfo object
391  this->get_boundary_info().remove(e);
392 
393  // delete the element
394  delete e;
395 
396  // explicitly zero the pointer
397  *pos = nullptr;
398 }
399 
400 
401 
403  const dof_id_type new_id)
404 {
405  // This doesn't get used in serial yet
406  Elem * el = _elements[old_id];
407  libmesh_assert (el);
408 
409  el->set_id(new_id);
410  libmesh_assert (!_elements[new_id]);
411  _elements[new_id] = el;
412  _elements[old_id] = nullptr;
413 }
414 
415 
416 
418  const dof_id_type id,
419  const processor_id_type proc_id)
420 {
421  // // We only append points with ReplicatedMesh
422  // libmesh_assert(id == DofObject::invalid_id || id == _nodes.size());
423  // Node *n = Node::build(p, _nodes.size()).release();
424  // n->processor_id() = proc_id;
425  // _nodes.push_back (n);
426 
427  Node * n = nullptr;
428 
429  // If the user requests a valid id, either
430  // provide the existing node or resize the container
431  // to fit the new node.
432  if (id != DofObject::invalid_id)
433  if (id < _nodes.size())
434  n = _nodes[id];
435  else
436  _nodes.resize(id+1);
437  else
438  _nodes.push_back (static_cast<Node *>(nullptr));
439 
440  // if the node already exists, then assign new (x,y,z) values
441  if (n)
442  *n = p;
443  // otherwise build a new node, put it in the right spot, and return
444  // a valid pointer.
445  else
446  {
447  n = Node::build(p, (id == DofObject::invalid_id) ?
448  cast_int<dof_id_type>(_nodes.size()-1) : id).release();
449  n->processor_id() = proc_id;
450 
452 
453 #ifdef LIBMESH_ENABLE_UNIQUE_ID
454  if (!n->valid_unique_id())
456 #endif
457 
458  if (id == DofObject::invalid_id)
459  _nodes.back() = n;
460  else
461  _nodes[id] = n;
462  }
463 
464  // better not pass back a nullptr.
465  libmesh_assert (n);
466 
467  return n;
468 }
469 
470 
471 
473 {
474  libmesh_assert(n);
475  // We only append points with ReplicatedMesh
476  libmesh_assert(!n->valid_id() || n->id() == _nodes.size());
477 
478  n->set_id (cast_int<dof_id_type>(_nodes.size()));
479 
480 #ifdef LIBMESH_ENABLE_UNIQUE_ID
481  if (!n->valid_unique_id())
483 #endif
484 
486 
487  _nodes.push_back(n);
488 
489  return n;
490 }
491 
492 
493 
495 {
496  if (!n)
497  libmesh_error_msg("Error, attempting to insert nullptr node.");
498 
499  if (n->id() == DofObject::invalid_id)
500  libmesh_error_msg("Error, cannot insert node with invalid id.");
501 
502  if (n->id() < _nodes.size())
503  {
504  // Don't allow inserting on top of an existing Node.
505 
506  // Doing so doesn't have to be *error*, in the case where a
507  // redundant insert is done, but when that happens we ought to
508  // always be able to make the code more efficient by avoiding
509  // the redundant insert, so let's keep screaming "Error" here.
510  if (_nodes[ n->id() ] != nullptr)
511  libmesh_error_msg("Error, cannot insert node on top of existing node.");
512  }
513  else
514  {
515  // Allocate just enough space to store the new node. This will
516  // cause highly non-ideal memory allocation behavior if called
517  // repeatedly...
518  _nodes.resize(n->id() + 1);
519  }
520 
521 #ifdef LIBMESH_ENABLE_UNIQUE_ID
522  if (!n->valid_unique_id())
524 #endif
525 
527 
528  // We have enough space and this spot isn't already occupied by
529  // another node, so go ahead and add it.
530  _nodes[ n->id() ] = n;
531 
532  // If we made it this far, we just inserted the node the user handed
533  // us, so we can give it right back.
534  return n;
535 }
536 
537 
538 
540 {
541  libmesh_assert(n);
542  libmesh_assert_less (n->id(), _nodes.size());
543 
544  // Initialize an iterator to eventually point to the element we want
545  // to delete
546  std::vector<Node *>::iterator pos;
547 
548  // In many cases, e->id() gives us a clue as to where e
549  // is located in the _elements vector. Try that first
550  // before trying the O(n_elem) search.
551  if (_nodes[n->id()] == n)
552  {
553  pos = _nodes.begin();
554  std::advance(pos, n->id());
555  }
556  else
557  {
558  pos = std::find (_nodes.begin(),
559  _nodes.end(),
560  n);
561  }
562 
563  // Huh? Node not in the vector?
564  libmesh_assert (pos != _nodes.end());
565 
566  // Delete the node from the BoundaryInfo object
567  this->get_boundary_info().remove(n);
568 
569  // delete the node
570  delete n;
571 
572  // explicitly zero the pointer
573  *pos = nullptr;
574 }
575 
576 
577 
579  const dof_id_type new_id)
580 {
581  // This doesn't get used in serial yet
582  Node * nd = _nodes[old_id];
583  libmesh_assert (nd);
584 
585  nd->set_id(new_id);
586  libmesh_assert (!_nodes[new_id]);
587  _nodes[new_id] = nd;
588  _nodes[old_id] = nullptr;
589 }
590 
591 
592 
594 {
595  // Call parent clear function
596  MeshBase::clear();
597 
598  // Clear our elements and nodes
599  // There is no need to remove the elements from
600  // the BoundaryInfo data structure since we
601  // already cleared it.
602  for (auto & elem : _elements)
603  delete elem;
604 
605  _elements.clear();
606 
607  // clear the nodes data structure
608  // There is no need to remove the nodes from
609  // the BoundaryInfo data structure since we
610  // already cleared it.
611  for (auto & node : _nodes)
612  delete node;
613 
614  _nodes.clear();
615 }
616 
617 
618 
620 {
621 #ifdef LIBMESH_ENABLE_UNIQUE_ID
623 #endif
624 }
625 
626 
627 
628 #ifdef LIBMESH_ENABLE_UNIQUE_ID
630 {
631  // This function must be run on all processors at once
632  parallel_object_only();
633 
634  unique_id_type max_local = _next_unique_id;
635  this->comm().max(max_local);
636  return max_local;
637 }
638 #endif
639 
640 
641 
643 {
644  LOG_SCOPE("renumber_nodes_and_elem()", "Mesh");
645 
646  // node and element id counters
647  dof_id_type next_free_elem = 0;
648  dof_id_type next_free_node = 0;
649 
650  // Will hold the set of nodes that are currently connected to elements
651  std::unordered_set<Node *> connected_nodes;
652 
653  // Loop over the elements. Note that there may
654  // be nullptrs in the _elements vector from the coarsening
655  // process. Pack the elements in to a contiguous array
656  // and then trim any excess.
657  {
658  std::vector<Elem *>::iterator in = _elements.begin();
659  std::vector<Elem *>::iterator out_iter = _elements.begin();
660  const std::vector<Elem *>::iterator end = _elements.end();
661 
662  for (; in != end; ++in)
663  if (*in != nullptr)
664  {
665  Elem * el = *in;
666 
667  *out_iter = *in;
668  ++out_iter;
669 
670  // Increment the element counter
671  el->set_id (next_free_elem++);
672 
674  {
675  // Add this elements nodes to the connected list
676  for (auto & n : el->node_ref_range())
677  connected_nodes.insert(&n);
678  }
679  else // We DO want node renumbering
680  {
681  // Loop over this element's nodes. Number them,
682  // if they have not been numbered already. Also,
683  // position them in the _nodes vector so that they
684  // are packed contiguously from the beginning.
685  for (auto & n : el->node_ref_range())
686  if (n.id() == next_free_node) // don't need to process
687  next_free_node++; // [(src == dst) below]
688 
689  else if (n.id() > next_free_node) // need to process
690  {
691  // The source and destination indices
692  // for this node
693  const dof_id_type src_idx = n.id();
694  const dof_id_type dst_idx = next_free_node++;
695 
696  // ensure we want to swap a valid nodes
697  libmesh_assert(_nodes[src_idx]);
698 
699  // Swap the source and destination nodes
700  std::swap(_nodes[src_idx],
701  _nodes[dst_idx] );
702 
703  // Set proper indices where that makes sense
704  if (_nodes[src_idx] != nullptr)
705  _nodes[src_idx]->set_id (src_idx);
706  _nodes[dst_idx]->set_id (dst_idx);
707  }
708  }
709  }
710 
711  // Erase any additional storage. These elements have been
712  // copied into nullptr voids by the procedure above, and are
713  // thus repeated and unnecessary.
714  _elements.erase (out_iter, end);
715  }
716 
717 
719  {
720  // Loop over the nodes. Note that there may
721  // be nullptrs in the _nodes vector from the coarsening
722  // process. Pack the nodes in to a contiguous array
723  // and then trim any excess.
724 
725  std::vector<Node *>::iterator in = _nodes.begin();
726  std::vector<Node *>::iterator out_iter = _nodes.begin();
727  const std::vector<Node *>::iterator end = _nodes.end();
728 
729  for (; in != end; ++in)
730  if (*in != nullptr)
731  {
732  // This is a reference so that if we change the pointer it will change in the vector
733  Node * & nd = *in;
734 
735  // If this node is still connected to an elem, put it in the list
736  if (connected_nodes.find(nd) != connected_nodes.end())
737  {
738  *out_iter = nd;
739  ++out_iter;
740 
741  // Increment the node counter
742  nd->set_id (next_free_node++);
743  }
744  else // This node is orphaned, delete it!
745  {
746  this->get_boundary_info().remove (nd);
747 
748  // delete the node
749  delete nd;
750  nd = nullptr;
751  }
752  }
753 
754  // Erase any additional storage. Whatever was
755  _nodes.erase (out_iter, end);
756  }
757  else // We really DO want node renumbering
758  {
759  // Any nodes in the vector >= _nodes[next_free_node]
760  // are not connected to any elements and may be deleted
761  // if desired.
762 
763  // Now, delete the unused nodes
764  {
765  std::vector<Node *>::iterator nd = _nodes.begin();
766  const std::vector<Node *>::iterator end = _nodes.end();
767 
768  std::advance (nd, next_free_node);
769 
770  for (auto & node : as_range(nd, end))
771  {
772  // Mesh modification code might have already deleted some
773  // nodes
774  if (node == nullptr)
775  continue;
776 
777  // remove any boundary information associated with
778  // this node
779  this->get_boundary_info().remove (node);
780 
781  // delete the node
782  delete node;
783  node = nullptr;
784  }
785 
786  _nodes.erase (nd, end);
787  }
788  }
789 
790  libmesh_assert_equal_to (next_free_elem, _elements.size());
791  libmesh_assert_equal_to (next_free_node, _nodes.size());
792 
794 }
795 
796 
797 
799 {
800  // Nodes first
801  for (auto n : index_range(_nodes))
802  if (this->_nodes[n] != nullptr)
803  this->_nodes[n]->set_id() = cast_int<dof_id_type>(n);
804 
805  // Elements next
806  for (auto e : index_range(_elements))
807  if (this->_elements[e] != nullptr)
808  this->_elements[e]->set_id() = cast_int<dof_id_type>(e);
809 }
810 
811 
813  boundary_id_type this_mesh_boundary_id,
814  boundary_id_type other_mesh_boundary_id,
815  Real tol,
816  bool clear_stitched_boundary_ids,
817  bool verbose,
818  bool use_binary_search,
819  bool enforce_all_nodes_match_on_boundaries)
820 {
821  LOG_SCOPE("stitch_meshes()", "ReplicatedMesh");
822  stitching_helper(&other_mesh,
823  this_mesh_boundary_id,
824  other_mesh_boundary_id,
825  tol,
826  clear_stitched_boundary_ids,
827  verbose,
828  use_binary_search,
829  enforce_all_nodes_match_on_boundaries,
830  true);
831 }
832 
834  boundary_id_type boundary_id_2,
835  Real tol,
836  bool clear_stitched_boundary_ids,
837  bool verbose,
838  bool use_binary_search,
839  bool enforce_all_nodes_match_on_boundaries)
840 {
841  stitching_helper(nullptr,
842  boundary_id_1,
843  boundary_id_2,
844  tol,
845  clear_stitched_boundary_ids,
846  verbose,
847  use_binary_search,
848  enforce_all_nodes_match_on_boundaries,
849  true);
850 }
851 
853  boundary_id_type this_mesh_boundary_id,
854  boundary_id_type other_mesh_boundary_id,
855  Real tol,
856  bool clear_stitched_boundary_ids,
857  bool verbose,
858  bool use_binary_search,
859  bool enforce_all_nodes_match_on_boundaries,
860  bool skip_find_neighbors)
861 {
862  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
863  std::map<dof_id_type, std::vector<dof_id_type>> node_to_elems_map;
864 
865  typedef dof_id_type key_type;
866  typedef std::pair<Elem *, unsigned char> val_type;
867  typedef std::pair<key_type, val_type> key_val_pair;
868  typedef std::unordered_multimap<key_type, val_type> map_type;
869  // Mapping between all side keys in this mesh and elements+side numbers relevant to the boundary in this mesh as well.
870  map_type side_to_elem_map;
871 
872  // If there is only one mesh (i.e. other_mesh == nullptr), then loop over this mesh twice
873  if (!other_mesh)
874  {
875  other_mesh = this;
876  }
877 
878  if ((this_mesh_boundary_id != BoundaryInfo::invalid_id) &&
879  (other_mesh_boundary_id != BoundaryInfo::invalid_id))
880  {
881  LOG_SCOPE("stitch_meshes node merging", "ReplicatedMesh");
882 
883  // While finding nodes on the boundary, also find the minimum edge length
884  // of all faces on both boundaries. This will later be used in relative
885  // distance checks when stitching nodes.
886  Real h_min = std::numeric_limits<Real>::max();
887  bool h_min_updated = false;
888 
889  // Loop below fills in these sets for the two meshes.
890  std::set<dof_id_type> this_boundary_node_ids, other_boundary_node_ids;
891 
892  // Pull objects out of the loop to reduce heap operations
893  std::unique_ptr<Elem> side;
894 
895  {
896  // Make temporary fixed-size arrays for loop
897  boundary_id_type id_array[2] = {this_mesh_boundary_id, other_mesh_boundary_id};
898  std::set<dof_id_type> * set_array[2] = {&this_boundary_node_ids, &other_boundary_node_ids};
899  const ReplicatedMesh * mesh_array[2] = {this, other_mesh};
900 
901  for (unsigned i=0; i<2; ++i)
902  {
903  // First we deal with node boundary IDs.
904  // We only enter this loop if we have at least one
905  // nodeset.
906  if (mesh_array[i]->get_boundary_info().n_nodeset_conds() > 0)
907  {
908  // build_node_list() returns a vector of (node-id, bc-id) tuples
909  for (const auto & t : mesh_array[i]->get_boundary_info().build_node_list())
910  {
911  boundary_id_type node_bc_id = std::get<1>(t);
912  if (node_bc_id == id_array[i])
913  {
914  dof_id_type this_node_id = std::get<0>(t);
915  set_array[i]->insert( this_node_id );
916 
917  // We need to set h_min to some value. It's too expensive to
918  // search for the element that actually contains this node,
919  // since that would require a PointLocator. As a result, we
920  // just use the first (non-NodeElem!) element in the mesh to
921  // give us hmin if it's never been set before.
922  if (!h_min_updated)
923  {
924  for (const auto & elem : mesh_array[i]->active_element_ptr_range())
925  {
926  Real current_h_min = elem->hmin();
927  if (current_h_min > 0.)
928  {
929  h_min = current_h_min;
930  h_min_updated = true;
931  break;
932  }
933  }
934 
935  // If, after searching all the active elements, we did not update
936  // h_min, give up and set h_min to 1 so that we don't repeat this
937  // fruitless search
938  if (!h_min_updated)
939  {
940  h_min_updated = true;
941  h_min = 1.0;
942  }
943  }
944  }
945  }
946  }
947 
948  // Container to catch boundary IDs passed back from BoundaryInfo.
949  std::vector<boundary_id_type> bc_ids;
950 
951  for (auto & el : mesh_array[i]->element_ptr_range())
952  {
953  // Now check whether elem has a face on the specified boundary
954  for (auto side_id : el->side_index_range())
955  if (el->neighbor_ptr(side_id) == nullptr)
956  {
957  // Get *all* boundary IDs on this side, not just the first one!
958  mesh_array[i]->get_boundary_info().boundary_ids (el, side_id, bc_ids);
959 
960  if (std::find(bc_ids.begin(), bc_ids.end(), id_array[i]) != bc_ids.end())
961  {
962  el->build_side_ptr(side, side_id);
963  for (auto & n : side->node_ref_range())
964  set_array[i]->insert(n.id());
965 
966  h_min = std::min(h_min, side->hmin());
967  h_min_updated = true;
968 
969  // This side is on the boundary, add its information to side_to_elem
970  if (skip_find_neighbors && (i==0))
971  {
972  key_type key = el->key(side_id);
973  val_type val;
974  val.first = el;
975  val.second = cast_int<unsigned char>(side_id);
976 
977  key_val_pair kvp;
978  kvp.first = key;
979  kvp.second = val;
980  side_to_elem_map.insert (kvp);
981  }
982  }
983 
984  // Also, check the edges on this side. We don't have to worry about
985  // updating neighbor info in this case since elements don't store
986  // neighbor info on edges.
987  for (auto edge_id : el->edge_index_range())
988  {
989  if (el->is_edge_on_side(edge_id, side_id))
990  {
991  // Get *all* boundary IDs on this edge, not just the first one!
992  mesh_array[i]->get_boundary_info().edge_boundary_ids (el, edge_id, bc_ids);
993 
994  if (std::find(bc_ids.begin(), bc_ids.end(), id_array[i]) != bc_ids.end())
995  {
996  std::unique_ptr<Elem> edge (el->build_edge_ptr(edge_id));
997  for (auto & n : edge->node_ref_range())
998  set_array[i]->insert( n.id() );
999 
1000  h_min = std::min(h_min, edge->hmin());
1001  h_min_updated = true;
1002  }
1003  }
1004  }
1005  }
1006  }
1007  }
1008  }
1009 
1010  if (verbose)
1011  {
1012  libMesh::out << "In ReplicatedMesh::stitch_meshes:\n"
1013  << "This mesh has " << this_boundary_node_ids.size()
1014  << " nodes on boundary " << this_mesh_boundary_id << ".\n"
1015  << "Other mesh has " << other_boundary_node_ids.size()
1016  << " nodes on boundary " << other_mesh_boundary_id << ".\n";
1017 
1018  if (h_min_updated)
1019  {
1020  libMesh::out << "Minimum edge length on both surfaces is " << h_min << ".\n";
1021  }
1022  else
1023  {
1024  libMesh::out << "No elements on specified surfaces." << std::endl;
1025  }
1026  }
1027 
1028  // We require nanoflann for the "binary search" (really kd-tree)
1029  // option to work. If it's not available, turn that option off,
1030  // warn the user, and fall back on the N^2 search algorithm.
1031  if (use_binary_search)
1032  {
1033 #ifndef LIBMESH_HAVE_NANOFLANN
1034  use_binary_search = false;
1035  libmesh_warning("The use_binary_search option in the "
1036  "ReplicatedMesh stitching algorithms requires nanoflann "
1037  "support. Falling back on N^2 search algorithm.");
1038 #endif
1039  }
1040 
1041  if (this_boundary_node_ids.size())
1042  {
1043  if (use_binary_search)
1044  {
1045 #ifdef LIBMESH_HAVE_NANOFLANN
1046  typedef nanoflann::KDTreeSingleIndexAdaptor<nanoflann::L2_Simple_Adaptor<Real, VectorOfNodesAdaptor>, VectorOfNodesAdaptor, 3> kd_tree_t;
1047 
1048  // Create the dataset needed to build the kd tree with nanoflann
1049  std::vector<std::pair<Point, dof_id_type>> this_mesh_nodes(this_boundary_node_ids.size());
1050  std::set<dof_id_type>::iterator current_node = this_boundary_node_ids.begin(),
1051  node_ids_end = this_boundary_node_ids.end();
1052  for (unsigned int ctr = 0; current_node != node_ids_end; ++current_node, ++ctr)
1053  {
1054  this_mesh_nodes[ctr].first = this->point(*current_node);
1055  this_mesh_nodes[ctr].second = *current_node;
1056  }
1057 
1058  VectorOfNodesAdaptor vec_nodes_adaptor(this_mesh_nodes);
1059 
1060  kd_tree_t this_kd_tree(3, vec_nodes_adaptor, 10);
1061  this_kd_tree.buildIndex();
1062 
1063  // Storage for nearest neighbor in the loop below
1064  std::vector<size_t> ret_index(1);
1065  std::vector<Real> ret_dist_sqr(1);
1066 
1067  // Loop over other mesh. For each node, find its nearest neighbor in this mesh, and fill in the maps.
1068  for (auto node : other_boundary_node_ids)
1069  {
1070  const Real query_pt[] = {other_mesh->point(node)(0), other_mesh->point(node)(1), other_mesh->point(node)(2)};
1071  this_kd_tree.knnSearch(&query_pt[0], 1, &ret_index[0], &ret_dist_sqr[0]);
1072  if (ret_dist_sqr[0] < TOLERANCE*TOLERANCE)
1073  {
1074  node_to_node_map[this_mesh_nodes[ret_index[0]].second] = node;
1075  other_to_this_node_map[node] = this_mesh_nodes[ret_index[0]].second;
1076  }
1077  }
1078 
1079  // If the 2 maps don't have the same size, it means we have overwritten a value in node_to_node_map
1080  // It means one node in this mesh is the nearest neighbor of several nodes in other mesh.
1081  // Not possible !
1082  if (node_to_node_map.size() != other_to_this_node_map.size())
1083  libmesh_error_msg("Error: Found multiple matching nodes in stitch_meshes");
1084 #endif
1085  }
1086  else
1087  {
1088  // In the unlikely event that two meshes composed entirely of
1089  // NodeElems are being stitched together, we will not have
1090  // selected a valid h_min value yet, and the distance
1091  // comparison below will be true for essentially any two
1092  // nodes. In this case we simply fall back on an absolute
1093  // distance check.
1094  if (!h_min_updated)
1095  {
1096  libmesh_warning("No valid h_min value was found, falling back on "
1097  "absolute distance check in the N^2 search algorithm.");
1098  h_min = 1.;
1099  }
1100 
1101  // Otherwise, use a simple N^2 search to find the closest matching points. This can be helpful
1102  // in the case that we have tolerance issues which cause mismatch between the two surfaces
1103  // that are being stitched.
1104  for (const auto & this_node_id : this_boundary_node_ids)
1105  {
1106  Node & this_node = this->node_ref(this_node_id);
1107 
1108  bool found_matching_nodes = false;
1109 
1110  for (const auto & other_node_id : other_boundary_node_ids)
1111  {
1112  const Node & other_node = other_mesh->node_ref(other_node_id);
1113 
1114  Real node_distance = (this_node - other_node).norm();
1115 
1116  if (node_distance < tol*h_min)
1117  {
1118  // Make sure we didn't already find a matching node!
1119  if (found_matching_nodes)
1120  libmesh_error_msg("Error: Found multiple matching nodes in stitch_meshes");
1121 
1122  node_to_node_map[this_node_id] = other_node_id;
1123  other_to_this_node_map[other_node_id] = this_node_id;
1124 
1125  found_matching_nodes = true;
1126  }
1127  }
1128  }
1129  }
1130  }
1131 
1132  // Build up the node_to_elems_map, using only one loop over other_mesh
1133  for (auto & el : other_mesh->element_ptr_range())
1134  {
1135  // For each node on the element, find the corresponding node
1136  // on "this" Mesh, 'this_node_id', if it exists, and push
1137  // the current element ID back onto node_to_elems_map[this_node_id].
1138  // For that we will use the reverse mapping we created at
1139  // the same time as the forward mapping.
1140  for (auto & n : el->node_ref_range())
1141  {
1142  dof_id_type other_node_id = n.id();
1143  std::map<dof_id_type, dof_id_type>::iterator it =
1144  other_to_this_node_map.find(other_node_id);
1145 
1146  if (it != other_to_this_node_map.end())
1147  {
1148  dof_id_type this_node_id = it->second;
1149  node_to_elems_map[this_node_id].push_back( el->id() );
1150  }
1151  }
1152  }
1153 
1154  if (verbose)
1155  {
1156  libMesh::out << "In ReplicatedMesh::stitch_meshes:\n"
1157  << "Found " << node_to_node_map.size()
1158  << " matching nodes.\n"
1159  << std::endl;
1160  }
1161 
1162  if (enforce_all_nodes_match_on_boundaries)
1163  {
1164  std::size_t n_matching_nodes = node_to_node_map.size();
1165  std::size_t this_mesh_n_nodes = this_boundary_node_ids.size();
1166  std::size_t other_mesh_n_nodes = other_boundary_node_ids.size();
1167  if ((n_matching_nodes != this_mesh_n_nodes) || (n_matching_nodes != other_mesh_n_nodes))
1168  libmesh_error_msg("Error: We expected the number of nodes to match.");
1169  }
1170  }
1171  else
1172  {
1173  if (verbose)
1174  {
1175  libMesh::out << "Skip node merging in ReplicatedMesh::stitch_meshes:" << std::endl;
1176  }
1177  }
1178 
1179  dof_id_type node_delta = this->max_node_id();
1180  dof_id_type elem_delta = this->max_elem_id();
1181 
1182  unique_id_type unique_delta =
1183 #ifdef LIBMESH_ENABLE_UNIQUE_ID
1184  this->parallel_max_unique_id();
1185 #else
1186  0;
1187 #endif
1188 
1189  // If other_mesh != nullptr, then we have to do a bunch of work
1190  // in order to copy it to this mesh
1191  if (this!=other_mesh)
1192  {
1193  LOG_SCOPE("stitch_meshes copying", "ReplicatedMesh");
1194 
1195  // Increment the node_to_node_map and node_to_elems_map
1196  // to account for id offsets
1197  for (auto & pr : node_to_node_map)
1198  pr.second += node_delta;
1199 
1200  for (auto & pr : node_to_elems_map)
1201  for (auto & entry : pr.second)
1202  entry += elem_delta;
1203 
1204  // Copy mesh data. If we skip the call to find_neighbors(), the lists
1205  // of neighbors will be copied verbatim from the other mesh
1206  this->copy_nodes_and_elements(*other_mesh, skip_find_neighbors,
1207  elem_delta, node_delta,
1208  unique_delta);
1209 
1210  // Copy BoundaryInfo from other_mesh too. We do this via the
1211  // list APIs rather than element-by-element for speed.
1212  BoundaryInfo & boundary = this->get_boundary_info();
1213  const BoundaryInfo & other_boundary = other_mesh->get_boundary_info();
1214 
1215  for (const auto & t : other_boundary.build_node_list())
1216  boundary.add_node(std::get<0>(t) + node_delta,
1217  std::get<1>(t));
1218 
1219  for (const auto & t : other_boundary.build_side_list())
1220  boundary.add_side(std::get<0>(t) + elem_delta,
1221  std::get<1>(t),
1222  std::get<2>(t));
1223 
1224  for (const auto & t : other_boundary.build_edge_list())
1225  boundary.add_edge(std::get<0>(t) + elem_delta,
1226  std::get<1>(t),
1227  std::get<2>(t));
1228 
1229  for (const auto & t : other_boundary.build_shellface_list())
1230  boundary.add_shellface(std::get<0>(t) + elem_delta,
1231  std::get<1>(t),
1232  std::get<2>(t));
1233 
1234  } // end if (other_mesh)
1235 
1236  // Finally, we need to "merge" the overlapping nodes
1237  // We do this by iterating over node_to_elems_map and updating
1238  // the elements so that they "point" to the nodes that came
1239  // from this mesh, rather than from other_mesh.
1240  // Then we iterate over node_to_node_map and delete the
1241  // duplicate nodes that came from other_mesh.
1242 
1243  {
1244  LOG_SCOPE("stitch_meshes node updates", "ReplicatedMesh");
1245 
1246  // Container to catch boundary IDs passed back from BoundaryInfo.
1247  std::vector<boundary_id_type> bc_ids;
1248 
1249  for (const auto & pr : node_to_elems_map)
1250  {
1251  dof_id_type target_node_id = pr.first;
1252  dof_id_type other_node_id = node_to_node_map[target_node_id];
1253  Node & target_node = this->node_ref(target_node_id);
1254 
1255  std::size_t n_elems = pr.second.size();
1256  for (std::size_t i=0; i<n_elems; i++)
1257  {
1258  dof_id_type elem_id = pr.second[i];
1259  Elem * el = this->elem_ptr(elem_id);
1260 
1261  // find the local node index that we want to update
1262  unsigned int local_node_index = el->local_node(other_node_id);
1263  libmesh_assert_not_equal_to(local_node_index, libMesh::invalid_uint);
1264 
1265  // We also need to copy over the nodeset info here,
1266  // because the node will get deleted below
1267  this->get_boundary_info().boundary_ids(el->node_ptr(local_node_index), bc_ids);
1268  el->set_node(local_node_index) = &target_node;
1269  this->get_boundary_info().add_node(&target_node, bc_ids);
1270  }
1271  }
1272  }
1273 
1274  {
1275  LOG_SCOPE("stitch_meshes node deletion", "ReplicatedMesh");
1276  for (const auto & pr : node_to_node_map)
1277  {
1278  // In the case that this==other_mesh, the two nodes might be the same (e.g. if
1279  // we're stitching a "sliver"), hence we need to skip node deletion in that case.
1280  if ((this == other_mesh) && (pr.second == pr.first))
1281  continue;
1282 
1283  dof_id_type this_node_id = pr.second;
1284  this->delete_node( this->node_ptr(this_node_id) );
1285  }
1286  }
1287 
1288  // If find_neighbors() wasn't called in prepare_for_use(), we need to
1289  // manually loop once more over all elements adjacent to the stitched boundary
1290  // and fix their lists of neighbors.
1291  // This is done according to the following steps:
1292  // 1. Loop over all copied elements adjacent to the boundary using node_to_elems_map (trying to avoid duplicates)
1293  // 2. Look at all their sides with a nullptr neighbor and update them using side_to_elem_map if necessary
1294  // 3. Update the corresponding side in side_to_elem_map as well
1295  if (skip_find_neighbors)
1296  {
1297  LOG_SCOPE("stitch_meshes neighbor fixes", "ReplicatedMesh");
1298 
1299  // Pull objects out of the loop to reduce heap operations
1300  std::unique_ptr<Elem> my_side, their_side;
1301 
1302  std::set<dof_id_type> fixed_elems;
1303  for (const auto & pr : node_to_elems_map)
1304  {
1305  std::size_t n_elems = pr.second.size();
1306  for (std::size_t i=0; i<n_elems; i++)
1307  {
1308  dof_id_type elem_id = pr.second[i];
1309  if (fixed_elems.find(elem_id) == fixed_elems.end())
1310  {
1311  Elem * el = this->elem_ptr(elem_id);
1312  fixed_elems.insert(elem_id);
1313  for (auto s : el->side_index_range())
1314  {
1315  if (el->neighbor_ptr(s) == nullptr)
1316  {
1317  key_type key = el->key(s);
1318  auto bounds = side_to_elem_map.equal_range(key);
1319 
1320  if (bounds.first != bounds.second)
1321  {
1322  // Get the side for this element
1323  el->side_ptr(my_side, s);
1324 
1325  // Look at all the entries with an equivalent key
1326  while (bounds.first != bounds.second)
1327  {
1328  // Get the potential element
1329  Elem * neighbor = bounds.first->second.first;
1330 
1331  // Get the side for the neighboring element
1332  const unsigned int ns = bounds.first->second.second;
1333  neighbor->side_ptr(their_side, ns);
1334  //libmesh_assert(my_side.get());
1335  //libmesh_assert(their_side.get());
1336 
1337  // If found a match with my side
1338  //
1339  // We need special tests here for 1D:
1340  // since parents and children have an equal
1341  // side (i.e. a node), we need to check
1342  // ns != ms, and we also check level() to
1343  // avoid setting our neighbor pointer to
1344  // any of our neighbor's descendants
1345  if ((*my_side == *their_side) &&
1346  (el->level() == neighbor->level()) &&
1347  ((el->dim() != 1) || (ns != s)))
1348  {
1349  // So share a side. Is this a mixed pair
1350  // of subactive and active/ancestor
1351  // elements?
1352  // If not, then we're neighbors.
1353  // If so, then the subactive's neighbor is
1354 
1355  if (el->subactive() ==
1356  neighbor->subactive())
1357  {
1358  // an element is only subactive if it has
1359  // been coarsened but not deleted
1360  el->set_neighbor (s,neighbor);
1361  neighbor->set_neighbor(ns,el);
1362  }
1363  else if (el->subactive())
1364  {
1365  el->set_neighbor(s,neighbor);
1366  }
1367  else if (neighbor->subactive())
1368  {
1369  neighbor->set_neighbor(ns,el);
1370  }
1371  // It's OK to invalidate the
1372  // bounds.first iterator here,
1373  // as we are immediately going
1374  // to break out of this while
1375  // loop. bounds.first will
1376  // therefore not be used for
1377  // anything else.
1378  side_to_elem_map.erase (bounds.first);
1379  break;
1380  }
1381 
1382  ++bounds.first;
1383  }
1384  }
1385  }
1386  }
1387  }
1388  }
1389  }
1390  }
1391 
1392  this->prepare_for_use( /*skip_renumber_nodes_and_elements= */ false, skip_find_neighbors);
1393 
1394  // After the stitching, we may want to clear boundary IDs from element
1395  // faces that are now internal to the mesh
1396  if (clear_stitched_boundary_ids)
1397  {
1398  LOG_SCOPE("stitch_meshes clear bcids", "ReplicatedMesh");
1399 
1400  // Container to catch boundary IDs passed back from BoundaryInfo.
1401  std::vector<boundary_id_type> bc_ids;
1402 
1403  for (auto & el : element_ptr_range())
1404  for (auto side_id : el->side_index_range())
1405  if (el->neighbor_ptr(side_id) != nullptr)
1406  {
1407  // Completely remove the side from the boundary_info object if it has either
1408  // this_mesh_boundary_id or other_mesh_boundary_id.
1409  this->get_boundary_info().boundary_ids (el, side_id, bc_ids);
1410 
1411  if (std::find(bc_ids.begin(), bc_ids.end(), this_mesh_boundary_id) != bc_ids.end() ||
1412  std::find(bc_ids.begin(), bc_ids.end(), other_mesh_boundary_id) != bc_ids.end())
1413  this->get_boundary_info().remove_side(el, side_id);
1414  }
1415 
1416  // Removing stitched-away boundary ids might have removed an id
1417  // *entirely*, so we need to recompute boundary id sets to check
1418  // for that.
1420  }
1421 }
1422 
1423 
1425 {
1426  return static_cast<dof_id_type>(std::distance (this->active_elements_begin(),
1427  this->active_elements_end()));
1428 }
1429 
1430 std::vector<dof_id_type>
1431 ReplicatedMesh::get_disconnected_subdomains(std::vector<subdomain_id_type> * subdomain_ids) const
1432 {
1433  // find number of disconnected subdomains
1434  std::vector<dof_id_type> representative_elem_ids;
1435 
1436  // use subdomain_ids as markers for all elements to indicate if the elements
1437  // have been visited. Note: here subdomain ID is unrelated with element
1438  // subdomain_id().
1439  std::vector<subdomain_id_type> subdomains;
1440  if (!subdomain_ids)
1441  subdomain_ids = &subdomains;
1442  subdomain_ids->clear();
1444 
1445  // counter of disconnected subdomains
1446  subdomain_id_type subdomain_counter = 0;
1447 
1448  // a stack for visiting elements, make its capacity sufficiently large to avoid
1449  // memory allocation and deallocation when the vector size changes
1450  std::vector<Elem *> list;
1451  list.reserve(n_elem());
1452 
1453  // counter of visited elements
1454  dof_id_type visited = 0;
1455  dof_id_type n_active = n_active_elem();
1456  do
1457  {
1458  for (const auto & elem : active_element_ptr_range())
1460  {
1461  list.push_back(elem);
1462  (*subdomain_ids)[elem->id()] = subdomain_counter;
1463  break;
1464  }
1465  // we should be able to find a seed here
1466  libmesh_assert(list.size() > 0);
1467 
1468  dof_id_type min_id = std::numeric_limits<dof_id_type>::max();
1469  while (list.size() > 0)
1470  {
1471  // pop up an element
1472  Elem * elem = list.back(); list.pop_back(); ++visited;
1473 
1474  min_id = std::min(elem->id(), min_id);
1475 
1476  for (auto s : elem->side_index_range())
1477  {
1478  Elem * neighbor = elem->neighbor_ptr(s);
1479  if (neighbor != nullptr && (*subdomain_ids)[neighbor->id()] == Elem::invalid_subdomain_id)
1480  {
1481  // neighbor must be active
1482  libmesh_assert(neighbor->active());
1483  list.push_back(neighbor);
1484  (*subdomain_ids)[neighbor->id()] = subdomain_counter;
1485  }
1486  }
1487  }
1488 
1489  representative_elem_ids.push_back(min_id);
1490  subdomain_counter++;
1491  }
1492  while (visited != n_active);
1493 
1494  return representative_elem_ids;
1495 }
1496 
1497 std::unordered_map<dof_id_type, std::vector<std::vector<Point>>>
1499 {
1500  if (mesh_dimension() != 2)
1501  libmesh_error_msg("Error: get_boundary_points only works for 2D now");
1502 
1503  // find number of disconnected subdomains
1504  // subdomains will hold the IDs of disconnected subdomains for all elements.
1505  std::vector<subdomain_id_type> subdomains;
1506  std::vector<dof_id_type> elem_ids = get_disconnected_subdomains(&subdomains);
1507 
1508  std::unordered_map<dof_id_type, std::vector<std::vector<Point>>> boundary_points;
1509 
1510  // get all boundary sides that are to be erased later during visiting
1511  // use a comparison functor to avoid run-time randomness due to pointers
1512  struct boundary_side_compare
1513  {
1514  bool operator()(const std::pair<const Elem *, unsigned int> & lhs,
1515  const std::pair<const Elem *, unsigned int> & rhs) const
1516  {
1517  if (lhs.first->id() < rhs.first->id())
1518  return true;
1519  else if (lhs.first->id() == rhs.first->id())
1520  {
1521  if (lhs.second < rhs.second)
1522  return true;
1523  }
1524  return false;
1525  }
1526  };
1527  std::set<std::pair<const Elem *, unsigned int>, boundary_side_compare> boundary_elements;
1528  for (const auto & elem : active_element_ptr_range())
1529  for (auto s : elem->side_index_range())
1530  if (elem->neighbor_ptr(s) == nullptr)
1531  boundary_elements.insert(std::pair<const Elem *, unsigned int>(elem, s));
1532 
1533  while (!boundary_elements.empty())
1534  {
1535  // get the first entry as the seed
1536  const Elem * eseed = boundary_elements.begin()->first;
1537  unsigned int sseed = boundary_elements.begin()->second;
1538 
1539  // get the subdomain ID that these boundary sides attached to
1540  subdomain_id_type subdomain_id = subdomains[eseed->id()];
1541 
1542  // start visiting the mesh to find all boundary nodes with the seed
1543  std::vector<Point> bpoints;
1544  const Elem * elem = eseed;
1545  unsigned int s = sseed;
1546  std::vector<unsigned int> local_side_nodes = elem->nodes_on_side(s);
1547  while (true)
1548  {
1549  std::pair<const Elem *, unsigned int> side(elem, s);
1550  libmesh_assert(boundary_elements.find(side) != boundary_elements.end());
1551  boundary_elements.erase(side);
1552 
1553  // push all nodes on the side except the node on the other end of the side (index 1)
1554  for (auto i : index_range(local_side_nodes))
1555  if (i != 1)
1556  bpoints.push_back(*static_cast<const Point *>(elem->node_ptr(local_side_nodes[i])));
1557 
1558  // use the last node to find next element and side
1559  const Node * node = elem->node_ptr(local_side_nodes[1]);
1560  std::set<const Elem *> neighbors;
1561  elem->find_point_neighbors(*node, neighbors);
1562 
1563  // if only one neighbor is found (itself), this node is a cornor node on boundary
1564  if (neighbors.size() != 1)
1565  neighbors.erase(elem);
1566 
1567  // find the connecting side
1568  bool found = false;
1569  for (const auto & neighbor : neighbors)
1570  {
1571  for (auto ss : neighbor->side_index_range())
1572  if (neighbor->neighbor_ptr(ss) == nullptr && !(elem == neighbor && s == ss))
1573  {
1574  local_side_nodes = neighbor->nodes_on_side(ss);
1575  // we expect the starting point of the side to be the same as the end of the previous side
1576  if (neighbor->node_ptr(local_side_nodes[0]) == node)
1577  {
1578  elem = neighbor;
1579  s = ss;
1580  found = true;
1581  break;
1582  }
1583  else if (neighbor->node_ptr(local_side_nodes[1]) == node)
1584  {
1585  elem = neighbor;
1586  s = ss;
1587  found = true;
1588  // flip nodes in local_side_nodes because the side is in an opposite direction
1589  auto temp(local_side_nodes);
1590  local_side_nodes[0] = temp[1];
1591  local_side_nodes[1] = temp[0];
1592  for (unsigned int i = 2; i < temp.size(); ++i)
1593  local_side_nodes[temp.size() + 1 - i] = temp[i];
1594  break;
1595  }
1596  }
1597  if (found)
1598  break;
1599  }
1600  if (!found)
1601  libmesh_error_msg("ERROR: mesh topology error on visiting boundary sides");
1602 
1603  // exit if we reach the starting point
1604  if (elem == eseed && s == sseed)
1605  break;
1606  }
1607  boundary_points[elem_ids[subdomain_id]].push_back(bpoints);
1608  }
1609 
1610  return boundary_points;
1611 }
1612 
1613 } // namespace libMesh
libMesh::ReplicatedMesh::update_parallel_id_counts
virtual void update_parallel_id_counts() override
Updates parallel caches so that methods like n_elem() accurately reflect changes on other processors.
Definition: replicated_mesh.C:619
libMesh::DofObject::valid_id
bool valid_id() const
Definition: dof_object.h:809
libMesh::Elem::find_point_neighbors
void find_point_neighbors(const Point &p, std::set< const Elem * > &neighbor_set) const
This function finds all active elements (including this one) which are in the same manifold as this e...
Definition: elem.C:560
libMesh::dof_id_type
uint8_t dof_id_type
Definition: id_types.h:67
libMesh::BoundaryInfo::boundary_ids
std::vector< boundary_id_type > boundary_ids(const Node *node) const
Definition: boundary_info.C:985
libMesh::BoundaryInfo
The BoundaryInfo class contains information relevant to boundary conditions including storing faces,...
Definition: boundary_info.h:57
libMesh::ReplicatedMesh::element_ptr_range
virtual SimpleRange< element_iterator > element_ptr_range() override
Definition: replicated_mesh.h:266
libMesh::invalid_uint
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value.
Definition: libmesh.h:249
libMesh::ReplicatedMesh::max_node_id
virtual dof_id_type max_node_id() const override
Definition: replicated_mesh.h:110
libMesh::MeshBase::default_mapping_type
ElemMappingType default_mapping_type() const
Returns the default master space to physical space mapping basis functions to be used on newly added ...
Definition: mesh_base.h:708
libMesh::ReplicatedMesh::_nodes
std::vector< Node * > _nodes
The vertices (spatial coordinates) of the mesh.
Definition: replicated_mesh.h:508
libMesh::ReplicatedMesh::_elements
std::vector< Elem * > _elements
The elements in the mesh.
Definition: replicated_mesh.h:513
libMesh::unique_id_type
uint8_t unique_id_type
Definition: id_types.h:86
libMesh::MeshBase::get_boundary_info
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition: mesh_base.h:132
libMesh::ReplicatedMesh::delete_elem
virtual void delete_elem(Elem *e) override
Removes element e from the mesh.
Definition: replicated_mesh.C:360
libMesh::Elem::level
unsigned int level() const
Definition: elem.h:2478
libMesh::ReplicatedMesh::parallel_max_unique_id
virtual unique_id_type parallel_max_unique_id() const override
Definition: replicated_mesh.C:629
libMesh::BoundaryInfo::add_node
void add_node(const Node *node, const boundary_id_type id)
Add Node node with boundary id id to the boundary information data structures.
Definition: boundary_info.C:636
libMesh::ReplicatedMesh::insert_elem
virtual Elem * insert_elem(Elem *e) override
Insert elem e to the element array, preserving its id and replacing/deleting any existing element wit...
Definition: replicated_mesh.C:328
libMesh::ReplicatedMesh::node_ptr
virtual const Node * node_ptr(const dof_id_type i) const override
Definition: replicated_mesh.C:182
libMesh::BoundaryInfo::build_shellface_list
void build_shellface_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &shellface_list, std::vector< boundary_id_type > &bc_id_list) const
Creates a list of element numbers, shellfaces, and boundary ids for those shellfaces.
Definition: boundary_info.C:2139
libMesh::VectorOfNodesAdaptor::kdtree_get_bbox
bool kdtree_get_bbox(BBOX &) const
Definition: replicated_mesh.C:79
libMesh::ReplicatedMesh::renumber_node
virtual void renumber_node(dof_id_type old_id, dof_id_type new_id) override
Changes the id of node old_id, both by changing node(old_id)->id() and by moving node(old_id) in the ...
Definition: replicated_mesh.C:578
libMesh::BoundaryInfo::edge_boundary_ids
std::vector< boundary_id_type > edge_boundary_ids(const Elem *const elem, const unsigned short int edge) const
Definition: boundary_info.C:1018
libMesh::ReplicatedMesh::delete_node
virtual void delete_node(Node *n) override
Removes the Node n from the mesh.
Definition: replicated_mesh.C:539
libMesh::DofObject::set_id
dof_id_type & set_id()
Definition: dof_object.h:776
libMesh::index_range
IntRange< std::size_t > index_range(const std::vector< T > &vec)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:106
libMesh::UnstructuredMesh::copy_nodes_and_elements
virtual void copy_nodes_and_elements(const UnstructuredMesh &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)
Deep copy of nodes and elements from another unstructured mesh class (used by subclass copy construct...
Definition: unstructured_mesh.C:61
libMesh
The libMesh namespace provides an interface to certain functionality in the library.
Definition: factoryfunction.C:55
libMesh::Elem::dim
virtual unsigned short dim() const =0
libMesh::Elem::set_mapping_data
void set_mapping_data(const unsigned char data)
Sets the value of the mapping data for the element.
Definition: elem.h:2548
end
IterBase * end
Also have a polymorphic pointer to the end object, this prevents iterating past the end.
Definition: variant_filter_iterator.h:343
libMesh::ReplicatedMesh::active_elements_begin
virtual element_iterator active_elements_begin() override
Active, local, and negation forms of the element iterators described above.
libMesh::TOLERANCE
static const Real TOLERANCE
Definition: libmesh_common.h:128
libMesh::DofObject::add_extra_integers
void add_extra_integers(const unsigned int n_integers)
Assigns a set of extra integers to this DofObject.
Definition: dof_object.C:503
libMesh::ParallelObject::comm
const Parallel::Communicator & comm() const
Definition: parallel_object.h:94
libMesh::Elem::set_neighbor
void set_neighbor(const unsigned int i, Elem *n)
Assigns n as the neighbor.
Definition: elem.h:2105
libMesh::ReplicatedMesh::add_elem
virtual Elem * add_elem(Elem *e) override
Add elem e to the end of the element array.
Definition: replicated_mesh.C:282
libMesh::MeshBase::set_subdomain_name_map
std::map< subdomain_id_type, std::string > & set_subdomain_name_map()
Definition: mesh_base.h:1631
libMesh::ReplicatedMesh::stitching_helper
void stitching_helper(const ReplicatedMesh *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)
Helper function for stitch_meshes and stitch_surfaces that does the mesh stitching.
Definition: replicated_mesh.C:852
libMesh::MeshBase::mesh_dimension
unsigned int mesh_dimension() const
Definition: mesh_base.C:135
libMesh::DofObject::set_unique_id
unique_id_type & set_unique_id()
Definition: dof_object.h:797
libMesh::DofObject::valid_unique_id
bool valid_unique_id() const
Definition: dof_object.h:817
libMesh::ReplicatedMesh::clear
virtual void clear() override
Clear all internal data.
Definition: replicated_mesh.C:593
libMesh::boundary_id_type
int8_t boundary_id_type
Definition: id_types.h:51
libMesh::DofObject::processor_id
processor_id_type processor_id() const
Definition: dof_object.h:829
libMesh::Elem::active
bool active() const
Definition: elem.h:2345
libMesh::BoundaryInfo::remove
void remove(const Node *node)
Removes the boundary conditions associated with node node, if any exist.
Definition: boundary_info.C:1358
dim
unsigned int dim
Definition: adaptivity_ex3.C:113
libMesh::BoundaryInfo::build_side_list
void build_side_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &side_list, std::vector< boundary_id_type > &bc_id_list) const
Creates a list of element numbers, sides, and ids for those sides.
Definition: boundary_info.C:1976
libMesh::ReplicatedMesh
The ReplicatedMesh class is derived from the MeshBase class, and is used to store identical copies of...
Definition: replicated_mesh.h:47
libMesh::Elem::invalid_subdomain_id
static const subdomain_id_type invalid_subdomain_id
A static integral constant representing an invalid subdomain id.
Definition: elem.h:244
libMesh::VectorOfNodesAdaptor::VectorOfNodesAdaptor
VectorOfNodesAdaptor(const std::vector< std::pair< Point, dof_id_type >> &nodes)
Definition: replicated_mesh.C:49
libMesh::ReplicatedMesh::active_element_ptr_range
virtual SimpleRange< element_iterator > active_element_ptr_range() override
Definition: replicated_mesh.h:273
libMesh::Elem::local_node
unsigned int local_node(const dof_id_type i) const
Definition: elem.h:1989
libMesh::libmesh_assert
libmesh_assert(ctx)
libMesh::Elem::subactive
bool subactive() const
Definition: elem.h:2363
libMesh::Elem::node_ref_range
SimpleRange< NodeRefIter > node_ref_range()
Returns a range with all nodes of an element, usable in range-based for loops.
Definition: elem.h:2152
libMesh::MeshBase::_elem_integer_names
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:1780
libMesh::ReplicatedMesh::max_elem_id
virtual dof_id_type max_elem_id() const override
Definition: replicated_mesh.h:124
libMesh::MeshBase::subdomain_ids
void subdomain_ids(std::set< subdomain_id_type > &ids) const
Constructs a list of all subdomain identifiers in the global mesh.
Definition: mesh_base.C:461
libMesh::Elem::set_mapping_type
void set_mapping_type(const ElemMappingType type)
Sets the value of the mapping type for the element.
Definition: elem.h:2532
libMesh::BoundaryInfo::build_edge_list
void build_edge_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &edge_list, std::vector< boundary_id_type > &bc_id_list) const
Creates a list of element numbers, edges, and boundary ids for those edges.
Definition: boundary_info.C:2091
libMesh::MeshBase::_next_unique_id
unique_id_type _next_unique_id
The next available unique id for assigning ids to DOF objects.
Definition: mesh_base.h:1727
libMesh::MeshBase::elem
virtual const Elem * elem(const dof_id_type i) const
Definition: mesh_base.h:554
libMesh::ReplicatedMesh::renumber_nodes_and_elements
virtual void renumber_nodes_and_elements() override
Remove nullptr elements from arrays.
Definition: replicated_mesh.C:642
libMesh::ReplicatedMesh::query_node_ptr
virtual const Node * query_node_ptr(const dof_id_type i) const override
Definition: replicated_mesh.C:206
libMesh::DofObject::invalid_id
static const dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition: dof_object.h:421
libMesh::Point
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:38
libMesh::Elem::side_ptr
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
libMesh::processor_id_type
uint8_t processor_id_type
Definition: id_types.h:104
libMesh::ReplicatedMesh::point
virtual const Point & point(const dof_id_type i) const override
Definition: replicated_mesh.C:174
libMesh::Node::build
static std::unique_ptr< Node > build(const Node &n)
Definition: node.h:314
libMesh::ReplicatedMesh::n_nodes
virtual dof_id_type n_nodes() const override
Definition: replicated_mesh.h:104
libMesh::Node
A Node is like a Point, but with more information.
Definition: node.h:52
libMesh::MeshTools::Generation::Private::idx
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.
Definition: mesh_generation.C:72
libMesh::MeshBase::node
virtual const Node & node(const dof_id_type i) const
Definition: mesh_base.h:471
libMesh::ReplicatedMesh::renumber_elem
virtual void renumber_elem(dof_id_type old_id, dof_id_type new_id) override
Changes the id of element old_id, both by changing elem(old_id)->id() and by moving elem(old_id) in t...
Definition: replicated_mesh.C:402
libMesh::BoundaryInfo::remove_side
void remove_side(const Elem *elem, const unsigned short int side)
Removes all boundary conditions associated with side side of element elem, if any exist.
Definition: boundary_info.C:1462
libMesh::as_range
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
libMesh::ReplicatedMesh::stitch_surfaces
void 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)
Similar to stitch_meshes, except that we stitch two adjacent surfaces within this mesh.
Definition: replicated_mesh.C:833
libMesh::DofObject::unique_id
unique_id_type unique_id() const
Definition: dof_object.h:784
libMesh::BoundaryInfo::regenerate_id_sets
void regenerate_id_sets()
Clears and regenerates the cached sets of ids.
Definition: boundary_info.C:159
libMesh::MeshBase::get_subdomain_name_map
const std::map< subdomain_id_type, std::string > & get_subdomain_name_map() const
Definition: mesh_base.h:1633
libMesh::MeshBase::_node_integer_names
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:1786
libMesh::Elem::set_node
virtual Node *& set_node(const unsigned int i)
Definition: elem.h:2059
libMesh::ReplicatedMesh::add_node
virtual Node * add_node(Node *n) override
Add Node n to the end of the vertex array.
Definition: replicated_mesh.C:472
libMesh::MeshBase::node_ref
virtual const Node & node_ref(const dof_id_type i) const
Definition: mesh_base.h:451
libMesh::UnstructuredMesh
The UnstructuredMesh class is derived from the MeshBase class.
Definition: unstructured_mesh.h:48
libMesh::MeshBase::_partitioner
std::unique_ptr< Partitioner > _partitioner
A partitioner to use at each prepare_for_use().
Definition: mesh_base.h:1721
distance
Real distance(const Point &p)
Definition: subdomains_ex3.C:50
libMesh::ReplicatedMesh::~ReplicatedMesh
virtual ~ReplicatedMesh()
Destructor.
Definition: replicated_mesh.C:99
swap
void swap(Iterator &lhs, Iterator &rhs)
swap, used to implement op=
Definition: variant_filter_iterator.h:478
libMesh::MeshBase::_skip_renumber_nodes_and_elements
bool _skip_renumber_nodes_and_elements
If this is true then renumbering will be kept to a minimum.
Definition: mesh_base.h:1746
libMesh::ReplicatedMesh::n_elem
virtual dof_id_type n_elem() const override
Definition: replicated_mesh.h:116
libMesh::VectorOfNodesAdaptor
Definition: replicated_mesh.C:43
libMesh::ReplicatedMesh::query_elem_ptr
virtual const Elem * query_elem_ptr(const dof_id_type i) const override
Definition: replicated_mesh.C:256
libMesh::ReplicatedMesh::fix_broken_node_and_element_numbering
virtual void fix_broken_node_and_element_numbering() override
There is no reason for a user to ever call this function.
Definition: replicated_mesh.C:798
libMesh::BoundaryInfo::build_node_list
void build_node_list(std::vector< dof_id_type > &node_id_list, std::vector< boundary_id_type > &bc_id_list) const
Creates a list of nodes and ids for those nodes.
Definition: boundary_info.C:1704
libMesh::Elem::hmin
virtual Real hmin() const
Definition: elem.C:359
libMesh::VectorOfNodesAdaptor::kdtree_get_point_count
size_t kdtree_get_point_count() const
Must return the number of data points.
Definition: replicated_mesh.C:56
libMesh::DofObject::id
dof_id_type id() const
Definition: dof_object.h:767
libMesh::BoundaryInfo::add_edge
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.
Definition: boundary_info.C:707
libMesh::ReplicatedMesh::elem_ptr
virtual const Elem * elem_ptr(const dof_id_type i) const override
Definition: replicated_mesh.C:232
libMesh::VectorOfNodesAdaptor::_nodes
const std::vector< std::pair< Point, dof_id_type > > _nodes
Definition: replicated_mesh.C:46
libMesh::Elem::side_index_range
IntRange< unsigned short > side_index_range() const
Definition: elem.h:2188
libMesh::Elem
This is the base class from which all geometric element types are derived.
Definition: elem.h:100
std::norm
MetaPhysicL::DualNumber< T, D > norm(const MetaPhysicL::DualNumber< T, D > &in)
libMesh::BoundaryInfo::add_shellface
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 ...
Definition: boundary_info.C:794
libMesh::MeshBase::default_mapping_data
unsigned char default_mapping_data() const
Returns any default data value used by the master space to physical space mapping.
Definition: mesh_base.h:724
libMesh::ReplicatedMesh::ReplicatedMesh
ReplicatedMesh(const Parallel::Communicator &comm_in, unsigned char dim=1)
Constructor.
Definition: replicated_mesh.C:85
libMesh::Elem::neighbor_ptr
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2085
libMesh::TestClass
Definition: id_types.h:33
libMesh::ReplicatedMesh::get_disconnected_subdomains
std::vector< dof_id_type > get_disconnected_subdomains(std::vector< subdomain_id_type > *subdomain_ids=nullptr) const
Return IDs of representative elements of all disconnected subdomains.
Definition: replicated_mesh.C:1431
libMesh::Real
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Definition: libmesh_common.h:121
libMesh::Elem::key
virtual dof_id_type key(const unsigned int s) const =0
libMesh::MeshBase::prepare_for_use
void prepare_for_use(const bool skip_renumber_nodes_and_elements=false, const bool skip_find_neighbors=false)
Prepare a newly ecreated (or read) mesh for use.
Definition: mesh_base.C:318
libMesh::MeshBase::clear
virtual void clear()
Deletes all the element and node data that is currently stored.
Definition: mesh_base.C:429
libMesh::ReplicatedMesh::stitch_meshes
void stitch_meshes(const ReplicatedMesh &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)
Stitch other_mesh to this mesh so that this mesh is the union of the two meshes.
Definition: replicated_mesh.C:812
libMesh::BoundaryInfo::invalid_id
static const boundary_id_type invalid_id
Number used for internal use.
Definition: boundary_info.h:899
libMesh::out
OStreamProxy out
libMesh::Elem::node_ptr
const Node * node_ptr(const unsigned int i) const
Definition: elem.h:2009
libMesh::BoundaryInfo::add_side
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.
Definition: boundary_info.C:886
libMesh::Elem::nodes_on_side
virtual std::vector< unsigned int > nodes_on_side(const unsigned int) const =0
libMesh::VectorOfNodesAdaptor::kdtree_get_pt
Real kdtree_get_pt(const size_t idx, int dim) const
Definition: replicated_mesh.C:63
libMesh::ReplicatedMesh::active_elements_end
virtual element_iterator active_elements_end() override
libMesh::ReplicatedMesh::add_point
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) override
functions for adding /deleting nodes elements.
Definition: replicated_mesh.C:417
libMesh::ReplicatedMesh::n_active_elem
virtual dof_id_type n_active_elem() const override
Definition: replicated_mesh.C:1424
libMesh::ReplicatedMesh::get_boundary_points
std::unordered_map< dof_id_type, std::vector< std::vector< Point > > > get_boundary_points() const
Return all points on boundary.
Definition: replicated_mesh.C:1498
libMesh::ReplicatedMesh::insert_node
virtual Node * insert_node(Node *n) override
Insert Node n into the Mesh at a location consistent with n->id(), allocating extra storage if necess...
Definition: replicated_mesh.C:494