libMesh
Loading...
Searching...
No Matches
distributed_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/distributed_mesh.h"
22
23// libMesh includes
24#include "libmesh/boundary_info.h"
25#include "libmesh/elem.h"
26#include "libmesh/libmesh_logging.h"
27#include "libmesh/mesh_communication.h"
28#include "libmesh/mesh_tools.h"
29#include "libmesh/partitioner.h"
30#include "libmesh/string_to_enum.h"
31
32// TIMPI includes
34#include "timpi/parallel_sync.h"
35
36
37namespace libMesh
38{
39
40// ------------------------------------------------------------
41// DistributedMesh class member functions
43 unsigned char d) :
44 UnstructuredMesh (comm_in,d), _is_serial(true),
45 _is_serial_on_proc_0(true),
46 _deleted_coarse_elements(false),
47 _n_nodes(0), _n_elem(0), _max_node_id(0), _max_elem_id(0),
48 _next_free_local_node_id(this->processor_id()),
49 _next_free_local_elem_id(this->processor_id()),
50 _next_free_unpartitioned_node_id(this->n_processors()),
51 _next_free_unpartitioned_elem_id(this->n_processors())
52#ifdef LIBMESH_ENABLE_UNIQUE_ID
53 , _next_unpartitioned_unique_id(this->n_processors())
54#endif
55{
56#ifdef LIBMESH_ENABLE_UNIQUE_ID
58#endif
59
60 const std::string default_partitioner = "parmetis";
61 const std::string my_partitioner =
62 libMesh::command_line_value("--default-partitioner",
63 default_partitioner);
65 (Utility::string_to_enum<PartitionerType>(my_partitioner));
66}
67
69{
70 LOG_SCOPE("operator=(&&)", "DistributedMesh");
71
72 // Move assign as an UnstructuredMesh.
73 this->UnstructuredMesh::operator=(std::move(other_mesh));
74
75 // Nodes and elements belong to DistributedMesh and have to be
76 // moved before we can move arbitrary GhostingFunctor, Partitioner,
77 // etc. subclasses.
78 this->move_nodes_and_elements(std::move(other_mesh));
79
80 // But move_nodes_and_elems misses (or guesses about) some of our
81 // subclass values, and we want more precision than a guess.
82 _deleted_coarse_elements = other_mesh._deleted_coarse_elements;
83 _extra_ghost_elems = std::move(other_mesh._extra_ghost_elems);
84
85 // Handle remaining MeshBase moves.
86 this->post_dofobject_moves(std::move(other_mesh));
87
88 return *this;
89}
90
92{
93 *this = std::move(cast_ref<DistributedMesh&>(other_mesh));
94
95 return *this;
96}
97
98bool DistributedMesh::subclass_locally_equals(const MeshBase & other_mesh_base) const
99{
100 const DistributedMesh * dist_mesh_ptr =
101 dynamic_cast<const DistributedMesh *>(&other_mesh_base);
102 if (!dist_mesh_ptr)
103 return false;
104 const DistributedMesh & other_mesh = *dist_mesh_ptr;
105
106 if (_is_serial != other_mesh._is_serial ||
109 _n_nodes != other_mesh._n_nodes ||
110 _n_elem != other_mesh._n_elem ||
111 _max_node_id != other_mesh._max_node_id ||
112 _max_elem_id != other_mesh._max_elem_id ||
113 // We expect these things to change in a prepare_for_use();
114 // they're conceptually "mutable"...
115/*
116 _next_free_local_node_id != other_mesh._next_free_local_node_id ||
117 _next_free_local_elem_id != other_mesh._next_free_local_elem_id ||
118 _next_free_unpartitioned_node_id != other_mesh._next_free_unpartitioned_node_id ||
119 _next_free_unpartitioned_elem_id != other_mesh._next_free_unpartitioned_elem_id ||
120#ifdef LIBMESH_ENABLE_UNIQUE_ID
121 _next_unpartitioned_unique_id != other_mesh._next_unpartitioned_unique_id ||
122#endif
123*/
124 !this->nodes_and_elements_equal(other_mesh))
125 return false;
126
127 if (_extra_ghost_elems.size() !=
128 other_mesh._extra_ghost_elems.size())
129 return false;
130 for (auto & elem : _extra_ghost_elems)
131 {
132 libmesh_assert(this->query_elem_ptr(elem->id()) == elem);
133 const Elem * other_elem = other_mesh.query_elem_ptr(elem->id());
134 if (!other_elem ||
135 !other_mesh._extra_ghost_elems.count(const_cast<Elem *>(other_elem)))
136 return false;
137 }
138
139 return true;
140}
141
143{
144 this->DistributedMesh::clear(); // Free nodes and elements
145}
146
147
148// This might be specialized later, but right now it's just here to
149// make sure the compiler doesn't give us a default (non-deep) copy
150// constructor instead.
152 DistributedMesh(static_cast<const MeshBase &>(other_mesh))
153{
154 _is_serial = other_mesh._is_serial;
157
158 _n_nodes = other_mesh.n_nodes();
159 _n_elem = other_mesh.n_elem();
160 _max_node_id = other_mesh.max_node_id();
161 _max_elem_id = other_mesh.max_elem_id();
163 other_mesh._next_free_local_node_id;
165 other_mesh._next_free_local_elem_id;
170#ifdef LIBMESH_ENABLE_UNIQUE_ID
172 other_mesh._next_unique_id;
175#endif
176
177 // Need to copy extra_ghost_elems
178 for (auto & elem : other_mesh._extra_ghost_elems)
179 _extra_ghost_elems.insert(this->elem_ptr(elem->id()));
180}
181
182
183
185 UnstructuredMesh (other_mesh), _is_serial(other_mesh.is_serial()),
186 _is_serial_on_proc_0(other_mesh.is_serial()),
187 _deleted_coarse_elements(true), // better safe than sorry...
188 _n_nodes(0), _n_elem(0), _max_node_id(0), _max_elem_id(0),
189 _next_free_local_node_id(this->processor_id()),
190 _next_free_local_elem_id(this->processor_id()),
191 _next_free_unpartitioned_node_id(this->n_processors()),
192 _next_free_unpartitioned_elem_id(this->n_processors())
193{
194 // Just copy, skipping preparation
195 this->copy_nodes_and_elements(other_mesh, true, 0, 0, 0, nullptr, true);
196
197 this->allow_find_neighbors(other_mesh.allow_find_neighbors());
199 this->allow_renumbering(other_mesh.allow_renumbering());
201 this->skip_partitioning(other_mesh.skip_partitioning());
202
203 this->copy_constraint_rows(other_mesh);
204
205 auto & this_boundary_info = this->get_boundary_info();
206 const auto & other_boundary_info = other_mesh.get_boundary_info();
207
208 this_boundary_info = other_boundary_info;
209
210 this->set_subdomain_name_map() = other_mesh.get_subdomain_name_map();
211
212 this->_preparation = other_mesh.preparation();
213
214#ifdef LIBMESH_ENABLE_UNIQUE_ID
216 this->processor_id();
218 (this->n_processors() - this->processor_id());
219#endif
221}
222
224{
225 DistributedMesh & other_mesh = cast_ref<DistributedMesh&>(other_meshbase);
226
227 this->_nodes = std::move(other_mesh._nodes);
228 this->_n_nodes = other_mesh.n_nodes();
229
230 this->_elements = std::move(other_mesh._elements);
231 this->_n_elem = other_mesh.n_elem();
232
233 _is_serial = other_mesh.is_serial();
235 _deleted_coarse_elements = true; // Better safe than sorry
236
237 _max_node_id = other_mesh.max_node_id();
238 _max_elem_id = other_mesh.max_elem_id();
239
244
245 #ifdef LIBMESH_ENABLE_UNIQUE_ID
247 #endif
248}
249
250// We use cached values for these so they can be called
251// from one processor without bothering the rest, but
252// we may need to update those caches before doing a full
253// renumbering
255{
256 // This function must be run on all processors at once
257 parallel_object_only();
258
259 _n_elem = this->parallel_n_elem();
260 _n_nodes = this->parallel_n_nodes();
263
265 ((_max_elem_id-1) / (this->n_processors() + 1) + 1) *
266 (this->n_processors() + 1) + this->n_processors();
268 ((_max_elem_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
269 (this->n_processors() + 1) + this->processor_id();
270
272 ((_max_node_id-1) / (this->n_processors() + 1) + 1) *
273 (this->n_processors() + 1) + this->n_processors();
275 ((_max_node_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
276 (this->n_processors() + 1) + this->processor_id();
277
278#ifdef LIBMESH_ENABLE_UNIQUE_ID
281 ((_next_unique_id-1) / (this->n_processors() + 1) + 1) *
282 (this->n_processors() + 1) + this->n_processors();
284 ((_next_unique_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
285 (this->n_processors() + 1) + this->processor_id();
286#endif
287
289}
290
291
292// Or in debug mode we may want to test the uncached values without
293// changing the cache
295{
296 // This function must be run on all processors at once
297 parallel_object_only();
298
299 dof_id_type n_local = this->n_local_elem();
300 this->comm().sum(n_local);
301 n_local += this->n_unpartitioned_elem();
302 return n_local;
303}
304
305
306
308{
309 // This function must be run on all processors at once
310 parallel_object_only();
311
312 dof_id_type max_local = 0;
313
315 rit = _elements.rbegin();
316
318 rend = _elements.rend();
319
320 // Look for the maximum element id. Search backwards through
321 // elements so we can break out early. Beware of nullptr entries that
322 // haven't yet been cleared from _elements.
323 for (; rit != rend; ++rit)
324 {
325 const DofObject *d = *rit;
326 if (d)
327 {
328 libmesh_assert(_elements[d->id()] == d);
329 max_local = d->id() + 1;
330 break;
331 }
332 }
333
334 this->comm().max(max_local);
335 return max_local;
336}
337
338
339
340#ifdef LIBMESH_ENABLE_UNIQUE_ID
342{
343 // This function must be run on all processors at once
344 parallel_object_only();
345
346 unique_id_type max_local = std::max(_next_unique_id,
348 this->comm().max(max_local);
349 return max_local;
350}
351
352
353
355{
356 _next_unique_id = id;
358 ((_next_unique_id-1) / (this->n_processors() + 1) + 1) *
359 (this->n_processors() + 1) + this->n_processors();
361 ((_next_unique_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
362 (this->n_processors() + 1) + this->processor_id();
363}
364#endif
365
366
367
369{
370 // This function must be run on all processors at once
371 parallel_object_only();
372
373 dof_id_type n_local = this->n_local_nodes();
374 this->comm().sum(n_local);
375 n_local += this->n_unpartitioned_nodes();
376 return n_local;
377}
378
379
380
382{
383 // This function must be run on all processors at once
384 parallel_object_only();
385
386 dof_id_type max_local = 0;
387
389 rit = _nodes.rbegin();
390
392 rend = _nodes.rend();
393
394 // Look for the maximum node id. Search backwards through
395 // nodes so we can break out early. Beware of nullptr entries that
396 // haven't yet been cleared from _nodes
397 for (; rit != rend; ++rit)
398 {
399 const DofObject *d = *rit;
400 if (d)
401 {
402 libmesh_assert(_nodes[d->id()] == d);
403 max_local = d->id() + 1;
404 break;
405 }
406 }
407
408 this->comm().max(max_local);
409 return max_local;
410}
411
412
413
415{
416 return this->node_ref(i);
417}
418
419
420
422{
424 libmesh_assert_equal_to (_nodes[i]->id(), i);
425
426 return _nodes[i];
427}
428
429
430
431
433{
435 libmesh_assert_equal_to (_nodes[i]->id(), i);
436
437 return _nodes[i];
438}
439
440
441
442
444{
445 if (const auto it = _nodes.find(i);
446 it != _nodes.end())
447 {
448 const Node * n = *it;
449 libmesh_assert (!n || n->id() == i);
450 return n;
451 }
452
453 return nullptr;
454}
455
456
457
458
460{
461 if (auto it = _nodes.find(i);
462 it != _nodes.end())
463 {
464 Node * n = *it;
465 libmesh_assert (!n || n->id() == i);
466 return n;
467 }
468
469 return nullptr;
470}
471
472
473
474
476{
478 libmesh_assert_equal_to (_elements[i]->id(), i);
479
480 return _elements[i];
481}
482
483
484
485
487{
489 libmesh_assert_equal_to (_elements[i]->id(), i);
490
491 return _elements[i];
492}
493
494
495
496
498{
499 if (const auto it = _elements.find(i);
500 it != _elements.end())
501 {
502 const Elem * e = *it;
503 libmesh_assert (!e || e->id() == i);
504 return e;
505 }
506
507 return nullptr;
508}
509
510
511
512
514{
515 if (auto it = _elements.find(i);
516 it != _elements.end())
517 {
518 Elem * e = *it;
519 libmesh_assert (!e || e->id() == i);
520 return e;
521 }
522
523 return nullptr;
524}
525
526
527
528
530{
531 // Don't try to add nullptrs!
533
534 // Trying to add an existing element is a no-op
535 if (e->valid_id() && _elements[e->id()] == e)
536 return e;
537
538 const processor_id_type elem_procid = e->processor_id();
539
540 if (!e->valid_id())
541 {
542 // We should only be creating new ids past the end of the range
543 // of existing ids
544 libmesh_assert_greater_equal(_next_free_unpartitioned_elem_id,
546 libmesh_assert_greater_equal(_next_free_local_elem_id, _max_elem_id);
547
548 // Use the unpartitioned ids for unpartitioned elems, and
549 // temporarily for ghost elems
551 if (elem_procid == this->processor_id())
552 next_id = &_next_free_local_elem_id;
553 e->set_id (*next_id);
554 }
555
556 {
557 // Advance next_ids up high enough that each is pointing to an
558 // unused id and any subsequent increments will still point us
559 // to unused ids
560 _max_elem_id = std::max(_max_elem_id,
561 static_cast<dof_id_type>(e->id()+1));
562
565 ((_max_elem_id-1) / (this->n_processors() + 1) + 1) *
566 (this->n_processors() + 1) + this->n_processors();
569 ((_max_elem_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
570 (this->n_processors() + 1) + this->processor_id();
571
572#ifndef NDEBUG
573 // We need a const dofobject_container so we don't inadvertently create
574 // nullptr entries when testing for non-nullptr ones
575 const dofobject_container<Elem> & const_elements = _elements;
576#endif
579 }
580
581 // Don't try to overwrite existing elems
582 libmesh_assert (!_elements[e->id()]);
583
584 _elements[e->id()] = e;
585
586 // We actually added a new element. Some of our caches might still
587 // be valid, but we should clear the ones which definitely are not.
588 this->clear_point_locator();
589 this->clear_stored_ranges();
590
591 // Try to make the cached elem data more accurate
592 if (elem_procid == this->processor_id() ||
593 elem_procid == DofObject::invalid_processor_id)
594 _n_elem++;
595
596#ifdef LIBMESH_ENABLE_UNIQUE_ID
597 if (!e->valid_unique_id())
598 {
599 if (processor_id() == e->processor_id())
600 {
602 _next_unique_id += this->n_processors() + 1;
603 }
604 else
605 {
608 }
609 }
610 else
611 {
612 _next_unique_id = std::max(_next_unique_id, e->unique_id()+1);
614 ((_next_unique_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
615 (this->n_processors() + 1) + this->processor_id();
616 }
617#endif
618
619 // Unpartitioned elems should be added on every processor
620 // And shouldn't be added in the same batch as ghost elems
621 // But we might be just adding on processor 0 to
622 // broadcast later
623 // #ifdef DEBUG
624 // if (elem_procid == DofObject::invalid_processor_id)
625 // {
626 // dof_id_type elem_id = e->id();
627 // this->comm().max(elem_id);
628 // libmesh_assert_equal_to (elem_id, e->id());
629 // }
630 // #endif
631
632 // Make sure any new element is given space for any extra integers
633 // we've requested
636
637 // And set mapping type and data on any new element
640
641 return e;
642}
643
644
645
646Elem * DistributedMesh::add_elem (std::unique_ptr<Elem> e)
647{
648 // The mesh now takes ownership of the Elem. Eventually the guts of
649 // add_elem() will get moved to a private helper function, and
650 // calling add_elem() directly will be deprecated.
651 return add_elem(e.release());
652}
653
654
655
657{
658 if (_elements[e->id()])
659 this->delete_elem(_elements[e->id()]);
660
661#ifdef LIBMESH_ENABLE_UNIQUE_ID
662 if (!e->valid_unique_id())
663 {
664 if (processor_id() == e->processor_id())
665 {
667 _next_unique_id += this->n_processors() + 1;
668 }
669 else
670 {
673 }
674 }
675 else
676 {
677 _next_unique_id = std::max(_next_unique_id, e->unique_id()+1);
679 ((_next_unique_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
680 (this->n_processors() + 1) + this->processor_id();
681 }
682#endif
683
684 // Try to make the cached elem data more accurate
685 processor_id_type elem_procid = e->processor_id();
686 if (elem_procid == this->processor_id() ||
687 elem_procid == DofObject::invalid_processor_id)
688 _n_elem++;
689
690 _elements[e->id()] = e;
691
692 // We actually added a new element. Some of our caches might still
693 // be valid, but we should clear the ones which definitely are not.
694 this->clear_point_locator();
695 this->clear_stored_ranges();
696
697 // Make sure any new element is given space for any extra integers
698 // we've requested
701
702 // And set mapping type and data on any new element
705
706 return e;
707}
708
709Elem * DistributedMesh::insert_elem (std::unique_ptr<Elem> e)
710{
711 // The mesh now takes ownership of the Elem. Eventually the guts of
712 // insert_elem(Elem*) will get moved to a private helper function, and
713 // calling insert_elem(Elem*) directly will be deprecated.
714 return insert_elem(e.release());
715}
716
717
719{
720 libmesh_assert (e);
721
722 // Try to make the cached elem data more accurate
723 _n_elem--;
724
725 // Was this a coarse element, not just a coarsening where we still
726 // have some ancestor structure? Was it a *local* element, that we
727 // might have been depending on as an owner of local nodes? We'll
728 // have to be more careful with our nodes in contract() later; no
729 // telling if we just locally orphaned a node that should be
730 // globally retained.
731 if (e->processor_id() == this->processor_id() &&
732 !e->parent())
734
735 // Delete the element from the BoundaryInfo object
736 this->get_boundary_info().remove(e);
737
738 // But not yet from the container; we might invalidate
739 // an iterator that way!
740
741 //_elements.erase(e->id());
742
743 // Instead, we set it to nullptr for now
744
745 _elements[e->id()] = nullptr;
746
747 // delete the element
748 delete e;
749
750 // Some of our caches might still be valid, but we should clear the
751 // ones which definitely are not.
752 this->clear_point_locator();
753 this->clear_stored_ranges();
754}
755
756
757
759 const dof_id_type new_id)
760{
761 // This could be a no-op
762 if (old_id == new_id)
763 return;
764
765 Elem * el = _elements[old_id];
766 libmesh_assert (el);
767 libmesh_assert_equal_to (el->id(), old_id);
768
769 el->set_id(new_id);
770 libmesh_assert (!_elements[new_id]);
771 _elements[new_id] = el;
772 _elements.erase(old_id);
773
774 // Should we delete any caches here? Our point locator indexes by
775 // element pointer and should be fine with an id change. Our stored
776 // ranges are no longer sorted, which is *probably* fine, but let's
777 // just be safe.
778 this->clear_stored_ranges();
779}
780
781
782
784 const dof_id_type id,
785 const processor_id_type proc_id)
786{
787 Node * old_n = this->query_node_ptr(id);
788
789 if (old_n)
790 {
791 *old_n = p;
792 old_n->processor_id() = proc_id;
793
794 return old_n;
795 }
796
797 Node * n = Node::build(p, id).release();
798 n->processor_id() = proc_id;
799
801}
802
803
805{
806 // This had better be a node in our mesh
807 libmesh_assert(_nodes[n.id()] == &n);
808
809 _nodes[n.id()] = nullptr;
810 _n_nodes--;
811
813 n.processor_id() = this->processor_id();
814
815 this->add_node(&n);
816}
817
818
820{
821 // Don't try to add nullptrs!
823
824 // Trying to add an existing node is a no-op
825 if (n->valid_id() && _nodes[n->id()] == n)
826 return n;
827
828 const processor_id_type node_procid = n->processor_id();
829
830 if (!n->valid_id())
831 {
832 // We should only be creating new ids past the end of the range
833 // of existing ids
834 libmesh_assert_greater_equal(_next_free_unpartitioned_node_id,
836 libmesh_assert_greater_equal(_next_free_local_node_id, _max_node_id);
837
838 // Use the unpartitioned ids for unpartitioned nodes,
839 // and temporarily for ghost nodes
841 if (node_procid == this->processor_id())
842 next_id = &_next_free_local_node_id;
843 n->set_id (*next_id);
844 }
845
846 {
847 // Advance next_ids up high enough that each is pointing to an
848 // unused id and any subsequent increments will still point us
849 // to unused ids
850 _max_node_id = std::max(_max_node_id,
851 static_cast<dof_id_type>(n->id()+1));
852
855 ((_max_node_id-1) / (this->n_processors() + 1) + 1) *
856 (this->n_processors() + 1) + this->n_processors();
859 ((_max_node_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
860 (this->n_processors() + 1) + this->processor_id();
861
862#ifndef NDEBUG
863 // We need a const dofobject_container so we don't inadvertently create
864 // nullptr entries when testing for non-nullptr ones
865 const dofobject_container<Node> & const_nodes = _nodes;
866#endif
869 }
870
871 // Don't try to overwrite existing nodes
872 libmesh_assert (!_nodes[n->id()]);
873
874 _nodes[n->id()] = n;
875
876 // Try to make the cached node data more accurate
877 if (node_procid == this->processor_id() ||
878 node_procid == DofObject::invalid_processor_id)
879 _n_nodes++;
880
881#ifdef LIBMESH_ENABLE_UNIQUE_ID
882 if (!n->valid_unique_id())
883 {
884 if (processor_id() == n->processor_id())
885 {
887 _next_unique_id += this->n_processors() + 1;
888 }
889 else
890 {
893 }
894 }
895 else
896 {
897 _next_unique_id = std::max(_next_unique_id, n->unique_id()+1);
899 ((_next_unique_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
900 (this->n_processors() + 1) + this->processor_id();
901 }
902#endif
903
906
907 // Unpartitioned nodes should be added on every processor
908 // And shouldn't be added in the same batch as ghost nodes
909 // But we might be just adding on processor 0 to
910 // broadcast later
911 // #ifdef DEBUG
912 // if (node_procid == DofObject::invalid_processor_id)
913 // {
914 // dof_id_type node_id = n->id();
915 // this->comm().max(node_id);
916 // libmesh_assert_equal_to (node_id, n->id());
917 // }
918 // #endif
919
920 return n;
921}
922
923Node * DistributedMesh::add_node (std::unique_ptr<Node> n)
924{
925 // The mesh now takes ownership of the Node. Eventually the guts of
926 // add_node() will get moved to a private helper function, and
927 // calling add_node() directly will be deprecated.
928 return add_node(n.release());
929}
930
932{
934 libmesh_assert(_nodes[n->id()]);
935
936 // Try to make the cached elem data more accurate
937 _n_nodes--;
938
939 // Delete the node from the BoundaryInfo object
940 this->get_boundary_info().remove(n);
941 _constraint_rows.erase(n);
942
943 // But not yet from the container; we might invalidate
944 // an iterator that way!
945
946 //_nodes.erase(n->id());
947
948 // Instead, we set it to nullptr for now
949
950 _nodes[n->id()] = nullptr;
951
952 // delete the node
953 delete n;
954}
955
956
957
959 const dof_id_type new_id)
960{
961 // This could be a no-op
962 if (old_id == new_id)
963 return;
964
965 Node * nd = _nodes[old_id];
966 libmesh_assert (nd);
967 libmesh_assert_equal_to (nd->id(), old_id);
968
969 // If we have nodes shipped to this processor for NodeConstraints
970 // use, then those nodes will exist in _nodes, but may not be
971 // locatable via a TopologyMap due to the insufficiency of elements
972 // connecting to them. If local refinement then wants to create a
973 // *new* node in the same location, it will initially get a temporary
974 // id, and then make_node_ids_parallel_consistent() will try to move
975 // it to the canonical id. We need to account for this case to
976 // avoid false positives and memory leaks.
977#ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
978 if (_nodes[new_id])
979 {
980 libmesh_assert_equal_to (*(Point *)_nodes[new_id],
981 *(Point *)_nodes[old_id]);
982 _nodes.erase(new_id);
983 }
984#else
985 // If we aren't shipping nodes for NodeConstraints, there should be
986 // no reason for renumbering one node onto another.
987 libmesh_assert (!_nodes[new_id]);
988#endif
989 _nodes[new_id] = nd;
990 nd->set_id(new_id);
991
992 _nodes.erase(old_id);
993}
994
995
996
998{
999 // Call parent clear function
1001
1002 // Clear our elements and nodes
1003 // There is no need to remove them from
1004 // the BoundaryInfo data structure since we
1005 // already cleared it.
1007
1008 for (auto & node : _nodes)
1009 delete node;
1010
1011 _nodes.clear();
1012
1013 // We're no longer distributed if we were before
1014 _is_serial = true;
1015 _is_serial_on_proc_0 = true;
1016
1017 // We deleted a ton of coarse elements, but their nodes got deleted too so
1018 // all is copacetic.
1020
1021 // Correct our caches
1022 _n_nodes = 0;
1023 _max_node_id = 0;
1026}
1027
1028
1029
1031{
1032 for (auto & elem : _elements)
1033 delete elem;
1034
1035 _elements.clear();
1036
1037 // Correct our caches
1038 _n_elem = 0;
1039 _max_elem_id = 0;
1042}
1043
1044
1045
1047{
1048 // If this is a truly parallel mesh, go through the redistribution/gather/delete remote steps
1049 if (!this->is_serial())
1050 {
1051 // Construct a MeshCommunication object to actually redistribute the nodes
1052 // and elements according to the partitioner, and then to re-gather the neighbors.
1054 mc.redistribute(*this);
1055
1057
1058 // We ought to still have valid neighbor links; we communicate
1059 // them for newly-redistributed elements
1060 // this->find_neighbors();
1061
1062 // Is this necessary? If we are called from prepare_for_use(), this will be called
1063 // anyway... but users can always call partition directly, in which case we do need
1064 // to call delete_remote_elements()...
1065 //
1066 // Regardless of whether it's necessary, it isn't safe. We
1067 // haven't communicated new node processor_ids yet, and we can't
1068 // delete nodes until we do.
1069 // this->delete_remote_elements();
1070 }
1071 else
1072 // The base class can handle non-distributed things, like
1073 // notifying any GhostingFunctors of changes
1075}
1076
1077
1078
1080{
1081 // this->recalculate_n_partitions();
1082
1083 // Let's do the base class cache clearing first, just in case our
1084 // later computations are ever changed to make use of a local
1085 // elements range cache
1087
1088 // Partitioning changes our numbers of unpartitioned objects
1090}
1091
1092
1093
1094template <typename T>
1096{
1097 // This function must be run on all processors at once
1098 parallel_object_only();
1099
1100 const dof_id_type pmax_node_id = this->parallel_max_node_id();
1101 const dof_id_type pmax_elem_id = this->parallel_max_elem_id();
1102 const dof_id_type pmax_id = std::max(pmax_node_id, pmax_elem_id);
1103
1104 for (dof_id_type i=0; i != pmax_id; ++i)
1105 {
1106 T * obj = objects[i]; // Returns nullptr if there's no map entry
1107
1108 // Local lookups by id should return the requested object
1109 libmesh_assert(!obj || obj->id() == i);
1110
1111 // All processors with an object should agree on id
1112#ifndef NDEBUG
1113 const dof_id_type dofid = obj && obj->valid_id() ?
1114 obj->id() : DofObject::invalid_id;
1115 libmesh_assert(this->comm().semiverify(obj ? &dofid : nullptr));
1116#endif
1117
1118 // All processors with an object should agree on processor id
1119 const dof_id_type procid = obj && obj->valid_processor_id() ?
1120 obj->processor_id() : DofObject::invalid_processor_id;
1121 libmesh_assert(this->comm().semiverify(obj ? &procid : nullptr));
1122
1123 dof_id_type min_procid = procid;
1124 this->comm().min(min_procid);
1125
1126 // Either:
1127 // 1.) I own this elem (min_procid == this->processor_id()) *and* I have a valid pointer to it (obj != nullptr)
1128 // or
1129 // 2.) I don't own this elem (min_procid != this->processor_id()). (In this case I may or may not have a valid pointer to it.)
1130
1131 // Original assert logic
1132 // libmesh_assert (min_procid != this->processor_id() || obj);
1133
1134 // More human-understandable logic...
1136 ((min_procid == this->processor_id()) && obj)
1137 ||
1138 (min_procid != this->processor_id())
1139 );
1140
1141#if defined(LIBMESH_ENABLE_UNIQUE_ID) && !defined(NDEBUG)
1142 // All processors with an object should agree on unique id
1143 const unique_id_type uniqueid = obj ? obj->unique_id() : 0;
1144 libmesh_assert(this->comm().semiverify(obj ? &uniqueid : nullptr));
1145#endif
1146 }
1147}
1148
1149
1150
1156
1157
1158
1160{
1161#ifndef NDEBUG
1162 // This function must be run on all processors at once
1163 parallel_object_only();
1164
1165 dof_id_type pmax_elem_id = this->parallel_max_elem_id();
1166
1167 for (dof_id_type i=0; i != pmax_elem_id; ++i)
1168 {
1169 Elem * el = _elements[i]; // Returns nullptr if there's no map entry
1170
1171 unsigned int p_level = el ? (el->p_level()) : libMesh::invalid_uint;
1172
1173 // All processors with an active element should agree on p level
1174 libmesh_assert(this->comm().semiverify((el && el->active()) ? &p_level : nullptr));
1175 }
1176#endif
1177}
1178
1179
1180
1181
1183{
1184#if defined(LIBMESH_ENABLE_AMR) && !defined(NDEBUG)
1185 // This function must be run on all processors at once
1186 parallel_object_only();
1187
1188 dof_id_type pmax_elem_id = this->parallel_max_elem_id();
1189
1190 for (dof_id_type i=0; i != pmax_elem_id; ++i)
1191 {
1192 Elem * el = _elements[i]; // Returns nullptr if there's no map entry
1193
1194 unsigned int refinement_flag = el ?
1195 static_cast<unsigned int> (el->refinement_flag()) : libMesh::invalid_uint;
1196 unsigned int p_refinement_flag = el ?
1197 static_cast<unsigned int> (el->p_refinement_flag()) : libMesh::invalid_uint;
1198
1199 libmesh_assert(this->comm().semiverify(el ? &refinement_flag : nullptr));
1200
1201 // p refinement flags aren't always kept correct on inactive
1202 // ghost elements
1203 libmesh_assert(this->comm().semiverify((el && el->active()) ? &p_refinement_flag : nullptr));
1204 }
1205#endif // LIBMESH_ENABLE_AMR
1206}
1207
1208
1209
1210template <typename T>
1213{
1214 // This function must be run on all processors at once
1215 parallel_object_only();
1216
1217 typedef typename dofobject_container<T>::veclike_iterator object_iterator;
1218
1219 // In parallel we may not know what objects other processors have.
1220 // Start by figuring out how many
1221 dof_id_type unpartitioned_objects = 0;
1222
1223 std::unordered_map<processor_id_type, dof_id_type>
1224 ghost_objects_from_proc;
1225
1226 object_iterator it = objects.begin();
1227 object_iterator end = objects.end();
1228
1229 while (it != end)
1230 {
1231 T * obj = *it;
1232
1233 // Remove any nullptr container entries while we're here.
1234 if (!obj)
1235 it = objects.erase(it);
1236 else
1237 {
1238 processor_id_type obj_procid = obj->processor_id();
1239 if (obj_procid == DofObject::invalid_processor_id)
1240 unpartitioned_objects++;
1241 else
1242 ghost_objects_from_proc[obj_procid]++;
1243
1244 // Finally, increment the iterator
1245 ++it;
1246 }
1247 }
1248
1249 std::vector<dof_id_type> objects_on_proc(this->n_processors(), 0);
1250 auto this_it = ghost_objects_from_proc.find(this->processor_id());
1251 this->comm().allgather
1252 ((this_it == ghost_objects_from_proc.end()) ?
1253 dof_id_type(0) : this_it->second, objects_on_proc);
1254
1255#ifndef NDEBUG
1256 libmesh_assert(this->comm().verify(unpartitioned_objects));
1257 for (processor_id_type p=0, np=this->n_processors(); p != np; ++p)
1258 if (ghost_objects_from_proc.count(p))
1259 libmesh_assert_less_equal (ghost_objects_from_proc[p], objects_on_proc[p]);
1260 else
1261 libmesh_assert_less_equal (0, objects_on_proc[p]);
1262#endif
1263
1264 // We'll renumber objects in blocks by processor id
1265 std::vector<dof_id_type> first_object_on_proc(this->n_processors());
1266 for (processor_id_type i=1, np=this->n_processors(); i != np; ++i)
1267 first_object_on_proc[i] = first_object_on_proc[i-1] +
1268 objects_on_proc[i-1];
1269 dof_id_type next_id = first_object_on_proc[this->processor_id()];
1270 dof_id_type first_free_id =
1271 first_object_on_proc[this->n_processors()-1] +
1272 objects_on_proc[this->n_processors()-1] +
1273 unpartitioned_objects;
1274
1275 // First set new local object ids and build request sets
1276 // for non-local object ids
1277
1278 // Request sets to send to each processor
1279 std::map<processor_id_type, std::vector<dof_id_type>>
1280 requested_ids;
1281
1282 // We know how many objects live on each processor, so reserve() space for
1283 // each.
1284 auto ghost_end = ghost_objects_from_proc.end();
1285 for (auto p : make_range(this->n_processors()))
1286 if (p != this->processor_id())
1287 {
1288 if (const auto p_it = ghost_objects_from_proc.find(p);
1289 p_it != ghost_end)
1290 requested_ids[p].reserve(p_it->second);
1291 }
1292
1293 end = objects.end();
1294 for (it = objects.begin(); it != end; ++it)
1295 {
1296 T * obj = *it;
1297 if (!obj)
1298 continue;
1299 if (obj->processor_id() == this->processor_id())
1300 obj->set_id(next_id++);
1301 else if (obj->processor_id() != DofObject::invalid_processor_id)
1302 requested_ids[obj->processor_id()].push_back(obj->id());
1303 }
1304
1305 // Next set ghost object ids from other processors
1306
1307 auto gather_functor =
1308 [
1309#ifndef NDEBUG
1310 this,
1311 &first_object_on_proc,
1312 &objects_on_proc,
1313#endif
1314 &objects]
1315 (processor_id_type, const std::vector<dof_id_type> & ids,
1316 std::vector<dof_id_type> & new_ids)
1317 {
1318 std::size_t ids_size = ids.size();
1319 new_ids.resize(ids_size);
1320
1321 for (std::size_t i=0; i != ids_size; ++i)
1322 {
1323 T * obj = objects[ids[i]];
1324 libmesh_assert(obj);
1325 libmesh_assert_equal_to (obj->processor_id(), this->processor_id());
1326 new_ids[i] = obj->id();
1327
1328 libmesh_assert_greater_equal (new_ids[i],
1329 first_object_on_proc[this->processor_id()]);
1330 libmesh_assert_less (new_ids[i],
1331 first_object_on_proc[this->processor_id()] +
1332 objects_on_proc[this->processor_id()]);
1333 }
1334 };
1335
1336 auto action_functor =
1337 [
1338#ifndef NDEBUG
1339 &first_object_on_proc,
1340 &objects_on_proc,
1341#endif
1342 &objects]
1343 (processor_id_type libmesh_dbg_var(pid),
1344 const std::vector<dof_id_type> & ids,
1345 const std::vector<dof_id_type> & data)
1346 {
1347 // Copy the id changes we've now been informed of
1348 for (auto i : index_range(ids))
1349 {
1350 T * obj = objects[ids[i]];
1351 libmesh_assert (obj);
1352 libmesh_assert_equal_to (obj->processor_id(), pid);
1353 libmesh_assert_greater_equal (data[i],
1354 first_object_on_proc[pid]);
1355 libmesh_assert_less (data[i],
1356 first_object_on_proc[pid] +
1357 objects_on_proc[pid]);
1358 obj->set_id(data[i]);
1359 }
1360 };
1361
1362 const dof_id_type * ex = nullptr;
1364 (this->comm(), requested_ids, gather_functor, action_functor, ex);
1365
1366#ifdef LIBMESH_ENABLE_UNIQUE_ID
1367 auto unique_gather_functor =
1368 [
1369#ifndef NDEBUG
1370 this,
1371#endif
1372 &objects]
1373 (processor_id_type, const std::vector<dof_id_type> & ids,
1374 std::vector<unique_id_type> & data)
1375 {
1376 std::size_t ids_size = ids.size();
1377 data.resize(ids_size);
1378
1379 for (std::size_t i=0; i != ids_size; ++i)
1380 {
1381 T * obj = objects[ids[i]];
1382 libmesh_assert(obj);
1383 libmesh_assert_equal_to (obj->processor_id(), this->processor_id());
1384 data[i] = obj->valid_unique_id() ? obj->unique_id() : DofObject::invalid_unique_id;
1385 }
1386 };
1387
1388 auto unique_action_functor =
1389 [&objects]
1390 (processor_id_type libmesh_dbg_var(pid),
1391 const std::vector<dof_id_type> & ids,
1392 const std::vector<unique_id_type> & data)
1393 {
1394 for (auto i : index_range(ids))
1395 {
1396 T * obj = objects[ids[i]];
1397 libmesh_assert (obj);
1398 libmesh_assert_equal_to (obj->processor_id(), pid);
1399 if (!obj->valid_unique_id() && data[i] != DofObject::invalid_unique_id)
1400 obj->set_unique_id(data[i]);
1401 }
1402 };
1403
1404 const unique_id_type * unique_ex = nullptr;
1406 (this->comm(), requested_ids, unique_gather_functor,
1407 unique_action_functor, unique_ex);
1408#endif
1409
1410 // Next set unpartitioned object ids
1411 next_id = 0;
1412 for (auto i : make_range(this->n_processors()))
1413 next_id += objects_on_proc[i];
1414 for (it = objects.begin(); it != end; ++it)
1415 {
1416 T * obj = *it;
1417 if (!obj)
1418 continue;
1419 if (obj->processor_id() == DofObject::invalid_processor_id)
1420 obj->set_id(next_id++);
1421 }
1422
1423 // Finally shuffle around objects so that container indices
1424 // match ids
1425 it = objects.begin();
1426 end = objects.end();
1427 while (it != end)
1428 {
1429 T * obj = *it;
1430 if (obj) // don't try shuffling already-nullptr entries
1431 {
1432 T * next = objects[obj->id()];
1433 // If we have to move this object
1434 if (next != obj)
1435 {
1436 // nullptr out its original position for now
1437 // (our shuffling may put another object there shortly)
1438 *it = nullptr;
1439
1440 // There may already be another object with this id that
1441 // needs to be moved itself
1442 while (next)
1443 {
1444 // We shouldn't be trying to give two objects the
1445 // same id
1446 libmesh_assert_not_equal_to (next->id(), obj->id());
1447 objects[obj->id()] = obj;
1448 obj = next;
1449 next = objects[obj->id()];
1450 }
1451 objects[obj->id()] = obj;
1452 }
1453 }
1454
1455 // Remove any container entries that were left as nullptr.
1456 if (!obj)
1457 it = objects.erase(it);
1458 else
1459 ++it;
1460 }
1461
1462 return first_free_id;
1463}
1464
1465
1467{
1468 parallel_object_only();
1469
1470#ifdef DEBUG
1471 // Make sure our ids and flags are consistent
1475#endif
1476
1477 LOG_SCOPE("renumber_nodes_and_elements()", "DistributedMesh");
1478
1479 // Nodes not connected to any elements, and nullptr node entries
1480 // in our container, should be deleted. But wait! If we've deleted coarse
1481 // local elements on some processor, other processors might have ghosted
1482 // nodes from it that are now no longer connected to any elements on it, but
1483 // that are connected to their own semilocal elements. We'll have to
1484 // communicate to ascertain if that's the case.
1486
1487 // What used nodes do we see on our proc?
1488 std::set<dof_id_type> used_nodes;
1489
1490 // What used node info should we send from our proc? Could we take ownership
1491 // of each node if we needed to?
1492 std::map<processor_id_type, std::map<dof_id_type, bool>>
1493 used_nodes_on_proc;
1494
1495 // flag the nodes we need
1496 for (auto & elem : this->element_ptr_range())
1497 for (const Node & node : elem->node_ref_range())
1498 {
1499 const dof_id_type n = node.id();
1500 used_nodes.insert(n);
1502 {
1503 const processor_id_type p = node.processor_id();
1504 if (p != this->processor_id())
1505 {
1506 auto & used_nodes_on_p = used_nodes_on_proc[p];
1507 if (elem->processor_id() == this->processor_id())
1508 used_nodes_on_p[n] = true;
1509 else
1510 if (!used_nodes_on_p.count(n))
1511 used_nodes_on_p[n] = false;
1512 }
1513 }
1514 }
1515
1517 {
1518 // "unsigned char" == "bool, but MPI::BOOL is iffy to use"
1519 typedef unsigned char boolish;
1520 std::map<processor_id_type, std::vector<std::pair<dof_id_type, boolish>>>
1521 used_nodes_on_proc_vecs;
1522 for (auto & [pid, nodemap] : used_nodes_on_proc)
1523 used_nodes_on_proc_vecs[pid].assign(nodemap.begin(), nodemap.end());
1524
1525 std::map<dof_id_type,processor_id_type> repartitioned_node_pids;
1526 std::map<processor_id_type, std::set<dof_id_type>>
1527 repartitioned_node_sets_to_push;
1528
1529 auto ids_action_functor =
1530 [&used_nodes, &repartitioned_node_pids,
1531 &repartitioned_node_sets_to_push]
1533 const std::vector<std::pair<dof_id_type, boolish>> & ids_and_bools)
1534 {
1535 for (auto [n, sender_could_become_owner] : ids_and_bools)
1536 {
1537 // If we don't see a use for our own node, but someone
1538 // else does, better figure out who should own it next.
1539 if (!used_nodes.count(n))
1540 {
1541 if (auto it = repartitioned_node_pids.find(n);
1542 sender_could_become_owner)
1543 {
1544 if (it != repartitioned_node_pids.end() &&
1545 pid < it->second)
1546 it->second = pid;
1547 else
1548 repartitioned_node_pids[n] = pid;
1549 }
1550 else
1551 if (it == repartitioned_node_pids.end())
1552 repartitioned_node_pids[n] =
1554
1555 repartitioned_node_sets_to_push[pid].insert(n);
1556 }
1557 }
1558 };
1559
1560 // We need two pushes instead of a pull here because we need to
1561 // know *all* the queries for a particular node before we can
1562 // respond to *any* of them.
1564 (this->comm(), used_nodes_on_proc_vecs, ids_action_functor);
1565
1566 // Repartition (what used to be) our own nodes first
1567 for (auto & [n, p] : repartitioned_node_pids)
1568 {
1569 Node & node = this->node_ref(n);
1570 libmesh_assert_equal_to(node.processor_id(), this->processor_id());
1571 libmesh_assert_not_equal_to_msg(p, DofObject::invalid_processor_id, "Node " << n << " is lost?");
1572 node.processor_id() = p;
1573 }
1574
1575 // Then push to repartition others' ghosted copies.
1576
1577 std::map<processor_id_type, std::vector<std::pair<dof_id_type,processor_id_type>>>
1578 repartitioned_node_vecs;
1579
1580 for (auto & [p, nodeset] : repartitioned_node_sets_to_push)
1581 {
1582 auto & rn_vec = repartitioned_node_vecs[p];
1583 for (auto n : nodeset)
1584 rn_vec.emplace_back(n, repartitioned_node_pids[n]);
1585 }
1586
1587 auto repartition_node_functor =
1588 [this]
1589 (processor_id_type libmesh_dbg_var(pid),
1590 const std::vector<std::pair<dof_id_type, processor_id_type>> & ids_and_pids)
1591 {
1592 for (auto [n, p] : ids_and_pids)
1593 {
1594 libmesh_assert_not_equal_to(p, DofObject::invalid_processor_id);
1595 Node & node = this->node_ref(n);
1596 libmesh_assert_equal_to(node.processor_id(), pid);
1597 node.processor_id() = p;
1598 }
1599 };
1600
1602 (this->comm(), repartitioned_node_vecs, repartition_node_functor);
1603 }
1604
1606
1607 // Nodes not connected to any local elements, and nullptr node entries
1608 // in our container, are deleted
1609 {
1610 node_iterator_imp it = _nodes.begin();
1611 node_iterator_imp end = _nodes.end();
1612
1613 while (it != end)
1614 {
1615 Node * nd = *it;
1616 if (!nd)
1617 it = _nodes.erase(it);
1618 else if (!used_nodes.count(nd->id()))
1619 {
1620 // remove any boundary information associated with
1621 // this node
1622 this->get_boundary_info().remove (nd);
1623 _constraint_rows.erase(nd);
1624
1625 // delete the node
1626 delete nd;
1627
1628 it = _nodes.erase(it);
1629 }
1630 else
1631 ++it;
1632 }
1633 }
1634
1636
1638 {
1640 return;
1641 }
1642
1643 // Finally renumber all the elements
1644 _n_elem = this->renumber_dof_objects (this->_elements);
1645
1646 // and all the remaining nodes
1647 _n_nodes = this->renumber_dof_objects (this->_nodes);
1648
1649 // And figure out what IDs we should use when adding new nodes and
1650 // new elements
1652
1653 // Make sure our caches are up to date and our
1654 // DofObjects are well packed
1655#ifdef DEBUG
1656 libmesh_assert_equal_to (this->n_nodes(), this->parallel_n_nodes());
1657 libmesh_assert_equal_to (this->n_elem(), this->parallel_n_elem());
1658 const dof_id_type pmax_node_id = this->parallel_max_node_id();
1659 const dof_id_type pmax_elem_id = this->parallel_max_elem_id();
1660 libmesh_assert_equal_to (this->max_node_id(), pmax_node_id);
1661 libmesh_assert_equal_to (this->max_elem_id(), pmax_elem_id);
1662 libmesh_assert_equal_to (this->n_nodes(), this->max_node_id());
1663 libmesh_assert_equal_to (this->n_elem(), this->max_elem_id());
1664
1665 // Make sure our ids and flags are consistent
1668
1669 // And make sure we've made our numbering monotonic
1671#endif
1672}
1673
1674
1675
1677{
1678 // We can't use range-for here because we need access to the special
1679 // iterators' methods, not just to their dereferenced values.
1680
1681 // Nodes first
1682 for (auto pr = this->_nodes.begin(),
1683 end = this->_nodes.end(); pr != end; ++pr)
1684 {
1685 Node * n = *pr;
1686 if (n != nullptr)
1687 {
1688 const dof_id_type id = pr.index();
1689 n->set_id() = id;
1690 libmesh_assert_equal_to(this->node_ptr(id), n);
1691 }
1692 }
1693
1694 // Elements next
1695 for (auto pr = this->_elements.begin(),
1696 end = this->_elements.end(); pr != end; ++pr)
1697 {
1698 Elem * e = *pr;
1699 if (e != nullptr)
1700 {
1701 const dof_id_type id = pr.index();
1702 e->set_id() = id;
1703 libmesh_assert_equal_to(this->elem_ptr(id), e);
1704 }
1705 }
1706}
1707
1708
1709
1711{
1712 parallel_object_only();
1713
1714 // Get local active elements first
1715 dof_id_type active_elements =
1716 static_cast<dof_id_type>(std::distance (this->active_local_elements_begin(),
1717 this->active_local_elements_end()));
1718 this->comm().sum(active_elements);
1719
1720 // Then add unpartitioned active elements, which should exist on
1721 // every processor
1722 active_elements +=
1723 static_cast<dof_id_type>(std::distance
1724 (this->active_pid_elements_begin(DofObject::invalid_processor_id),
1725 this->active_pid_elements_end(DofObject::invalid_processor_id)));
1726 return active_elements;
1727}
1728
1729
1730
1732{
1733#ifdef DEBUG
1734 // Make sure our neighbor links are all fine
1736
1737 // And our child/parent links, and our flags
1739
1740 // Make sure our ids and flags are consistent
1743
1744 libmesh_assert_equal_to (this->n_nodes(), this->parallel_n_nodes());
1745 libmesh_assert_equal_to (this->n_elem(), this->parallel_n_elem());
1746 const dof_id_type pmax_node_id = this->parallel_max_node_id();
1747 const dof_id_type pmax_elem_id = this->parallel_max_elem_id();
1748 libmesh_assert_equal_to (this->max_node_id(), pmax_node_id);
1749 libmesh_assert_equal_to (this->max_elem_id(), pmax_elem_id);
1750#endif
1751
1752 _is_serial = false;
1753 _is_serial_on_proc_0 = false;
1754
1756
1757 libmesh_assert_equal_to (this->max_elem_id(), this->parallel_max_elem_id());
1758
1759 // Now make sure the containers actually shrink - strip
1760 // any newly-created nullptr voids out of the element array
1763 while (e_it != e_end)
1764 if (!*e_it)
1765 e_it = _elements.erase(e_it);
1766 else
1767 ++e_it;
1768
1771 while (n_it != n_end)
1772 if (!*n_it)
1773 n_it = _nodes.erase(n_it);
1774 else
1775 ++n_it;
1776
1777 // We may have deleted no-longer-connected nodes or coarsened-away
1778 // elements; let's update our caches.
1780
1781 // We may have deleted nodes or elements that were the only local
1782 // representatives of some particular boundary id(s); let's update
1783 // those caches.
1785
1786#ifdef DEBUG
1787 // We might not have well-packed objects if the user didn't allow us
1788 // to renumber
1789 // libmesh_assert_equal_to (this->n_nodes(), this->max_node_id());
1790 // libmesh_assert_equal_to (this->n_elem(), this->max_elem_id());
1791
1792 // Make sure our neighbor links are all fine
1794
1795 // And our child/parent links, and our flags
1797
1798 // Make sure our ids and flags are consistent
1801#endif
1802
1804}
1805
1806
1808{
1809 // First add the elem like normal
1810 add_elem(e);
1811
1812 // Now add it to the set that won't be deleted when we call
1813 // delete_remote_elements()
1814 _extra_ghost_elems.insert(e);
1815}
1816
1817void
1818DistributedMesh::clear_extra_ghost_elems(const std::set<Elem *> & extra_ghost_elems)
1819{
1820 std::set<Elem *> tmp;
1821 std::set_difference(_extra_ghost_elems.begin(), _extra_ghost_elems.end(),
1822 extra_ghost_elems.begin(), extra_ghost_elems.end(),
1823 std::inserter(tmp, tmp.begin()));
1824 _extra_ghost_elems = tmp;
1825}
1826
1828{
1829 if (_is_serial)
1830 return;
1832 _is_serial = true;
1833 _is_serial_on_proc_0 = true;
1834
1835 // Make sure our caches are up to date and our
1836 // DofObjects are well packed
1837#ifdef DEBUG
1838 libmesh_assert_equal_to (this->n_nodes(), this->parallel_n_nodes());
1839 libmesh_assert_equal_to (this->n_elem(), this->parallel_n_elem());
1840 const dof_id_type pmax_node_id = this->parallel_max_node_id();
1841 const dof_id_type pmax_elem_id = this->parallel_max_elem_id();
1842 libmesh_assert_equal_to (this->max_node_id(), pmax_node_id);
1843 libmesh_assert_equal_to (this->max_elem_id(), pmax_elem_id);
1844
1845 // If we've disabled renumbering we can't be sure we're contiguous
1846 // libmesh_assert_equal_to (this->n_nodes(), this->max_node_id());
1847 // libmesh_assert_equal_to (this->n_elem(), this->max_elem_id());
1848
1849 // Make sure our neighbor links are all fine
1851
1852 // Make sure our ids and flags are consistent
1855#endif
1856}
1857
1859{
1861 return;
1862
1863 _is_serial_on_proc_0 = true;
1864 MeshCommunication().gather(0, *this);
1865}
1866
1867
1868} // namespace libMesh
void max(const T &r, T &o, Request &req) const
void min(const T &r, T &o, Request &req) const
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
void regenerate_id_sets()
Clears and regenerates the cached sets of ids.
void remove(const Node *node)
Removes the boundary conditions associated with node node, if any exist.
The DistributedMesh class is derived from the MeshBase class, and is intended to provide identical fu...
virtual Elem * add_elem(Elem *e) override final
Add elem e to the end of the element array.
virtual void delete_elem(Elem *e) override final
Removes element e from the mesh.
dof_id_type parallel_max_node_id() const
virtual dof_id_type n_active_elem() const override final
virtual dof_id_type n_nodes() const override final
dof_id_type _n_nodes
Cached data from the last renumber_nodes_and_elements call.
virtual const Point & point(const dof_id_type i) const override final
virtual void redistribute() override
Redistribute elements between processors.
dof_id_type renumber_dof_objects(dofobject_container< T > &)
Renumber a parallel objects container.
virtual void fix_broken_node_and_element_numbering() override
There is no reason for a user to ever call this function.
virtual void clear() override
Clear all internal data.
dof_id_type _next_free_unpartitioned_node_id
virtual void clear_extra_ghost_elems()
Clears extra ghost elements.
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...
virtual const Elem * elem_ptr(const dof_id_type i) const override final
virtual void delete_node(Node *n) override final
Removes the Node n from the mesh.
virtual const Node * query_node_ptr(const dof_id_type i) const override final
void libmesh_assert_valid_parallel_flags() const
Verify refinement_flag and p_refinement_flag consistency of our elements containers.
void libmesh_assert_valid_parallel_p_levels() const
Verify p_level consistency of our elements containers.
std::set< Elem * > _extra_ghost_elems
These are extra ghost elements that we want to make sure not to delete when we call delete_remote_ele...
virtual void renumber_nodes_and_elements() override
Remove nullptr elements from arrays.
virtual dof_id_type parallel_n_elem() const override
virtual unique_id_type parallel_max_unique_id() const override
virtual dof_id_type max_node_id() const override final
virtual void set_next_unique_id(unique_id_type id) override
Sets the next available unique id to be used.
virtual bool is_serial() const override final
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.
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 dof_id_type parallel_n_nodes() const override
const std::set< Elem * > & extra_ghost_elems() const
Const accessor to the ghosted elements.
dof_id_type _next_free_unpartitioned_elem_id
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...
DistributedMesh & operator=(const DistributedMesh &)=delete
Copy assignment is not allowed.
virtual dof_id_type max_elem_id() const override final
dofobject_container< Elem > _elements
The elements in the mesh.
processor_id_type pid unsigned int level std::set< subdomain_id_type > ss 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 dofobject_container< Node > _nodes
Move node and elements from a DistributedMesh.
virtual void clear_elems() override
Clear internal Elem data.
virtual void update_post_partitioning() override
Recalculate cached data after elements and nodes have been repartitioned.
virtual ~DistributedMesh()
Destructor.
virtual bool is_serial_on_zero() const override final
dof_id_type parallel_max_elem_id() const
virtual void delete_remote_elements() override
Deletes all nonlocal elements of the mesh except for "ghosts" which touch a local element,...
dof_id_type _next_free_local_node_id
Guaranteed globally unused IDs for use when adding new nodes or elements.
virtual void add_extra_ghost_elem(Elem *e)
Inserts the element and adds it to a list of elements that should not get deleted or have their desce...
bool _is_serial
A boolean remembering whether we're serialized or not.
bool _deleted_coarse_elements
A boolean remembering whether we've recently deleted top-level elements or not.
DistributedMesh(const Parallel::Communicator &comm_in, unsigned char dim=1)
Constructor.
virtual void own_node(Node &n) override final
Takes ownership of node n on this partition of a distributed mesh, by setting n.processor_id() to thi...
virtual dof_id_type n_elem() const override final
virtual void update_parallel_id_counts() override
Updates parallel caches so that methods like n_elem() accurately reflect changes on other processors.
virtual void libmesh_assert_valid_parallel_ids() const override
Verify id and processor_id consistency of our elements and nodes containers.
virtual const Elem * query_elem_ptr(const dof_id_type i) const override final
unique_id_type _next_unpartitioned_unique_id
The next available unique id for assigning ids to unpartitioned DOF objects.
virtual void gather_to_zero() override
Gathers all elements and nodes of the mesh onto processor zero.
virtual void allgather() override
Gathers all elements and nodes of the mesh onto every processor.
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 ...
void libmesh_assert_valid_parallel_object_ids(const dofobject_container< T > &) const
Verify id, processor_id, and if applicable unique_id consistency of a parallel objects container.
bool _is_serial_on_proc_0
A boolean remembering whether we're serialized to proc 0 or not.
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.
virtual const Node * node_ptr(const dof_id_type i) const override final
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
static constexpr unique_id_type invalid_unique_id
An invalid unique_id to distinguish an uninitialized DofObject.
Definition dof_object.h:478
bool valid_unique_id() const
Definition dof_object.h:869
dof_id_type id() const
Definition dof_object.h:819
static constexpr processor_id_type invalid_processor_id
An invalid processor_id to distinguish DoFs that have not been assigned to a processor.
Definition dof_object.h:484
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
RefinementState refinement_flag() const
Definition elem.h:3227
bool active() const
Definition elem.h:2958
const Elem * parent() const
Definition elem.h:3047
void set_mapping_type(const ElemMappingType type)
Sets the value of the mapping type for the element.
Definition elem.h:3145
unsigned int p_level() const
Definition elem.h:3125
void set_mapping_data(const unsigned char data)
Sets the value of the mapping data for the element.
Definition elem.h:3161
RefinementState p_refinement_flag() const
Definition elem.h:3243
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
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
bool skip_partitioning() const
Definition mesh_base.h:1431
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
virtual void redistribute()
Redistribute elements between processors.
Definition mesh_base.C:1168
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
dof_id_type n_unpartitioned_elem() const
Definition mesh_base.h:703
virtual void update_post_partitioning()
Recalculate any cached data (or invalidate any caches that are computed on the fly) after elements an...
Definition mesh_base.C:1180
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
dof_id_type n_local_elem() const
Definition mesh_base.h:697
dof_id_type n_unpartitioned_nodes() const
Definition mesh_base.h:597
virtual void clear()
Deletes all the element and node data that is currently stored.
Definition mesh_base.C:1036
dof_id_type n_local_nodes() const
Definition mesh_base.h:591
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
virtual unique_id_type parallel_max_unique_id() const =0
This is the MeshCommunication class.
void delete_remote_elements(DistributedMesh &, const std::set< Elem * > &) const
This method takes an input DistributedMesh which may be distributed among all the processors.
void redistribute(DistributedMesh &mesh, bool newly_coarsened_only=false) const
This method takes a parallel distributed mesh and redistributes the elements.
void allgather(MeshBase &mesh) const
This method takes an input DistributedMesh which may be distributed among all the processors.
void gather(const processor_id_type root_id, MeshBase &) 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
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
processor_id_type n_processors() 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 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.
virtual void move_nodes_and_elements(MeshBase &&other_mesh)=0
Move node and elements from other_mesh to this mesh.
This mapvector templated class is intended to provide the performance characteristics of a std::map w...
Definition mapvector.h:45
veclike_iterator begin()
Definition mapvector.h:193
void erase(index_t i)
Definition mapvector.h:183
veclike_iterator end()
Definition mapvector.h:203
void pull_parallel_vector_data(const Communicator &comm, const MapToVectors &queries, GatherFunctor &gather_data, const ActionFunctor &act_on_data, const datum *example)
void push_parallel_vector_data(const Communicator &comm, MapToVectors &&data, const ActionFunctor &act_on_data)
void libmesh_assert_valid_elem_ids(const MeshBase &mesh)
A function for verifying that ids and processor assignment of elements are correctly sorted (monotone...
void libmesh_assert_valid_neighbors(const MeshBase &mesh, bool assert_valid_remote_elems=true)
A function for verifying that neighbor connectivity is correct (each element is a neighbor of or desc...
void libmesh_assert_valid_refinement_tree(const MeshBase &mesh)
A function for verifying that elements on this processor have valid descendants and consistent active...
The libMesh namespace provides an interface to certain functionality in the library.
uint8_t unique_id_type
Definition id_types.h:86
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition int_range.h:153
libmesh_assert(ctx)
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
Definition libmesh.h:303
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
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition int_range.h:176