libMesh
Loading...
Searching...
No Matches
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
37namespace 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.
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
62bool 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 auto & this_boundary_info = this->get_boundary_info();
116 const auto & other_boundary_info = other_mesh.get_boundary_info();
117
118 this_boundary_info = other_boundary_info;
119
120 this->set_subdomain_name_map() = other_mesh.get_subdomain_name_map();
121
122 this->_preparation = other_mesh.preparation();
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())
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
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{
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
332Elem * 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
381Elem * 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{
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{
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
557Node * 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{
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
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 // Implicitly get max_elem_id/max_node_id by trimming the vectors
666 auto trim_vec = [](auto & dof_vec) {
667 auto last_non_null = std::find_if(dof_vec.rbegin(), dof_vec.rend(), [](DofObject * d) { return (d != nullptr); });
668 dof_vec.resize(last_non_null.base()-dof_vec.begin());
669 };
670
671 trim_vec(this->_nodes);
672 trim_vec(this->_elements);
673
675
677}
678
679
680
681#ifdef LIBMESH_ENABLE_UNIQUE_ID
683{
684 // This function must be run on all processors at once
685 parallel_object_only();
686
688 this->comm().max(max_local);
689 return max_local;
690}
691
692
693
698#endif
699
700
701
703{
704 LOG_SCOPE("renumber_nodes_and_elem()", "Mesh");
705
706 // node and element id counters
707 dof_id_type next_free_elem = 0;
708 dof_id_type next_free_node = 0;
709
710 // Will hold the set of nodes that are currently connected to elements
711 std::unordered_set<Node *> connected_nodes;
712
713 // Loop over the elements. Note that there may
714 // be nullptrs in the _elements vector from the coarsening
715 // process. Pack the elements in to a contiguous array
716 // and then trim any excess.
717 {
718 std::vector<Elem *>::iterator in = _elements.begin();
719 std::vector<Elem *>::iterator out_iter = _elements.begin();
720 const std::vector<Elem *>::iterator end = _elements.end();
721
722 for (; in != end; ++in)
723 if (*in != nullptr)
724 {
725 Elem * el = *in;
726
727 *out_iter = *in;
728 ++out_iter;
729
730 // Increment the element counter
731 el->set_id (next_free_elem++);
732
734 {
735 // Add this elements nodes to the connected list
736 for (auto & n : el->node_ref_range())
737 connected_nodes.insert(&n);
738 }
739 else // We DO want node renumbering
740 {
741 // Loop over this element's nodes. Number them,
742 // if they have not been numbered already. Also,
743 // position them in the _nodes vector so that they
744 // are packed contiguously from the beginning.
745 for (auto & n : el->node_ref_range())
746 if (n.id() == next_free_node) // don't need to process
747 next_free_node++; // [(src == dst) below]
748
749 else if (n.id() > next_free_node) // need to process
750 {
751 // The source and destination indices
752 // for this node
753 const dof_id_type src_idx = n.id();
754 const dof_id_type dst_idx = next_free_node++;
755
756 // ensure we want to swap a valid nodes
757 libmesh_assert(_nodes[src_idx]);
758
759 // Swap the source and destination nodes
760 std::swap(_nodes[src_idx],
761 _nodes[dst_idx] );
762
763 // Set proper indices where that makes sense
764 if (_nodes[src_idx] != nullptr)
765 _nodes[src_idx]->set_id (src_idx);
766 _nodes[dst_idx]->set_id (dst_idx);
767 }
768 }
769 }
770
771 // Erase any additional storage. These elements have been
772 // copied into nullptr voids by the procedure above, and are
773 // thus repeated and unnecessary.
774 _elements.erase (out_iter, end);
775 }
776
777
779 {
780 // Loop over the nodes. Note that there may
781 // be nullptrs in the _nodes vector from the coarsening
782 // process. Pack the nodes in to a contiguous array
783 // and then trim any excess.
784
785 std::vector<Node *>::iterator in = _nodes.begin();
786 std::vector<Node *>::iterator out_iter = _nodes.begin();
787 const std::vector<Node *>::iterator end = _nodes.end();
788
789 for (; in != end; ++in)
790 if (*in != nullptr)
791 {
792 // This is a reference so that if we change the pointer it will change in the vector
793 Node * & nd = *in;
794
795 // If this node is still connected to an elem, put it in the list
796 if (connected_nodes.count(nd))
797 {
798 *out_iter = nd;
799 ++out_iter;
800
801 // Increment the node counter
802 nd->set_id (next_free_node++);
803 }
804 else // This node is orphaned, delete it!
805 {
806 this->get_boundary_info().remove (nd);
807 _constraint_rows.erase(nd);
808
809 // delete the node
810 --_n_nodes;
811 delete nd;
812 nd = nullptr;
813 }
814 }
815
816 // Erase any additional storage. Whatever was
817 _nodes.erase (out_iter, end);
818 }
819 else // We really DO want node renumbering
820 {
821 // Any nodes in the vector >= _nodes[next_free_node]
822 // are not connected to any elements and may be deleted
823 // if desired.
824
825 // Now, delete the unused nodes
826 {
827 std::vector<Node *>::iterator nd = _nodes.begin();
828 const std::vector<Node *>::iterator end = _nodes.end();
829
830 std::advance (nd, next_free_node);
831
832 for (auto & node : as_range(nd, end))
833 {
834 // Mesh modification code might have already deleted some
835 // nodes
836 if (node == nullptr)
837 continue;
838
839 // remove any boundary information associated with
840 // this node
841 this->get_boundary_info().remove (node);
842 _constraint_rows.erase(node);
843
844 // delete the node
845 --_n_nodes;
846 delete node;
847 node = nullptr;
848 }
849
850 _nodes.erase (nd, end);
851 }
852 }
853
855
856 libmesh_assert_equal_to (next_free_elem, _elements.size());
857 libmesh_assert_equal_to (next_free_node, _nodes.size());
858
860}
861
862
863
865{
866 // Nodes first
867 for (auto n : index_range(_nodes))
868 if (this->_nodes[n] != nullptr)
869 this->_nodes[n]->set_id() = cast_int<dof_id_type>(n);
870
871 // Elements next
872 for (auto e : index_range(_elements))
873 if (this->_elements[e] != nullptr)
874 this->_elements[e]->set_id() = cast_int<dof_id_type>(e);
875}
876
877
879{
880 return static_cast<dof_id_type>(std::distance (this->active_elements_begin(),
881 this->active_elements_end()));
882}
883
884std::vector<dof_id_type>
885ReplicatedMesh::get_disconnected_subdomains(std::vector<subdomain_id_type> * subdomain_ids) const
886{
887 // find number of disconnected subdomains
888 std::vector<dof_id_type> representative_elem_ids;
889
890 // use subdomain_ids as markers for all elements to indicate if the elements
891 // have been visited. Note: here subdomain ID is unrelated with element
892 // subdomain_id().
893 std::vector<subdomain_id_type> subdomains;
894 if (!subdomain_ids)
895 subdomain_ids = &subdomains;
896 subdomain_ids->clear();
898
899 // counter of disconnected subdomains
900 subdomain_id_type subdomain_counter = 0;
901
902 // a stack for visiting elements, make its capacity sufficiently large to avoid
903 // memory allocation and deallocation when the vector size changes
904 std::vector<const Elem *> list;
905 list.reserve(n_elem());
906
907 // counter of visited elements
908 dof_id_type visited = 0;
909 dof_id_type n_active = n_active_elem();
910 do
911 {
912 for (const auto & elem : active_element_ptr_range())
913 if ((*subdomain_ids)[elem->id()] == Elem::invalid_subdomain_id)
914 {
915 list.push_back(elem);
916 (*subdomain_ids)[elem->id()] = subdomain_counter;
917 break;
918 }
919 // we should be able to find a seed here
920 libmesh_assert(list.size() > 0);
921
922 dof_id_type min_id = std::numeric_limits<dof_id_type>::max();
923 while (list.size() > 0)
924 {
925 // pop up an element
926 const Elem * elem = list.back(); list.pop_back(); ++visited;
927
928 min_id = std::min(elem->id(), min_id);
929
930 for (auto s : elem->side_index_range())
931 {
932 const Elem * neighbor = elem->neighbor_ptr(s);
933 if (neighbor != nullptr && (*subdomain_ids)[neighbor->id()] == Elem::invalid_subdomain_id)
934 {
935 // neighbor must be active
936 libmesh_assert(neighbor->active());
937 list.push_back(neighbor);
938 (*subdomain_ids)[neighbor->id()] = subdomain_counter;
939 }
940 }
941 }
942
943 representative_elem_ids.push_back(min_id);
944 subdomain_counter++;
945 }
946 while (visited != n_active);
947
948 return representative_elem_ids;
949}
950
951std::unordered_map<dof_id_type, std::vector<std::vector<Point>>>
953{
954 libmesh_error_msg_if(mesh_dimension() != 2,
955 "Error: get_boundary_points only works for 2D now");
956
957 // find number of disconnected subdomains
958 // subdomains will hold the IDs of disconnected subdomains for all elements.
959 std::vector<subdomain_id_type> subdomains;
960 std::vector<dof_id_type> elem_ids = get_disconnected_subdomains(&subdomains);
961
962 std::unordered_map<dof_id_type, std::vector<std::vector<Point>>> boundary_points;
963
964 // get all boundary sides that are to be erased later during visiting
965 // use a comparison functor to avoid run-time randomness due to pointers
966 struct boundary_side_compare
967 {
968 bool operator()(const std::pair<const Elem *, unsigned int> & lhs,
969 const std::pair<const Elem *, unsigned int> & rhs) const
970 {
971 if (lhs.first->id() < rhs.first->id())
972 return true;
973 else if (lhs.first->id() == rhs.first->id())
974 {
975 if (lhs.second < rhs.second)
976 return true;
977 }
978 return false;
979 }
980 };
981 std::set<std::pair<const Elem *, unsigned int>, boundary_side_compare> boundary_elements;
982 for (const auto & elem : active_element_ptr_range())
983 for (auto s : elem->side_index_range())
984 if (elem->neighbor_ptr(s) == nullptr)
985 boundary_elements.insert(std::pair<const Elem *, unsigned int>(elem, s));
986
987 while (!boundary_elements.empty())
988 {
989 // get the first entry as the seed
990 const Elem * eseed = boundary_elements.begin()->first;
991 unsigned int sseed = boundary_elements.begin()->second;
992
993 // get the subdomain ID that these boundary sides attached to
994 subdomain_id_type subdomain_id = subdomains[eseed->id()];
995
996 // start visiting the mesh to find all boundary nodes with the seed
997 std::vector<Point> bpoints;
998 const Elem * elem = eseed;
999 unsigned int s = sseed;
1000 std::vector<unsigned int> local_side_nodes = elem->nodes_on_side(s);
1001 while (true)
1002 {
1003 std::pair<const Elem *, unsigned int> side(elem, s);
1004 libmesh_assert(boundary_elements.count(side));
1005 boundary_elements.erase(side);
1006
1007 // push all nodes on the side except the node on the other end of the side (index 1)
1008 for (auto i : index_range(local_side_nodes))
1009 if (i != 1)
1010 bpoints.push_back(*static_cast<const Point *>(elem->node_ptr(local_side_nodes[i])));
1011
1012 // use the last node to find next element and side
1013 const Node * node = elem->node_ptr(local_side_nodes[1]);
1014 std::set<const Elem *> neighbors;
1015 elem->find_point_neighbors(*node, neighbors);
1016
1017 // if only one neighbor is found (itself), this node is a cornor node on boundary
1018 if (neighbors.size() != 1)
1019 neighbors.erase(elem);
1020
1021 // find the connecting side
1022 bool found = false;
1023 for (const auto & neighbor : neighbors)
1024 {
1025 for (auto ss : neighbor->side_index_range())
1026 if (neighbor->neighbor_ptr(ss) == nullptr && !(elem == neighbor && s == ss))
1027 {
1028 local_side_nodes = neighbor->nodes_on_side(ss);
1029 // we expect the starting point of the side to be the same as the end of the previous side
1030 if (neighbor->node_ptr(local_side_nodes[0]) == node)
1031 {
1032 elem = neighbor;
1033 s = ss;
1034 found = true;
1035 break;
1036 }
1037 else if (neighbor->node_ptr(local_side_nodes[1]) == node)
1038 {
1039 elem = neighbor;
1040 s = ss;
1041 found = true;
1042 // flip nodes in local_side_nodes because the side is in an opposite direction
1043 auto temp(local_side_nodes);
1044 local_side_nodes[0] = temp[1];
1045 local_side_nodes[1] = temp[0];
1046 for (unsigned int i = 2; i < temp.size(); ++i)
1047 local_side_nodes[temp.size() + 1 - i] = temp[i];
1048 break;
1049 }
1050 }
1051 if (found)
1052 break;
1053 }
1054
1055 libmesh_error_msg_if(!found, "ERROR: mesh topology error on visiting boundary sides");
1056
1057 // exit if we reach the starting point
1058 if (elem == eseed && s == sseed)
1059 break;
1060 }
1061 boundary_points[elem_ids[subdomain_id]].push_back(bpoints);
1062 }
1063
1064 return boundary_points;
1065}
1066
1067} // namespace libMesh
void max(const T &r, T &o, Request &req) const
void remove(const Node *node)
Removes the boundary conditions associated with node node, if any exist.
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:872
The DofObject defines an abstract base class for objects that have degrees of freedom associated with...
Definition dof_object.h:55
bool valid_id() const
Definition dof_object.h:875
processor_id_type processor_id() const
Definition dof_object.h:895
dof_id_type & set_id()
Definition dof_object.h:841
void add_extra_integers(const unsigned int n_integers)
Assigns a set of extra integers to this DofObject.
Definition dof_object.C:482
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition dof_object.h:473
unique_id_type unique_id() const
Definition dof_object.h:849
bool valid_unique_id() const
Definition dof_object.h:883
dof_id_type id() const
Definition dof_object.h:833
void set_unique_id(unique_id_type new_id)
Sets the unique_id for this DofObject.
Definition dof_object.h:862
This is the base class from which all geometric element types are derived.
Definition elem.h:96
bool active() const
Definition elem.h:3079
SimpleRange< NodeRefIter > node_ref_range()
Returns a range with all nodes of an element, usable in range-based for loops.
Definition elem.h:2803
virtual std::vector< unsigned int > nodes_on_side(const unsigned int) const =0
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:907
static constexpr subdomain_id_type invalid_subdomain_id
A static integral constant representing an invalid subdomain id.
Definition elem.h:246
const Node * node_ptr(const unsigned int i) const
Definition elem.h:2637
void set_mapping_type(const ElemMappingType type)
Sets the value of the mapping type for the element.
Definition elem.h:3266
void set_mapping_data(const unsigned char data)
Sets the value of the mapping data for the element.
Definition elem.h:3282
const Elem * neighbor_ptr(unsigned int i) const
Definition elem.h:2736
IntRange< unsigned short > side_index_range() const
Definition elem.h:2848
This is the MeshBase class.
Definition mesh_base.h:81
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:1382
virtual const Node & node_ref(const dof_id_type i) const
Definition mesh_base.h:742
virtual bool is_serial() const
Definition mesh_base.h:357
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
Preparation preparation() const
Definition mesh_base.h:213
bool allow_renumbering() const
Definition mesh_base.h:1360
unsigned int mesh_dimension() const
Definition mesh_base.C:435
bool skip_partitioning() const
Definition mesh_base.h:1435
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,...
Definition mesh_base.C:1127
bool allow_detect_interior_parents() const
Definition mesh_base.h:1374
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:1366
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:1359
const std::map< subdomain_id_type, std::string > & get_subdomain_name_map() const
Definition mesh_base.h:1930
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:938
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:2401
unique_id_type _next_unique_id
The next available unique id for assigning ids to DOF objects.
Definition mesh_base.h:2257
bool _skip_renumber_nodes_and_elements
If this is true then renumbering will be kept to a minimum.
Definition mesh_base.h:2282
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:2383
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:2389
virtual void clear()
Deletes all the element and node data that is currently stored.
Definition mesh_base.C:1036
constraint_rows_type _constraint_rows
Definition mesh_base.h:2454
std::unique_ptr< Partitioner > _partitioner
A partitioner to use at each prepare_for_use().
Definition mesh_base.h:2251
bool allow_remote_element_removal() const
Definition mesh_base.h:1383
bool allow_find_neighbors() const
Definition mesh_base.h:1367
dof_id_type _n_constraint_rows
Definition mesh_base.h:2456
unsigned char default_mapping_data() const
Returns any default data value used by the master space to physical space mapping.
Definition mesh_base.h:956
void clear_point_locator()
Releases the current PointLocator object.
Definition mesh_base.C:1881
void copy_constraint_rows(const MeshBase &other_mesh)
Copy the constraints from the other mesh to this mesh.
Definition mesh_base.C:2506
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:2395
std::map< subdomain_id_type, std::string > & set_subdomain_name_map()
Definition mesh_base.h:1928
dof_id_type count_constraint_rows()
Helper function for subclasses to use to set _n_constraint_rows.
Definition mesh_base.C:2487
Preparation _preparation
Flags indicating in what ways this mesh has been prepared.
Definition mesh_base.h:2207
void skip_partitioning(bool skip)
If true is passed in then nothing on this mesh will be (re)partitioned.
Definition mesh_base.h:1433
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:1373
void post_dofobject_moves(MeshBase &&other_mesh)
Moves any superclass data (e.g.
Definition mesh_base.C:2411
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:1984
This is the MeshCommunication class.
void allgather(MeshBase &mesh) const
This method takes an input DistributedMesh which may be distributed among all the processors.
A Node is like a Point, but with more information.
Definition node.h:55
static std::unique_ptr< Node > build(const Node &n)
Definition node.h:315
const Parallel::Communicator & comm() const
static std::unique_ptr< Partitioner > build(const PartitionerType solver_package)
Builds a Partitioner of the type specified by partitioner_type.
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:41
The ReplicatedMesh class is derived from the MeshBase class, and is used to store identical copies of...
virtual void clear() override
Clear all internal data.
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.
std::vector< Elem * > _elements
The elements in the mesh.
virtual Node * add_node(Node *n) override final
Add Node n to the end of the vertex array.
virtual void update_parallel_id_counts() override
Updates parallel caches so that methods like n_elem() accurately reflect changes on other processors.
virtual void move_nodes_and_elements(MeshBase &&other_mesh) override
Move node and elements from a ReplicatedMesh.
virtual void delete_node(Node *n) override final
Removes the Node n from the mesh.
ReplicatedMesh(const Parallel::Communicator &comm_in, unsigned char dim=1)
Constructor.
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 ...
virtual dof_id_type max_node_id() const override final
virtual dof_id_type max_elem_id() const override final
virtual const Node * node_ptr(const dof_id_type i) 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.
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...
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 dof_id_type n_active_elem() const override final
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...
virtual dof_id_type n_elem() const override final
virtual void delete_elem(Elem *e) override final
Removes element e from the mesh.
virtual const Elem * query_elem_ptr(const dof_id_type i) const override final
std::unordered_map< dof_id_type, std::vector< std::vector< Point > > > get_boundary_points() const
Return all points on boundary.
virtual const Elem * elem_ptr(const dof_id_type i) const override final
virtual Elem * add_elem(Elem *e) override final
Add elem e to the end of the element array.
ReplicatedMesh & operator=(const ReplicatedMesh &)=delete
Copy assignment is not allowed.
virtual dof_id_type n_nodes() const override final
unsigned int level ElemType type std::set< subdomain_id_type > ss
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 MeshBase & assign(MeshBase &&other_mesh) override
Shim to call the move assignment operator for this class.
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.
virtual const Point & point(const dof_id_type i) const override final
virtual void set_next_unique_id(unique_id_type id) override final
Sets the next available unique id to be used.
virtual unique_id_type parallel_max_unique_id() const override final
virtual const Node * query_node_ptr(const dof_id_type i) const override final
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...
virtual ~ReplicatedMesh()
Destructor.
The UnstructuredMesh class is derived from the MeshBase class.
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...
UnstructuredMesh & operator=(const UnstructuredMesh &)=delete
Copy assignment is not allowed.
The libMesh namespace provides an interface to certain functionality in the library.
uint8_t unique_id_type
Definition id_types.h:86
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition int_range.h:153
libmesh_assert(ctx)
T command_line_value(const std::string &, T)
Definition libmesh.C:971
uint8_t dof_id_type
Definition id_types.h:67
uint8_t processor_id_type
Definition id_types.h:104