libMesh
replicated_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/replicated_mesh.h"
22 
23 #include "libmesh/boundary_info.h"
24 #include "libmesh/elem.h"
25 #include "libmesh/libmesh_logging.h"
26 #include "libmesh/mesh_communication.h"
27 #include "libmesh/parallel_implementation.h"
28 #include "libmesh/partitioner.h"
29 #include "libmesh/point.h"
30 #include "libmesh/string_to_enum.h"
31 #include "libmesh/utility.h"
32 
33 // C++ includes
34 #include <unordered_map>
35 #include <unordered_set>
36 
37 namespace libMesh
38 {
39 
40 // ------------------------------------------------------------
41 // ReplicatedMesh class member functions
43  unsigned char d) :
44  UnstructuredMesh (comm_in,d),
45  _n_nodes(0), _n_elem(0)
46 {
47 #ifdef LIBMESH_ENABLE_UNIQUE_ID
48  // In serial we just need to reset the next unique id to zero
49  // here in the constructor.
50  _next_unique_id = 0;
51 #endif
52 
53  const std::string default_partitioner = "metis";
54  const std::string my_partitioner =
55  libMesh::command_line_value("--default-partitioner",
56  default_partitioner);
58  (Utility::string_to_enum<PartitionerType>(my_partitioner));
59 }
60 
61 
62 bool ReplicatedMesh::subclass_locally_equals(const MeshBase & other_mesh_base) const
63 {
64  const ReplicatedMesh * rep_mesh_ptr =
65  dynamic_cast<const ReplicatedMesh *>(&other_mesh_base);
66  if (!rep_mesh_ptr)
67  return false;
68  const ReplicatedMesh & other_mesh = *rep_mesh_ptr;
69 
70  if (_n_nodes != other_mesh._n_nodes ||
71  _n_elem != other_mesh._n_elem ||
72 #ifdef LIBMESH_ENABLE_UNIQUE_ID
73  _next_unique_id != other_mesh._next_unique_id ||
74 #endif
75  !this->nodes_and_elements_equal(other_mesh))
76  return false;
77 
78  return true;
79 }
80 
81 
83 {
84  this->ReplicatedMesh::clear(); // Free nodes and elements
85 }
86 
87 
88 // This might be specialized later, but right now it's just here to
89 // make sure the compiler doesn't give us a default (non-deep) copy
90 // constructor instead.
92  ReplicatedMesh(static_cast<const MeshBase&>(other_mesh))
93 {
94 #ifdef LIBMESH_ENABLE_UNIQUE_ID
95  this->_next_unique_id = other_mesh._next_unique_id;
96 #endif
97 }
98 
99 
101  UnstructuredMesh (other_mesh),
102  _n_nodes(0), _n_elem(0) // copy_* will increment this
103 {
104  // Just copy, skipping preparation
105  this->copy_nodes_and_elements(other_mesh, true, 0, 0, 0, nullptr, true);
106 
107  this->allow_find_neighbors(other_mesh.allow_find_neighbors());
109  this->allow_renumbering(other_mesh.allow_renumbering());
111  this->skip_partitioning(other_mesh.skip_partitioning());
112 
113  this->copy_constraint_rows(other_mesh);
114 
115  this->_preparation = other_mesh.preparation();
116 
117  auto & this_boundary_info = this->get_boundary_info();
118  const auto & other_boundary_info = other_mesh.get_boundary_info();
119 
120  this_boundary_info = other_boundary_info;
121 
122  this->set_subdomain_name_map() = other_mesh.get_subdomain_name_map();
123 
124  // If other_mesh is distributed, then we've got parts of it on each
125  // processor but we're not replicated yet; fix that.
126  if (!other_mesh.is_serial())
127  MeshCommunication().allgather(*this);
128 }
129 
131 {
132  LOG_SCOPE("operator=(&&)", "ReplicatedMesh");
133 
134  // Move assign as an UnstructuredMesh
135  this->UnstructuredMesh::operator=(std::move(other_mesh));
136 
137  // Nodes and elements belong to ReplicatedMesh and have to be
138  // moved before we can move arbitrary GhostingFunctor, Partitioner,
139  // etc. subclasses.
140  this->move_nodes_and_elements(std::move(other_mesh));
141 
142  // Handle those remaining moves.
143  this->post_dofobject_moves(std::move(other_mesh));
144 
145  return *this;
146 }
147 
149 {
150  *this = std::move(cast_ref<ReplicatedMesh&>(other_mesh));
151 
152  return *this;
153 }
154 
156 {
157  ReplicatedMesh & other_mesh = cast_ref<ReplicatedMesh&>(other_meshbase);
158 
159  this->_nodes = std::move(other_mesh._nodes);
160  this->_n_nodes = other_mesh.n_nodes();
161 
162  this->_elements = std::move(other_mesh._elements);
163  this->_n_elem = other_mesh.n_elem();
164 }
165 
166 
167 const Point & ReplicatedMesh::point (const dof_id_type i) const
168 {
169  return this->node_ref(i);
170 }
171 
172 
173 
174 
176 {
177  libmesh_assert_less (i, this->max_node_id());
178  libmesh_assert(_nodes[i]);
179  libmesh_assert_equal_to (_nodes[i]->id(), i); // This will change soon
180 
181  return _nodes[i];
182 }
183 
184 
185 
186 
188 {
189  libmesh_assert_less (i, this->max_node_id());
190  libmesh_assert(_nodes[i]);
191  libmesh_assert_equal_to (_nodes[i]->id(), i); // This will change soon
192 
193  return _nodes[i];
194 }
195 
196 
197 
198 
200 {
201  if (i >= this->max_node_id())
202  return nullptr;
203  libmesh_assert (_nodes[i] == nullptr ||
204  _nodes[i]->id() == i); // This will change soon
205 
206  return _nodes[i];
207 }
208 
209 
210 
211 
213 {
214  if (i >= this->max_node_id())
215  return nullptr;
216  libmesh_assert (_nodes[i] == nullptr ||
217  _nodes[i]->id() == i); // This will change soon
218 
219  return _nodes[i];
220 }
221 
222 
223 
224 
226 {
227  libmesh_assert_less (i, this->max_elem_id());
229  libmesh_assert_equal_to (_elements[i]->id(), i); // This will change soon
230 
231  return _elements[i];
232 }
233 
234 
235 
236 
238 {
239  libmesh_assert_less (i, this->max_elem_id());
241  libmesh_assert_equal_to (_elements[i]->id(), i); // This will change soon
242 
243  return _elements[i];
244 }
245 
246 
247 
248 
250 {
251  if (i >= this->max_elem_id())
252  return nullptr;
253  libmesh_assert (_elements[i] == nullptr ||
254  _elements[i]->id() == i); // This will change soon
255 
256  return _elements[i];
257 }
258 
259 
260 
261 
263 {
264  if (i >= this->max_elem_id())
265  return nullptr;
266  libmesh_assert (_elements[i] == nullptr ||
267  _elements[i]->id() == i); // This will change soon
268 
269  return _elements[i];
270 }
271 
272 
273 
274 
276 {
277  libmesh_assert(e);
278 
279  // We no longer merely append elements with ReplicatedMesh
280 
281  // If the user requests a valid id that doesn't correspond to an
282  // existing element, let's give them that id, resizing the elements
283  // container if necessary.
284  if (!e->valid_id())
285  e->set_id (cast_int<dof_id_type>(_elements.size()));
286 
287 #ifdef LIBMESH_ENABLE_UNIQUE_ID
288  if (!e->valid_unique_id())
290  else
291  _next_unique_id = std::max(_next_unique_id, e->unique_id()+1);
292 #endif
293 
294  const dof_id_type id = e->id();
295 
296  if (id < _elements.size())
297  {
298  // This should *almost* never happen, but we rely on it when
299  // using allgather to replicate a not-yet-actually-replicated
300  // ReplicatedMesh under construction in parallel.
301  if (e == _elements[id])
302  return e;
303 
304  // Overwriting existing elements is still probably a mistake.
306  }
307  else
308  {
309  _elements.resize(id+1, nullptr);
310  }
311 
312  ++_n_elem;
313  _elements[id] = e;
314 
315  // We actually added a new element. Some of our caches might still
316  // be valid, but we should clear the ones which definitely are not.
317  this->clear_point_locator();
318  this->clear_stored_ranges();
319 
320  // Make sure any new element is given space for any extra integers
321  // we've requested
324 
325  // And set mapping type and data on any new element
328 
329  return e;
330 }
331 
332 Elem * ReplicatedMesh::add_elem (std::unique_ptr<Elem> e)
333 {
334  // The mesh now takes ownership of the Elem. Eventually the guts of
335  // add_elem() will get moved to a private helper function, and
336  // calling add_elem() directly will be deprecated.
337  return add_elem(e.release());
338 }
339 
340 
341 
343 {
344 #ifdef LIBMESH_ENABLE_UNIQUE_ID
345  if (!e->valid_unique_id())
347  else
348  _next_unique_id = std::max(_next_unique_id, e->unique_id()+1);
349 #endif
350 
351  dof_id_type eid = e->id();
352  libmesh_assert_less (eid, _elements.size());
353  Elem * oldelem = _elements[eid];
354 
355  if (oldelem)
356  {
357  libmesh_assert_equal_to (oldelem->id(), eid);
358  this->delete_elem(oldelem);
359  }
360 
361  ++_n_elem;
362  _elements[eid] = e;
363 
364  // We actually added a new element. Some of our caches might still
365  // be valid, but we should clear the ones which definitely are not.
366  this->clear_point_locator();
367  this->clear_stored_ranges();
368 
369  // Make sure any new element is given space for any extra integers
370  // we've requested
373 
374  // And set mapping type and data on any new element
377 
378  return e;
379 }
380 
381 Elem * ReplicatedMesh::insert_elem (std::unique_ptr<Elem> e)
382 {
383  // The mesh now takes ownership of the Elem. Eventually the guts of
384  // insert_elem(Elem*) will get moved to a private helper function, and
385  // calling insert_elem(Elem*) directly will be deprecated.
386  return insert_elem(e.release());
387 }
388 
389 
390 
392 {
393  libmesh_assert(e);
394 
395  // Initialize an iterator to eventually point to the element we want to delete
396  std::vector<Elem *>::iterator pos = _elements.end();
397 
398  // In many cases, e->id() gives us a clue as to where e
399  // is located in the _elements vector. Try that first
400  // before trying the O(n_elem) search.
401  libmesh_assert_less (e->id(), _elements.size());
402 
403  if (_elements[e->id()] == e)
404  {
405  // We found it!
406  pos = _elements.begin();
407  std::advance(pos, e->id());
408  }
409 
410  else
411  {
412  // This search is O(n_elem)
413  pos = std::find (_elements.begin(),
414  _elements.end(),
415  e);
416  }
417 
418  // Huh? Element not in the vector?
419  libmesh_assert (pos != _elements.end());
420 
421  // Remove the element from the BoundaryInfo object
422  this->get_boundary_info().remove(e);
423 
424  // delete the element
425  --_n_elem;
426  delete e;
427 
428  // explicitly zero the pointer
429  *pos = nullptr;
430 
431  // Some of our caches might still be valid, but we should clear the
432  // ones which definitely are not.
433  this->clear_point_locator();
434  this->clear_stored_ranges();
435 }
436 
437 
438 
440  const dof_id_type new_id)
441 {
442  // This could be a no-op
443  if (old_id == new_id)
444  return;
445 
446  // This doesn't get used in serial yet
447  Elem * el = _elements[old_id];
448  libmesh_assert (el);
449 
450  if (new_id >= _elements.size())
451  _elements.resize(new_id+1, nullptr);
452 
453  el->set_id(new_id);
454  libmesh_assert (!_elements[new_id]);
455  _elements[new_id] = el;
456  _elements[old_id] = nullptr;
457 
458  // Should we delete any caches here? Our point locator indexes by
459  // element pointer and should be fine with an id change. Our stored
460  // ranges are no longer sorted, which is *probably* fine, but let's
461  // just be safe.
462  this->clear_stored_ranges();
463 }
464 
465 
466 
468  const dof_id_type id,
469  const processor_id_type proc_id)
470 {
471  Node * n = nullptr;
472 
473  // If the user requests a valid id, either
474  // provide the existing node or resize the container
475  // to fit the new node.
476  if (id != DofObject::invalid_id)
477  if (id < _nodes.size())
478  n = _nodes[id];
479  else
480  _nodes.resize(id+1);
481  else
482  _nodes.push_back (static_cast<Node *>(nullptr));
483 
484  // if the node already exists, then assign new (x,y,z) values
485  if (n)
486  *n = p;
487  // otherwise build a new node, put it in the right spot, and return
488  // a valid pointer.
489  else
490  {
491  n = Node::build(p, (id == DofObject::invalid_id) ?
492  cast_int<dof_id_type>(_nodes.size()-1) : id).release();
493  n->processor_id() = proc_id;
494 
497 
498 #ifdef LIBMESH_ENABLE_UNIQUE_ID
499  if (!n->valid_unique_id())
501  else
502  _next_unique_id = std::max(_next_unique_id, n->unique_id()+1);
503 #endif
504 
505  ++_n_nodes;
506  if (id == DofObject::invalid_id)
507  _nodes.back() = n;
508  else
509  _nodes[id] = n;
510  }
511 
512  // better not pass back a nullptr.
513  libmesh_assert (n);
514 
515  return n;
516 }
517 
518 
519 
521 {
522  libmesh_assert(n);
523 
524  // If the user requests a valid id, either set the existing
525  // container entry or resize the container to fit the new node.
526  if (n->valid_id())
527  {
528  const dof_id_type id = n->id();
529  if (id < _nodes.size())
530  libmesh_assert(!_nodes[id]);
531  else
532  _nodes.resize(id+1); // default nullptr
533 
534  _nodes[id] = n;
535  }
536  else
537  {
538  n->set_id (cast_int<dof_id_type>(_nodes.size()));
539  _nodes.push_back(n);
540  }
541 
542  ++_n_nodes;
543 
544 #ifdef LIBMESH_ENABLE_UNIQUE_ID
545  if (!n->valid_unique_id())
547  else
548  _next_unique_id = std::max(_next_unique_id, n->unique_id()+1);
549 #endif
550 
553 
554  return n;
555 }
556 
557 Node * ReplicatedMesh::add_node (std::unique_ptr<Node> n)
558 {
559  // The mesh now takes ownership of the Node. Eventually the guts of
560  // add_node() will get moved to a private helper function, and
561  // calling add_node() directly will be deprecated.
562  return add_node(n.release());
563 }
564 
566 {
567  libmesh_assert(n);
568  libmesh_assert_less (n->id(), _nodes.size());
569 
570  // Initialize an iterator to eventually point to the element we want
571  // to delete
572  std::vector<Node *>::iterator pos;
573 
574  // In many cases, e->id() gives us a clue as to where e
575  // is located in the _elements vector. Try that first
576  // before trying the O(n_elem) search.
577  if (_nodes[n->id()] == n)
578  {
579  pos = _nodes.begin();
580  std::advance(pos, n->id());
581  }
582  else
583  {
584  pos = std::find (_nodes.begin(),
585  _nodes.end(),
586  n);
587  }
588 
589  // Huh? Node not in the vector?
590  libmesh_assert (pos != _nodes.end());
591 
592  // Delete the node from the BoundaryInfo object
593  this->get_boundary_info().remove(n);
594  _constraint_rows.erase(n);
595 
596  // delete the node
597  --_n_nodes;
598  delete n;
599 
600  // explicitly zero the pointer
601  *pos = nullptr;
602 }
603 
604 
605 
607  const dof_id_type new_id)
608 {
609  // This could be a no-op
610  if (old_id == new_id)
611  return;
612 
613  // This doesn't get used in serial yet
614  Node * nd = _nodes[old_id];
615  libmesh_assert (nd);
616 
617  if (new_id >= _nodes.size())
618  _nodes.resize(new_id+1, nullptr);
619 
620  nd->set_id(new_id);
621  libmesh_assert (!_nodes[new_id]);
622  _nodes[new_id] = nd;
623  _nodes[old_id] = nullptr;
624 }
625 
626 
627 
629 {
630  // Call parent clear function
631  MeshBase::clear();
632 
633  // Clear our elements and nodes
634  // There is no need to remove them from
635  // the BoundaryInfo data structure since we
636  // already cleared it.
638 
639  for (auto & node : _nodes)
640  delete node;
641 
642  _n_nodes = 0;
643  _nodes.clear();
644 }
645 
646 
647 
649 {
650  for (auto & elem : _elements)
651  delete elem;
652 
653  _n_elem = 0;
654  _elements.clear();
655 }
656 
657 
658 
660 {
661 #ifdef LIBMESH_ENABLE_UNIQUE_ID
663 #endif
664 
665  this->_preparation.has_synched_id_counts = true;
666 }
667 
668 
669 
670 #ifdef LIBMESH_ENABLE_UNIQUE_ID
672 {
673  // This function must be run on all processors at once
674  parallel_object_only();
675 
676  unique_id_type max_local = _next_unique_id;
677  this->comm().max(max_local);
678  return max_local;
679 }
680 
681 
682 
684 {
685  _next_unique_id = id;
686 }
687 #endif
688 
689 
690 
692 {
693  LOG_SCOPE("renumber_nodes_and_elem()", "Mesh");
694 
695  // node and element id counters
696  dof_id_type next_free_elem = 0;
697  dof_id_type next_free_node = 0;
698 
699  // Will hold the set of nodes that are currently connected to elements
700  std::unordered_set<Node *> connected_nodes;
701 
702  // Loop over the elements. Note that there may
703  // be nullptrs in the _elements vector from the coarsening
704  // process. Pack the elements in to a contiguous array
705  // and then trim any excess.
706  {
707  std::vector<Elem *>::iterator in = _elements.begin();
708  std::vector<Elem *>::iterator out_iter = _elements.begin();
709  const std::vector<Elem *>::iterator end = _elements.end();
710 
711  for (; in != end; ++in)
712  if (*in != nullptr)
713  {
714  Elem * el = *in;
715 
716  *out_iter = *in;
717  ++out_iter;
718 
719  // Increment the element counter
720  el->set_id (next_free_elem++);
721 
723  {
724  // Add this elements nodes to the connected list
725  for (auto & n : el->node_ref_range())
726  connected_nodes.insert(&n);
727  }
728  else // We DO want node renumbering
729  {
730  // Loop over this element's nodes. Number them,
731  // if they have not been numbered already. Also,
732  // position them in the _nodes vector so that they
733  // are packed contiguously from the beginning.
734  for (auto & n : el->node_ref_range())
735  if (n.id() == next_free_node) // don't need to process
736  next_free_node++; // [(src == dst) below]
737 
738  else if (n.id() > next_free_node) // need to process
739  {
740  // The source and destination indices
741  // for this node
742  const dof_id_type src_idx = n.id();
743  const dof_id_type dst_idx = next_free_node++;
744 
745  // ensure we want to swap a valid nodes
746  libmesh_assert(_nodes[src_idx]);
747 
748  // Swap the source and destination nodes
749  std::swap(_nodes[src_idx],
750  _nodes[dst_idx] );
751 
752  // Set proper indices where that makes sense
753  if (_nodes[src_idx] != nullptr)
754  _nodes[src_idx]->set_id (src_idx);
755  _nodes[dst_idx]->set_id (dst_idx);
756  }
757  }
758  }
759 
760  // Erase any additional storage. These elements have been
761  // copied into nullptr voids by the procedure above, and are
762  // thus repeated and unnecessary.
763  _elements.erase (out_iter, end);
764  }
765 
766 
768  {
769  // Loop over the nodes. Note that there may
770  // be nullptrs in the _nodes vector from the coarsening
771  // process. Pack the nodes in to a contiguous array
772  // and then trim any excess.
773 
774  std::vector<Node *>::iterator in = _nodes.begin();
775  std::vector<Node *>::iterator out_iter = _nodes.begin();
776  const std::vector<Node *>::iterator end = _nodes.end();
777 
778  for (; in != end; ++in)
779  if (*in != nullptr)
780  {
781  // This is a reference so that if we change the pointer it will change in the vector
782  Node * & nd = *in;
783 
784  // If this node is still connected to an elem, put it in the list
785  if (connected_nodes.count(nd))
786  {
787  *out_iter = nd;
788  ++out_iter;
789 
790  // Increment the node counter
791  nd->set_id (next_free_node++);
792  }
793  else // This node is orphaned, delete it!
794  {
795  this->get_boundary_info().remove (nd);
796  _constraint_rows.erase(nd);
797 
798  // delete the node
799  --_n_nodes;
800  delete nd;
801  nd = nullptr;
802  }
803  }
804 
805  // Erase any additional storage. Whatever was
806  _nodes.erase (out_iter, end);
807  }
808  else // We really DO want node renumbering
809  {
810  // Any nodes in the vector >= _nodes[next_free_node]
811  // are not connected to any elements and may be deleted
812  // if desired.
813 
814  // Now, delete the unused nodes
815  {
816  std::vector<Node *>::iterator nd = _nodes.begin();
817  const std::vector<Node *>::iterator end = _nodes.end();
818 
819  std::advance (nd, next_free_node);
820 
821  for (auto & node : as_range(nd, end))
822  {
823  // Mesh modification code might have already deleted some
824  // nodes
825  if (node == nullptr)
826  continue;
827 
828  // remove any boundary information associated with
829  // this node
830  this->get_boundary_info().remove (node);
831  _constraint_rows.erase(node);
832 
833  // delete the node
834  --_n_nodes;
835  delete node;
836  node = nullptr;
837  }
838 
839  _nodes.erase (nd, end);
840  }
841  }
842 
844 
845  libmesh_assert_equal_to (next_free_elem, _elements.size());
846  libmesh_assert_equal_to (next_free_node, _nodes.size());
847 
849 }
850 
851 
852 
854 {
855  // Nodes first
856  for (auto n : index_range(_nodes))
857  if (this->_nodes[n] != nullptr)
858  this->_nodes[n]->set_id() = cast_int<dof_id_type>(n);
859 
860  // Elements next
861  for (auto e : index_range(_elements))
862  if (this->_elements[e] != nullptr)
863  this->_elements[e]->set_id() = cast_int<dof_id_type>(e);
864 }
865 
866 
868 {
869  return static_cast<dof_id_type>(std::distance (this->active_elements_begin(),
870  this->active_elements_end()));
871 }
872 
873 std::vector<dof_id_type>
874 ReplicatedMesh::get_disconnected_subdomains(std::vector<subdomain_id_type> * subdomain_ids) const
875 {
876  // find number of disconnected subdomains
877  std::vector<dof_id_type> representative_elem_ids;
878 
879  // use subdomain_ids as markers for all elements to indicate if the elements
880  // have been visited. Note: here subdomain ID is unrelated with element
881  // subdomain_id().
882  std::vector<subdomain_id_type> subdomains;
883  if (!subdomain_ids)
884  subdomain_ids = &subdomains;
885  subdomain_ids->clear();
887 
888  // counter of disconnected subdomains
889  subdomain_id_type subdomain_counter = 0;
890 
891  // a stack for visiting elements, make its capacity sufficiently large to avoid
892  // memory allocation and deallocation when the vector size changes
893  std::vector<const Elem *> list;
894  list.reserve(n_elem());
895 
896  // counter of visited elements
897  dof_id_type visited = 0;
898  dof_id_type n_active = n_active_elem();
899  do
900  {
901  for (const auto & elem : active_element_ptr_range())
902  if ((*subdomain_ids)[elem->id()] == Elem::invalid_subdomain_id)
903  {
904  list.push_back(elem);
905  (*subdomain_ids)[elem->id()] = subdomain_counter;
906  break;
907  }
908  // we should be able to find a seed here
909  libmesh_assert(list.size() > 0);
910 
911  dof_id_type min_id = std::numeric_limits<dof_id_type>::max();
912  while (list.size() > 0)
913  {
914  // pop up an element
915  const Elem * elem = list.back(); list.pop_back(); ++visited;
916 
917  min_id = std::min(elem->id(), min_id);
918 
919  for (auto s : elem->side_index_range())
920  {
921  const Elem * neighbor = elem->neighbor_ptr(s);
922  if (neighbor != nullptr && (*subdomain_ids)[neighbor->id()] == Elem::invalid_subdomain_id)
923  {
924  // neighbor must be active
925  libmesh_assert(neighbor->active());
926  list.push_back(neighbor);
927  (*subdomain_ids)[neighbor->id()] = subdomain_counter;
928  }
929  }
930  }
931 
932  representative_elem_ids.push_back(min_id);
933  subdomain_counter++;
934  }
935  while (visited != n_active);
936 
937  return representative_elem_ids;
938 }
939 
940 std::unordered_map<dof_id_type, std::vector<std::vector<Point>>>
942 {
943  libmesh_error_msg_if(mesh_dimension() != 2,
944  "Error: get_boundary_points only works for 2D now");
945 
946  // find number of disconnected subdomains
947  // subdomains will hold the IDs of disconnected subdomains for all elements.
948  std::vector<subdomain_id_type> subdomains;
949  std::vector<dof_id_type> elem_ids = get_disconnected_subdomains(&subdomains);
950 
951  std::unordered_map<dof_id_type, std::vector<std::vector<Point>>> boundary_points;
952 
953  // get all boundary sides that are to be erased later during visiting
954  // use a comparison functor to avoid run-time randomness due to pointers
955  struct boundary_side_compare
956  {
957  bool operator()(const std::pair<const Elem *, unsigned int> & lhs,
958  const std::pair<const Elem *, unsigned int> & rhs) const
959  {
960  if (lhs.first->id() < rhs.first->id())
961  return true;
962  else if (lhs.first->id() == rhs.first->id())
963  {
964  if (lhs.second < rhs.second)
965  return true;
966  }
967  return false;
968  }
969  };
970  std::set<std::pair<const Elem *, unsigned int>, boundary_side_compare> boundary_elements;
971  for (const auto & elem : active_element_ptr_range())
972  for (auto s : elem->side_index_range())
973  if (elem->neighbor_ptr(s) == nullptr)
974  boundary_elements.insert(std::pair<const Elem *, unsigned int>(elem, s));
975 
976  while (!boundary_elements.empty())
977  {
978  // get the first entry as the seed
979  const Elem * eseed = boundary_elements.begin()->first;
980  unsigned int sseed = boundary_elements.begin()->second;
981 
982  // get the subdomain ID that these boundary sides attached to
983  subdomain_id_type subdomain_id = subdomains[eseed->id()];
984 
985  // start visiting the mesh to find all boundary nodes with the seed
986  std::vector<Point> bpoints;
987  const Elem * elem = eseed;
988  unsigned int s = sseed;
989  std::vector<unsigned int> local_side_nodes = elem->nodes_on_side(s);
990  while (true)
991  {
992  std::pair<const Elem *, unsigned int> side(elem, s);
993  libmesh_assert(boundary_elements.count(side));
994  boundary_elements.erase(side);
995 
996  // push all nodes on the side except the node on the other end of the side (index 1)
997  for (auto i : index_range(local_side_nodes))
998  if (i != 1)
999  bpoints.push_back(*static_cast<const Point *>(elem->node_ptr(local_side_nodes[i])));
1000 
1001  // use the last node to find next element and side
1002  const Node * node = elem->node_ptr(local_side_nodes[1]);
1003  std::set<const Elem *> neighbors;
1004  elem->find_point_neighbors(*node, neighbors);
1005 
1006  // if only one neighbor is found (itself), this node is a cornor node on boundary
1007  if (neighbors.size() != 1)
1008  neighbors.erase(elem);
1009 
1010  // find the connecting side
1011  bool found = false;
1012  for (const auto & neighbor : neighbors)
1013  {
1014  for (auto ss : neighbor->side_index_range())
1015  if (neighbor->neighbor_ptr(ss) == nullptr && !(elem == neighbor && s == ss))
1016  {
1017  local_side_nodes = neighbor->nodes_on_side(ss);
1018  // we expect the starting point of the side to be the same as the end of the previous side
1019  if (neighbor->node_ptr(local_side_nodes[0]) == node)
1020  {
1021  elem = neighbor;
1022  s = ss;
1023  found = true;
1024  break;
1025  }
1026  else if (neighbor->node_ptr(local_side_nodes[1]) == node)
1027  {
1028  elem = neighbor;
1029  s = ss;
1030  found = true;
1031  // flip nodes in local_side_nodes because the side is in an opposite direction
1032  auto temp(local_side_nodes);
1033  local_side_nodes[0] = temp[1];
1034  local_side_nodes[1] = temp[0];
1035  for (unsigned int i = 2; i < temp.size(); ++i)
1036  local_side_nodes[temp.size() + 1 - i] = temp[i];
1037  break;
1038  }
1039  }
1040  if (found)
1041  break;
1042  }
1043 
1044  libmesh_error_msg_if(!found, "ERROR: mesh topology error on visiting boundary sides");
1045 
1046  // exit if we reach the starting point
1047  if (elem == eseed && s == sseed)
1048  break;
1049  }
1050  boundary_points[elem_ids[subdomain_id]].push_back(bpoints);
1051  }
1052 
1053  return boundary_points;
1054 }
1055 
1056 } // namespace libMesh
virtual dof_id_type n_nodes() const override final
UnstructuredMesh & operator=(const UnstructuredMesh &)=delete
Copy assignment is not allowed.
virtual dof_id_type max_node_id() const override final
virtual bool subclass_locally_equals(const MeshBase &other_mesh) const override
Shim to allow operator == (&) to behave like a virtual function without having to be one...
The ReplicatedMesh class is derived from the MeshBase class, and is used to store identical copies of...
bool _skip_renumber_nodes_and_elements
If this is true then renumbering will be kept to a minimum.
Definition: mesh_base.h:2237
A Node is like a Point, but with more information.
Definition: node.h:52
virtual dof_id_type n_elem() const override final
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
virtual const Node * query_node_ptr(const dof_id_type i) const override final
void set_mapping_type(const ElemMappingType type)
Sets the value of the mapping type for the element.
Definition: elem.h:3142
std::vector< Elem * > _elements
The elements in the mesh.
void allow_renumbering(bool allow)
If false is passed in then this mesh will no longer be renumbered when being prepared for use...
Definition: mesh_base.h:1345
virtual void renumber_node(dof_id_type old_id, dof_id_type new_id) override final
Changes the id of node old_id, both by changing node(old_id)->id() and by moving node(old_id) in the ...
IntRange< unsigned short > side_index_range() const
Definition: elem.h:2724
void skip_partitioning(bool skip)
If true is passed in then nothing on this mesh will be (re)partitioned.
Definition: mesh_base.h:1419
virtual ~ReplicatedMesh()
Destructor.
virtual void set_next_unique_id(unique_id_type id) override final
Sets the next available unique id to be used.
bool allow_detect_interior_parents() const
Definition: mesh_base.h:1360
void remove(const Node *node)
Removes the boundary conditions associated with node node, if any exist.
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.
virtual dof_id_type max_elem_id() const override final
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
constraint_rows_type _constraint_rows
Definition: mesh_base.h:2409
unique_id_type unique_id() const
Definition: dof_object.h:835
void copy_constraint_rows(const MeshBase &other_mesh)
Copy the constraints from the other mesh to this mesh.
Definition: mesh_base.C:2450
const Parallel::Communicator & comm() const
The libMesh namespace provides an interface to certain functionality in the library.
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::unordered_map< dof_id_type, std::vector< std::vector< Point > > > get_boundary_points() const
Return all points on boundary.
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
virtual void delete_elem(Elem *e) override final
Removes element e from the mesh.
virtual Node * add_node(Node *n) override final
Add Node n to the end of the vertex array.
virtual MeshBase & assign(MeshBase &&other_mesh) override
Shim to call the move assignment operator for this class.
Real distance(const Point &p)
Preparation preparation() const
Definition: mesh_base.h:213
void clear_stored_ranges()
Clears stored ranges, to indicate that the mesh has changed and they should be regenerated when next ...
Definition: mesh_base.C:1943
uint8_t processor_id_type
Definition: id_types.h:104
This is the MeshBase class.
Definition: mesh_base.h:80
dof_id_type & set_id()
Definition: dof_object.h:827
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:931
virtual const Elem * query_elem_ptr(const dof_id_type i) const override final
virtual unique_id_type parallel_max_unique_id() const override final
virtual void delete_node(Node *n) override final
Removes the Node n from the mesh.
unique_id_type _next_unique_id
The next available unique id for assigning ids to DOF objects.
Definition: mesh_base.h:2212
virtual void clear() override
Free all new memory associated with the object, but restore its original state, with the mesh pointer...
Definition: dof_map.C:871
virtual const Elem * elem_ptr(const dof_id_type i) const override final
void allow_remote_element_removal(bool allow)
If false is passed in then this mesh will no longer have remote elements deleted when being prepared ...
Definition: mesh_base.h:1368
virtual bool is_serial() const
Definition: mesh_base.h:347
friend class MeshCommunication
Make the MeshCommunication class a friend so that it can directly broadcast *_integer_names.
Definition: mesh_base.h:2439
virtual void renumber_elem(dof_id_type old_id, dof_id_type new_id) override final
Changes the id of element old_id, both by changing elem(old_id)->id() and by moving elem(old_id) in t...
T command_line_value(const std::string &, T)
Definition: libmesh.C:971
static std::unique_ptr< Partitioner > build(const PartitionerType solver_package)
Builds a Partitioner of the type specified by partitioner_type.
Definition: partitioner.C:159
dof_id_type id() const
Definition: dof_object.h:819
const std::map< subdomain_id_type, std::string > & get_subdomain_name_map() const
Definition: mesh_base.h:1896
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition: dof_object.h:473
virtual void clear() override
Clear all internal data.
The UnstructuredMesh class is derived from the MeshBase class.
unsigned char default_mapping_data() const
Returns any default data value used by the master space to physical space mapping.
Definition: mesh_base.h:949
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
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:967
std::unique_ptr< Partitioner > _partitioner
A partitioner to use at each prepare_for_use().
Definition: mesh_base.h:2206
virtual void move_nodes_and_elements(MeshBase &&other_mesh) override
Move node and elements from a ReplicatedMesh.
void set_mapping_data(const unsigned char data)
Sets the value of the mapping data for the element.
Definition: elem.h:3158
void clear_point_locator()
Releases the current PointLocator object.
Definition: mesh_base.C:1859
void subdomain_ids(std::set< subdomain_id_type > &ids, const bool global=true) const
Constructs a list of all subdomain identifiers in the local mesh if global == false, and in the global mesh if global == true (default).
Definition: mesh_base.C:1119
bool allow_find_neighbors() const
Definition: mesh_base.h:1353
libmesh_assert(ctx)
bool valid_unique_id() const
Definition: dof_object.h:869
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
std::map< subdomain_id_type, std::string > & set_subdomain_name_map()
Definition: mesh_base.h:1894
bool allow_remote_element_removal() const
Definition: mesh_base.h:1369
virtual void clear()
Deletes all the element and node data that is currently stored.
Definition: mesh_base.C:1029
virtual Elem * add_elem(Elem *e) override final
Add elem e to the end of the element array.
virtual void renumber_nodes_and_elements() override
After partitioning a mesh it is useful to renumber the nodes and elements so that they lie in contigu...
unsigned int level ElemType type std::set< subdomain_id_type > ss
virtual const Point & point(const dof_id_type i) const override final
bool skip_partitioning() const
Definition: mesh_base.h:1421
SimpleRange< NodeRefIter > node_ref_range()
Returns a range with all nodes of an element, usable in range-based for loops.
Definition: elem.h:2679
std::vector< dof_id_type > _node_integer_default_values
The array of default initialization values for integer data associated with each node in the mesh...
Definition: mesh_base.h:2356
static std::unique_ptr< Node > build(const Node &n)
Definition: node.h:315
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::vector< dof_id_type > _elem_integer_default_values
The array of default initialization values for integer data associated with each element in the mesh...
Definition: mesh_base.h:2344
virtual dof_id_type n_active_elem() const override final
void set_unique_id(unique_id_type new_id)
Sets the unique_id for this DofObject.
Definition: dof_object.h:848
ReplicatedMesh & operator=(const ReplicatedMesh &)=delete
Copy assignment is not allowed.
virtual void update_parallel_id_counts() override
Updates parallel caches so that methods like n_elem() accurately reflect changes on other processors...
virtual void clear_elems() override
Clear internal Elem data.
virtual void fix_broken_node_and_element_numbering() override
There is no reason for a user to ever call this function.
bool valid_id() const
Definition: dof_object.h:861
void max(const T &r, T &o, Request &req) const
void post_dofobject_moves(MeshBase &&other_mesh)
Moves any superclass data (e.g.
Definition: mesh_base.C:2356
const Node * node_ptr(const unsigned int i) const
Definition: elem.h:2513
virtual const Node * node_ptr(const dof_id_type i) const override final
void add_extra_integers(const unsigned int n_integers)
Assigns a set of extra integers to this DofObject.
Definition: dof_object.C:482
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...
unsigned int mesh_dimension() const
Definition: mesh_base.C:430
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 final
functions for adding /deleting nodes elements.
const DofMap &dof_map LIBMESH_COMMA unsigned int dof_map LIBMESH_COMMA var_num unsigned char rflag processor_id_type pid const DofMap &dof_map LIBMESH_COMMA unsigned int dof_map LIBMESH_COMMA var_num DECLARE_NODE_ITERATORS(multi_evaluable_, std::vector< const DofMap * > dof_maps, dof_maps) protected dof_id_typ _n_nodes)
The vertices (spatial coordinates) of the mesh.
virtual const Node & node_ref(const dof_id_type i) const
Definition: mesh_base.h:735
virtual Elem * insert_elem(Elem *e) override final
Insert elem e to the element array, preserving its id and replacing/deleting any existing element wit...
bool allow_renumbering() const
Definition: mesh_base.h:1346
ReplicatedMesh(const Parallel::Communicator &comm_in, unsigned char dim=1)
Constructor.
static constexpr subdomain_id_type invalid_subdomain_id
A static integral constant representing an invalid subdomain id.
Definition: elem.h:246
bool active() const
Definition: elem.h:2955
processor_id_type processor_id() const
Definition: dof_object.h:881
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
uint8_t unique_id_type
Definition: id_types.h:86
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
virtual std::vector< unsigned int > nodes_on_side(const unsigned int) const =0
uint8_t dof_id_type
Definition: id_types.h:67