libMesh
replicated_mesh.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 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  this->copy_nodes_and_elements(other_mesh, true);
105 
106  this->allow_find_neighbors(other_mesh.allow_find_neighbors());
107  this->allow_renumbering(other_mesh.allow_renumbering());
109  this->skip_partitioning(other_mesh.skip_partitioning());
110 
111  // The prepare_for_use() in copy_nodes_and_elements() is going to be
112  // tricky to remove without breaking backwards compatibility, but it
113  // updates some things we want to just copy.
114  this->copy_cached_data(other_mesh);
115 
116  this->copy_constraint_rows(other_mesh);
117 
118  this->_is_prepared = other_mesh.is_prepared();
119 
120  auto & this_boundary_info = this->get_boundary_info();
121  const auto & other_boundary_info = other_mesh.get_boundary_info();
122 
123  this_boundary_info = other_boundary_info;
124 
125  this->set_subdomain_name_map() = other_mesh.get_subdomain_name_map();
126 
127  // If other_mesh is distributed, then we've got parts of it on each
128  // processor but we're not replicated yet; fix that.
129  if (!other_mesh.is_serial())
130  MeshCommunication().allgather(*this);
131 }
132 
134 {
135  LOG_SCOPE("operator=(&&)", "ReplicatedMesh");
136 
137  // Move assign as an UnstructuredMesh
138  this->UnstructuredMesh::operator=(std::move(other_mesh));
139 
140  // Nodes and elements belong to ReplicatedMesh and have to be
141  // moved before we can move arbitrary GhostingFunctor, Partitioner,
142  // etc. subclasses.
143  this->move_nodes_and_elements(std::move(other_mesh));
144 
145  // Handle those remaining moves.
146  this->post_dofobject_moves(std::move(other_mesh));
147 
148  return *this;
149 }
150 
152 {
153  *this = std::move(cast_ref<ReplicatedMesh&>(other_mesh));
154 
155  return *this;
156 }
157 
159 {
160  ReplicatedMesh & other_mesh = cast_ref<ReplicatedMesh&>(other_meshbase);
161 
162  this->_nodes = std::move(other_mesh._nodes);
163  this->_n_nodes = other_mesh.n_nodes();
164 
165  this->_elements = std::move(other_mesh._elements);
166  this->_n_elem = other_mesh.n_elem();
167 }
168 
169 
170 const Point & ReplicatedMesh::point (const dof_id_type i) const
171 {
172  return this->node_ref(i);
173 }
174 
175 
176 
177 
179 {
180  libmesh_assert_less (i, this->max_node_id());
181  libmesh_assert(_nodes[i]);
182  libmesh_assert_equal_to (_nodes[i]->id(), i); // This will change soon
183 
184  return _nodes[i];
185 }
186 
187 
188 
189 
191 {
192  libmesh_assert_less (i, this->max_node_id());
193  libmesh_assert(_nodes[i]);
194  libmesh_assert_equal_to (_nodes[i]->id(), i); // This will change soon
195 
196  return _nodes[i];
197 }
198 
199 
200 
201 
203 {
204  if (i >= this->max_node_id())
205  return nullptr;
206  libmesh_assert (_nodes[i] == nullptr ||
207  _nodes[i]->id() == i); // This will change soon
208 
209  return _nodes[i];
210 }
211 
212 
213 
214 
216 {
217  if (i >= this->max_node_id())
218  return nullptr;
219  libmesh_assert (_nodes[i] == nullptr ||
220  _nodes[i]->id() == i); // This will change soon
221 
222  return _nodes[i];
223 }
224 
225 
226 
227 
229 {
230  libmesh_assert_less (i, this->max_elem_id());
232  libmesh_assert_equal_to (_elements[i]->id(), i); // This will change soon
233 
234  return _elements[i];
235 }
236 
237 
238 
239 
241 {
242  libmesh_assert_less (i, this->max_elem_id());
244  libmesh_assert_equal_to (_elements[i]->id(), i); // This will change soon
245 
246  return _elements[i];
247 }
248 
249 
250 
251 
253 {
254  if (i >= this->max_elem_id())
255  return nullptr;
256  libmesh_assert (_elements[i] == nullptr ||
257  _elements[i]->id() == i); // This will change soon
258 
259  return _elements[i];
260 }
261 
262 
263 
264 
266 {
267  if (i >= this->max_elem_id())
268  return nullptr;
269  libmesh_assert (_elements[i] == nullptr ||
270  _elements[i]->id() == i); // This will change soon
271 
272  return _elements[i];
273 }
274 
275 
276 
277 
279 {
280  libmesh_assert(e);
281 
282  // We no longer merely append elements with ReplicatedMesh
283 
284  // If the user requests a valid id that doesn't correspond to an
285  // existing element, let's give them that id, resizing the elements
286  // container if necessary.
287  if (!e->valid_id())
288  e->set_id (cast_int<dof_id_type>(_elements.size()));
289 
290 #ifdef LIBMESH_ENABLE_UNIQUE_ID
291  if (!e->valid_unique_id())
293  else
294  _next_unique_id = std::max(_next_unique_id, e->unique_id()+1);
295 #endif
296 
297  const dof_id_type id = e->id();
298 
299  if (id < _elements.size())
300  {
301  // This should *almost* never happen, but we rely on it when
302  // using allgather to replicate a not-yet-actually-replicated
303  // ReplicatedMesh under construction in parallel.
304  if (e == _elements[id])
305  return e;
306 
307  // Overwriting existing elements is still probably a mistake.
309  }
310  else
311  {
312  _elements.resize(id+1, nullptr);
313  }
314 
315  ++_n_elem;
316  _elements[id] = e;
317 
318  // Make sure any new element is given space for any extra integers
319  // we've requested
322 
323  // And set mapping type and data on any new element
326 
327  return e;
328 }
329 
330 Elem * ReplicatedMesh::add_elem (std::unique_ptr<Elem> e)
331 {
332  // The mesh now takes ownership of the Elem. Eventually the guts of
333  // add_elem() will get moved to a private helper function, and
334  // calling add_elem() directly will be deprecated.
335  return add_elem(e.release());
336 }
337 
338 
339 
341 {
342 #ifdef LIBMESH_ENABLE_UNIQUE_ID
343  if (!e->valid_unique_id())
345  else
346  _next_unique_id = std::max(_next_unique_id, e->unique_id()+1);
347 #endif
348 
349  dof_id_type eid = e->id();
350  libmesh_assert_less (eid, _elements.size());
351  Elem * oldelem = _elements[eid];
352 
353  if (oldelem)
354  {
355  libmesh_assert_equal_to (oldelem->id(), eid);
356  this->delete_elem(oldelem);
357  }
358 
359  ++_n_elem;
360  _elements[eid] = e;
361 
362  // Make sure any new element is given space for any extra integers
363  // we've requested
366 
367  // And set mapping type and data on any new element
370 
371  return e;
372 }
373 
374 Elem * ReplicatedMesh::insert_elem (std::unique_ptr<Elem> e)
375 {
376  // The mesh now takes ownership of the Elem. Eventually the guts of
377  // insert_elem(Elem*) will get moved to a private helper function, and
378  // calling insert_elem(Elem*) directly will be deprecated.
379  return insert_elem(e.release());
380 }
381 
382 
383 
385 {
386  libmesh_assert(e);
387 
388  // Initialize an iterator to eventually point to the element we want to delete
389  std::vector<Elem *>::iterator pos = _elements.end();
390 
391  // In many cases, e->id() gives us a clue as to where e
392  // is located in the _elements vector. Try that first
393  // before trying the O(n_elem) search.
394  libmesh_assert_less (e->id(), _elements.size());
395 
396  if (_elements[e->id()] == e)
397  {
398  // We found it!
399  pos = _elements.begin();
400  std::advance(pos, e->id());
401  }
402 
403  else
404  {
405  // This search is O(n_elem)
406  pos = std::find (_elements.begin(),
407  _elements.end(),
408  e);
409  }
410 
411  // Huh? Element not in the vector?
412  libmesh_assert (pos != _elements.end());
413 
414  // Remove the element from the BoundaryInfo object
415  this->get_boundary_info().remove(e);
416 
417  // delete the element
418  --_n_elem;
419  delete e;
420 
421  // explicitly zero the pointer
422  *pos = nullptr;
423 }
424 
425 
426 
428  const dof_id_type new_id)
429 {
430  // This could be a no-op
431  if (old_id == new_id)
432  return;
433 
434  // This doesn't get used in serial yet
435  Elem * el = _elements[old_id];
436  libmesh_assert (el);
437 
438  if (new_id >= _elements.size())
439  _elements.resize(new_id+1, nullptr);
440 
441  el->set_id(new_id);
442  libmesh_assert (!_elements[new_id]);
443  _elements[new_id] = el;
444  _elements[old_id] = nullptr;
445 }
446 
447 
448 
450  const dof_id_type id,
451  const processor_id_type proc_id)
452 {
453  Node * n = nullptr;
454 
455  // If the user requests a valid id, either
456  // provide the existing node or resize the container
457  // to fit the new node.
458  if (id != DofObject::invalid_id)
459  if (id < _nodes.size())
460  n = _nodes[id];
461  else
462  _nodes.resize(id+1);
463  else
464  _nodes.push_back (static_cast<Node *>(nullptr));
465 
466  // if the node already exists, then assign new (x,y,z) values
467  if (n)
468  *n = p;
469  // otherwise build a new node, put it in the right spot, and return
470  // a valid pointer.
471  else
472  {
473  n = Node::build(p, (id == DofObject::invalid_id) ?
474  cast_int<dof_id_type>(_nodes.size()-1) : id).release();
475  n->processor_id() = proc_id;
476 
479 
480 #ifdef LIBMESH_ENABLE_UNIQUE_ID
481  if (!n->valid_unique_id())
483  else
484  _next_unique_id = std::max(_next_unique_id, n->unique_id()+1);
485 #endif
486 
487  ++_n_nodes;
488  if (id == DofObject::invalid_id)
489  _nodes.back() = n;
490  else
491  _nodes[id] = n;
492  }
493 
494  // better not pass back a nullptr.
495  libmesh_assert (n);
496 
497  return n;
498 }
499 
500 
501 
503 {
504  libmesh_assert(n);
505 
506  // If the user requests a valid id, either set the existing
507  // container entry or resize the container to fit the new node.
508  if (n->valid_id())
509  {
510  const dof_id_type id = n->id();
511  if (id < _nodes.size())
512  libmesh_assert(!_nodes[id]);
513  else
514  _nodes.resize(id+1); // default nullptr
515 
516  _nodes[id] = n;
517  }
518  else
519  {
520  n->set_id (cast_int<dof_id_type>(_nodes.size()));
521  _nodes.push_back(n);
522  }
523 
524  ++_n_nodes;
525 
526 #ifdef LIBMESH_ENABLE_UNIQUE_ID
527  if (!n->valid_unique_id())
529  else
530  _next_unique_id = std::max(_next_unique_id, n->unique_id()+1);
531 #endif
532 
535 
536  return n;
537 }
538 
539 Node * ReplicatedMesh::add_node (std::unique_ptr<Node> n)
540 {
541  // The mesh now takes ownership of the Node. Eventually the guts of
542  // add_node() will get moved to a private helper function, and
543  // calling add_node() directly will be deprecated.
544  return add_node(n.release());
545 }
546 
547 #ifdef LIBMESH_ENABLE_DEPRECATED
548 
550 {
551  libmesh_deprecated();
552  libmesh_error_msg_if(!n, "Error, attempting to insert nullptr node.");
553  libmesh_error_msg_if(n->id() == DofObject::invalid_id, "Error, cannot insert node with invalid id.");
554 
555  if (n->id() < _nodes.size())
556  {
557  // Trying to add an existing node is a no-op. This lets us use
558  // a straightforward MeshCommunication::allgather() to fix a
559  // not-actually-replicated-yet ReplicatedMesh, as occurs when
560  // reading Nemesis files.
561  if (n->valid_id() && _nodes[n->id()] == n)
562  return n;
563 
564  // Don't allow anything else to replace an existing Node.
565  libmesh_error_msg_if(_nodes[ n->id() ] != nullptr,
566  "Error, cannot insert new node on top of existing node.");
567  }
568  else
569  {
570  // Allocate just enough space to store the new node. This will
571  // cause highly non-ideal memory allocation behavior if called
572  // repeatedly...
573  _nodes.resize(n->id() + 1);
574  }
575 
576 #ifdef LIBMESH_ENABLE_UNIQUE_ID
577  if (!n->valid_unique_id())
579  else
580  _next_unique_id = std::max(_next_unique_id, n->unique_id()+1);
581 #endif
582 
585 
586  // We have enough space and this spot isn't already occupied by
587  // another node, so go ahead and add it.
588  ++_n_nodes;
589  _nodes[ n->id() ] = n;
590 
591  // If we made it this far, we just inserted the node the user handed
592  // us, so we can give it right back.
593  return n;
594 }
595 
596 Node * ReplicatedMesh::insert_node(std::unique_ptr<Node> n)
597 {
598  libmesh_deprecated();
599  // The mesh now takes ownership of the Node. Eventually the guts of
600  // insert_node(Node*) will get moved to a private helper function, and
601  // calling insert_node(Node*) directly will be deprecated.
602  return insert_node(n.release());
603 }
604 
605 #endif
606 
608 {
609  libmesh_assert(n);
610  libmesh_assert_less (n->id(), _nodes.size());
611 
612  // Initialize an iterator to eventually point to the element we want
613  // to delete
614  std::vector<Node *>::iterator pos;
615 
616  // In many cases, e->id() gives us a clue as to where e
617  // is located in the _elements vector. Try that first
618  // before trying the O(n_elem) search.
619  if (_nodes[n->id()] == n)
620  {
621  pos = _nodes.begin();
622  std::advance(pos, n->id());
623  }
624  else
625  {
626  pos = std::find (_nodes.begin(),
627  _nodes.end(),
628  n);
629  }
630 
631  // Huh? Node not in the vector?
632  libmesh_assert (pos != _nodes.end());
633 
634  // Delete the node from the BoundaryInfo object
635  this->get_boundary_info().remove(n);
636  _constraint_rows.erase(n);
637 
638  // delete the node
639  --_n_nodes;
640  delete n;
641 
642  // explicitly zero the pointer
643  *pos = nullptr;
644 }
645 
646 
647 
649  const dof_id_type new_id)
650 {
651  // This could be a no-op
652  if (old_id == new_id)
653  return;
654 
655  // This doesn't get used in serial yet
656  Node * nd = _nodes[old_id];
657  libmesh_assert (nd);
658 
659  if (new_id >= _nodes.size())
660  _nodes.resize(new_id+1, nullptr);
661 
662  nd->set_id(new_id);
663  libmesh_assert (!_nodes[new_id]);
664  _nodes[new_id] = nd;
665  _nodes[old_id] = nullptr;
666 }
667 
668 
669 
671 {
672  // Call parent clear function
673  MeshBase::clear();
674 
675  // Clear our elements and nodes
676  // There is no need to remove them from
677  // the BoundaryInfo data structure since we
678  // already cleared it.
680 
681  for (auto & node : _nodes)
682  delete node;
683 
684  _n_nodes = 0;
685  _nodes.clear();
686 }
687 
688 
689 
691 {
692  for (auto & elem : _elements)
693  delete elem;
694 
695  _n_elem = 0;
696  _elements.clear();
697 }
698 
699 
700 
702 {
703 #ifdef LIBMESH_ENABLE_UNIQUE_ID
705 #endif
706 }
707 
708 
709 
710 #ifdef LIBMESH_ENABLE_UNIQUE_ID
712 {
713  // This function must be run on all processors at once
714  parallel_object_only();
715 
716  unique_id_type max_local = _next_unique_id;
717  this->comm().max(max_local);
718  return max_local;
719 }
720 
721 
722 
724 {
725  _next_unique_id = id;
726 }
727 #endif
728 
729 
730 
732 {
733  LOG_SCOPE("renumber_nodes_and_elem()", "Mesh");
734 
735  // node and element id counters
736  dof_id_type next_free_elem = 0;
737  dof_id_type next_free_node = 0;
738 
739  // Will hold the set of nodes that are currently connected to elements
740  std::unordered_set<Node *> connected_nodes;
741 
742  // Loop over the elements. Note that there may
743  // be nullptrs in the _elements vector from the coarsening
744  // process. Pack the elements in to a contiguous array
745  // and then trim any excess.
746  {
747  std::vector<Elem *>::iterator in = _elements.begin();
748  std::vector<Elem *>::iterator out_iter = _elements.begin();
749  const std::vector<Elem *>::iterator end = _elements.end();
750 
751  for (; in != end; ++in)
752  if (*in != nullptr)
753  {
754  Elem * el = *in;
755 
756  *out_iter = *in;
757  ++out_iter;
758 
759  // Increment the element counter
760  el->set_id (next_free_elem++);
761 
763  {
764  // Add this elements nodes to the connected list
765  for (auto & n : el->node_ref_range())
766  connected_nodes.insert(&n);
767  }
768  else // We DO want node renumbering
769  {
770  // Loop over this element's nodes. Number them,
771  // if they have not been numbered already. Also,
772  // position them in the _nodes vector so that they
773  // are packed contiguously from the beginning.
774  for (auto & n : el->node_ref_range())
775  if (n.id() == next_free_node) // don't need to process
776  next_free_node++; // [(src == dst) below]
777 
778  else if (n.id() > next_free_node) // need to process
779  {
780  // The source and destination indices
781  // for this node
782  const dof_id_type src_idx = n.id();
783  const dof_id_type dst_idx = next_free_node++;
784 
785  // ensure we want to swap a valid nodes
786  libmesh_assert(_nodes[src_idx]);
787 
788  // Swap the source and destination nodes
789  std::swap(_nodes[src_idx],
790  _nodes[dst_idx] );
791 
792  // Set proper indices where that makes sense
793  if (_nodes[src_idx] != nullptr)
794  _nodes[src_idx]->set_id (src_idx);
795  _nodes[dst_idx]->set_id (dst_idx);
796  }
797  }
798  }
799 
800  // Erase any additional storage. These elements have been
801  // copied into nullptr voids by the procedure above, and are
802  // thus repeated and unnecessary.
803  _elements.erase (out_iter, end);
804  }
805 
806 
808  {
809  // Loop over the nodes. Note that there may
810  // be nullptrs in the _nodes vector from the coarsening
811  // process. Pack the nodes in to a contiguous array
812  // and then trim any excess.
813 
814  std::vector<Node *>::iterator in = _nodes.begin();
815  std::vector<Node *>::iterator out_iter = _nodes.begin();
816  const std::vector<Node *>::iterator end = _nodes.end();
817 
818  for (; in != end; ++in)
819  if (*in != nullptr)
820  {
821  // This is a reference so that if we change the pointer it will change in the vector
822  Node * & nd = *in;
823 
824  // If this node is still connected to an elem, put it in the list
825  if (connected_nodes.count(nd))
826  {
827  *out_iter = nd;
828  ++out_iter;
829 
830  // Increment the node counter
831  nd->set_id (next_free_node++);
832  }
833  else // This node is orphaned, delete it!
834  {
835  this->get_boundary_info().remove (nd);
836  _constraint_rows.erase(nd);
837 
838  // delete the node
839  --_n_nodes;
840  delete nd;
841  nd = nullptr;
842  }
843  }
844 
845  // Erase any additional storage. Whatever was
846  _nodes.erase (out_iter, end);
847  }
848  else // We really DO want node renumbering
849  {
850  // Any nodes in the vector >= _nodes[next_free_node]
851  // are not connected to any elements and may be deleted
852  // if desired.
853 
854  // Now, delete the unused nodes
855  {
856  std::vector<Node *>::iterator nd = _nodes.begin();
857  const std::vector<Node *>::iterator end = _nodes.end();
858 
859  std::advance (nd, next_free_node);
860 
861  for (auto & node : as_range(nd, end))
862  {
863  // Mesh modification code might have already deleted some
864  // nodes
865  if (node == nullptr)
866  continue;
867 
868  // remove any boundary information associated with
869  // this node
870  this->get_boundary_info().remove (node);
871  _constraint_rows.erase(node);
872 
873  // delete the node
874  --_n_nodes;
875  delete node;
876  node = nullptr;
877  }
878 
879  _nodes.erase (nd, end);
880  }
881  }
882 
883  libmesh_assert_equal_to (next_free_elem, _elements.size());
884  libmesh_assert_equal_to (next_free_node, _nodes.size());
885 
887 }
888 
889 
890 
892 {
893  // Nodes first
894  for (auto n : index_range(_nodes))
895  if (this->_nodes[n] != nullptr)
896  this->_nodes[n]->set_id() = cast_int<dof_id_type>(n);
897 
898  // Elements next
899  for (auto e : index_range(_elements))
900  if (this->_elements[e] != nullptr)
901  this->_elements[e]->set_id() = cast_int<dof_id_type>(e);
902 }
903 
904 
906 {
907  return static_cast<dof_id_type>(std::distance (this->active_elements_begin(),
908  this->active_elements_end()));
909 }
910 
911 std::vector<dof_id_type>
912 ReplicatedMesh::get_disconnected_subdomains(std::vector<subdomain_id_type> * subdomain_ids) const
913 {
914  // find number of disconnected subdomains
915  std::vector<dof_id_type> representative_elem_ids;
916 
917  // use subdomain_ids as markers for all elements to indicate if the elements
918  // have been visited. Note: here subdomain ID is unrelated with element
919  // subdomain_id().
920  std::vector<subdomain_id_type> subdomains;
921  if (!subdomain_ids)
922  subdomain_ids = &subdomains;
923  subdomain_ids->clear();
925 
926  // counter of disconnected subdomains
927  subdomain_id_type subdomain_counter = 0;
928 
929  // a stack for visiting elements, make its capacity sufficiently large to avoid
930  // memory allocation and deallocation when the vector size changes
931  std::vector<const Elem *> list;
932  list.reserve(n_elem());
933 
934  // counter of visited elements
935  dof_id_type visited = 0;
936  dof_id_type n_active = n_active_elem();
937  do
938  {
939  for (const auto & elem : active_element_ptr_range())
940  if ((*subdomain_ids)[elem->id()] == Elem::invalid_subdomain_id)
941  {
942  list.push_back(elem);
943  (*subdomain_ids)[elem->id()] = subdomain_counter;
944  break;
945  }
946  // we should be able to find a seed here
947  libmesh_assert(list.size() > 0);
948 
949  dof_id_type min_id = std::numeric_limits<dof_id_type>::max();
950  while (list.size() > 0)
951  {
952  // pop up an element
953  const Elem * elem = list.back(); list.pop_back(); ++visited;
954 
955  min_id = std::min(elem->id(), min_id);
956 
957  for (auto s : elem->side_index_range())
958  {
959  const Elem * neighbor = elem->neighbor_ptr(s);
960  if (neighbor != nullptr && (*subdomain_ids)[neighbor->id()] == Elem::invalid_subdomain_id)
961  {
962  // neighbor must be active
963  libmesh_assert(neighbor->active());
964  list.push_back(neighbor);
965  (*subdomain_ids)[neighbor->id()] = subdomain_counter;
966  }
967  }
968  }
969 
970  representative_elem_ids.push_back(min_id);
971  subdomain_counter++;
972  }
973  while (visited != n_active);
974 
975  return representative_elem_ids;
976 }
977 
978 std::unordered_map<dof_id_type, std::vector<std::vector<Point>>>
980 {
981  libmesh_error_msg_if(mesh_dimension() != 2,
982  "Error: get_boundary_points only works for 2D now");
983 
984  // find number of disconnected subdomains
985  // subdomains will hold the IDs of disconnected subdomains for all elements.
986  std::vector<subdomain_id_type> subdomains;
987  std::vector<dof_id_type> elem_ids = get_disconnected_subdomains(&subdomains);
988 
989  std::unordered_map<dof_id_type, std::vector<std::vector<Point>>> boundary_points;
990 
991  // get all boundary sides that are to be erased later during visiting
992  // use a comparison functor to avoid run-time randomness due to pointers
993  struct boundary_side_compare
994  {
995  bool operator()(const std::pair<const Elem *, unsigned int> & lhs,
996  const std::pair<const Elem *, unsigned int> & rhs) const
997  {
998  if (lhs.first->id() < rhs.first->id())
999  return true;
1000  else if (lhs.first->id() == rhs.first->id())
1001  {
1002  if (lhs.second < rhs.second)
1003  return true;
1004  }
1005  return false;
1006  }
1007  };
1008  std::set<std::pair<const Elem *, unsigned int>, boundary_side_compare> boundary_elements;
1009  for (const auto & elem : active_element_ptr_range())
1010  for (auto s : elem->side_index_range())
1011  if (elem->neighbor_ptr(s) == nullptr)
1012  boundary_elements.insert(std::pair<const Elem *, unsigned int>(elem, s));
1013 
1014  while (!boundary_elements.empty())
1015  {
1016  // get the first entry as the seed
1017  const Elem * eseed = boundary_elements.begin()->first;
1018  unsigned int sseed = boundary_elements.begin()->second;
1019 
1020  // get the subdomain ID that these boundary sides attached to
1021  subdomain_id_type subdomain_id = subdomains[eseed->id()];
1022 
1023  // start visiting the mesh to find all boundary nodes with the seed
1024  std::vector<Point> bpoints;
1025  const Elem * elem = eseed;
1026  unsigned int s = sseed;
1027  std::vector<unsigned int> local_side_nodes = elem->nodes_on_side(s);
1028  while (true)
1029  {
1030  std::pair<const Elem *, unsigned int> side(elem, s);
1031  libmesh_assert(boundary_elements.count(side));
1032  boundary_elements.erase(side);
1033 
1034  // push all nodes on the side except the node on the other end of the side (index 1)
1035  for (auto i : index_range(local_side_nodes))
1036  if (i != 1)
1037  bpoints.push_back(*static_cast<const Point *>(elem->node_ptr(local_side_nodes[i])));
1038 
1039  // use the last node to find next element and side
1040  const Node * node = elem->node_ptr(local_side_nodes[1]);
1041  std::set<const Elem *> neighbors;
1042  elem->find_point_neighbors(*node, neighbors);
1043 
1044  // if only one neighbor is found (itself), this node is a cornor node on boundary
1045  if (neighbors.size() != 1)
1046  neighbors.erase(elem);
1047 
1048  // find the connecting side
1049  bool found = false;
1050  for (const auto & neighbor : neighbors)
1051  {
1052  for (auto ss : neighbor->side_index_range())
1053  if (neighbor->neighbor_ptr(ss) == nullptr && !(elem == neighbor && s == ss))
1054  {
1055  local_side_nodes = neighbor->nodes_on_side(ss);
1056  // we expect the starting point of the side to be the same as the end of the previous side
1057  if (neighbor->node_ptr(local_side_nodes[0]) == node)
1058  {
1059  elem = neighbor;
1060  s = ss;
1061  found = true;
1062  break;
1063  }
1064  else if (neighbor->node_ptr(local_side_nodes[1]) == node)
1065  {
1066  elem = neighbor;
1067  s = ss;
1068  found = true;
1069  // flip nodes in local_side_nodes because the side is in an opposite direction
1070  auto temp(local_side_nodes);
1071  local_side_nodes[0] = temp[1];
1072  local_side_nodes[1] = temp[0];
1073  for (unsigned int i = 2; i < temp.size(); ++i)
1074  local_side_nodes[temp.size() + 1 - i] = temp[i];
1075  break;
1076  }
1077  }
1078  if (found)
1079  break;
1080  }
1081 
1082  libmesh_error_msg_if(!found, "ERROR: mesh topology error on visiting boundary sides");
1083 
1084  // exit if we reach the starting point
1085  if (elem == eseed && s == sseed)
1086  break;
1087  }
1088  boundary_points[elem_ids[subdomain_id]].push_back(bpoints);
1089  }
1090 
1091  return boundary_points;
1092 }
1093 
1094 } // 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
unique_id_type & set_unique_id()
Definition: dof_object.h:858
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:1950
bool is_prepared() const
Definition: mesh_base.h:198
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)
Deep copy of nodes and elements from another mesh object (used by subclass copy constructors and by m...
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:2033
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:3128
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:1196
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:2710
void skip_partitioning(bool skip)
If true is passed in then nothing on this mesh will be (re)partitioned.
Definition: mesh_base.h:1254
virtual ~ReplicatedMesh()
Destructor.
virtual void set_next_unique_id(unique_id_type id) override final
Sets the next available unique id to be used.
void copy_cached_data(const MeshBase &other_mesh)
Helper class to copy cached data, to synchronize with a possibly unprepared other_mesh.
Definition: mesh_base.C:2004
virtual Node * insert_node(Node *n) override final
These methods are deprecated.
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:2104
unique_id_type unique_id() const
Definition: dof_object.h:844
void copy_constraint_rows(const MeshBase &other_mesh)
Copy the constraints from the other mesh to this mesh.
Definition: mesh_base.C:2063
const Parallel::Communicator & comm() const
The libMesh namespace provides an interface to certain functionality in the library.
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition: mesh_base.h:165
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:2045
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)
uint8_t processor_id_type
Definition: id_types.h:104
This is the MeshBase class.
Definition: mesh_base.h:75
dof_id_type & set_id()
Definition: dof_object.h:836
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:812
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:1925
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:901
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:1212
virtual bool is_serial() const
Definition: mesh_base.h:211
static const subdomain_id_type invalid_subdomain_id
A static integral constant representing an invalid subdomain id.
Definition: elem.h:251
friend class MeshCommunication
Make the MeshCommunication class a friend so that it can directly broadcast *_integer_names.
Definition: mesh_base.h:2134
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:1024
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:828
const std::map< subdomain_id_type, std::string > & get_subdomain_name_map() const
Definition: mesh_base.h:1694
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:830
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:993
std::unique_ptr< Partitioner > _partitioner
A partitioner to use at each prepare_for_use().
Definition: mesh_base.h:1919
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:3144
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:959
bool allow_find_neighbors() const
Definition: mesh_base.h:1204
libmesh_assert(ctx)
static const dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition: dof_object.h:482
bool valid_unique_id() const
Definition: dof_object.h:893
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:1203
bool allow_remote_element_removal() const
Definition: mesh_base.h:1213
virtual void clear()
Deletes all the element and node data that is currently stored.
Definition: mesh_base.C:920
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:1256
SimpleRange< NodeRefIter > node_ref_range()
Returns a range with all nodes of an element, usable in range-based for loops.
Definition: elem.h:2665
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:2051
static std::unique_ptr< Node > build(const Node &n)
Definition: node.h:315
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2598
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:2039
virtual dof_id_type n_active_elem() const override final
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:885
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:1969
const Node * node_ptr(const unsigned int i) const
Definition: elem.h:2507
virtual const Node * node_ptr(const dof_id_type i) const override final
bool _is_prepared
Flag indicating if the mesh has been prepared for use.
Definition: mesh_base.h:1896
void add_extra_integers(const unsigned int n_integers)
Assigns a set of extra integers to this DofObject.
Definition: dof_object.C:490
unsigned int mesh_dimension() const
Definition: mesh_base.C:372
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:596
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:1197
ReplicatedMesh(const Parallel::Communicator &comm_in, unsigned char dim=1)
Constructor.
const DofMap &dof_map LIBMESH_COMMA unsigned int std::string & set_subdomain_name_map()
Definition: mesh_base.h:1692
bool active() const
Definition: elem.h:2941
processor_id_type processor_id() const
Definition: dof_object.h:905
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:117
virtual std::vector< unsigned int > nodes_on_side(const unsigned int) const =0
uint8_t dof_id_type
Definition: id_types.h:67