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}
676
677
678
679#ifdef LIBMESH_ENABLE_UNIQUE_ID
681{
682 // This function must be run on all processors at once
683 parallel_object_only();
684
686 this->comm().max(max_local);
687 return max_local;
688}
689
690
691
696#endif
697
698
699
701{
702 LOG_SCOPE("renumber_nodes_and_elem()", "Mesh");
703
704 // node and element id counters
705 dof_id_type next_free_elem = 0;
706 dof_id_type next_free_node = 0;
707
708 // Will hold the set of nodes that are currently connected to elements
709 std::unordered_set<Node *> connected_nodes;
710
711 // Loop over the elements. Note that there may
712 // be nullptrs in the _elements vector from the coarsening
713 // process. Pack the elements in to a contiguous array
714 // and then trim any excess.
715 {
716 std::vector<Elem *>::iterator in = _elements.begin();
717 std::vector<Elem *>::iterator out_iter = _elements.begin();
718 const std::vector<Elem *>::iterator end = _elements.end();
719
720 for (; in != end; ++in)
721 if (*in != nullptr)
722 {
723 Elem * el = *in;
724
725 *out_iter = *in;
726 ++out_iter;
727
728 // Increment the element counter
729 el->set_id (next_free_elem++);
730
732 {
733 // Add this elements nodes to the connected list
734 for (auto & n : el->node_ref_range())
735 connected_nodes.insert(&n);
736 }
737 else // We DO want node renumbering
738 {
739 // Loop over this element's nodes. Number them,
740 // if they have not been numbered already. Also,
741 // position them in the _nodes vector so that they
742 // are packed contiguously from the beginning.
743 for (auto & n : el->node_ref_range())
744 if (n.id() == next_free_node) // don't need to process
745 next_free_node++; // [(src == dst) below]
746
747 else if (n.id() > next_free_node) // need to process
748 {
749 // The source and destination indices
750 // for this node
751 const dof_id_type src_idx = n.id();
752 const dof_id_type dst_idx = next_free_node++;
753
754 // ensure we want to swap a valid nodes
755 libmesh_assert(_nodes[src_idx]);
756
757 // Swap the source and destination nodes
758 std::swap(_nodes[src_idx],
759 _nodes[dst_idx] );
760
761 // Set proper indices where that makes sense
762 if (_nodes[src_idx] != nullptr)
763 _nodes[src_idx]->set_id (src_idx);
764 _nodes[dst_idx]->set_id (dst_idx);
765 }
766 }
767 }
768
769 // Erase any additional storage. These elements have been
770 // copied into nullptr voids by the procedure above, and are
771 // thus repeated and unnecessary.
772 _elements.erase (out_iter, end);
773 }
774
775
777 {
778 // Loop over the nodes. Note that there may
779 // be nullptrs in the _nodes vector from the coarsening
780 // process. Pack the nodes in to a contiguous array
781 // and then trim any excess.
782
783 std::vector<Node *>::iterator in = _nodes.begin();
784 std::vector<Node *>::iterator out_iter = _nodes.begin();
785 const std::vector<Node *>::iterator end = _nodes.end();
786
787 for (; in != end; ++in)
788 if (*in != nullptr)
789 {
790 // This is a reference so that if we change the pointer it will change in the vector
791 Node * & nd = *in;
792
793 // If this node is still connected to an elem, put it in the list
794 if (connected_nodes.count(nd))
795 {
796 *out_iter = nd;
797 ++out_iter;
798
799 // Increment the node counter
800 nd->set_id (next_free_node++);
801 }
802 else // This node is orphaned, delete it!
803 {
804 this->get_boundary_info().remove (nd);
805 _constraint_rows.erase(nd);
806
807 // delete the node
808 --_n_nodes;
809 delete nd;
810 nd = nullptr;
811 }
812 }
813
814 // Erase any additional storage. Whatever was
815 _nodes.erase (out_iter, end);
816 }
817 else // We really DO want node renumbering
818 {
819 // Any nodes in the vector >= _nodes[next_free_node]
820 // are not connected to any elements and may be deleted
821 // if desired.
822
823 // Now, delete the unused nodes
824 {
825 std::vector<Node *>::iterator nd = _nodes.begin();
826 const std::vector<Node *>::iterator end = _nodes.end();
827
828 std::advance (nd, next_free_node);
829
830 for (auto & node : as_range(nd, end))
831 {
832 // Mesh modification code might have already deleted some
833 // nodes
834 if (node == nullptr)
835 continue;
836
837 // remove any boundary information associated with
838 // this node
839 this->get_boundary_info().remove (node);
840 _constraint_rows.erase(node);
841
842 // delete the node
843 --_n_nodes;
844 delete node;
845 node = nullptr;
846 }
847
848 _nodes.erase (nd, end);
849 }
850 }
851
853
854 libmesh_assert_equal_to (next_free_elem, _elements.size());
855 libmesh_assert_equal_to (next_free_node, _nodes.size());
856
858}
859
860
861
863{
864 // Nodes first
865 for (auto n : index_range(_nodes))
866 if (this->_nodes[n] != nullptr)
867 this->_nodes[n]->set_id() = cast_int<dof_id_type>(n);
868
869 // Elements next
870 for (auto e : index_range(_elements))
871 if (this->_elements[e] != nullptr)
872 this->_elements[e]->set_id() = cast_int<dof_id_type>(e);
873}
874
875
877{
878 return static_cast<dof_id_type>(std::distance (this->active_elements_begin(),
879 this->active_elements_end()));
880}
881
882std::vector<dof_id_type>
883ReplicatedMesh::get_disconnected_subdomains(std::vector<subdomain_id_type> * subdomain_ids) const
884{
885 // find number of disconnected subdomains
886 std::vector<dof_id_type> representative_elem_ids;
887
888 // use subdomain_ids as markers for all elements to indicate if the elements
889 // have been visited. Note: here subdomain ID is unrelated with element
890 // subdomain_id().
891 std::vector<subdomain_id_type> subdomains;
892 if (!subdomain_ids)
893 subdomain_ids = &subdomains;
894 subdomain_ids->clear();
896
897 // counter of disconnected subdomains
898 subdomain_id_type subdomain_counter = 0;
899
900 // a stack for visiting elements, make its capacity sufficiently large to avoid
901 // memory allocation and deallocation when the vector size changes
902 std::vector<const Elem *> list;
903 list.reserve(n_elem());
904
905 // counter of visited elements
906 dof_id_type visited = 0;
907 dof_id_type n_active = n_active_elem();
908 do
909 {
910 for (const auto & elem : active_element_ptr_range())
911 if ((*subdomain_ids)[elem->id()] == Elem::invalid_subdomain_id)
912 {
913 list.push_back(elem);
914 (*subdomain_ids)[elem->id()] = subdomain_counter;
915 break;
916 }
917 // we should be able to find a seed here
918 libmesh_assert(list.size() > 0);
919
920 dof_id_type min_id = std::numeric_limits<dof_id_type>::max();
921 while (list.size() > 0)
922 {
923 // pop up an element
924 const Elem * elem = list.back(); list.pop_back(); ++visited;
925
926 min_id = std::min(elem->id(), min_id);
927
928 for (auto s : elem->side_index_range())
929 {
930 const Elem * neighbor = elem->neighbor_ptr(s);
931 if (neighbor != nullptr && (*subdomain_ids)[neighbor->id()] == Elem::invalid_subdomain_id)
932 {
933 // neighbor must be active
934 libmesh_assert(neighbor->active());
935 list.push_back(neighbor);
936 (*subdomain_ids)[neighbor->id()] = subdomain_counter;
937 }
938 }
939 }
940
941 representative_elem_ids.push_back(min_id);
942 subdomain_counter++;
943 }
944 while (visited != n_active);
945
946 return representative_elem_ids;
947}
948
949std::unordered_map<dof_id_type, std::vector<std::vector<Point>>>
951{
952 libmesh_error_msg_if(mesh_dimension() != 2,
953 "Error: get_boundary_points only works for 2D now");
954
955 // find number of disconnected subdomains
956 // subdomains will hold the IDs of disconnected subdomains for all elements.
957 std::vector<subdomain_id_type> subdomains;
958 std::vector<dof_id_type> elem_ids = get_disconnected_subdomains(&subdomains);
959
960 std::unordered_map<dof_id_type, std::vector<std::vector<Point>>> boundary_points;
961
962 // get all boundary sides that are to be erased later during visiting
963 // use a comparison functor to avoid run-time randomness due to pointers
964 struct boundary_side_compare
965 {
966 bool operator()(const std::pair<const Elem *, unsigned int> & lhs,
967 const std::pair<const Elem *, unsigned int> & rhs) const
968 {
969 if (lhs.first->id() < rhs.first->id())
970 return true;
971 else if (lhs.first->id() == rhs.first->id())
972 {
973 if (lhs.second < rhs.second)
974 return true;
975 }
976 return false;
977 }
978 };
979 std::set<std::pair<const Elem *, unsigned int>, boundary_side_compare> boundary_elements;
980 for (const auto & elem : active_element_ptr_range())
981 for (auto s : elem->side_index_range())
982 if (elem->neighbor_ptr(s) == nullptr)
983 boundary_elements.insert(std::pair<const Elem *, unsigned int>(elem, s));
984
985 while (!boundary_elements.empty())
986 {
987 // get the first entry as the seed
988 const Elem * eseed = boundary_elements.begin()->first;
989 unsigned int sseed = boundary_elements.begin()->second;
990
991 // get the subdomain ID that these boundary sides attached to
992 subdomain_id_type subdomain_id = subdomains[eseed->id()];
993
994 // start visiting the mesh to find all boundary nodes with the seed
995 std::vector<Point> bpoints;
996 const Elem * elem = eseed;
997 unsigned int s = sseed;
998 std::vector<unsigned int> local_side_nodes = elem->nodes_on_side(s);
999 while (true)
1000 {
1001 std::pair<const Elem *, unsigned int> side(elem, s);
1002 libmesh_assert(boundary_elements.count(side));
1003 boundary_elements.erase(side);
1004
1005 // push all nodes on the side except the node on the other end of the side (index 1)
1006 for (auto i : index_range(local_side_nodes))
1007 if (i != 1)
1008 bpoints.push_back(*static_cast<const Point *>(elem->node_ptr(local_side_nodes[i])));
1009
1010 // use the last node to find next element and side
1011 const Node * node = elem->node_ptr(local_side_nodes[1]);
1012 std::set<const Elem *> neighbors;
1013 elem->find_point_neighbors(*node, neighbors);
1014
1015 // if only one neighbor is found (itself), this node is a cornor node on boundary
1016 if (neighbors.size() != 1)
1017 neighbors.erase(elem);
1018
1019 // find the connecting side
1020 bool found = false;
1021 for (const auto & neighbor : neighbors)
1022 {
1023 for (auto ss : neighbor->side_index_range())
1024 if (neighbor->neighbor_ptr(ss) == nullptr && !(elem == neighbor && s == ss))
1025 {
1026 local_side_nodes = neighbor->nodes_on_side(ss);
1027 // we expect the starting point of the side to be the same as the end of the previous side
1028 if (neighbor->node_ptr(local_side_nodes[0]) == node)
1029 {
1030 elem = neighbor;
1031 s = ss;
1032 found = true;
1033 break;
1034 }
1035 else if (neighbor->node_ptr(local_side_nodes[1]) == node)
1036 {
1037 elem = neighbor;
1038 s = ss;
1039 found = true;
1040 // flip nodes in local_side_nodes because the side is in an opposite direction
1041 auto temp(local_side_nodes);
1042 local_side_nodes[0] = temp[1];
1043 local_side_nodes[1] = temp[0];
1044 for (unsigned int i = 2; i < temp.size(); ++i)
1045 local_side_nodes[temp.size() + 1 - i] = temp[i];
1046 break;
1047 }
1048 }
1049 if (found)
1050 break;
1051 }
1052
1053 libmesh_error_msg_if(!found, "ERROR: mesh topology error on visiting boundary sides");
1054
1055 // exit if we reach the starting point
1056 if (elem == eseed && s == sseed)
1057 break;
1058 }
1059 boundary_points[elem_ids[subdomain_id]].push_back(bpoints);
1060 }
1061
1062 return boundary_points;
1063}
1064
1065} // 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:871
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:861
processor_id_type processor_id() const
Definition dof_object.h:881
dof_id_type & set_id()
Definition dof_object.h:827
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:835
bool valid_unique_id() const
Definition dof_object.h:869
dof_id_type id() const
Definition dof_object.h:819
void set_unique_id(unique_id_type new_id)
Sets the unique_id for this DofObject.
Definition dof_object.h:848
This is the base class from which all geometric element types are derived.
Definition elem.h:96
bool active() const
Definition elem.h:2958
SimpleRange< NodeRefIter > node_ref_range()
Returns a range with all nodes of an element, usable in range-based for loops.
Definition elem.h:2682
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:967
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:2516
void set_mapping_type(const ElemMappingType type)
Sets the value of the mapping type for the element.
Definition elem.h:3145
void set_mapping_data(const unsigned char data)
Sets the value of the mapping data for the element.
Definition elem.h:3161
const Elem * neighbor_ptr(unsigned int i) const
Definition elem.h:2615
IntRange< unsigned short > side_index_range() const
Definition elem.h:2727
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:1378
virtual const Node & node_ref(const dof_id_type i) const
Definition mesh_base.h:745
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:1356
unsigned int mesh_dimension() const
Definition mesh_base.C:430
bool skip_partitioning() const
Definition mesh_base.h:1431
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:1126
bool allow_detect_interior_parents() const
Definition mesh_base.h:1370
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:1362
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:1355
const std::map< subdomain_id_type, std::string > & get_subdomain_name_map() const
Definition mesh_base.h:1926
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:941
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:2389
unique_id_type _next_unique_id
The next available unique id for assigning ids to DOF objects.
Definition mesh_base.h:2245
bool _skip_renumber_nodes_and_elements
If this is true then renumbering will be kept to a minimum.
Definition mesh_base.h:2270
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:2371
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:2377
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:2442
std::unique_ptr< Partitioner > _partitioner
A partitioner to use at each prepare_for_use().
Definition mesh_base.h:2239
bool allow_remote_element_removal() const
Definition mesh_base.h:1379
bool allow_find_neighbors() const
Definition mesh_base.h:1363
unsigned char default_mapping_data() const
Returns any default data value used by the master space to physical space mapping.
Definition mesh_base.h:959
void clear_point_locator()
Releases the current PointLocator object.
Definition mesh_base.C:1866
void copy_constraint_rows(const MeshBase &other_mesh)
Copy the constraints from the other mesh to this mesh.
Definition mesh_base.C:2490
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:2383
std::map< subdomain_id_type, std::string > & set_subdomain_name_map()
Definition mesh_base.h:1924
Preparation _preparation
Flags indicating in what ways this mesh has been prepared.
Definition mesh_base.h:2195
void skip_partitioning(bool skip)
If true is passed in then nothing on this mesh will be (re)partitioned.
Definition mesh_base.h:1429
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:1369
void post_dofobject_moves(MeshBase &&other_mesh)
Moves any superclass data (e.g.
Definition mesh_base.C:2396
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:1969
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:40
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