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 /*bool other_is_distributed_type=*/true)
154{
157 other_mesh._next_free_local_node_id;
159 other_mesh._next_free_local_elem_id;
164#ifdef LIBMESH_ENABLE_UNIQUE_ID
166 other_mesh._next_unique_id;
169#endif
170
171 // Need to copy extra_ghost_elems
172 for (auto & elem : other_mesh._extra_ghost_elems)
173 _extra_ghost_elems.insert(this->elem_ptr(elem->id()));
174}
175
176
177
178DistributedMesh::DistributedMesh (const MeshBase & other_mesh, bool other_is_distributed_type) :
179 UnstructuredMesh (other_mesh), _is_serial(other_mesh.is_serial()),
180 _is_serial_on_proc_0(other_mesh.is_serial_on_zero()),
181 _deleted_coarse_elements(true), // better safe than sorry...
182 _n_nodes(0), _n_elem(0), _max_node_id(0), _max_elem_id(0), // recomputed below
183 _next_free_local_node_id(this->processor_id()),
184 _next_free_local_elem_id(this->processor_id()),
185 _next_free_unpartitioned_node_id(this->n_processors()),
186 _next_free_unpartitioned_elem_id(this->n_processors())
187{
188 // Just copy, skipping preparation
189 this->copy_nodes_and_elements(other_mesh, true, 0, 0, 0, nullptr, true);
190
191 this->allow_find_neighbors(other_mesh.allow_find_neighbors());
193 this->allow_renumbering(other_mesh.allow_renumbering());
195 this->skip_partitioning(other_mesh.skip_partitioning());
196
197 this->copy_constraint_rows(other_mesh);
198
199 auto & this_boundary_info = this->get_boundary_info();
200 const auto & other_boundary_info = other_mesh.get_boundary_info();
201
202 this_boundary_info = other_boundary_info;
203
204 this->set_subdomain_name_map() = other_mesh.get_subdomain_name_map();
205
206 // add_node()/add_elem() only increment _n_nodes/_n_elem for
207 // nodes/elements they consider locally owned (or unpartitioned), so after
208 // copying only the objects visible to this processor, those counts
209 // reflect an incomplete local view rather than other_mesh's totals,
210 // and need to be copied afresh.
211 _n_nodes = other_mesh.n_nodes();
212 _n_elem = other_mesh.n_elem();
213 _max_node_id = other_mesh.max_node_id();
214 _max_elem_id = other_mesh.max_elem_id();
215
216 // If other_mesh is actually a DistributedMesh, we can just copy its raw
217 // counters directly, mirroring other_mesh's own bookkeeping exactly. This
218 // is done in the DistributedMesh copy constructor.
219 // For any other MeshBase subclass (e.g. ReplicatedMesh) we have no
220 // raw counterpart fields to copy, so derive the free-id-range
221 // counters from _max_elem_id/_max_node_id the same way
222 // update_parallel_id_counts() does via the set_next_ids() method. For
223 // the unique id counters we copy other_mesh's own local next_unique_id()
224 // and derive the next available unique ids
225 // (partitioned and unpartitioned) via set_next_unique_ids().
226 if (!other_is_distributed_type)
227 {
228 this->set_next_ids();
229#ifdef LIBMESH_ENABLE_UNIQUE_ID
230 this->set_next_unique_ids(other_mesh.next_unique_id());
231#endif
232 }
233
234 // Copy other_mesh's actual preparation state, including whatever it
235 // reports for has_synched_id_counts: the counts above are a faithful copy
236 // of other_mesh's own (possibly not-yet-synced) counts, not a freshly
237 // verified computation, so our synced-ness should match other_mesh's.
238 this->_preparation = other_mesh.preparation();
239}
240
242{
243 DistributedMesh & other_mesh = cast_ref<DistributedMesh&>(other_meshbase);
244
245 this->_nodes = std::move(other_mesh._nodes);
246 this->_n_nodes = other_mesh.n_nodes();
247
248 this->_elements = std::move(other_mesh._elements);
249 this->_n_elem = other_mesh.n_elem();
250
251 _is_serial = other_mesh.is_serial();
253 _deleted_coarse_elements = true; // Better safe than sorry
254
255 _max_node_id = other_mesh.max_node_id();
256 _max_elem_id = other_mesh.max_elem_id();
257
262
263 #ifdef LIBMESH_ENABLE_UNIQUE_ID
265 #endif
266}
267
269{
271 ((_max_elem_id-1) / (this->n_processors() + 1) + 1) *
272 (this->n_processors() + 1) + this->n_processors();
274 ((_max_elem_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
275 (this->n_processors() + 1) + this->processor_id();
276
278 ((_max_node_id-1) / (this->n_processors() + 1) + 1) *
279 (this->n_processors() + 1) + this->n_processors();
281 ((_max_node_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
282 (this->n_processors() + 1) + this->processor_id();
283}
284
285#ifdef LIBMESH_ENABLE_UNIQUE_ID
287{
288 // Unique ids are laid out in repeating groups of (n_processors()+1)
289 // consecutive integers ("alignment" here means a value's position, i.e.
290 // its residue mod (n_processors()+1), within one of these groups). In
291 // every group, the first n_processors() slots (residues 0..n_processors()-1)
292 // are each permanently earmarked for one processor's locally-owned
293 // objects, and the last slot (residue n_processors()) is permanently
294 // earmarked for unpartitioned objects.
295 //
296 // parallel_max_unique_id carries no guarantee of landing on any
297 // particular residue, so rather than simply offsetting it by
298 // processor_id()/n_processors(), we have to explicitly round up to the
299 // start of the next full, entirely-unused group before adding this
300 // processor's (or the unpartitioned pool's) fixed offset into that group.
301 //
302 // (parallel_max_unique_id - 1) / (n_processors()+1) + 1 is the standard
303 // integer idiom for ceil(parallel_max_unique_id / (n_processors()+1)):
304 // the number of complete groups needed to reach at least past the
305 // current max id. Multiplying back by (n_processors()+1) gives the first
306 // id of the next group that is guaranteed to be entirely unused; adding
307 // n_processors(), the unpartitioned pool's fixed offset within a group,
308 // lands on that pool's reserved slot in that group.
310 ((parallel_max_unique_id - 1) / (this->n_processors() + 1) + 1) *
311 (this->n_processors() + 1) +
312 this->n_processors();
313
314 // Same idiom, but shifted by (n_processors()-1) before dividing so that
315 // the rounding accounts for processor_id() possibly being less than the
316 // residue the raw max id already occupies within its group; this can
317 // land this processor's slot in the same group as the unpartitioned
318 // pool's above, or push it into the following group, depending on where
319 // the max id and this processor's offset happen to fall.
321 ((parallel_max_unique_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
322 (this->n_processors() + 1) +
323 this->processor_id();
324}
325#endif
326
327// We use cached values for these so they can be called
328// from one processor without bothering the rest, but
329// we may need to update those caches before doing a full
330// renumbering
332{
333 // This function must be run on all processors at once
334 parallel_object_only();
335
336 _n_elem = this->parallel_n_elem();
337 _n_nodes = this->parallel_n_nodes();
340
341 this->set_next_ids();
342
343 // set_next_unique_ids() needs the true parallel max unique id (not just
344 // this processor's possibly-stale cached _next_unique_id) since the whole
345 // point of this function is to recompute counters that may have drifted
346 // out of sync.
347#ifdef LIBMESH_ENABLE_UNIQUE_ID
349#endif
350
352
354}
355
356
357// Or in debug mode we may want to test the uncached values without
358// changing the cache
360{
361 // This function must be run on all processors at once
362 parallel_object_only();
363
364 dof_id_type n_local = this->n_local_elem();
365 this->comm().sum(n_local);
366 n_local += this->n_unpartitioned_elem();
367 return n_local;
368}
369
370
371
373{
374 // This function must be run on all processors at once
375 parallel_object_only();
376
377 dof_id_type max_local = 0;
378
380 rit = _elements.rbegin();
381
383 rend = _elements.rend();
384
385 // Look for the maximum element id. Search backwards through
386 // elements so we can break out early. Beware of nullptr entries that
387 // haven't yet been cleared from _elements.
388 for (; rit != rend; ++rit)
389 {
390 const DofObject *d = *rit;
391 if (d)
392 {
393 libmesh_assert(_elements[d->id()] == d);
394 max_local = d->id() + 1;
395 break;
396 }
397 }
398
399 this->comm().max(max_local);
400 return max_local;
401}
402
403
404
405#ifdef LIBMESH_ENABLE_UNIQUE_ID
407{
408 // This function must be run on all processors at once
409 parallel_object_only();
410
411 unique_id_type max_local = std::max(_next_unique_id,
413 this->comm().max(max_local);
414 return max_local;
415}
416
417
418
420{
421 _next_unique_id = id;
423 ((_next_unique_id-1) / (this->n_processors() + 1) + 1) *
424 (this->n_processors() + 1) + this->n_processors();
426 ((_next_unique_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
427 (this->n_processors() + 1) + this->processor_id();
428}
429#endif
430
431
432
434{
435 // This function must be run on all processors at once
436 parallel_object_only();
437
438 dof_id_type n_local = this->n_local_nodes();
439 this->comm().sum(n_local);
440 n_local += this->n_unpartitioned_nodes();
441 return n_local;
442}
443
444
445
447{
448 // This function must be run on all processors at once
449 parallel_object_only();
450
451 dof_id_type max_local = 0;
452
454 rit = _nodes.rbegin();
455
457 rend = _nodes.rend();
458
459 // Look for the maximum node id. Search backwards through
460 // nodes so we can break out early. Beware of nullptr entries that
461 // haven't yet been cleared from _nodes
462 for (; rit != rend; ++rit)
463 {
464 const DofObject *d = *rit;
465 if (d)
466 {
467 libmesh_assert(_nodes[d->id()] == d);
468 max_local = d->id() + 1;
469 break;
470 }
471 }
472
473 this->comm().max(max_local);
474 return max_local;
475}
476
477
478
480{
481 return this->node_ref(i);
482}
483
484
485
487{
489 libmesh_assert_equal_to (_nodes[i]->id(), i);
490
491 return _nodes[i];
492}
493
494
495
496
498{
500 libmesh_assert_equal_to (_nodes[i]->id(), i);
501
502 return _nodes[i];
503}
504
505
506
507
509{
510 if (const auto it = _nodes.find(i);
511 it != _nodes.end())
512 {
513 const Node * n = *it;
514 libmesh_assert (!n || n->id() == i);
515 return n;
516 }
517
518 return nullptr;
519}
520
521
522
523
525{
526 if (auto it = _nodes.find(i);
527 it != _nodes.end())
528 {
529 Node * n = *it;
530 libmesh_assert (!n || n->id() == i);
531 return n;
532 }
533
534 return nullptr;
535}
536
537
538
539
541{
543 libmesh_assert_equal_to (_elements[i]->id(), i);
544
545 return _elements[i];
546}
547
548
549
550
552{
554 libmesh_assert_equal_to (_elements[i]->id(), i);
555
556 return _elements[i];
557}
558
559
560
561
563{
564 if (const auto it = _elements.find(i);
565 it != _elements.end())
566 {
567 const Elem * e = *it;
568 libmesh_assert (!e || e->id() == i);
569 return e;
570 }
571
572 return nullptr;
573}
574
575
576
577
579{
580 if (auto it = _elements.find(i);
581 it != _elements.end())
582 {
583 Elem * e = *it;
584 libmesh_assert (!e || e->id() == i);
585 return e;
586 }
587
588 return nullptr;
589}
590
591
592
593
595{
596 // Don't try to add nullptrs!
598
599 // Trying to add an existing element is a no-op
600 if (e->valid_id() && _elements[e->id()] == e)
601 return e;
602
603 const processor_id_type elem_procid = e->processor_id();
604
605 if (!e->valid_id())
606 {
607 // We should only be creating new ids past the end of the range
608 // of existing ids
609 libmesh_assert_greater_equal(_next_free_unpartitioned_elem_id,
611 libmesh_assert_greater_equal(_next_free_local_elem_id, _max_elem_id);
612
613 // Use the unpartitioned ids for unpartitioned elems, and
614 // temporarily for ghost elems
616 if (elem_procid == this->processor_id())
617 next_id = &_next_free_local_elem_id;
618 e->set_id (*next_id);
619 }
620
621 {
622 // Advance next_ids up high enough that each is pointing to an
623 // unused id and any subsequent increments will still point us
624 // to unused ids
625 _max_elem_id = std::max(_max_elem_id,
626 static_cast<dof_id_type>(e->id()+1));
627
630 ((_max_elem_id-1) / (this->n_processors() + 1) + 1) *
631 (this->n_processors() + 1) + this->n_processors();
634 ((_max_elem_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
635 (this->n_processors() + 1) + this->processor_id();
636
637#ifndef NDEBUG
638 // We need a const dofobject_container so we don't inadvertently create
639 // nullptr entries when testing for non-nullptr ones
640 const dofobject_container<Elem> & const_elements = _elements;
641#endif
644 }
645
646 // Don't try to overwrite existing elems
647 libmesh_assert (!_elements[e->id()]);
648
649 _elements[e->id()] = e;
650
651 // We actually added a new element. Some of our caches might still
652 // be valid, but we should clear the ones which definitely are not.
653 this->clear_point_locator();
654 this->clear_stored_ranges();
655
656 // Try to make the cached elem data more accurate
657 if (elem_procid == this->processor_id() ||
658 elem_procid == DofObject::invalid_processor_id)
659 _n_elem++;
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 // Unpartitioned elems should be added on every processor
685 // And shouldn't be added in the same batch as ghost elems
686 // But we might be just adding on processor 0 to
687 // broadcast later
688 // #ifdef DEBUG
689 // if (elem_procid == DofObject::invalid_processor_id)
690 // {
691 // dof_id_type elem_id = e->id();
692 // this->comm().max(elem_id);
693 // libmesh_assert_equal_to (elem_id, e->id());
694 // }
695 // #endif
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
709
710
711Elem * DistributedMesh::add_elem (std::unique_ptr<Elem> e)
712{
713 // The mesh now takes ownership of the Elem. Eventually the guts of
714 // add_elem() will get moved to a private helper function, and
715 // calling add_elem() directly will be deprecated.
716 return add_elem(e.release());
717}
718
719
720
722{
723 if (_elements[e->id()])
724 this->delete_elem(_elements[e->id()]);
725
726#ifdef LIBMESH_ENABLE_UNIQUE_ID
727 if (!e->valid_unique_id())
728 {
729 if (processor_id() == e->processor_id())
730 {
732 _next_unique_id += this->n_processors() + 1;
733 }
734 else
735 {
738 }
739 }
740 else
741 {
742 _next_unique_id = std::max(_next_unique_id, e->unique_id()+1);
744 ((_next_unique_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
745 (this->n_processors() + 1) + this->processor_id();
746 }
747#endif
748
749 // Try to make the cached elem data more accurate
750 processor_id_type elem_procid = e->processor_id();
751 if (elem_procid == this->processor_id() ||
752 elem_procid == DofObject::invalid_processor_id)
753 _n_elem++;
754
755 _elements[e->id()] = e;
756
757 // We actually added a new element. Some of our caches might still
758 // be valid, but we should clear the ones which definitely are not.
759 this->clear_point_locator();
760 this->clear_stored_ranges();
761
762 // Make sure any new element is given space for any extra integers
763 // we've requested
766
767 // And set mapping type and data on any new element
770
771 return e;
772}
773
774Elem * DistributedMesh::insert_elem (std::unique_ptr<Elem> e)
775{
776 // The mesh now takes ownership of the Elem. Eventually the guts of
777 // insert_elem(Elem*) will get moved to a private helper function, and
778 // calling insert_elem(Elem*) directly will be deprecated.
779 return insert_elem(e.release());
780}
781
782
784{
785 libmesh_assert (e);
786
787 // Try to make the cached elem data more accurate
788 _n_elem--;
789
790 // Was this a coarse element, not just a coarsening where we still
791 // have some ancestor structure? Was it a *local* element, that we
792 // might have been depending on as an owner of local nodes? We'll
793 // have to be more careful with our nodes in contract() later; no
794 // telling if we just locally orphaned a node that should be
795 // globally retained.
796 if (e->processor_id() == this->processor_id() &&
797 !e->parent())
799
800 // Delete the element from the BoundaryInfo object
801 this->get_boundary_info().remove(e);
802
803 // But not yet from the container; we might invalidate
804 // an iterator that way!
805
806 //_elements.erase(e->id());
807
808 // Instead, we set it to nullptr for now
809
810 _elements[e->id()] = nullptr;
811
812 // delete the element
813 delete e;
814
815 // Some of our caches might still be valid, but we should clear the
816 // ones which definitely are not.
817 this->clear_point_locator();
818 this->clear_stored_ranges();
819}
820
821
822
824 const dof_id_type new_id)
825{
826 // This could be a no-op
827 if (old_id == new_id)
828 return;
829
830 Elem * el = _elements[old_id];
831 libmesh_assert (el);
832 libmesh_assert_equal_to (el->id(), old_id);
833
834 el->set_id(new_id);
835 libmesh_assert (!_elements[new_id]);
836 _elements[new_id] = el;
837 _elements.erase(old_id);
838
839 // Should we delete any caches here? Our point locator indexes by
840 // element pointer and should be fine with an id change. Our stored
841 // ranges are no longer sorted, which is *probably* fine, but let's
842 // just be safe.
843 this->clear_stored_ranges();
844}
845
846
847
849 const dof_id_type id,
850 const processor_id_type proc_id)
851{
852 Node * old_n = this->query_node_ptr(id);
853
854 if (old_n)
855 {
856 *old_n = p;
857 old_n->processor_id() = proc_id;
858
859 return old_n;
860 }
861
862 Node * n = Node::build(p, id).release();
863 n->processor_id() = proc_id;
864
866}
867
868
870{
871 // This had better be a node in our mesh
872 libmesh_assert(_nodes[n.id()] == &n);
873
874 _nodes[n.id()] = nullptr;
875 _n_nodes--;
876
878 n.processor_id() = this->processor_id();
879
880 this->add_node(&n);
881}
882
883
885{
886 // Don't try to add nullptrs!
888
889 // Trying to add an existing node is a no-op
890 if (n->valid_id() && _nodes[n->id()] == n)
891 return n;
892
893 const processor_id_type node_procid = n->processor_id();
894
895 if (!n->valid_id())
896 {
897 // We should only be creating new ids past the end of the range
898 // of existing ids
899 libmesh_assert_greater_equal(_next_free_unpartitioned_node_id,
901 libmesh_assert_greater_equal(_next_free_local_node_id, _max_node_id);
902
903 // Use the unpartitioned ids for unpartitioned nodes,
904 // and temporarily for ghost nodes
906 if (node_procid == this->processor_id())
907 next_id = &_next_free_local_node_id;
908 n->set_id (*next_id);
909 }
910
911 {
912 // Advance next_ids up high enough that each is pointing to an
913 // unused id and any subsequent increments will still point us
914 // to unused ids
915 _max_node_id = std::max(_max_node_id,
916 static_cast<dof_id_type>(n->id()+1));
917
920 ((_max_node_id-1) / (this->n_processors() + 1) + 1) *
921 (this->n_processors() + 1) + this->n_processors();
924 ((_max_node_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
925 (this->n_processors() + 1) + this->processor_id();
926
927#ifndef NDEBUG
928 // We need a const dofobject_container so we don't inadvertently create
929 // nullptr entries when testing for non-nullptr ones
930 const dofobject_container<Node> & const_nodes = _nodes;
931#endif
934 }
935
936 // Don't try to overwrite existing nodes
937 libmesh_assert (!_nodes[n->id()]);
938
939 _nodes[n->id()] = n;
940
941 // Try to make the cached node data more accurate
942 if (node_procid == this->processor_id() ||
943 node_procid == DofObject::invalid_processor_id)
944 _n_nodes++;
945
946#ifdef LIBMESH_ENABLE_UNIQUE_ID
947 if (!n->valid_unique_id())
948 {
949 if (processor_id() == n->processor_id())
950 {
952 _next_unique_id += this->n_processors() + 1;
953 }
954 else
955 {
958 }
959 }
960 else
961 {
962 _next_unique_id = std::max(_next_unique_id, n->unique_id()+1);
964 ((_next_unique_id + this->n_processors() - 1) / (this->n_processors() + 1) + 1) *
965 (this->n_processors() + 1) + this->processor_id();
966 }
967#endif
968
971
972 // Unpartitioned nodes should be added on every processor
973 // And shouldn't be added in the same batch as ghost nodes
974 // But we might be just adding on processor 0 to
975 // broadcast later
976 // #ifdef DEBUG
977 // if (node_procid == DofObject::invalid_processor_id)
978 // {
979 // dof_id_type node_id = n->id();
980 // this->comm().max(node_id);
981 // libmesh_assert_equal_to (node_id, n->id());
982 // }
983 // #endif
984
985 return n;
986}
987
988Node * DistributedMesh::add_node (std::unique_ptr<Node> n)
989{
990 // The mesh now takes ownership of the Node. Eventually the guts of
991 // add_node() will get moved to a private helper function, and
992 // calling add_node() directly will be deprecated.
993 return add_node(n.release());
994}
995
997{
999 libmesh_assert(_nodes[n->id()]);
1000
1001 // Try to make the cached elem data more accurate
1002 _n_nodes--;
1003
1004 // Delete the node from the BoundaryInfo object
1005 this->get_boundary_info().remove(n);
1006 _constraint_rows.erase(n);
1007
1008 // But not yet from the container; we might invalidate
1009 // an iterator that way!
1010
1011 //_nodes.erase(n->id());
1012
1013 // Instead, we set it to nullptr for now
1014
1015 _nodes[n->id()] = nullptr;
1016
1017 // delete the node
1018 delete n;
1019}
1020
1021
1022
1024 const dof_id_type new_id)
1025{
1026 // This could be a no-op
1027 if (old_id == new_id)
1028 return;
1029
1030 Node * nd = _nodes[old_id];
1031 libmesh_assert (nd);
1032 libmesh_assert_equal_to (nd->id(), old_id);
1033
1034 // If we have nodes shipped to this processor for NodeConstraints
1035 // use, then those nodes will exist in _nodes, but may not be
1036 // locatable via a TopologyMap due to the insufficiency of elements
1037 // connecting to them. If local refinement then wants to create a
1038 // *new* node in the same location, it will initially get a temporary
1039 // id, and then make_node_ids_parallel_consistent() will try to move
1040 // it to the canonical id. We need to account for this case to
1041 // avoid false positives and memory leaks.
1042#ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS
1043 if (_nodes[new_id])
1044 {
1045 libmesh_assert_equal_to (*(Point *)_nodes[new_id],
1046 *(Point *)_nodes[old_id]);
1047 _nodes.erase(new_id);
1048 }
1049#else
1050 // If we aren't shipping nodes for NodeConstraints, there should be
1051 // no reason for renumbering one node onto another.
1052 libmesh_assert (!_nodes[new_id]);
1053#endif
1054 _nodes[new_id] = nd;
1055 nd->set_id(new_id);
1056
1057 _nodes.erase(old_id);
1058}
1059
1060
1061
1063{
1064 // Call parent clear function
1066
1067 // Clear our elements and nodes
1068 // There is no need to remove them from
1069 // the BoundaryInfo data structure since we
1070 // already cleared it.
1072
1073 for (auto & node : _nodes)
1074 delete node;
1075
1076 _nodes.clear();
1077
1078 // We're no longer distributed if we were before
1079 _is_serial = true;
1080 _is_serial_on_proc_0 = true;
1081
1082 // We deleted a ton of coarse elements, but their nodes got deleted too so
1083 // all is copacetic.
1085
1086 // Correct our caches
1087 _n_nodes = 0;
1088 _max_node_id = 0;
1091}
1092
1093
1094
1096{
1097 for (auto & elem : _elements)
1098 delete elem;
1099
1100 _elements.clear();
1101
1102 // Correct our caches
1103 _n_elem = 0;
1104 _max_elem_id = 0;
1107}
1108
1109
1110
1112{
1113 // If this is a truly parallel mesh, go through the redistribution/gather/delete remote steps
1114 if (!this->is_serial())
1115 {
1116 // Construct a MeshCommunication object to actually redistribute the nodes
1117 // and elements according to the partitioner, and then to re-gather the neighbors.
1119 mc.redistribute(*this);
1120
1122
1123 // We communicate valid neighbor links for newly-redistributed
1124 // elements, but we may still have remote_elem links that should
1125 // be corrected but whose correction wasn't communicated.
1126 this->find_neighbors(/*reset_remote_elements*/ false,
1127 /*reset_current_list*/ false /*non-default*/,
1128 /*assert_valid*/ false,
1129 /*check_non_remote*/ false /*non-default!*/);
1130
1131 // Is this necessary? If we are called from prepare_for_use(), this will be called
1132 // anyway... but users can always call partition directly, in which case we do need
1133 // to call delete_remote_elements()...
1134 //
1135 // Regardless of whether it's necessary, it isn't safe. We
1136 // haven't communicated new node processor_ids yet, and we can't
1137 // delete nodes until we do.
1138 // this->delete_remote_elements();
1139 }
1140 else
1141 // The base class can handle non-distributed things, like
1142 // notifying any GhostingFunctors of changes
1144}
1145
1146
1147
1149{
1150 // this->recalculate_n_partitions();
1151
1152 // Let's do the base class cache clearing first, just in case our
1153 // later computations are ever changed to make use of a local
1154 // elements range cache
1156
1157 // Partitioning changes our numbers of unpartitioned objects
1159}
1160
1161
1162
1163template <typename T>
1165{
1166 // This function must be run on all processors at once
1167 parallel_object_only();
1168
1169 const dof_id_type pmax_node_id = this->parallel_max_node_id();
1170 const dof_id_type pmax_elem_id = this->parallel_max_elem_id();
1171 const dof_id_type pmax_id = std::max(pmax_node_id, pmax_elem_id);
1172
1173 for (dof_id_type i=0; i != pmax_id; ++i)
1174 {
1175 T * obj = objects[i]; // Returns nullptr if there's no map entry
1176
1177 // Local lookups by id should return the requested object
1178 libmesh_assert(!obj || obj->id() == i);
1179
1180 // All processors with an object should agree on id
1181#ifndef NDEBUG
1182 const dof_id_type dofid = obj && obj->valid_id() ?
1183 obj->id() : DofObject::invalid_id;
1184 libmesh_assert(this->comm().semiverify(obj ? &dofid : nullptr));
1185#endif
1186
1187 // All processors with an object should agree on processor id
1188 const dof_id_type procid = obj && obj->valid_processor_id() ?
1189 obj->processor_id() : DofObject::invalid_processor_id;
1190 libmesh_assert(this->comm().semiverify(obj ? &procid : nullptr));
1191
1192 dof_id_type min_procid = procid;
1193 this->comm().min(min_procid);
1194
1195 // Either:
1196 // 1.) I own this elem (min_procid == this->processor_id()) *and* I have a valid pointer to it (obj != nullptr)
1197 // or
1198 // 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.)
1199
1200 // Original assert logic
1201 // libmesh_assert (min_procid != this->processor_id() || obj);
1202
1203 // More human-understandable logic...
1205 ((min_procid == this->processor_id()) && obj)
1206 ||
1207 (min_procid != this->processor_id())
1208 );
1209
1210#if defined(LIBMESH_ENABLE_UNIQUE_ID) && !defined(NDEBUG)
1211 // All processors with an object should agree on unique id
1212 const unique_id_type uniqueid = obj ? obj->unique_id() : 0;
1213 libmesh_assert(this->comm().semiverify(obj ? &uniqueid : nullptr));
1214#endif
1215 }
1216}
1217
1218
1219
1225
1226
1227
1229{
1230#ifndef NDEBUG
1231 // This function must be run on all processors at once
1232 parallel_object_only();
1233
1234 dof_id_type pmax_elem_id = this->parallel_max_elem_id();
1235
1236 for (dof_id_type i=0; i != pmax_elem_id; ++i)
1237 {
1238 Elem * el = _elements[i]; // Returns nullptr if there's no map entry
1239
1240 unsigned int p_level = el ? (el->p_level()) : libMesh::invalid_uint;
1241
1242 // All processors with an active element should agree on p level
1243 libmesh_assert(this->comm().semiverify((el && el->active()) ? &p_level : nullptr));
1244 }
1245#endif
1246}
1247
1248
1249
1250
1252{
1253#if defined(LIBMESH_ENABLE_AMR) && !defined(NDEBUG)
1254 // This function must be run on all processors at once
1255 parallel_object_only();
1256
1257 dof_id_type pmax_elem_id = this->parallel_max_elem_id();
1258
1259 for (dof_id_type i=0; i != pmax_elem_id; ++i)
1260 {
1261 Elem * el = _elements[i]; // Returns nullptr if there's no map entry
1262
1263 unsigned int refinement_flag = el ?
1264 static_cast<unsigned int> (el->refinement_flag()) : libMesh::invalid_uint;
1265 unsigned int p_refinement_flag = el ?
1266 static_cast<unsigned int> (el->p_refinement_flag()) : libMesh::invalid_uint;
1267
1268 libmesh_assert(this->comm().semiverify(el ? &refinement_flag : nullptr));
1269
1270 // p refinement flags aren't always kept correct on inactive
1271 // ghost elements
1272 libmesh_assert(this->comm().semiverify((el && el->active()) ? &p_refinement_flag : nullptr));
1273 }
1274#endif // LIBMESH_ENABLE_AMR
1275}
1276
1277
1278
1279template <typename T>
1282{
1283 // This function must be run on all processors at once
1284 parallel_object_only();
1285
1286 typedef typename dofobject_container<T>::veclike_iterator object_iterator;
1287
1288 // In parallel we may not know what objects other processors have.
1289 // Start by figuring out how many
1290 dof_id_type unpartitioned_objects = 0;
1291
1292 std::unordered_map<processor_id_type, dof_id_type>
1293 ghost_objects_from_proc;
1294
1295 object_iterator it = objects.begin();
1296 object_iterator end = objects.end();
1297
1298 while (it != end)
1299 {
1300 T * obj = *it;
1301
1302 // Remove any nullptr container entries while we're here.
1303 if (!obj)
1304 it = objects.erase(it);
1305 else
1306 {
1307 processor_id_type obj_procid = obj->processor_id();
1308 if (obj_procid == DofObject::invalid_processor_id)
1309 unpartitioned_objects++;
1310 else
1311 ghost_objects_from_proc[obj_procid]++;
1312
1313 // Finally, increment the iterator
1314 ++it;
1315 }
1316 }
1317
1318 std::vector<dof_id_type> objects_on_proc(this->n_processors(), 0);
1319 auto this_it = ghost_objects_from_proc.find(this->processor_id());
1320 this->comm().allgather
1321 ((this_it == ghost_objects_from_proc.end()) ?
1322 dof_id_type(0) : this_it->second, objects_on_proc);
1323
1324#ifndef NDEBUG
1325 libmesh_assert(this->comm().verify(unpartitioned_objects));
1326 for (processor_id_type p=0, np=this->n_processors(); p != np; ++p)
1327 if (ghost_objects_from_proc.count(p))
1328 libmesh_assert_less_equal (ghost_objects_from_proc[p], objects_on_proc[p]);
1329 else
1330 libmesh_assert_less_equal (0, objects_on_proc[p]);
1331#endif
1332
1333 // We'll renumber objects in blocks by processor id
1334 std::vector<dof_id_type> first_object_on_proc(this->n_processors());
1335 for (processor_id_type i=1, np=this->n_processors(); i != np; ++i)
1336 first_object_on_proc[i] = first_object_on_proc[i-1] +
1337 objects_on_proc[i-1];
1338 dof_id_type next_id = first_object_on_proc[this->processor_id()];
1339 dof_id_type first_free_id =
1340 first_object_on_proc[this->n_processors()-1] +
1341 objects_on_proc[this->n_processors()-1] +
1342 unpartitioned_objects;
1343
1344 // First set new local object ids and build request sets
1345 // for non-local object ids
1346
1347 // Request sets to send to each processor
1348 std::map<processor_id_type, std::vector<dof_id_type>>
1349 requested_ids;
1350
1351 // We know how many objects live on each processor, so reserve() space for
1352 // each.
1353 auto ghost_end = ghost_objects_from_proc.end();
1354 for (auto p : make_range(this->n_processors()))
1355 if (p != this->processor_id())
1356 {
1357 if (const auto p_it = ghost_objects_from_proc.find(p);
1358 p_it != ghost_end)
1359 requested_ids[p].reserve(p_it->second);
1360 }
1361
1362 end = objects.end();
1363 for (it = objects.begin(); it != end; ++it)
1364 {
1365 T * obj = *it;
1366 if (!obj)
1367 continue;
1368 if (obj->processor_id() == this->processor_id())
1369 obj->set_id(next_id++);
1370 else if (obj->processor_id() != DofObject::invalid_processor_id)
1371 requested_ids[obj->processor_id()].push_back(obj->id());
1372 }
1373
1374 // Next set ghost object ids from other processors
1375
1376 auto gather_functor =
1377 [
1378#ifndef NDEBUG
1379 this,
1380 &first_object_on_proc,
1381 &objects_on_proc,
1382#endif
1383 &objects]
1384 (processor_id_type, const std::vector<dof_id_type> & ids,
1385 std::vector<dof_id_type> & new_ids)
1386 {
1387 std::size_t ids_size = ids.size();
1388 new_ids.resize(ids_size);
1389
1390 for (std::size_t i=0; i != ids_size; ++i)
1391 {
1392 T * obj = objects[ids[i]];
1393 libmesh_assert(obj);
1394 libmesh_assert_equal_to (obj->processor_id(), this->processor_id());
1395 new_ids[i] = obj->id();
1396
1397 libmesh_assert_greater_equal (new_ids[i],
1398 first_object_on_proc[this->processor_id()]);
1399 libmesh_assert_less (new_ids[i],
1400 first_object_on_proc[this->processor_id()] +
1401 objects_on_proc[this->processor_id()]);
1402 }
1403 };
1404
1405 auto action_functor =
1406 [
1407#ifndef NDEBUG
1408 &first_object_on_proc,
1409 &objects_on_proc,
1410#endif
1411 &objects]
1412 (processor_id_type libmesh_dbg_var(pid),
1413 const std::vector<dof_id_type> & ids,
1414 const std::vector<dof_id_type> & data)
1415 {
1416 // Copy the id changes we've now been informed of
1417 for (auto i : index_range(ids))
1418 {
1419 T * obj = objects[ids[i]];
1420 libmesh_assert (obj);
1421 libmesh_assert_equal_to (obj->processor_id(), pid);
1422 libmesh_assert_greater_equal (data[i],
1423 first_object_on_proc[pid]);
1424 libmesh_assert_less (data[i],
1425 first_object_on_proc[pid] +
1426 objects_on_proc[pid]);
1427 obj->set_id(data[i]);
1428 }
1429 };
1430
1431 const dof_id_type * ex = nullptr;
1433 (this->comm(), requested_ids, gather_functor, action_functor, ex);
1434
1435#ifdef LIBMESH_ENABLE_UNIQUE_ID
1436 auto unique_gather_functor =
1437 [
1438#ifndef NDEBUG
1439 this,
1440#endif
1441 &objects]
1442 (processor_id_type, const std::vector<dof_id_type> & ids,
1443 std::vector<unique_id_type> & data)
1444 {
1445 std::size_t ids_size = ids.size();
1446 data.resize(ids_size);
1447
1448 for (std::size_t i=0; i != ids_size; ++i)
1449 {
1450 T * obj = objects[ids[i]];
1451 libmesh_assert(obj);
1452 libmesh_assert_equal_to (obj->processor_id(), this->processor_id());
1453 data[i] = obj->valid_unique_id() ? obj->unique_id() : DofObject::invalid_unique_id;
1454 }
1455 };
1456
1457 auto unique_action_functor =
1458 [&objects]
1459 (processor_id_type libmesh_dbg_var(pid),
1460 const std::vector<dof_id_type> & ids,
1461 const std::vector<unique_id_type> & data)
1462 {
1463 for (auto i : index_range(ids))
1464 {
1465 T * obj = objects[ids[i]];
1466 libmesh_assert (obj);
1467 libmesh_assert_equal_to (obj->processor_id(), pid);
1468 if (!obj->valid_unique_id() && data[i] != DofObject::invalid_unique_id)
1469 obj->set_unique_id(data[i]);
1470 }
1471 };
1472
1473 const unique_id_type * unique_ex = nullptr;
1475 (this->comm(), requested_ids, unique_gather_functor,
1476 unique_action_functor, unique_ex);
1477#endif
1478
1479 // Next set unpartitioned object ids
1480 next_id = 0;
1481 for (auto i : make_range(this->n_processors()))
1482 next_id += objects_on_proc[i];
1483 for (it = objects.begin(); it != end; ++it)
1484 {
1485 T * obj = *it;
1486 if (!obj)
1487 continue;
1488 if (obj->processor_id() == DofObject::invalid_processor_id)
1489 obj->set_id(next_id++);
1490 }
1491
1492 // Finally shuffle around objects so that container indices
1493 // match ids
1494 it = objects.begin();
1495 end = objects.end();
1496 while (it != end)
1497 {
1498 T * obj = *it;
1499 if (obj) // don't try shuffling already-nullptr entries
1500 {
1501 T * next = objects[obj->id()];
1502 // If we have to move this object
1503 if (next != obj)
1504 {
1505 // nullptr out its original position for now
1506 // (our shuffling may put another object there shortly)
1507 *it = nullptr;
1508
1509 // There may already be another object with this id that
1510 // needs to be moved itself
1511 while (next)
1512 {
1513 // We shouldn't be trying to give two objects the
1514 // same id
1515 libmesh_assert_not_equal_to (next->id(), obj->id());
1516 objects[obj->id()] = obj;
1517 obj = next;
1518 next = objects[obj->id()];
1519 }
1520 objects[obj->id()] = obj;
1521 }
1522 }
1523
1524 // Remove any container entries that were left as nullptr.
1525 if (!obj)
1526 it = objects.erase(it);
1527 else
1528 ++it;
1529 }
1530
1531 return first_free_id;
1532}
1533
1534
1536{
1537 parallel_object_only();
1538
1539#ifdef DEBUG
1540 // Make sure our ids and flags are consistent
1544#endif
1545
1546 LOG_SCOPE("renumber_nodes_and_elements()", "DistributedMesh");
1547
1548 // Nodes not connected to any elements, and nullptr node entries
1549 // in our container, should be deleted. But wait! If we've deleted coarse
1550 // local elements on some processor, other processors might have ghosted
1551 // nodes from it that are now no longer connected to any elements on it, but
1552 // that are connected to their own semilocal elements. We'll have to
1553 // communicate to ascertain if that's the case.
1555
1556 // What used nodes do we see on our proc?
1557 std::set<dof_id_type> used_nodes;
1558
1559 // What used node info should we send from our proc? Could we take ownership
1560 // of each node if we needed to?
1561 std::map<processor_id_type, std::map<dof_id_type, bool>>
1562 used_nodes_on_proc;
1563
1564 // flag the nodes we need
1565 for (auto & elem : this->element_ptr_range())
1566 for (const Node & node : elem->node_ref_range())
1567 {
1568 const dof_id_type n = node.id();
1569 used_nodes.insert(n);
1571 {
1572 const processor_id_type p = node.processor_id();
1573 if (p != this->processor_id())
1574 {
1575 auto & used_nodes_on_p = used_nodes_on_proc[p];
1576 if (elem->processor_id() == this->processor_id())
1577 used_nodes_on_p[n] = true;
1578 else
1579 if (!used_nodes_on_p.count(n))
1580 used_nodes_on_p[n] = false;
1581 }
1582 }
1583 }
1584
1586 {
1587 // "unsigned char" == "bool, but MPI::BOOL is iffy to use"
1588 typedef unsigned char boolish;
1589 std::map<processor_id_type, std::vector<std::pair<dof_id_type, boolish>>>
1590 used_nodes_on_proc_vecs;
1591 for (auto & [pid, nodemap] : used_nodes_on_proc)
1592 used_nodes_on_proc_vecs[pid].assign(nodemap.begin(), nodemap.end());
1593
1594 std::map<dof_id_type,processor_id_type> repartitioned_node_pids;
1595 std::map<processor_id_type, std::set<dof_id_type>>
1596 repartitioned_node_sets_to_push;
1597
1598 auto ids_action_functor =
1599 [&used_nodes, &repartitioned_node_pids,
1600 &repartitioned_node_sets_to_push]
1602 const std::vector<std::pair<dof_id_type, boolish>> & ids_and_bools)
1603 {
1604 for (auto [n, sender_could_become_owner] : ids_and_bools)
1605 {
1606 // If we don't see a use for our own node, but someone
1607 // else does, better figure out who should own it next.
1608 if (!used_nodes.count(n))
1609 {
1610 if (auto it = repartitioned_node_pids.find(n);
1611 sender_could_become_owner)
1612 {
1613 if (it != repartitioned_node_pids.end() &&
1614 pid < it->second)
1615 it->second = pid;
1616 else
1617 repartitioned_node_pids[n] = pid;
1618 }
1619 else
1620 if (it == repartitioned_node_pids.end())
1621 repartitioned_node_pids[n] =
1623
1624 repartitioned_node_sets_to_push[pid].insert(n);
1625 }
1626 }
1627 };
1628
1629 // We need two pushes instead of a pull here because we need to
1630 // know *all* the queries for a particular node before we can
1631 // respond to *any* of them.
1633 (this->comm(), used_nodes_on_proc_vecs, ids_action_functor);
1634
1635 // Repartition (what used to be) our own nodes first
1636 for (auto & [n, p] : repartitioned_node_pids)
1637 {
1638 Node & node = this->node_ref(n);
1639 libmesh_assert_equal_to(node.processor_id(), this->processor_id());
1640 libmesh_assert_not_equal_to_msg(p, DofObject::invalid_processor_id, "Node " << n << " is lost?");
1641 node.processor_id() = p;
1642 }
1643
1644 // Then push to repartition others' ghosted copies.
1645
1646 std::map<processor_id_type, std::vector<std::pair<dof_id_type,processor_id_type>>>
1647 repartitioned_node_vecs;
1648
1649 for (auto & [p, nodeset] : repartitioned_node_sets_to_push)
1650 {
1651 auto & rn_vec = repartitioned_node_vecs[p];
1652 for (auto n : nodeset)
1653 rn_vec.emplace_back(n, repartitioned_node_pids[n]);
1654 }
1655
1656 auto repartition_node_functor =
1657 [this]
1658 (processor_id_type libmesh_dbg_var(pid),
1659 const std::vector<std::pair<dof_id_type, processor_id_type>> & ids_and_pids)
1660 {
1661 for (auto [n, p] : ids_and_pids)
1662 {
1663 libmesh_assert_not_equal_to(p, DofObject::invalid_processor_id);
1664 Node & node = this->node_ref(n);
1665 libmesh_assert_equal_to(node.processor_id(), pid);
1666 node.processor_id() = p;
1667 }
1668 };
1669
1671 (this->comm(), repartitioned_node_vecs, repartition_node_functor);
1672 }
1673
1675
1676 // Nodes not connected to any local elements, and nullptr node entries
1677 // in our container, are deleted
1678 {
1679 node_iterator_imp it = _nodes.begin();
1680 node_iterator_imp end = _nodes.end();
1681
1682 while (it != end)
1683 {
1684 Node * nd = *it;
1685 if (!nd)
1686 it = _nodes.erase(it);
1687 else if (!used_nodes.count(nd->id()))
1688 {
1689 // remove any boundary information associated with
1690 // this node
1691 this->get_boundary_info().remove (nd);
1692 _constraint_rows.erase(nd);
1693
1694 // delete the node
1695 delete nd;
1696
1697 it = _nodes.erase(it);
1698 }
1699 else
1700 ++it;
1701 }
1702 }
1703
1705
1707 {
1709 return;
1710 }
1711
1712 // Finally renumber all the elements
1713 _n_elem = this->renumber_dof_objects (this->_elements);
1714
1715 // and all the remaining nodes
1716 _n_nodes = this->renumber_dof_objects (this->_nodes);
1717
1718 // And figure out what IDs we should use when adding new nodes and
1719 // new elements
1721
1722 // Make sure our caches are up to date and our
1723 // DofObjects are well packed
1724#ifdef DEBUG
1725 libmesh_assert_equal_to (this->n_nodes(), this->parallel_n_nodes());
1726 libmesh_assert_equal_to (this->n_elem(), this->parallel_n_elem());
1727 const dof_id_type pmax_node_id = this->parallel_max_node_id();
1728 const dof_id_type pmax_elem_id = this->parallel_max_elem_id();
1729 libmesh_assert_equal_to (this->max_node_id(), pmax_node_id);
1730 libmesh_assert_equal_to (this->max_elem_id(), pmax_elem_id);
1731 libmesh_assert_equal_to (this->n_nodes(), this->max_node_id());
1732 libmesh_assert_equal_to (this->n_elem(), this->max_elem_id());
1733
1734 // Make sure our ids and flags are consistent
1737
1738 // And make sure we've made our numbering monotonic
1740#endif
1741}
1742
1743
1744
1746{
1747 // We can't use range-for here because we need access to the special
1748 // iterators' methods, not just to their dereferenced values.
1749
1750 // Nodes first
1751 for (auto pr = this->_nodes.begin(),
1752 end = this->_nodes.end(); pr != end; ++pr)
1753 {
1754 Node * n = *pr;
1755 if (n != nullptr)
1756 {
1757 const dof_id_type id = pr.index();
1758 n->set_id() = id;
1759 libmesh_assert_equal_to(this->node_ptr(id), n);
1760 }
1761 }
1762
1763 // Elements next
1764 for (auto pr = this->_elements.begin(),
1765 end = this->_elements.end(); pr != end; ++pr)
1766 {
1767 Elem * e = *pr;
1768 if (e != nullptr)
1769 {
1770 const dof_id_type id = pr.index();
1771 e->set_id() = id;
1772 libmesh_assert_equal_to(this->elem_ptr(id), e);
1773 }
1774 }
1775}
1776
1777
1778
1780{
1781 parallel_object_only();
1782
1783 // Get local active elements first
1784 dof_id_type active_elements =
1785 static_cast<dof_id_type>(std::distance (this->active_local_elements_begin(),
1786 this->active_local_elements_end()));
1787 this->comm().sum(active_elements);
1788
1789 // Then add unpartitioned active elements, which should exist on
1790 // every processor
1791 active_elements +=
1792 static_cast<dof_id_type>(std::distance
1793 (this->active_pid_elements_begin(DofObject::invalid_processor_id),
1794 this->active_pid_elements_end(DofObject::invalid_processor_id)));
1795 return active_elements;
1796}
1797
1798
1799
1801{
1802#ifdef DEBUG
1803 // Make sure our neighbor links are all fine
1805
1806 // And our child/parent links, and our flags
1808
1809 // Make sure our ids and flags are consistent
1812
1813 libmesh_assert_equal_to (this->n_nodes(), this->parallel_n_nodes());
1814 libmesh_assert_equal_to (this->n_elem(), this->parallel_n_elem());
1815 const dof_id_type pmax_node_id = this->parallel_max_node_id();
1816 const dof_id_type pmax_elem_id = this->parallel_max_elem_id();
1817 libmesh_assert_equal_to (this->max_node_id(), pmax_node_id);
1818 libmesh_assert_equal_to (this->max_elem_id(), pmax_elem_id);
1819#endif
1820
1821 _is_serial = false;
1822 _is_serial_on_proc_0 = false;
1823
1825
1826 libmesh_assert_equal_to (this->max_elem_id(), this->parallel_max_elem_id());
1827
1828 // Now make sure the containers actually shrink - strip
1829 // any newly-created nullptr voids out of the element array
1832 while (e_it != e_end)
1833 if (!*e_it)
1834 e_it = _elements.erase(e_it);
1835 else
1836 ++e_it;
1837
1840 while (n_it != n_end)
1841 if (!*n_it)
1842 n_it = _nodes.erase(n_it);
1843 else
1844 ++n_it;
1845
1846 // We may have deleted no-longer-connected nodes or coarsened-away
1847 // elements; let's update our caches.
1849
1850 // We may have deleted nodes or elements that were the only local
1851 // representatives of some particular boundary id(s); let's update
1852 // those caches.
1854
1855#ifdef DEBUG
1856 // We might not have well-packed objects if the user didn't allow us
1857 // to renumber
1858 // libmesh_assert_equal_to (this->n_nodes(), this->max_node_id());
1859 // libmesh_assert_equal_to (this->n_elem(), this->max_elem_id());
1860
1861 // Make sure our neighbor links are all fine
1863
1864 // And our child/parent links, and our flags
1866
1867 // Make sure our ids and flags are consistent
1870#endif
1871
1873}
1874
1875
1877{
1878 // First add the elem like normal
1879 add_elem(e);
1880
1881 // Now add it to the set that won't be deleted when we call
1882 // delete_remote_elements()
1883 _extra_ghost_elems.insert(e);
1884}
1885
1886void
1887DistributedMesh::clear_extra_ghost_elems(const std::set<Elem *> & extra_ghost_elems)
1888{
1889 std::set<Elem *> tmp;
1890 std::set_difference(_extra_ghost_elems.begin(), _extra_ghost_elems.end(),
1891 extra_ghost_elems.begin(), extra_ghost_elems.end(),
1892 std::inserter(tmp, tmp.begin()));
1893 _extra_ghost_elems = tmp;
1894}
1895
1897{
1898 if (_is_serial)
1899 return;
1901 _is_serial = true;
1902 _is_serial_on_proc_0 = true;
1903
1904 // Make sure our caches are up to date and our
1905 // DofObjects are well packed
1906#ifdef DEBUG
1907 libmesh_assert_equal_to (this->n_nodes(), this->parallel_n_nodes());
1908 libmesh_assert_equal_to (this->n_elem(), this->parallel_n_elem());
1909 const dof_id_type pmax_node_id = this->parallel_max_node_id();
1910 const dof_id_type pmax_elem_id = this->parallel_max_elem_id();
1911 libmesh_assert_equal_to (this->max_node_id(), pmax_node_id);
1912 libmesh_assert_equal_to (this->max_elem_id(), pmax_elem_id);
1913
1914 // If we've disabled renumbering we can't be sure we're contiguous
1915 // libmesh_assert_equal_to (this->n_nodes(), this->max_node_id());
1916 // libmesh_assert_equal_to (this->n_elem(), this->max_elem_id());
1917
1918 // Make sure our neighbor links are all fine
1920
1921 // Make sure our ids and flags are consistent
1924#endif
1925}
1926
1928{
1930 return;
1931
1932 _is_serial_on_proc_0 = true;
1933 MeshCommunication().gather(0, *this);
1934}
1935
1936
1937} // 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.
void set_next_ids()
Derives _next_free_local_node_id, _next_free_local_elem_id, _next_free_unpartitioned_node_id,...
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.
void set_next_unique_ids(unique_id_type parallel_max_unique_id)
Derives _next_unique_id and _next_unpartitioned_unique_id from parallel_max_unique_id,...
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:872
The DofObject defines an abstract base class for objects that have degrees of freedom associated with...
Definition dof_object.h:55
bool valid_id() const
Definition dof_object.h:875
processor_id_type processor_id() const
Definition dof_object.h:895
dof_id_type & set_id()
Definition dof_object.h:841
void add_extra_integers(const unsigned int n_integers)
Assigns a set of extra integers to this DofObject.
Definition dof_object.C:482
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition dof_object.h:473
unique_id_type unique_id() const
Definition dof_object.h:849
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:883
dof_id_type id() const
Definition dof_object.h:833
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:862
This is the base class from which all geometric element types are derived.
Definition elem.h:96
RefinementState refinement_flag() const
Definition elem.h:3348
bool active() const
Definition elem.h:3079
const Elem * parent() const
Definition elem.h:3168
void set_mapping_type(const ElemMappingType type)
Sets the value of the mapping type for the element.
Definition elem.h:3266
unsigned int p_level() const
Definition elem.h:3246
void set_mapping_data(const unsigned char data)
Sets the value of the mapping data for the element.
Definition elem.h:3282
RefinementState p_refinement_flag() const
Definition elem.h:3364
This is the MeshBase class.
Definition mesh_base.h:81
void allow_remote_element_removal(bool allow)
If false is passed in then this mesh will no longer have remote elements deleted when being prepared ...
Definition mesh_base.h:1382
virtual const Node & node_ref(const dof_id_type i) const
Definition mesh_base.h:742
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
Preparation preparation() const
Definition mesh_base.h:213
bool allow_renumbering() const
Definition mesh_base.h:1360
virtual dof_id_type n_elem() const =0
bool skip_partitioning() const
Definition mesh_base.h:1435
bool allow_detect_interior_parents() const
Definition mesh_base.h:1374
void allow_find_neighbors(bool allow)
If false is passed then this mesh will no longer work to find element neighbors when being prepared f...
Definition mesh_base.h:1366
void allow_renumbering(bool allow)
If false is passed in then this mesh will no longer be renumbered when being prepared for use.
Definition mesh_base.h:1359
const std::map< subdomain_id_type, std::string > & get_subdomain_name_map() const
Definition mesh_base.h:1930
virtual void redistribute()
Redistribute elements between processors.
Definition mesh_base.C:1169
virtual dof_id_type n_nodes() const =0
ElemMappingType default_mapping_type() const
Returns the default master space to physical space mapping basis functions to be used on newly added ...
Definition mesh_base.h:938
std::vector< dof_id_type > _node_integer_default_values
The array of default initialization values for integer data associated with each node in the mesh.
Definition mesh_base.h:2401
unique_id_type _next_unique_id
The next available unique id for assigning ids to DOF objects.
Definition mesh_base.h:2257
bool _skip_renumber_nodes_and_elements
If this is true then renumbering will be kept to a minimum.
Definition mesh_base.h:2282
virtual dof_id_type max_node_id() const =0
dof_id_type n_unpartitioned_elem() const
Definition mesh_base.h:700
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:1181
std::vector< std::string > _elem_integer_names
The array of names for integer data associated with each element in the mesh.
Definition mesh_base.h:2383
std::vector< dof_id_type > _elem_integer_default_values
The array of default initialization values for integer data associated with each element in the mesh.
Definition mesh_base.h:2389
dof_id_type n_local_elem() const
Definition mesh_base.h:694
dof_id_type n_unpartitioned_nodes() const
Definition mesh_base.h:594
virtual dof_id_type max_elem_id() const =0
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:588
constraint_rows_type _constraint_rows
Definition mesh_base.h:2454
std::unique_ptr< Partitioner > _partitioner
A partitioner to use at each prepare_for_use().
Definition mesh_base.h:2251
bool allow_remote_element_removal() const
Definition mesh_base.h:1383
bool allow_find_neighbors() const
Definition mesh_base.h:1367
dof_id_type _n_constraint_rows
Definition mesh_base.h:2456
unsigned char default_mapping_data() const
Returns any default data value used by the master space to physical space mapping.
Definition mesh_base.h:956
void clear_point_locator()
Releases the current PointLocator object.
Definition mesh_base.C:1881
void copy_constraint_rows(const MeshBase &other_mesh)
Copy the constraints from the other mesh to this mesh.
Definition mesh_base.C:2506
std::vector< std::string > _node_integer_names
The array of names for integer data associated with each node in the mesh.
Definition mesh_base.h:2395
std::map< subdomain_id_type, std::string > & set_subdomain_name_map()
Definition mesh_base.h:1928
unique_id_type next_unique_id() const
Definition mesh_base.h:607
dof_id_type count_constraint_rows()
Helper function for subclasses to use to set _n_constraint_rows.
Definition mesh_base.C:2487
Preparation _preparation
Flags indicating in what ways this mesh has been prepared.
Definition mesh_base.h:2207
void skip_partitioning(bool skip)
If true is passed in then nothing on this mesh will be (re)partitioned.
Definition mesh_base.h:1433
void allow_detect_interior_parents(bool allow)
If false is passed then this mesh will no longer work to detect interior parents when being prepared ...
Definition mesh_base.h:1373
void post_dofobject_moves(MeshBase &&other_mesh)
Moves any superclass data (e.g.
Definition mesh_base.C:2411
void clear_stored_ranges()
Clears stored ranges, to indicate that the mesh has changed and they should be regenerated when next ...
Definition mesh_base.C:1984
This is the MeshCommunication class.
void 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:41
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 find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true, const bool assert_valid=true, const bool check_non_remote=true) override
Other functions from MeshBase requiring re-definition.
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