libMesh
Loading...
Searching...
No Matches
mesh_tools.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/elem.h"
22#include "libmesh/elem_range.h"
23#include "libmesh/libmesh_logging.h"
24#include "libmesh/mesh_base.h"
25#include "libmesh/mesh_communication.h"
26#include "libmesh/mesh_serializer.h"
27#include "libmesh/mesh_tools.h"
28#include "libmesh/node_range.h"
29#include "libmesh/parallel.h"
30#include "libmesh/parallel_algebra.h"
31#include "libmesh/parallel_ghost_sync.h"
32#include "libmesh/sphere.h"
33#include "libmesh/threads.h"
34#include "libmesh/enum_to_string.h"
35#include "libmesh/enum_elem_type.h"
36#include "libmesh/int_range.h"
37#include "libmesh/utility.h"
38#include "libmesh/boundary_info.h"
39
40#ifndef NDEBUG
41# include "libmesh/remote_elem.h"
42#endif
43
44// C++ includes
45#include <limits>
46#include <numeric> // for std::accumulate
47#include <set>
48#include <unordered_map>
49#include <unordered_set>
50
51
52
53// ------------------------------------------------------------
54// anonymous namespace for helper classes
55namespace {
56
57using namespace libMesh;
58
66class SumElemWeight
67{
68public:
69 SumElemWeight () :
70 _weight(0)
71 {}
72
73 SumElemWeight (SumElemWeight &, Threads::split) :
74 _weight(0)
75 {}
76
77 void operator()(const ConstElemRange & range)
78 {
79 for (const auto & elem : range)
80 _weight += elem->n_nodes();
81 }
82
83 dof_id_type weight() const
84 { return _weight; }
85
86 // If we don't have threads we never need a join, and icpc yells a
87 // warning if it sees an anonymous function that's never used
88#if LIBMESH_USING_THREADS
89 void join (const SumElemWeight & other)
90 { _weight += other.weight(); }
91#endif
92
93private:
94 dof_id_type _weight;
95};
96
97
104class FindBBox
105{
106public:
107 FindBBox () : _bbox()
108 {}
109
110 FindBBox (FindBBox & other, Threads::split) :
111 _bbox(other._bbox)
112 {}
113
114 void operator()(const ConstNodeRange & range)
115 {
116 for (const auto & node : range)
117 {
118 libmesh_assert(node);
119 _bbox.union_with(*node);
120 }
121 }
122
123 void operator()(const ConstElemRange & range)
124 {
125 for (const auto & elem : range)
126 {
127 libmesh_assert(elem);
128 _bbox.union_with(elem->loose_bounding_box());
129 }
130 }
131
132 Point & min() { return _bbox.min(); }
133
134 Point & max() { return _bbox.max(); }
135
136 // If we don't have threads we never need a join, and icpc yells a
137 // warning if it sees an anonymous function that's never used
138#if LIBMESH_USING_THREADS
139 void join (const FindBBox & other)
140 {
141 _bbox.union_with(other._bbox);
142 }
143#endif
144
145 libMesh::BoundingBox & bbox ()
146 {
147 return _bbox;
148 }
149
150private:
151 BoundingBox _bbox;
152};
153
154#ifdef DEBUG
155void assert_semiverify_dofobj(const Parallel::Communicator & communicator,
156 const DofObject * d,
157 unsigned int sysnum = libMesh::invalid_uint)
158{
159 if (d)
160 {
161 const unsigned int n_sys = d->n_systems();
162
163 std::vector<unsigned int> n_vars (n_sys, 0);
164 for (unsigned int s = 0; s != n_sys; ++s)
165 if (sysnum == s ||
166 sysnum == libMesh::invalid_uint)
167 n_vars[s] = d->n_vars(s);
168
169 const unsigned int tot_n_vars =
170 std::accumulate(n_vars.begin(), n_vars.end(), 0);
171
172 std::vector<unsigned int> n_comp (tot_n_vars, 0);
173 std::vector<dof_id_type> first_dof (tot_n_vars, 0);
174
175 for (unsigned int s = 0, i=0; s != n_sys; ++s)
176 {
177 if (sysnum != s &&
178 sysnum != libMesh::invalid_uint)
179 continue;
180
181 for (unsigned int v = 0; v != n_vars[s]; ++v, ++i)
182 {
183 n_comp[i] = d->n_comp(s,v);
184 first_dof[i] = n_comp[i] ? d->dof_number(s,v,0) : DofObject::invalid_id;
185 }
186 }
187
188 libmesh_assert(communicator.semiverify(&n_sys));
189 libmesh_assert(communicator.semiverify(&n_vars));
190 libmesh_assert(communicator.semiverify(&n_comp));
191 libmesh_assert(communicator.semiverify(&first_dof));
192 }
193 else
194 {
195 const unsigned int * p_ui = nullptr;
196 const std::vector<unsigned int> * p_vui = nullptr;
197 const std::vector<dof_id_type> * p_vdid = nullptr;
198
199 libmesh_assert(communicator.semiverify(p_ui));
200 libmesh_assert(communicator.semiverify(p_vui));
201 libmesh_assert(communicator.semiverify(p_vui));
202 libmesh_assert(communicator.semiverify(p_vdid));
203 }
204}
205
206
207
208#ifdef LIBMESH_ENABLE_UNIQUE_ID
209void assert_dofobj_unique_id(const Parallel::Communicator & comm,
210 const DofObject * d,
211 const std::unordered_set<unique_id_type> & unique_ids)
212{
213 // Duplicating some semiverify code here so we can reuse
214 // tempmin,tempmax afterward
215
216 unique_id_type tempmin, tempmax;
217 if (d)
218 {
219 tempmin = tempmax = d->unique_id();
220 }
221 else
222 {
225 }
226 comm.min(tempmin);
227 comm.max(tempmax);
228 bool invalid = d && ((d->unique_id() != tempmin) ||
229 (d->unique_id() != tempmax));
230 comm.max(invalid);
231
232 // First verify that everything is in sync
233 libmesh_assert(!invalid);
234
235 // Then verify that any remote id doesn't duplicate a local one.
236 if (!d && tempmin == tempmax)
237 libmesh_assert(!unique_ids.count(tempmin));
238}
239#endif // LIBMESH_ENABLE_UNIQUE_ID
240#endif // DEBUG
241
242void find_nodal_neighbors_helper(const dof_id_type global_id,
243 const std::vector<const Elem *> & node_to_elem_vec,
244 std::vector<const Node *> & neighbors)
245{
246 // We'll construct a std::set<const Node *> for more efficient
247 // searching while finding the nodal neighbors, and return it to the
248 // user in a std::vector.
249 std::set<const Node *> neighbor_set;
250
251 // Look through the elements that contain this node
252 // find the local node id... then find the side that
253 // node lives on in the element
254 // next, look for the _other_ node on that side
255 // That other node is a "nodal_neighbor"... save it
256 for (const auto & elem : node_to_elem_vec)
257 {
258 // We only care about active elements...
259 if (elem->active())
260 {
261 // Which local node number is global_id?
262 unsigned local_node_number = elem->local_node(global_id);
263
264 // Make sure it was found
265 libmesh_assert_not_equal_to(local_node_number, libMesh::invalid_uint);
266
267 const unsigned short n_edges = elem->n_edges();
268
269 // If this element has no edges, the edge-based algorithm below doesn't make sense.
270 if (!n_edges)
271 {
272 switch (elem->type())
273 {
274 case EDGE2:
275 {
276 switch (local_node_number)
277 {
278 case 0:
279 // The other node is a nodal neighbor
280 neighbor_set.insert(elem->node_ptr(1));
281 break;
282
283 case 1:
284 // The other node is a nodal neighbor
285 neighbor_set.insert(elem->node_ptr(0));
286 break;
287
288 default:
289 libmesh_error_msg("Invalid local node number: " << local_node_number << " found." << std::endl);
290 }
291 break;
292 }
293
294 case EDGE3:
295 {
296 switch (local_node_number)
297 {
298 // The outside nodes have node 2 as a neighbor
299 case 0:
300 case 1:
301 neighbor_set.insert(elem->node_ptr(2));
302 break;
303
304 // The middle node has the outer nodes as neighbors
305 case 2:
306 neighbor_set.insert(elem->node_ptr(0));
307 neighbor_set.insert(elem->node_ptr(1));
308 break;
309
310 default:
311 libmesh_error_msg("Invalid local node number: " << local_node_number << " found." << std::endl);
312 }
313 break;
314 }
315
316 case EDGE4:
317 {
318 switch (local_node_number)
319 {
320 case 0:
321 // The left-middle node is a nodal neighbor
322 neighbor_set.insert(elem->node_ptr(2));
323 break;
324
325 case 1:
326 // The right-middle node is a nodal neighbor
327 neighbor_set.insert(elem->node_ptr(3));
328 break;
329
330 // The left-middle node
331 case 2:
332 neighbor_set.insert(elem->node_ptr(0));
333 neighbor_set.insert(elem->node_ptr(3));
334 break;
335
336 // The right-middle node
337 case 3:
338 neighbor_set.insert(elem->node_ptr(1));
339 neighbor_set.insert(elem->node_ptr(2));
340 break;
341
342 default:
343 libmesh_error_msg("Invalid local node number: " << local_node_number << " found." << std::endl);
344 }
345 break;
346 }
347
348 default:
349 libmesh_error_msg("Unrecognized ElemType: " << Utility::enum_to_string(elem->type()) << std::endl);
350 }
351 }
352
353 const auto elem_order = Elem::type_to_default_order_map[elem->type()];
354
355 // Index of the current edge
356 unsigned current_edge = 0;
357
358 const unsigned short n_nodes = elem->n_nodes();
359
360 while (current_edge < n_edges)
361 {
362 // Find the edge the node is on
363 bool found_edge = false;
364 for (; current_edge<n_edges; ++current_edge)
365 if (elem->is_node_on_edge(local_node_number, current_edge))
366 {
367 found_edge = true;
368 break;
369 }
370
371 // Did we find one?
372 if (found_edge)
373 {
374 const Node * node_to_save = nullptr;
375
376 // Find another node in this element on this edge
377 for (unsigned other_node_this_edge = 0; other_node_this_edge != n_nodes; other_node_this_edge++)
378 {
379 const bool both_vertices = elem->is_vertex(local_node_number) &&
380 elem->is_vertex(other_node_this_edge);
381 if ( elem->is_node_on_edge(other_node_this_edge, current_edge) && // On the current edge
382 elem->node_id(other_node_this_edge) != global_id && // But not the original node
383 // vertex nodes on the same edge of higher order elements are not nodal neighbors
384 (elem_order == 1 || !both_vertices))
385 {
386 // We've found a nodal neighbor! Save a pointer to it..
387 node_to_save = elem->node_ptr(other_node_this_edge);
388
389 // Make sure we found something
390 libmesh_assert(node_to_save != nullptr);
391
392 neighbor_set.insert(node_to_save);
393 }
394 }
395 }
396
397 // Keep looking for edges, node may be on more than one edge
398 current_edge++;
399 }
400 } // if (elem->active())
401 } // for
402
403 // Assign the entries from the set to the vector. Note: this
404 // replaces any existing contents in neighbors and modifies its size
405 // accordingly.
406 neighbors.assign(neighbor_set.begin(), neighbor_set.end());
407}
408
409}
410
411
412namespace libMesh
413{
414
415// ------------------------------------------------------------
416// MeshTools functions
417
418namespace MeshTools
419{
420
422{
423 if (!mesh.is_serial())
424 {
425 libmesh_parallel_only(mesh.comm());
427 mesh.comm().sum(weight);
428 dof_id_type unpartitioned_weight =
430 return weight + unpartitioned_weight;
431 }
432
433 SumElemWeight sew;
434
436 mesh.elements_end()),
437 sew);
438 return sew.weight();
439
440}
441
442
443
445{
446 SumElemWeight sew;
447
448 Threads::parallel_reduce (ConstElemRange (mesh.pid_elements_begin(pid),
449 mesh.pid_elements_end(pid)),
450 sew);
451 return sew.weight();
452}
453
454
455
457 std::vector<std::vector<dof_id_type>> & nodes_to_elem_map)
458{
459 // A vector indexed over all nodes is too inefficient to use for a
460 // distributed mesh. Use the unordered_map API instead.
461 if (!mesh.is_serial())
462 libmesh_deprecated();
463
464 nodes_to_elem_map.resize (mesh.max_node_id());
465
466 for (const auto & elem : mesh.element_ptr_range())
467 for (auto & node : elem->node_ref_range())
468 {
469 libmesh_assert_less (node.id(), nodes_to_elem_map.size());
470 libmesh_assert_less (elem->id(), mesh.n_elem());
471
472 nodes_to_elem_map[node.id()].push_back(elem->id());
473 }
474}
475
476
477
479 std::vector<std::vector<const Elem *>> & nodes_to_elem_map)
480{
481 // A vector indexed over all nodes is too inefficient to use for a
482 // distributed mesh. Use the unordered_map API instead.
483 if (!mesh.is_serial())
484 libmesh_deprecated();
485
486 nodes_to_elem_map.resize (mesh.max_node_id());
487
488 for (const auto & elem : mesh.element_ptr_range())
489 for (auto & node : elem->node_ref_range())
490 {
491 libmesh_assert_less (node.id(), nodes_to_elem_map.size());
492
493 nodes_to_elem_map[node.id()].push_back(elem);
494 }
495}
496
497
498
500 std::unordered_map<dof_id_type, std::vector<dof_id_type>> & nodes_to_elem_map)
501{
502 nodes_to_elem_map.clear();
503
504 for (const auto & elem : mesh.element_ptr_range())
505 for (auto & node : elem->node_ref_range())
506 nodes_to_elem_map[node.id()].push_back(elem->id());
507}
508
509
510
512 std::unordered_map<dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map)
513{
514 nodes_to_elem_map.clear();
515
516 for (const auto & elem : mesh.element_ptr_range())
517 for (auto & node : elem->node_ref_range())
518 nodes_to_elem_map[node.id()].push_back(elem);
519}
520
521
522
523std::unordered_set<dof_id_type>
525{
526 std::unordered_set<dof_id_type> boundary_nodes;
527
528 // Loop over elements, find those on boundary, and
529 // mark them as true in on_boundary.
530 for (const auto & elem : mesh.active_element_ptr_range())
531 for (auto s : elem->side_index_range())
532 if (elem->neighbor_ptr(s) == nullptr) // on the boundary
533 {
534 auto nodes_on_side = elem->nodes_on_side(s);
535
536 for (auto & local_id : nodes_on_side)
537 boundary_nodes.insert(elem->node_ptr(local_id)->id());
538 }
539
540 return boundary_nodes;
541}
542
543std::unordered_set<dof_id_type>
545{
546 std::unordered_set<dof_id_type> block_boundary_nodes;
547
548 // Loop over elements, find those on boundary, and
549 // mark them as true in on_boundary.
550 for (const auto & elem : mesh.active_element_ptr_range())
551 for (auto s : elem->side_index_range())
552 if (elem->neighbor_ptr(s) && (elem->neighbor_ptr(s)->subdomain_id() != elem->subdomain_id()))
553 {
554 auto nodes_on_side = elem->nodes_on_side(s);
555
556 for (auto & local_id : nodes_on_side)
557 block_boundary_nodes.insert(elem->node_ptr(local_id)->id());
558 }
559
560 return block_boundary_nodes;
561}
562
563
564
567{
568 // This function must be run on all processors at once
569 libmesh_parallel_only(mesh.comm());
570
571 FindBBox find_bbox;
572
573 // Start with any unpartitioned elements we know about locally
575 mesh.pid_elements_end(DofObject::invalid_processor_id)),
576 find_bbox);
577
578 // And combine with our local elements
579 find_bbox.bbox().union_with(create_local_bounding_box(mesh));
580
581 // Compare the bounding boxes across processors
582 mesh.comm().min(find_bbox.min());
583 mesh.comm().max(find_bbox.max());
584
585 return find_bbox.bbox();
586}
587
588
589
592{
593 // This function must be run on all processors at once
594 libmesh_parallel_only(mesh.comm());
595
596 FindBBox find_bbox;
597
598 // Start with any unpartitioned nodes we know about locally
601 find_bbox);
602
603 // Add our local nodes
604 Threads::parallel_reduce (ConstNodeRange (mesh.local_nodes_begin(),
605 mesh.local_nodes_end()),
606 find_bbox);
607
608 // Compare the bounding boxes across processors
609 mesh.comm().min(find_bbox.min());
610 mesh.comm().max(find_bbox.max());
611
612 return find_bbox.bbox();
613}
614
615
616
617Sphere
619{
621
622 const Real diag = (bbox.second - bbox.first).norm();
623 const Point cent = (bbox.second + bbox.first)/2;
624
625 return Sphere (cent, .5*diag);
626}
627
628
629
632{
633 FindBBox find_bbox;
634
635 Threads::parallel_reduce (ConstElemRange (mesh.local_elements_begin(),
636 mesh.local_elements_end()),
637 find_bbox);
638
639 return find_bbox.bbox();
640}
641
642
643
646 const processor_id_type pid)
647{
648 // This can only be run in parallel, with consistent arguments.
649 libmesh_parallel_only(mesh.comm());
651
652 libmesh_assert_less (pid, mesh.n_processors());
653
654 FindBBox find_bbox;
655
656 Threads::parallel_reduce (ConstElemRange (mesh.pid_elements_begin(pid),
657 mesh.pid_elements_end(pid)),
658 find_bbox);
659
660 // Compare the bounding boxes across processors
661 mesh.comm().min(find_bbox.min());
662 mesh.comm().max(find_bbox.max());
663
664 return find_bbox.bbox();
665}
666
667
668
669Sphere
671 const processor_id_type pid)
672{
675
676 const Real diag = (bbox.second - bbox.first).norm();
677 const Point cent = (bbox.second + bbox.first)/2;
678
679 return Sphere (cent, .5*diag);
680}
681
682
683
686 const subdomain_id_type sid)
687{
688 // This can only be run in parallel, with consistent arguments.
689 libmesh_parallel_only(mesh.comm());
691
692 FindBBox find_bbox;
693
695 (ConstElemRange (mesh.active_local_subdomain_elements_begin(sid),
696 mesh.active_local_subdomain_elements_end(sid)),
697 find_bbox);
698
699 // Compare the bounding boxes across processors
700 mesh.comm().min(find_bbox.min());
701 mesh.comm().max(find_bbox.max());
702
703 return find_bbox.bbox();
704}
705
706
707
708Sphere
710 const subdomain_id_type sid)
711{
714
715 const Real diag = (bbox.second - bbox.first).norm();
716 const Point cent = (bbox.second + bbox.first)/2;
717
718 return Sphere (cent, .5*diag);
719}
720
721
722
724 std::vector<ElemType> & et)
725{
726 // Loop over the the elements. If the current element type isn't in
727 // the vector, insert it.
728 for (const auto & elem : mesh.element_ptr_range())
729 if (!std::count(et.begin(), et.end(), elem->type()))
730 et.push_back(elem->type());
731}
732
733
734
736 const ElemType type)
737{
738 return static_cast<dof_id_type>(std::distance(mesh.type_elements_begin(type),
739 mesh.type_elements_end (type)));
740}
741
742
743
745 const ElemType type)
746{
747 return static_cast<dof_id_type>(std::distance(mesh.active_type_elements_begin(type),
748 mesh.active_type_elements_end (type)));
749}
750
752 const ElemType type,
753 const unsigned int level)
754{
755 dof_id_type cnt = 0;
756
757 // iterate over the elements of the specified type
758 for (const auto & elem : as_range(mesh.type_elements_begin(type),
759 mesh.type_elements_end(type)))
760 if ((elem->level() == level) && !elem->subactive())
761 cnt++;
762
763 return cnt;
764}
765
766
768{
769 struct LevelCounter {
770 unsigned int nl;
771
772 LevelCounter () : nl(0) {}
773
774 LevelCounter (LevelCounter &, Threads::split) :
775 nl(0) {}
776
777 void operator()(const ConstElemRange & range) {
778 for (const Elem * elem : range)
779 nl = std::max(elem->level() + 1, nl);
780 }
781
782 void join(const LevelCounter & other) {
783 nl = std::max(nl, other.nl);
784 }
785 };
786
787 LevelCounter counter;
788
790
791 return counter.nl;
792}
793
794
795
796unsigned int n_active_levels(const MeshBase & mesh)
797{
798 libmesh_parallel_only(mesh.comm());
799
800 unsigned int nl = n_active_local_levels(mesh);
801
802 for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
803 mesh.unpartitioned_elements_end()))
804 if (elem->active())
805 nl = std::max(elem->level() + 1, nl);
806
807 mesh.comm().max(nl);
808 return nl;
809}
810
811
812
813unsigned int n_local_levels(const MeshBase & mesh)
814{
815 unsigned int nl = 0;
816
817 for (const auto & elem : as_range(mesh.local_elements_begin(),
818 mesh.local_elements_end()))
819 nl = std::max(elem->level() + 1, nl);
820
821 return nl;
822}
823
824
825
826unsigned int n_levels(const MeshBase & mesh)
827{
828 libmesh_parallel_only(mesh.comm());
829
830 unsigned int nl = n_local_levels(mesh);
831
832 for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
833 mesh.unpartitioned_elements_end()))
834 nl = std::max(elem->level() + 1, nl);
835
836 mesh.comm().max(nl);
837
838 // n_levels() is only valid and should only be called in cases where
839 // the mesh is validly distributed (or serialized). Let's run an
840 // expensive test in debug mode to make sure this is such a case.
841#ifdef DEBUG
842 const unsigned int paranoid_nl = paranoid_n_levels(mesh);
843 libmesh_assert_equal_to(nl, paranoid_nl);
844#endif
845 return nl;
846}
847
848
849
850unsigned int paranoid_n_levels(const MeshBase & mesh)
851{
852 libmesh_parallel_only(mesh.comm());
853
854 unsigned int nl = 0;
855 for (const auto & elem : mesh.element_ptr_range())
856 nl = std::max(elem->level() + 1, nl);
857
858 mesh.comm().max(nl);
859 return nl;
860}
861
862
863
865 Real constraint_tol)
866{
867 LOG_SCOPE("n_connected_components()", "MeshTools");
868
869 // Yes, I'm being lazy. This is for mesh analysis before a
870 // simulation, not anything going in any loops.
871 if (!mesh.is_serial_on_zero())
872 libmesh_not_implemented();
873
874 dof_id_type n_components = 0;
875
876 if (mesh.processor_id())
877 {
878 mesh.comm().broadcast(n_components);
879 return n_components;
880 }
881
882 // All nodes in a set here are connected (at least indirectly) to
883 // all other nodes in the same set, but have not yet been discovered
884 // to be connected to nodes in other sets.
885 //
886 // Using an unordered_set of ids rather than a set of pointers seems
887 // to be roughly 150% faster?
888 // typedef const Node * node_entry_type
889 typedef dof_id_type node_entry_type;
890 std::vector<std::unordered_set<node_entry_type>> components;
891
892 // With a typical mesh with few components and somewhat-contiguous
893 // ordering, vector performance should be fine. With a mesh with
894 // many components or completely scrambled ordering, performance
895 // can be a disaster.
896 auto find_component = [&components](node_entry_type n) {
897 for (auto & c: components)
898 if (c.find(n) != c.end())
899 return &c;
900
901 return (std::unordered_set<node_entry_type> *)(nullptr);
902 };
903
904 auto add_to_component =
905 [&find_component]
906 (std::unordered_set<node_entry_type> & component, node_entry_type n) {
907 // We may already be in the desired component
908 if (component.find(n) != component.end())
909 return;
910
911 auto current_component = find_component(n);
912
913 // Didn't we *just* check this?
914 libmesh_assert (&component != current_component);
915
916 // If we're unknown, we should be in the desired component
917 if (!current_component)
918 component.insert(n);
919
920 // If we think we're in another component, it should actually be
921 // part of the desired component
922 else
923 {
924 // Merge the component likely to be smaller into the one
925 // likely to be larger - this is orders of magnitude faster
926 // than the other way around!
927 current_component->merge(component);
928 current_component->swap(component);
929 libmesh_assert(current_component->empty());
930 }
931 };
932
933 auto & constraint_rows = mesh.get_constraint_rows();
934
935 for (const auto & elem : mesh.element_ptr_range())
936 {
937 // const node_entry_type first_node = elem->node_ptr(0);
938 const node_entry_type first_node = elem->node_id(0);
939
940 auto component = find_component(first_node);
941
942 // If we didn't find one, make a new one, reusing an existing
943 // slot if possible or growing our vector if necessary
944 if (!component)
945 for (auto & c: components)
946 if (c.empty())
947 component = &c;
948
949 if (!component)
950 component = &components.emplace_back();
951
952 for (const Node & node : elem->node_ref_range())
953 {
954 // const node_entry_type n = &node;
955 const node_entry_type n = node.id();
956 add_to_component(*component, n);
957
958 auto it = constraint_rows.find(&node);
959 if (it == constraint_rows.end())
960 continue;
961
962 for (const auto & [pr, val] : it->second)
963 {
964 // Ignore too-trivial constraint coefficients if
965 // we get a non-default-0 constraint_tol
966 if (std::abs(val) < constraint_tol)
967 continue;
968
969 const Elem * spline_elem = pr.first;
970 libmesh_assert(spline_elem == mesh.elem_ptr(spline_elem->id()));
971
972 const Node * spline_node =
973 spline_elem->node_ptr(pr.second);
974
975 // add_to_component(*component, spline_node);
976 add_to_component(*component, spline_node->id());
977 }
978 }
979 }
980
981 for (auto & component : components)
982 if (!component.empty())
983 ++n_components;
984
985 // We calculated this on proc 0; now let everyone else know too
986 mesh.comm().broadcast(n_components);
987
988 return n_components;
989}
990
991
992
994 std::set<dof_id_type> & not_subactive_node_ids)
995{
996 for (const auto & elem : mesh.element_ptr_range())
997 if (!elem->subactive())
998 for (auto & n : elem->node_ref_range())
999 not_subactive_node_ids.insert(n.id());
1000}
1001
1002
1003
1006{
1007 return cast_int<dof_id_type>(std::distance(begin, end));
1008}
1009
1010
1011
1014{
1015 return cast_int<dof_id_type>(std::distance(begin, end));
1016}
1017
1018
1019
1021 unsigned int dim)
1022{
1023 libmesh_parallel_only(mesh.comm());
1024
1027
1028 Real vol = 0;
1029
1030 // first my local elements
1031 for (const auto & elem : as_range(mesh.local_elements_begin(),
1032 mesh.local_elements_end()))
1033 if (elem->dim() == dim)
1034 vol += elem->volume();
1035
1036 // then count any unpartitioned objects, once
1037 if (mesh.processor_id() == 0)
1038 for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
1039 mesh.unpartitioned_elements_end()))
1040 if (elem->dim() == dim)
1041 vol += elem->volume();
1042
1043 mesh.comm().sum(vol);
1044 return vol;
1045}
1046
1047
1048
1049unsigned int n_p_levels (const MeshBase & mesh)
1050{
1051 libmesh_parallel_only(mesh.comm());
1052
1053 unsigned int max_p_level = 0;
1054
1055 // first my local elements
1056 for (const auto & elem : as_range(mesh.local_elements_begin(),
1057 mesh.local_elements_end()))
1058 max_p_level = std::max(elem->p_level(), max_p_level);
1059
1060 // then any unpartitioned objects
1061 for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
1062 mesh.unpartitioned_elements_end()))
1063 max_p_level = std::max(elem->p_level(), max_p_level);
1064
1065 mesh.comm().max(max_p_level);
1066 return max_p_level + 1;
1067}
1068
1069
1070
1072 const Node & node,
1073 const std::vector<std::vector<const Elem *>> & nodes_to_elem_map,
1074 std::vector<const Node *> & neighbors)
1075{
1076 find_nodal_neighbors_helper(node.id(), nodes_to_elem_map[node.id()],
1077 neighbors);
1078}
1079
1080
1081
1083 const Node & node,
1084 const std::unordered_map<dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map,
1085 std::vector<const Node *> & neighbors)
1086{
1087 const std::vector<const Elem *> node_to_elem_vec =
1088 libmesh_map_find(nodes_to_elem_map, node.id());
1089 find_nodal_neighbors_helper(node.id(), node_to_elem_vec, neighbors);
1090}
1091
1093 const MeshBase & mesh,
1094 const Node & node,
1095 const std::unordered_map<dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map,
1096 std::vector<const Node *> & neighbors)
1097{
1098 // Find all the nodal neighbors... that is the nodes directly connected
1099 // to this node through one edge.
1100 find_nodal_neighbors(mesh, node, nodes_to_elem_map, neighbors);
1101
1102 // If no neighbors are found, use all nodes on the containing side as
1103 // neighbors.
1104 if (!neighbors.size())
1105 {
1106 // Grab the element containing node
1107 const auto * elem = libmesh_map_find(nodes_to_elem_map, node.id()).front();
1108 // Find the element side containing node
1109 for (const auto &side : elem->side_index_range())
1110 {
1111 const auto &nodes_on_side = elem->nodes_on_side(side);
1112 const auto it =
1113 std::find_if(nodes_on_side.begin(), nodes_on_side.end(), [&](auto local_node_id) {
1114 return elem->node_id(local_node_id) == node.id();
1115 });
1116
1117 if (it != nodes_on_side.end())
1118 {
1119 for (const auto &local_node_id : nodes_on_side)
1120 // No need to add node itself as a neighbor
1121 if (const auto *node_ptr = elem->node_ptr(local_node_id);
1122 *node_ptr != node)
1123 neighbors.push_back(node_ptr);
1124 break;
1125 }
1126 }
1127 }
1128 libmesh_assert(neighbors.size());
1129}
1130
1131
1132
1134 std::map<dof_id_type, std::vector<dof_id_type>> & hanging_nodes)
1135{
1136 // Loop through all the elements
1137 for (auto & elem : mesh.active_local_element_ptr_range())
1138 if (elem->type() == QUAD4)
1139 for (auto s : elem->side_index_range())
1140 {
1141 // Loop over the sides looking for sides that have hanging nodes
1142 // This code is inspired by compute_proj_constraints()
1143 const Elem * neigh = elem->neighbor_ptr(s);
1144
1145 // If not a boundary side
1146 if (neigh != nullptr)
1147 {
1148 // Is there a coarser element next to this one?
1149 if (neigh->level() < elem->level())
1150 {
1151 const Elem * ancestor = elem;
1152 while (neigh->level() < ancestor->level())
1153 ancestor = ancestor->parent();
1154 unsigned int s_neigh = neigh->which_neighbor_am_i(ancestor);
1155 libmesh_assert_less (s_neigh, neigh->n_neighbors());
1156
1157 // Couple of helper uints...
1158 unsigned int local_node1=0;
1159 unsigned int local_node2=0;
1160
1161 bool found_in_neighbor = false;
1162
1163 // Find the two vertices that make up this side
1164 while (!elem->is_node_on_side(local_node1++,s)) { }
1165 local_node1--;
1166
1167 // Start looking for the second one with the next node
1168 local_node2=local_node1+1;
1169
1170 // Find the other one
1171 while (!elem->is_node_on_side(local_node2++,s)) { }
1172 local_node2--;
1173
1174 //Pull out their global ids:
1175 dof_id_type node1 = elem->node_id(local_node1);
1176 dof_id_type node2 = elem->node_id(local_node2);
1177
1178 // Now find which node is present in the neighbor
1179 // FIXME This assumes a level one rule!
1180 // The _other_ one is the hanging node
1181
1182 // First look for the first one
1183 // FIXME could be streamlined a bit
1184 for (unsigned int n=0;n<neigh->n_sides();n++)
1185 if (neigh->node_id(n) == node1)
1186 found_in_neighbor=true;
1187
1188 dof_id_type hanging_node=0;
1189
1190 if (!found_in_neighbor)
1191 hanging_node=node1;
1192 else // If it wasn't node1 then it must be node2!
1193 hanging_node=node2;
1194
1195 // Reset for reuse
1196 local_node1=0;
1197
1198 // Find the first node that makes up the side in the neighbor (these should be the parent nodes)
1199 while (!neigh->is_node_on_side(local_node1++,s_neigh)) { }
1200 local_node1--;
1201
1202 local_node2=local_node1+1;
1203
1204 // Find the second node...
1205 while (!neigh->is_node_on_side(local_node2++,s_neigh)) { }
1206 local_node2--;
1207
1208 // Save them if we haven't already found the parents for this one
1209 if (hanging_nodes[hanging_node].size()<2)
1210 {
1211 hanging_nodes[hanging_node].push_back(neigh->node_id(local_node1));
1212 hanging_nodes[hanging_node].push_back(neigh->node_id(local_node2));
1213 }
1214 }
1215 }
1216 }
1217}
1218
1219
1220
1222{
1223 std::vector<Elem *> nodeelem_to_delete;
1224
1225 for (auto & elem : mesh.element_ptr_range())
1226 if (elem->type() == NODEELEM &&
1227 elem->mapping_type() == RATIONAL_BERNSTEIN_MAP)
1228 nodeelem_to_delete.push_back(elem);
1229
1230 auto & constraint_rows = mesh.get_constraint_rows();
1231
1232 // All our constraint_rows ought to be for spline constraints we're
1233 // about to get rid of.
1234#ifndef NDEBUG
1235 for (auto & node_row : constraint_rows)
1236 for (auto pr : node_row.second)
1237 {
1238 const Elem * elem = pr.first.first;
1239 libmesh_assert(elem->type() == NODEELEM);
1241 }
1242#endif
1243
1244 constraint_rows.clear();
1245
1246 for (Elem * elem : nodeelem_to_delete)
1247 {
1248 Node * node = elem->node_ptr(0);
1249 mesh.delete_elem(elem);
1250 mesh.delete_node(node);
1251 }
1252}
1253
1254
1255
1257{
1258 LOG_SCOPE("valid_is_prepared()", "MeshTools");
1259
1260 if (!mesh.is_prepared())
1261 return true;
1262
1263 std::unique_ptr<MeshBase> mesh_clone = mesh.clone();
1264
1265 // Try preparing (without allowing repartitioning or renumbering, to
1266 // avoid false assertion failures)
1267 bool old_allow_renumbering = mesh_clone->allow_renumbering();
1268 mesh_clone->allow_renumbering(false);
1269
1270 bool old_skip_partitioning = mesh_clone->skip_partitioning();
1271 mesh_clone->skip_partitioning(true);
1272
1273 bool old_allow_remote_element_removal = mesh_clone->allow_remote_element_removal();
1274 mesh_clone->allow_remote_element_removal(false);
1275
1276 // Call prepare_for_use() on the clone
1277 mesh_clone->prepare_for_use();
1278
1279 // Restore original flag values
1280 mesh_clone->allow_renumbering(old_allow_renumbering);
1281 mesh_clone->skip_partitioning(old_skip_partitioning);
1282 mesh_clone->allow_remote_element_removal(old_allow_remote_element_removal);
1283
1284 // Check whether the original and clone compare equal
1285 return (mesh == *mesh_clone);
1286}
1287
1288
1289
1290#ifndef NDEBUG
1291
1293{
1294 LOG_SCOPE("libmesh_assert_equal_n_systems()", "MeshTools");
1295
1296 unsigned int n_sys = libMesh::invalid_uint;
1297
1298 for (const auto & elem : mesh.element_ptr_range())
1299 {
1300 if (n_sys == libMesh::invalid_uint)
1301 n_sys = elem->n_systems();
1302 else
1303 libmesh_assert_equal_to (elem->n_systems(), n_sys);
1304 }
1305
1306 for (const auto & node : mesh.node_ptr_range())
1307 {
1308 if (n_sys == libMesh::invalid_uint)
1309 n_sys = node->n_systems();
1310 else
1311 libmesh_assert_equal_to (node->n_systems(), n_sys);
1312 }
1313}
1314
1315
1316
1317#ifdef LIBMESH_ENABLE_AMR
1319{
1320 LOG_SCOPE("libmesh_assert_old_dof_objects()", "MeshTools");
1321
1322 for (const auto & elem : mesh.element_ptr_range())
1323 {
1324 if (elem->refinement_flag() == Elem::JUST_REFINED ||
1325 elem->refinement_flag() == Elem::INACTIVE)
1326 continue;
1327
1328 if (elem->has_dofs())
1329 libmesh_assert(elem->get_old_dof_object());
1330
1331 for (auto & node : elem->node_ref_range())
1332 if (node.has_dofs())
1333 libmesh_assert(node.get_old_dof_object());
1334 }
1335}
1336#else
1338#endif // LIBMESH_ENABLE_AMR
1339
1340
1341
1343{
1344 LOG_SCOPE("libmesh_assert_valid_node_pointers()", "MeshTools");
1345
1346 // Here we specifically do not want "auto &" because we need to
1347 // reseat the (temporary) pointer variable in the loop below,
1348 // without modifying the original.
1349 for (const Elem * elem : mesh.element_ptr_range())
1350 {
1351 libmesh_assert (elem);
1352 while (elem)
1353 {
1354 elem->libmesh_assert_valid_node_pointers();
1355 for (auto n : elem->neighbor_ptr_range())
1356 if (n && n != remote_elem)
1357 n->libmesh_assert_valid_node_pointers();
1358
1359 libmesh_assert_not_equal_to (elem->parent(), remote_elem);
1360 elem = elem->parent();
1361 }
1362 }
1363}
1364
1365
1366
1368{
1369 LOG_SCOPE("libmesh_assert_valid_remote_elems()", "MeshTools");
1370
1371 for (const auto & elem : as_range(mesh.local_elements_begin(),
1372 mesh.local_elements_end()))
1373 {
1374 libmesh_assert (elem);
1375
1376 // We currently don't allow active_local_elements to have
1377 // remote_elem neighbors
1378 if (elem->active())
1379 for (auto n : elem->neighbor_ptr_range())
1380 libmesh_assert_not_equal_to (n, remote_elem);
1381
1382#ifdef LIBMESH_ENABLE_AMR
1383 const Elem * parent = elem->parent();
1384 if (parent)
1385 libmesh_assert_not_equal_to (parent, remote_elem);
1386
1387 // We can only be strict about active elements' subactive
1388 // children
1389 if (elem->active() && elem->has_children())
1390 for (auto & child : elem->child_ref_range())
1391 libmesh_assert_not_equal_to (&child, remote_elem);
1392#endif
1393 }
1394}
1395
1396
1397
1399{
1400 LOG_SCOPE("libmesh_assert_valid_elem_ids()", "MeshTools");
1401
1402 processor_id_type lastprocid = 0;
1403 dof_id_type lastelemid = 0;
1404
1405 for (const auto & elem : mesh.active_element_ptr_range())
1406 {
1407 libmesh_assert (elem);
1408 processor_id_type elemprocid = elem->processor_id();
1409 dof_id_type elemid = elem->id();
1410
1411 libmesh_assert_greater_equal (elemid, lastelemid);
1412 libmesh_assert_greater_equal (elemprocid, lastprocid);
1413
1414 lastelemid = elemid;
1415 lastprocid = elemprocid;
1416 }
1417}
1418
1419
1420
1422{
1423 LOG_SCOPE("libmesh_assert_valid_amr_elem_ids()", "MeshTools");
1424
1425 for (const auto & elem : mesh.element_ptr_range())
1426 {
1427 libmesh_assert (elem);
1428
1429 const Elem * parent = elem->parent();
1430
1431 if (parent)
1432 {
1433 libmesh_assert_greater_equal (elem->id(), parent->id());
1434 libmesh_assert_greater_equal (elem->processor_id(), parent->processor_id());
1435 }
1436 }
1437}
1438
1439
1440
1442{
1443 LOG_SCOPE("libmesh_assert_valid_amr_interior_parents()", "MeshTools");
1444
1445 for (const auto & elem : mesh.element_ptr_range())
1446 {
1447 libmesh_assert (elem);
1448
1449 // We can skip to the next element if we're full-dimension
1450 // and therefore don't have any interior parents
1451 if (elem->dim() >= LIBMESH_DIM)
1452 continue;
1453
1454 const Elem * ip = elem->interior_parent();
1455
1456 const Elem * parent = elem->parent();
1457
1458 if (ip && (ip != remote_elem) && parent)
1459 {
1460 libmesh_assert_equal_to (ip->top_parent(),
1461 elem->top_parent()->interior_parent());
1462
1463 if (ip->level() == elem->level())
1464 libmesh_assert_equal_to (ip->parent(),
1465 parent->interior_parent());
1466 else
1467 {
1468 libmesh_assert_less (ip->level(), elem->level());
1469 libmesh_assert_equal_to (ip, parent->interior_parent());
1470 }
1471 }
1472 }
1473}
1474
1475
1476
1477void libmesh_assert_contiguous_dof_ids(const MeshBase & mesh, unsigned int sysnum)
1478{
1479 LOG_SCOPE("libmesh_assert_contiguous_dof_ids()", "MeshTools");
1480
1481 if (mesh.n_processors() == 1)
1482 return;
1483
1484 libmesh_parallel_only(mesh.comm());
1485
1486 dof_id_type min_dof_id = std::numeric_limits<dof_id_type>::max(),
1487 max_dof_id = std::numeric_limits<dof_id_type>::min();
1488
1489 // Figure out what our local dof id range is
1490 for (const auto * node : mesh.local_node_ptr_range())
1491 {
1492 for (auto v : make_range(node->n_vars(sysnum)))
1493 for (auto c : make_range(node->n_comp(sysnum, v)))
1494 {
1495 dof_id_type id = node->dof_number(sysnum, v, c);
1496 min_dof_id = std::min (min_dof_id, id);
1497 max_dof_id = std::max (max_dof_id, id);
1498 }
1499 }
1500
1501 // Make sure no other processors' ids are inside it
1502 for (const auto * node : mesh.node_ptr_range())
1503 {
1504 if (node->processor_id() == mesh.processor_id())
1505 continue;
1506 for (auto v : make_range(node->n_vars(sysnum)))
1507 for (auto c : make_range(node->n_comp(sysnum, v)))
1508 {
1509 dof_id_type id = node->dof_number(sysnum, v, c);
1510 libmesh_assert (id < min_dof_id ||
1511 id > max_dof_id);
1512 }
1513 }
1514}
1515
1516
1517
1518template <>
1520{
1521 LOG_SCOPE("libmesh_assert_topology_consistent_procids()", "MeshTools");
1522
1523 // This parameter is not used when !LIBMESH_ENABLE_AMR
1525
1526 // If we're adaptively refining, check processor ids for consistency
1527 // between parents and children.
1528#ifdef LIBMESH_ENABLE_AMR
1529
1530 // Ancestor elements we won't worry about, but subactive and active
1531 // elements ought to have parents with consistent processor ids
1532 for (const auto & elem : mesh.element_ptr_range())
1533 {
1534 libmesh_assert(elem);
1535
1536 if (!elem->active() && !elem->subactive())
1537 continue;
1538
1539 const Elem * parent = elem->parent();
1540
1541 if (parent)
1542 {
1543 libmesh_assert(parent->has_children());
1544 processor_id_type parent_procid = parent->processor_id();
1545 bool matching_child_id = false;
1546 // If we've got a remote_elem then we don't know whether
1547 // it's responsible for the parent's processor id; all
1548 // we can do is assume it is and let its processor fail
1549 // an assert if there's something wrong.
1550 for (auto & child : parent->child_ref_range())
1551 if (&child == remote_elem ||
1552 child.processor_id() == parent_procid)
1553 matching_child_id = true;
1554 libmesh_assert(matching_child_id);
1555 }
1556 }
1557#endif
1558}
1559
1560
1561
1562template <>
1564{
1565 LOG_SCOPE("libmesh_assert_topology_consistent_procids()", "MeshTools");
1566
1567 if (mesh.n_processors() == 1)
1568 return;
1569
1570 libmesh_parallel_only(mesh.comm());
1571
1572 // We want this test to be valid even when called after nodes have
1573 // been added asynchronously but before they're renumbered.
1574 //
1575 // Plus, some code (looking at you, stitch_meshes) modifies
1576 // DofObject ids without keeping max_elem_id()/max_node_id()
1577 // consistent, but that's done in a safe way for performance
1578 // reasons, so we'll play along and just figure out new max ids
1579 // ourselves.
1580 dof_id_type parallel_max_node_id = 0;
1581 for (const auto & node : mesh.node_ptr_range())
1582 parallel_max_node_id = std::max<dof_id_type>(parallel_max_node_id,
1583 node->id()+1);
1584 mesh.comm().max(parallel_max_node_id);
1585
1586
1587 std::vector<bool> node_touched_by_me(parallel_max_node_id, false);
1588
1589 for (const auto & elem : as_range(mesh.local_elements_begin(),
1590 mesh.local_elements_end()))
1591 {
1592 libmesh_assert (elem);
1593
1594 for (auto & node : elem->node_ref_range())
1595 {
1596 dof_id_type nodeid = node.id();
1597 node_touched_by_me[nodeid] = true;
1598 }
1599 }
1600 std::vector<bool> node_touched_by_anyone(node_touched_by_me);
1601 mesh.comm().max(node_touched_by_anyone);
1602
1603 for (const auto & node : mesh.local_node_ptr_range())
1604 {
1605 libmesh_assert(node);
1606 dof_id_type nodeid = node->id();
1607 libmesh_assert(!node_touched_by_anyone[nodeid] ||
1608 node_touched_by_me[nodeid]);
1609 }
1610}
1611
1612
1613
1615{
1616 for (const auto & elem : mesh.active_element_ptr_range())
1617 for (auto & node : elem->node_ref_range())
1618 libmesh_assert_equal_to
1619 (node.processor_id(),
1620 node.choose_processor_id(node.processor_id(),
1621 elem->processor_id()));
1622}
1623
1624
1625
1626#ifdef LIBMESH_ENABLE_AMR
1628{
1629 LOG_SCOPE("libmesh_assert_valid_refinement_tree()", "MeshTools");
1630
1631 for (const auto & elem : mesh.element_ptr_range())
1632 {
1633 libmesh_assert(elem);
1634 if (elem->has_children())
1635 for (auto & child : elem->child_ref_range())
1636 if (&child != remote_elem)
1637 libmesh_assert_equal_to (child.parent(), elem);
1638 if (elem->active())
1639 {
1640 libmesh_assert(!elem->ancestor());
1641 libmesh_assert(!elem->subactive());
1642 }
1643 else if (elem->ancestor())
1644 {
1645 libmesh_assert(!elem->subactive());
1646 }
1647 else
1648 libmesh_assert(elem->subactive());
1649
1650 if (elem->p_refinement_flag() == Elem::JUST_REFINED)
1651 libmesh_assert_greater(elem->p_level(), 0);
1652 }
1653}
1654#else
1656{
1657}
1658#endif // LIBMESH_ENABLE_AMR
1659
1660#endif // !NDEBUG
1661
1662
1663
1664#ifdef DEBUG
1665
1667 const Elem * bad_elem)
1668{
1669 for (const auto & elem : mesh.element_ptr_range())
1670 {
1671 libmesh_assert (elem);
1672 libmesh_assert_not_equal_to (elem->parent(), bad_elem);
1673 for (auto n : elem->neighbor_ptr_range())
1674 libmesh_assert_not_equal_to (n, bad_elem);
1675
1676#ifdef LIBMESH_ENABLE_AMR
1677 if (elem->has_children())
1678 for (auto & child : elem->child_ref_range())
1679 libmesh_assert_not_equal_to (&child, bad_elem);
1680#endif
1681 }
1682}
1683
1684
1686{
1687 LOG_SCOPE("libmesh_assert_equal_points()", "MeshTools");
1688
1689 dof_id_type pmax_node_id = mesh.max_node_id();
1690 mesh.comm().max(pmax_node_id);
1691
1692 for (dof_id_type i=0; i != pmax_node_id; ++i)
1693 {
1694 const Point * p = mesh.query_node_ptr(i);
1695
1697 }
1698}
1699
1700
1702{
1703 LOG_SCOPE("libmesh_assert_equal_connectivity()", "MeshTools");
1704
1705 dof_id_type pmax_elem_id = mesh.max_elem_id();
1706 mesh.comm().max(pmax_elem_id);
1707
1708 for (dof_id_type i=0; i != pmax_elem_id; ++i)
1709 {
1710 const Elem * e = mesh.query_elem_ptr(i);
1711
1712 std::vector<dof_id_type> nodes;
1713 if (e)
1714 for (auto n : e->node_index_range())
1715 nodes.push_back(e->node_id(n));
1716
1717 libmesh_assert(mesh.comm().semiverify(e ? &nodes : nullptr));
1718 }
1719}
1720
1721
1723{
1724 LOG_SCOPE("libmesh_assert_connected_nodes()", "MeshTools");
1725
1726 std::set<const Node *> used_nodes;
1727
1728 for (const auto & elem : mesh.element_ptr_range())
1729 {
1730 libmesh_assert (elem);
1731
1732 for (auto & n : elem->node_ref_range())
1733 used_nodes.insert(&n);
1734 }
1735
1736 for (const auto & node : mesh.node_ptr_range())
1737 {
1738 libmesh_assert(node);
1739 libmesh_assert(used_nodes.count(node));
1740 }
1741}
1742
1743
1744
1746{
1747 libmesh_parallel_only(mesh.comm());
1748
1749 const auto & constraint_rows = mesh.get_constraint_rows();
1750
1751 bool have_constraint_rows = !constraint_rows.empty();
1752 mesh.comm().max(have_constraint_rows);
1753 if (!have_constraint_rows)
1754 return;
1755
1756 for (auto & row : constraint_rows)
1757 {
1758 const Node * node = row.first;
1759 libmesh_assert(node == mesh.node_ptr(node->id()));
1760
1761 for (auto & pr : row.second)
1762 {
1763 const Elem * spline_elem = pr.first.first;
1764 libmesh_assert(spline_elem == mesh.elem_ptr(spline_elem->id()));
1765 }
1766 }
1767
1768 dof_id_type pmax_node_id = mesh.max_node_id();
1769 mesh.comm().max(pmax_node_id);
1770
1771 for (dof_id_type i=0; i != pmax_node_id; ++i)
1772 {
1773 const Node * node = mesh.query_node_ptr(i);
1774
1775 bool have_constraint = constraint_rows.count(node);
1776
1777 const std::size_t my_n_constraints = have_constraint ?
1778 libmesh_map_find(constraint_rows, node).size() : std::size_t(-1);
1779 const std::size_t * n_constraints = node ?
1780 &my_n_constraints : nullptr;
1781
1782 libmesh_assert(mesh.comm().semiverify(n_constraints));
1783 }
1784}
1785
1786
1787
1789{
1790 LOG_SCOPE("libmesh_assert_valid_boundary_ids()", "MeshTools");
1791
1792 if (mesh.n_processors() == 1)
1793 return;
1794
1795 libmesh_parallel_only(mesh.comm());
1796
1797 const BoundaryInfo & boundary_info = mesh.get_boundary_info();
1798
1799 dof_id_type pmax_elem_id = mesh.max_elem_id();
1800 mesh.comm().max(pmax_elem_id);
1801
1802 for (dof_id_type i=0; i != pmax_elem_id; ++i)
1803 {
1804 const Elem * elem = mesh.query_elem_ptr(i);
1805 const unsigned int my_n_nodes = elem ? elem->n_nodes() : 0;
1806 const unsigned int my_n_edges = elem ? elem->n_edges() : 0;
1807 const unsigned int my_n_sides = elem ? elem->n_sides() : 0;
1808 unsigned int
1809 n_nodes = my_n_nodes,
1810 n_edges = my_n_edges,
1811 n_sides = my_n_sides;
1812
1813 mesh.comm().max(n_nodes);
1814 mesh.comm().max(n_edges);
1815 mesh.comm().max(n_sides);
1816
1817 if (elem)
1818 {
1819 libmesh_assert_equal_to(my_n_nodes, n_nodes);
1820 libmesh_assert_equal_to(my_n_edges, n_edges);
1821 libmesh_assert_equal_to(my_n_sides, n_sides);
1822 }
1823
1824 // Let's test all IDs on the element with one communication
1825 // rather than n_nodes + n_edges + n_sides communications, to
1826 // cut down on latency in dbg modes.
1827 std::vector<boundary_id_type> all_bcids;
1828
1829 for (unsigned int n=0; n != n_nodes; ++n)
1830 {
1831 std::vector<boundary_id_type> bcids;
1832 if (elem)
1833 {
1834 boundary_info.boundary_ids(elem->node_ptr(n), bcids);
1835
1836 // Ordering of boundary ids shouldn't matter
1837 std::sort(bcids.begin(), bcids.end());
1838 }
1839 // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1840
1841 all_bcids.insert(all_bcids.end(), bcids.begin(),
1842 bcids.end());
1843 // Separator
1844 all_bcids.push_back(BoundaryInfo::invalid_id);
1845 }
1846
1847 for (unsigned short e=0; e != n_edges; ++e)
1848 {
1849 std::vector<boundary_id_type> bcids;
1850
1851 if (elem)
1852 {
1853 boundary_info.edge_boundary_ids(elem, e, bcids);
1854
1855 // Ordering of boundary ids shouldn't matter
1856 std::sort(bcids.begin(), bcids.end());
1857 }
1858
1859 // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1860
1861 all_bcids.insert(all_bcids.end(), bcids.begin(),
1862 bcids.end());
1863 // Separator
1864 all_bcids.push_back(BoundaryInfo::invalid_id);
1865
1866 if (elem)
1867 {
1868 boundary_info.raw_edge_boundary_ids(elem, e, bcids);
1869
1870 // Ordering of boundary ids shouldn't matter
1871 std::sort(bcids.begin(), bcids.end());
1872
1873 all_bcids.insert(all_bcids.end(), bcids.begin(),
1874 bcids.end());
1875 // Separator
1876 all_bcids.push_back(BoundaryInfo::invalid_id);
1877 }
1878
1879 // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1880 }
1881
1882 for (unsigned short s=0; s != n_sides; ++s)
1883 {
1884 std::vector<boundary_id_type> bcids;
1885
1886 if (elem)
1887 {
1888 boundary_info.boundary_ids(elem, s, bcids);
1889
1890 // Ordering of boundary ids shouldn't matter
1891 std::sort(bcids.begin(), bcids.end());
1892
1893 all_bcids.insert(all_bcids.end(), bcids.begin(),
1894 bcids.end());
1895 // Separator
1896 all_bcids.push_back(BoundaryInfo::invalid_id);
1897 }
1898
1899 // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1900
1901 if (elem)
1902 {
1903 boundary_info.raw_boundary_ids(elem, s, bcids);
1904
1905 // Ordering of boundary ids shouldn't matter
1906 std::sort(bcids.begin(), bcids.end());
1907
1908 all_bcids.insert(all_bcids.end(), bcids.begin(),
1909 bcids.end());
1910 // Separator
1911 all_bcids.push_back(BoundaryInfo::invalid_id);
1912 }
1913
1914 // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1915 }
1916
1917 for (unsigned short sf=0; sf != 2; ++sf)
1918 {
1919 std::vector<boundary_id_type> bcids;
1920
1921 if (elem)
1922 {
1923 boundary_info.shellface_boundary_ids(elem, sf, bcids);
1924
1925 // Ordering of boundary ids shouldn't matter
1926 std::sort(bcids.begin(), bcids.end());
1927
1928 all_bcids.insert(all_bcids.end(), bcids.begin(),
1929 bcids.end());
1930 // Separator
1931 all_bcids.push_back(BoundaryInfo::invalid_id);
1932 }
1933
1934 // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1935
1936 if (elem)
1937 {
1938 boundary_info.raw_shellface_boundary_ids(elem, sf, bcids);
1939
1940 // Ordering of boundary ids shouldn't matter
1941 std::sort(bcids.begin(), bcids.end());
1942
1943 all_bcids.insert(all_bcids.end(), bcids.begin(),
1944 bcids.end());
1945 // Separator
1946 all_bcids.push_back(BoundaryInfo::invalid_id);
1947 }
1948
1949 // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1950 }
1951
1953 (elem ? &all_bcids : nullptr));
1954 }
1955}
1956
1957
1958void libmesh_assert_valid_dof_ids(const MeshBase & mesh, unsigned int sysnum)
1959{
1960 LOG_SCOPE("libmesh_assert_valid_dof_ids()", "MeshTools");
1961
1962 if (mesh.n_processors() == 1)
1963 return;
1964
1965 libmesh_parallel_only(mesh.comm());
1966
1967 dof_id_type pmax_elem_id = mesh.max_elem_id();
1968 mesh.comm().max(pmax_elem_id);
1969
1970 for (dof_id_type i=0; i != pmax_elem_id; ++i)
1971 assert_semiverify_dofobj(mesh.comm(),
1973 sysnum);
1974
1975 dof_id_type pmax_node_id = mesh.max_node_id();
1976 mesh.comm().max(pmax_node_id);
1977
1978 for (dof_id_type i=0; i != pmax_node_id; ++i)
1979 assert_semiverify_dofobj(mesh.comm(),
1981 sysnum);
1982}
1983
1984
1985#ifdef LIBMESH_ENABLE_UNIQUE_ID
1987{
1988 LOG_SCOPE("libmesh_assert_valid_unique_ids()", "MeshTools");
1989
1990 libmesh_parallel_only(mesh.comm());
1991
1992 // Storage for semi-local DofObject ids.
1993 std::unordered_set<unique_id_type> semilocal_unique_ids;
1994
1995 auto gather_elem_ids = [&]()
1996 {
1997 for (auto const & elem : mesh.active_element_ptr_range())
1998 {
1999 auto [it, inserted] = semilocal_unique_ids.insert(elem->unique_id());
2000 libmesh_assert(inserted);
2001 libmesh_ignore(it);
2002 }
2003 };
2004
2005 auto gather_node_ids = [&]()
2006 {
2007 for (auto const & node : mesh.node_ptr_range())
2008 {
2009 auto [it, inserted] = semilocal_unique_ids.insert(node->unique_id());
2010 libmesh_assert(inserted);
2011 libmesh_ignore(it);
2012 }
2013 };
2014
2015 auto verify_elems = [&]()
2016 {
2017 dof_id_type pmax_elem_id = mesh.max_elem_id();
2018 mesh.comm().max(pmax_elem_id);
2019
2020 for (auto i : make_range(pmax_elem_id))
2021 {
2022 const Elem * elem = mesh.query_elem_ptr(i);
2023 assert_dofobj_unique_id(mesh.comm(), elem, semilocal_unique_ids);
2024 }
2025 };
2026
2027 auto verify_nodes = [&]()
2028 {
2029 dof_id_type pmax_node_id = mesh.max_node_id();
2030 mesh.comm().max(pmax_node_id);
2031
2032 for (auto i : make_range(pmax_node_id))
2033 {
2034 const Node * node = mesh.query_node_ptr(i);
2035 assert_dofobj_unique_id(mesh.comm(), node, semilocal_unique_ids);
2036 }
2037 };
2038
2040 {
2041 // First collect all the unique_ids we can see and make sure there's
2042 // no duplicates
2043 gather_elem_ids();
2044 gather_node_ids();
2045
2046 // Then make sure elements/nodes are all in sync and remote
2047 // elements don't duplicate semilocal
2048 verify_elems();
2049 verify_nodes();
2050 }
2051 else
2052 {
2053 // If the mesh allows Node and Elem unique_ids to overlap, then we only
2054 // check for validity and uniqueness of an Elem (resp. Node) unique id
2055 // within the set of Elem (resp. Node) unique_ids.
2056 gather_elem_ids();
2057 verify_elems();
2058
2059 // Clear id list before checking Nodes
2060 semilocal_unique_ids.clear();
2061
2062 // Finally, check Nodes
2063 gather_node_ids();
2064 verify_nodes();
2065 }
2066}
2067#endif
2068
2070{
2071 libmesh_parallel_only(mesh.comm());
2072
2073 dof_id_type parallel_max_elem_id = mesh.max_elem_id();
2074 mesh.comm().max(parallel_max_elem_id);
2075
2076 for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
2077 {
2078 const Elem * elem = mesh.query_elem_ptr(i);
2079 processor_id_type pid =
2081 mesh.comm().min(pid);
2082 libmesh_assert(elem || pid != mesh.processor_id());
2083 }
2084
2085 dof_id_type parallel_max_node_id = mesh.max_node_id();
2086 mesh.comm().max(parallel_max_node_id);
2087
2088 for (dof_id_type i=0; i != parallel_max_node_id; ++i)
2089 {
2090 const Node * node = mesh.query_node_ptr(i);
2091 processor_id_type pid =
2093 mesh.comm().min(pid);
2094 libmesh_assert(node || pid != mesh.processor_id());
2095 }
2096}
2097
2098
2100{
2101 libmesh_parallel_only(mesh.comm());
2102 auto locator = mesh.sub_point_locator();
2103
2104 dof_id_type parallel_max_elem_id = mesh.max_elem_id();
2105 mesh.comm().max(parallel_max_elem_id);
2106
2107 for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
2108 {
2109 const Elem * elem = mesh.query_elem_ptr(i);
2110
2111 const unsigned int my_n_nodes = elem ? elem->n_nodes() : 0;
2112 unsigned int n_nodes = my_n_nodes;
2113 mesh.comm().max(n_nodes);
2114
2115 if (n_nodes)
2116 libmesh_assert(mesh.comm().semiverify(elem ? &my_n_nodes : nullptr));
2117
2118 for (unsigned int n=0; n != n_nodes; ++n)
2119 {
2120 const Node * node = elem ? elem->node_ptr(n) : nullptr;
2121 processor_id_type pid =
2123 mesh.comm().min(pid);
2124 libmesh_assert(node || pid != mesh.processor_id());
2125 }
2126 }
2127}
2128
2129
2130
2131template <>
2133{
2134 LOG_SCOPE("libmesh_assert_parallel_consistent_procids()", "MeshTools");
2135
2136 if (mesh.n_processors() == 1)
2137 return;
2138
2139 libmesh_parallel_only(mesh.comm());
2140
2141 // Some code (looking at you, stitch_meshes) modifies DofObject ids
2142 // without keeping max_elem_id()/max_node_id() consistent, but
2143 // that's done in a safe way for performance reasons, so we'll play
2144 // along and just figure out new max ids ourselves.
2145 dof_id_type parallel_max_elem_id = 0;
2146 for (const auto & elem : mesh.element_ptr_range())
2147 parallel_max_elem_id = std::max<dof_id_type>(parallel_max_elem_id,
2148 elem->id()+1);
2149 mesh.comm().max(parallel_max_elem_id);
2150
2151 // Check processor ids for consistency between processors
2152
2153 for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
2154 {
2155 const Elem * elem = mesh.query_elem_ptr(i);
2156
2157 processor_id_type min_id =
2158 elem ? elem->processor_id() :
2159 std::numeric_limits<processor_id_type>::max();
2160 mesh.comm().min(min_id);
2161
2162 processor_id_type max_id =
2163 elem ? elem->processor_id() :
2164 std::numeric_limits<processor_id_type>::min();
2165 mesh.comm().max(max_id);
2166
2167 if (elem)
2168 {
2169 libmesh_assert_equal_to (min_id, elem->processor_id());
2170 libmesh_assert_equal_to (max_id, elem->processor_id());
2171 }
2172
2173 if (min_id == mesh.processor_id())
2174 libmesh_assert(elem);
2175 }
2176}
2177
2178
2179
2181{
2182 LOG_SCOPE("libmesh_assert_parallel_consistent_new_node_procids()", "MeshTools");
2183
2184 if (mesh.n_processors() == 1)
2185 return;
2186
2187 libmesh_parallel_only(mesh.comm());
2188
2189 // We want this test to hit every node when called even after nodes
2190 // have been added asynchronously but before everything has been
2191 // renumbered.
2192 dof_id_type parallel_max_elem_id = mesh.max_elem_id();
2193 mesh.comm().max(parallel_max_elem_id);
2194
2195 std::vector<bool> elem_touched_by_anyone(parallel_max_elem_id, false);
2196
2197 for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
2198 {
2199 const Elem * elem = mesh.query_elem_ptr(i);
2200
2201 const unsigned int my_n_nodes = elem ? elem->n_nodes() : 0;
2202 unsigned int n_nodes = my_n_nodes;
2203 mesh.comm().max(n_nodes);
2204
2205 if (n_nodes)
2206 libmesh_assert(mesh.comm().semiverify(elem ? &my_n_nodes : nullptr));
2207
2208 for (unsigned int n=0; n != n_nodes; ++n)
2209 {
2210 const Node * node = elem ? elem->node_ptr(n) : nullptr;
2211 const processor_id_type pid = node ? node->processor_id() : 0;
2212 libmesh_assert(mesh.comm().semiverify (node ? &pid : nullptr));
2213 }
2214 }
2215}
2216
2217template <>
2219{
2220 LOG_SCOPE("libmesh_assert_parallel_consistent_procids()", "MeshTools");
2221
2222 if (mesh.n_processors() == 1)
2223 return;
2224
2225 libmesh_parallel_only(mesh.comm());
2226
2227 // We want this test to be valid even when called even after nodes
2228 // have been added asynchronously but before they're renumbered
2229 //
2230 // Plus, some code (looking at you, stitch_meshes) modifies
2231 // DofObject ids without keeping max_elem_id()/max_node_id()
2232 // consistent, but that's done in a safe way for performance
2233 // reasons, so we'll play along and just figure out new max ids
2234 // ourselves.
2235 dof_id_type parallel_max_node_id = 0;
2236 for (const auto & node : mesh.node_ptr_range())
2237 parallel_max_node_id = std::max<dof_id_type>(parallel_max_node_id,
2238 node->id()+1);
2239 mesh.comm().max(parallel_max_node_id);
2240
2241 std::vector<bool> node_touched_by_anyone(parallel_max_node_id, false);
2242
2243 for (const auto & elem : as_range(mesh.local_elements_begin(),
2244 mesh.local_elements_end()))
2245 {
2246 libmesh_assert (elem);
2247
2248 for (auto & node : elem->node_ref_range())
2249 {
2250 dof_id_type nodeid = node.id();
2251 node_touched_by_anyone[nodeid] = true;
2252 }
2253 }
2254 mesh.comm().max(node_touched_by_anyone);
2255
2256 // Check processor ids for consistency between processors
2257 // on any node an element touches
2258 for (dof_id_type i=0; i != parallel_max_node_id; ++i)
2259 {
2260 if (!node_touched_by_anyone[i])
2261 continue;
2262
2263 const Node * node = mesh.query_node_ptr(i);
2264 const processor_id_type pid = node ? node->processor_id() : 0;
2265
2266 libmesh_assert(mesh.comm().semiverify (node ? &pid : nullptr));
2267 }
2268}
2269
2270
2271
2272#ifdef LIBMESH_ENABLE_AMR
2274{
2275 LOG_SCOPE("libmesh_assert_valid_refinement_flags()", "MeshTools");
2276
2277 libmesh_parallel_only(mesh.comm());
2278 if (mesh.n_processors() == 1)
2279 return;
2280
2281 dof_id_type pmax_elem_id = mesh.max_elem_id();
2282 mesh.comm().max(pmax_elem_id);
2283
2284 std::vector<unsigned char> my_elem_h_state(pmax_elem_id, 255);
2285 std::vector<unsigned char> my_elem_p_state(pmax_elem_id, 255);
2286
2287 for (const auto & elem : mesh.element_ptr_range())
2288 {
2289 libmesh_assert (elem);
2290 dof_id_type elemid = elem->id();
2291
2292 my_elem_h_state[elemid] =
2293 static_cast<unsigned char>(elem->refinement_flag());
2294
2295 my_elem_p_state[elemid] =
2296 static_cast<unsigned char>(elem->p_refinement_flag());
2297 }
2298 std::vector<unsigned char> min_elem_h_state(my_elem_h_state);
2299 mesh.comm().min(min_elem_h_state);
2300
2301 std::vector<unsigned char> min_elem_p_state(my_elem_p_state);
2302 mesh.comm().min(min_elem_p_state);
2303
2304 for (dof_id_type i=0; i!= pmax_elem_id; ++i)
2305 {
2306 libmesh_assert(my_elem_h_state[i] == 255 ||
2307 my_elem_h_state[i] == min_elem_h_state[i]);
2308 libmesh_assert(my_elem_p_state[i] == 255 ||
2309 my_elem_p_state[i] == min_elem_p_state[i]);
2310 }
2311}
2312#else
2314{
2315}
2316#endif // LIBMESH_ENABLE_AMR
2317
2318
2319
2321 bool assert_valid_remote_elems)
2322{
2323 LOG_SCOPE("libmesh_assert_valid_neighbors()", "MeshTools");
2324
2325 for (const auto & elem : mesh.element_ptr_range())
2326 {
2327 libmesh_assert (elem);
2328 elem->libmesh_assert_valid_neighbors();
2329 }
2330
2331 if (mesh.n_processors() == 1)
2332 return;
2333
2334 libmesh_parallel_only(mesh.comm());
2335
2336 dof_id_type pmax_elem_id = mesh.max_elem_id();
2337 mesh.comm().max(pmax_elem_id);
2338
2339 for (dof_id_type i=0; i != pmax_elem_id; ++i)
2340 {
2341 const Elem * elem = mesh.query_elem_ptr(i);
2342
2343 const unsigned int my_n_neigh = elem ? elem->n_neighbors() : 0;
2344 unsigned int n_neigh = my_n_neigh;
2345 mesh.comm().max(n_neigh);
2346 if (elem)
2347 libmesh_assert_equal_to (my_n_neigh, n_neigh);
2348
2349 for (unsigned int n = 0; n != n_neigh; ++n)
2350 {
2351 dof_id_type my_neighbor = DofObject::invalid_id;
2352 dof_id_type * p_my_neighbor = nullptr;
2353
2354 // If we have a non-remote_elem neighbor link, then we can
2355 // verify it.
2356 if (elem && elem->neighbor_ptr(n) != remote_elem)
2357 {
2358 p_my_neighbor = &my_neighbor;
2359 if (elem->neighbor_ptr(n))
2360 my_neighbor = elem->neighbor_ptr(n)->id();
2361
2362 // But wait - if we haven't set remote_elem links yet then
2363 // some nullptr links on ghost elements might be
2364 // future-remote_elem links, so we can't verify those.
2365 if (!assert_valid_remote_elems &&
2366 !elem->neighbor_ptr(n) &&
2367 elem->processor_id() != mesh.processor_id())
2368 p_my_neighbor = nullptr;
2369 }
2370 libmesh_assert(mesh.comm().semiverify(p_my_neighbor));
2371 }
2372 }
2373}
2374#endif // DEBUG
2375
2376
2377
2378// Functors for correct_node_proc_ids
2379namespace {
2380
2381typedef std::unordered_map<dof_id_type, processor_id_type> proc_id_map_type;
2382
2383struct SyncNodeSet
2384{
2385 typedef unsigned char datum; // bool but without bit twiddling issues
2386
2387 SyncNodeSet(std::unordered_set<const Node *> & _set,
2388 MeshBase & _mesh) :
2389 node_set(_set), mesh(_mesh) {}
2390
2391 std::unordered_set<const Node *> & node_set;
2392
2394
2395 // ------------------------------------------------------------
2396 void gather_data (const std::vector<dof_id_type> & ids,
2397 std::vector<datum> & data)
2398 {
2399 // Find whether each requested node belongs in the set
2400 data.resize(ids.size());
2401
2402 for (auto i : index_range(ids))
2403 {
2404 const dof_id_type id = ids[i];
2405
2406 // We'd better have every node we're asked for
2407 Node * node = mesh.node_ptr(id);
2408
2409 // Return if the node is in the set.
2410 data[i] = node_set.count(node);
2411 }
2412 }
2413
2414 // ------------------------------------------------------------
2415 bool act_on_data (const std::vector<dof_id_type> & ids,
2416 const std::vector<datum> in_set)
2417 {
2418 bool data_changed = false;
2419
2420 // Add nodes we've been informed of to our own set
2421 for (auto i : index_range(ids))
2422 {
2423 if (in_set[i])
2424 {
2425 Node * node = mesh.node_ptr(ids[i]);
2426 if (!node_set.count(node))
2427 {
2428 node_set.insert(node);
2429 data_changed = true;
2430 }
2431 }
2432 }
2433
2434 return data_changed;
2435 }
2436};
2437
2438
2439struct NodesNotInSet
2440{
2441 NodesNotInSet(const std::unordered_set<const Node *> _set)
2442 : node_set(_set) {}
2443
2444 bool operator() (const Node * node) const
2445 {
2446 if (node_set.count(node))
2447 return false;
2448 return true;
2449 }
2450
2451 const std::unordered_set<const Node *> node_set;
2452};
2453
2454
2455struct SyncProcIdsFromMap
2456{
2457 typedef processor_id_type datum;
2458
2459 SyncProcIdsFromMap(const proc_id_map_type & _map,
2460 MeshBase & _mesh) :
2461 new_proc_ids(_map), mesh(_mesh) {}
2462
2463 const proc_id_map_type & new_proc_ids;
2464
2465 MeshBase & mesh;
2466
2467 // ------------------------------------------------------------
2468 void gather_data (const std::vector<dof_id_type> & ids,
2469 std::vector<datum> & data)
2470 {
2471 // Find the new processor id of each requested node
2472 data.resize(ids.size());
2473
2474 for (auto i : index_range(ids))
2475 {
2476 const dof_id_type id = ids[i];
2477
2478 // Return the node's new processor id if it has one, or its
2479 // old processor id if not.
2480 if (const auto it = new_proc_ids.find(id);
2481 it != new_proc_ids.end())
2482 data[i] = it->second;
2483 else
2484 {
2485 // We'd better find every node we're asked for
2486 const Node & node = mesh.node_ref(id);
2487 data[i] = node.processor_id();
2488 }
2489 }
2490 }
2491
2492 // ------------------------------------------------------------
2493 void act_on_data (const std::vector<dof_id_type> & ids,
2494 const std::vector<datum> proc_ids)
2495 {
2496 // Set the node processor ids we've now been informed of
2497 for (auto i : index_range(ids))
2498 {
2499 Node & node = mesh.node_ref(ids[i]);
2500 node.processor_id() = proc_ids[i];
2501 }
2502 }
2503};
2504}
2505
2506
2507
2509{
2510 LOG_SCOPE("correct_node_proc_ids()","MeshTools");
2511
2512 // This function must be run on all processors at once
2513 libmesh_parallel_only(mesh.comm());
2514
2515 // We require all processors to agree on nodal processor ids before
2516 // going into this algorithm.
2517#ifdef DEBUG
2519#endif
2520
2521 // If we have any unpartitioned elements at this
2522 // stage there is a problem
2523 libmesh_assert (n_elem(mesh.unpartitioned_elements_begin(),
2524 mesh.unpartitioned_elements_end()) == 0);
2525
2526 // Fix nodes' processor ids. Coarsening may have left us with nodes
2527 // which are no longer touched by any elements of the same processor
2528 // id, and for DofMap to work we need to fix that.
2529
2530 // This is harder now that libMesh no longer requires a distributed
2531 // mesh to ghost all nodal neighbors: it is possible for two active
2532 // elements on two different processors to share the same node in
2533 // such a way that neither processor knows the others' element
2534 // exists!
2535
2536 // While we're at it, if this mesh is configured to allow
2537 // repartitioning, we'll repartition *all* the nodes' processor ids
2538 // using the canonical Node heuristic, to try and improve DoF load
2539 // balancing. But if the mesh is disallowing repartitioning, we
2540 // won't touch processor_id on any node where it's valid, regardless
2541 // of whether or not it's canonical.
2542 bool repartition_all_nodes = !mesh.skip_noncritical_partitioning();
2543 std::unordered_set<const Node *> valid_nodes;
2544
2545 // If we aren't allowed to repartition, then we're going to leave
2546 // every node we can at its current processor_id, and *only*
2547 // repartition the nodes whose current processor id is incompatible
2548 // with DoFMap (because it doesn't touch an active element, e.g. due
2549 // to coarsening)
2550 if (!repartition_all_nodes)
2551 {
2552 for (const auto & elem : mesh.active_element_ptr_range())
2553 for (const auto & node : elem->node_ref_range())
2554 if (elem->processor_id() == node.processor_id())
2555 valid_nodes.insert(&node);
2556
2557 SyncNodeSet syncv(valid_nodes, mesh);
2558
2560 (mesh.comm(), mesh.nodes_begin(), mesh.nodes_end(), syncv);
2561 }
2562
2563 // We build up a set of compatible processor ids for each node
2564 proc_id_map_type new_proc_ids;
2565
2566 for (auto & elem : mesh.active_element_ptr_range())
2567 {
2568 processor_id_type pid = elem->processor_id();
2569
2570 for (auto & node : elem->node_ref_range())
2571 {
2572 const dof_id_type id = node.id();
2573 if (auto it = new_proc_ids.find(id);
2574 it == new_proc_ids.end())
2575 new_proc_ids.emplace(id, pid);
2576 else
2577 it->second = node.choose_processor_id(it->second, pid);
2578 }
2579 }
2580
2581 // Sort the new pids to push to each processor
2582 std::map<processor_id_type, std::vector<std::pair<dof_id_type, processor_id_type>>>
2583 ids_to_push;
2584
2585 for (const auto & node : mesh.node_ptr_range())
2586 if (const auto it = std::as_const(new_proc_ids).find(node->id());
2587 it != new_proc_ids.end() && node->processor_id() != DofObject::invalid_processor_id)
2588 ids_to_push[node->processor_id()].emplace_back(node->id(), /*pid=*/it->second);
2589
2590 auto action_functor =
2591 [& mesh, & new_proc_ids]
2593 const std::vector<std::pair<dof_id_type, processor_id_type>> & data)
2594 {
2595 for (const auto & [id, pid] : data)
2596 {
2597 if (const auto it = new_proc_ids.find(id);
2598 it == new_proc_ids.end())
2599 new_proc_ids.emplace(id, pid);
2600 else
2601 {
2602 const Node & node = mesh.node_ref(id);
2603 it->second = node.choose_processor_id(it->second, pid);
2604 }
2605 }
2606 };
2607
2609 (mesh.comm(), ids_to_push, action_functor);
2610
2611 // Now new_proc_ids is correct for every node we used to own. Let's
2612 // ask every other processor about the nodes they used to own. But
2613 // first we'll need to keep track of which nodes we used to own,
2614 // lest we get them confused with nodes we newly own.
2615 std::unordered_set<Node *> ex_local_nodes;
2616 for (auto & node : mesh.local_node_ptr_range())
2617 if (const auto it = new_proc_ids.find(node->id());
2618 it != new_proc_ids.end() && it->second != mesh.processor_id())
2619 ex_local_nodes.insert(node);
2620
2621 SyncProcIdsFromMap sync(new_proc_ids, mesh);
2622 if (repartition_all_nodes)
2624 (mesh.comm(), mesh.nodes_begin(), mesh.nodes_end(), sync);
2625 else
2626 {
2627 NodesNotInSet nnis(valid_nodes);
2628
2630 (mesh.comm(), mesh.nodes_begin(), mesh.nodes_end(), nnis, sync);
2631 }
2632
2633 // And finally let's update the nodes we used to own.
2634 for (const auto & node : ex_local_nodes)
2635 {
2636 if (valid_nodes.count(node))
2637 continue;
2638
2639 const dof_id_type id = node->id();
2640 const proc_id_map_type::iterator it = new_proc_ids.find(id);
2641 libmesh_assert(it != new_proc_ids.end());
2642 node->processor_id() = it->second;
2643 }
2644
2645 // We should still have consistent nodal processor ids coming out of
2646 // this algorithm, but if we're allowed to repartition the mesh then
2647 // they should be canonically correct too.
2648#ifdef DEBUG
2649 libmesh_assert_valid_procids<Node>(mesh);
2650 //if (repartition_all_nodes)
2651 // libmesh_assert_canonical_node_procids(mesh);
2652#endif
2653}
2654
2655
2656
2661
2662} // namespace MeshTools
2663
2664} // namespace libMesh
unsigned int n_vars
unsigned int dim
void max(const T &r, T &o, Request &req) const
timpi_pure bool semiverify(const T *r) const
void min(const T &r, T &o, Request &req) const
timpi_pure bool verify(const T &r) const
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
The BoundaryInfo class contains information relevant to boundary conditions including storing faces,...
void shellface_boundary_ids(const Elem *const elem, const unsigned short int shellface, std::vector< boundary_id_type > &vec_to_fill) const
void raw_shellface_boundary_ids(const Elem *const elem, const unsigned short int shellface, std::vector< boundary_id_type > &vec_to_fill) const
void edge_boundary_ids(const Elem *const elem, const unsigned short int edge, std::vector< boundary_id_type > &vec_to_fill) const
void boundary_ids(const Node *node, std::vector< boundary_id_type > &vec_to_fill) const
Fills a user-provided std::vector with the boundary ids associated with Node node.
void raw_boundary_ids(const Elem *const elem, const unsigned short int side, std::vector< boundary_id_type > &vec_to_fill) const
void raw_edge_boundary_ids(const Elem *const elem, const unsigned short int edge, std::vector< boundary_id_type > &vec_to_fill) const
static const boundary_id_type invalid_id
Number used for internal use.
Defines a Cartesian bounding box by the two corner extremum.
The DofObject defines an abstract base class for objects that have degrees of freedom associated with...
Definition dof_object.h:55
unsigned int n_systems() const
Definition dof_object.h:913
unsigned int n_comp(const unsigned int s, const unsigned int var) const
Definition dof_object.h:978
dof_id_type dof_number(const unsigned int s, const unsigned int var, const unsigned int comp) const
processor_id_type processor_id() const
Definition dof_object.h:881
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
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
unsigned int n_vars(const unsigned int s, const unsigned int vg) const
Definition dof_object.h:943
This is the base class from which all geometric element types are derived.
Definition elem.h:96
virtual bool is_node_on_side(const unsigned int n, const unsigned int s) const =0
bool has_children() const
Definition elem.h:2996
virtual unsigned int n_nodes() const =0
const Elem * parent() const
Definition elem.h:3047
unsigned int which_neighbor_am_i(const Elem *e) const
This function tells you which neighbor e is.
Definition elem.h:2936
unsigned int n_neighbors() const
Definition elem.h:713
SimpleRange< ChildRefIter > child_ref_range()
Returns a range with all children of a parent element, usable in range-based for loops.
Definition elem.h:2355
unsigned int level() const
Definition elem.h:3091
ElemMappingType mapping_type() const
Definition elem.h:3137
const Node * node_ptr(const unsigned int i) const
Definition elem.h:2516
static const Order type_to_default_order_map[INVALID_ELEM]
This array maps the integer representation of the ElemType enum to the default approximation order of...
Definition elem.h:990
virtual ElemType type() const =0
IntRange< unsigned short > node_index_range() const
Definition elem.h:2700
const Elem * interior_parent() const
Definition elem.C:1160
virtual unsigned int n_edges() const =0
virtual unsigned int n_sides() const =0
dof_id_type node_id(const unsigned int i) const
Definition elem.h:2484
const Elem * neighbor_ptr(unsigned int i) const
Definition elem.h:2615
const Elem * top_parent() const
Definition elem.h:3073
This is the MeshBase class.
Definition mesh_base.h:81
virtual const Node & node_ref(const dof_id_type i) const
Definition mesh_base.h:745
virtual bool is_serial() const
Definition mesh_base.h:357
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
bool is_prepared() const
Definition mesh_base.C:1064
virtual const Node * node_ptr(const dof_id_type i) const =0
unsigned int mesh_dimension() const
Definition mesh_base.C:430
virtual dof_id_type n_elem() const =0
virtual void delete_node(Node *n)=0
Removes the Node n from the mesh.
const ConstElemRange & active_local_element_stored_range() const
Definition mesh_base.C:1954
virtual std::unique_ptr< MeshBase > clone() const =0
Virtual "copy constructor".
virtual void delete_elem(Elem *e)=0
Removes element e from the mesh.
virtual dof_id_type max_node_id() const =0
virtual const Node * query_node_ptr(const dof_id_type i) const =0
virtual const Elem * elem_ptr(const dof_id_type i) const =0
virtual dof_id_type max_elem_id() const =0
constraint_rows_type & get_constraint_rows()
Constraint rows accessors.
Definition mesh_base.h:1935
virtual const Elem * query_elem_ptr(const dof_id_type i) const =0
void allow_node_and_elem_unique_id_overlap(bool allow)
If true is passed, then this mesh will no longer require unique_ids to be unique across the set of al...
Definition mesh_base.h:1387
std::unique_ptr< PointLocatorBase > sub_point_locator() const
Definition mesh_base.C:1833
virtual bool is_serial_on_zero() const
Definition mesh_base.h:364
void skip_noncritical_partitioning(bool skip)
If true is passed in then the elements on this mesh will no longer be (re)partitioned,...
Definition mesh_base.h:1411
This is the MeshCommunication class.
void assign_global_indices(MeshBase &) const
This method assigns globally unique, partition-agnostic indices to the nodes and elements in the mesh...
A Node is like a Point, but with more information.
Definition node.h:55
processor_id_type choose_processor_id(processor_id_type pid1, processor_id_type pid2) const
Return which of pid1 and pid2 would be preferred by the current load-balancing heuristic applied to t...
Definition node.C:78
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
processor_id_type n_processors() const
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
This class defines a sphere.
Definition sphere.h:73
The StoredRange class defines a contiguous, divisible set of objects.
Dummy "splitting object" used to distinguish splitting constructors from copy constructors.
MeshBase & mesh
std::unordered_set< const Node * > & node_set
const proc_id_map_type & new_proc_ids
DIE A HORRIBLE DEATH HERE typedef MPI_Comm communicator
void push_parallel_vector_data(const Communicator &comm, MapToVectors &&data, const ActionFunctor &act_on_data)
void globally_renumber_nodes_and_elements(MeshBase &)
There is no reason for a user to ever call this function.
void libmesh_assert_equal_connectivity(const MeshBase &mesh)
A function for testing that element nodal connectivities match across processors.
void libmesh_assert_valid_node_pointers(const MeshBase &mesh)
A function for walking across the mesh to try and ferret out invalidated or misassigned pointers.
dof_id_type weight(const MeshBase &mesh, const processor_id_type pid)
Definition mesh_tools.C:444
void build_nodes_to_elem_map(const MeshBase &mesh, std::vector< std::vector< dof_id_type > > &nodes_to_elem_map)
After calling this function the input vector nodes_to_elem_map will contain the node to element conne...
Definition mesh_tools.C:456
void libmesh_assert_old_dof_objects(const MeshBase &mesh)
A function for testing that all non-recently-created DofObjects within a mesh have old_dof_object dat...
void elem_types(const MeshBase &mesh, std::vector< ElemType > &et)
Fills in a vector of all element types in the mesh.
Definition mesh_tools.C:723
Real volume(const MeshBase &mesh, unsigned int dim=libMesh::invalid_uint)
Find the total volume of a mesh (interpreting that as area for dim = 2, or total arc length for dim =...
void libmesh_assert_valid_unique_ids(const MeshBase &mesh)
A function for verifying that unique ids match across processors.
Sphere bounding_sphere(const MeshBase &mesh)
Definition mesh_tools.C:618
void libmesh_assert_valid_elem_ids(const MeshBase &mesh)
A function for verifying that ids and processor assignment of elements are correctly sorted (monotone...
dof_id_type n_elem(const MeshBase::const_element_iterator &begin, const MeshBase::const_element_iterator &end)
Count up the number of elements of a specific type (as defined by an iterator range).
dof_id_type n_connected_components(const MeshBase &mesh, Real constraint_tol=0)
Definition mesh_tools.C:864
dof_id_type n_non_subactive_elem_of_type_at_level(const MeshBase &mesh, const ElemType type, const unsigned int level)
Definition mesh_tools.C:751
void libmesh_assert_parallel_consistent_new_node_procids(const MeshBase &mesh)
A function for verifying that processor assignment is parallel consistent (every processor agrees on ...
libMesh::BoundingBox create_bounding_box(const MeshBase &mesh)
Definition mesh_tools.C:566
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...
unsigned int n_p_levels(const MeshBase &mesh)
libMesh::BoundingBox create_local_bounding_box(const MeshBase &mesh)
Definition mesh_tools.C:631
void find_nodal_neighbors(const MeshBase &mesh, const Node &n, const std::vector< std::vector< const Elem * > > &nodes_to_elem_map, std::vector< const Node * > &neighbors)
Given a mesh and a node in the mesh, the vector will be filled with every node directly attached to t...
void libmesh_assert_valid_refinement_flags(const MeshBase &mesh)
A function for verifying that refinement flags on elements are consistent between processors.
libMesh::BoundingBox create_subdomain_bounding_box(const MeshBase &mesh, const subdomain_id_type sid)
Definition mesh_tools.C:685
void libmesh_assert_valid_boundary_ids(const MeshBase &mesh)
A function for verifying that boundary condition ids match across processors.
unsigned int n_active_local_levels(const MeshBase &mesh)
Definition mesh_tools.C:767
void libmesh_assert_valid_amr_interior_parents(const MeshBase &mesh)
A function for verifying that any interior_parent pointers on elements are consistent with AMR (paren...
bool valid_is_prepared(const MeshBase &mesh)
A function for testing whether a mesh's cached is_prepared() setting is not a false positive.
void libmesh_assert_valid_remote_elems(const MeshBase &mesh)
A function for verifying that active local elements' neighbors are never remote elements.
void libmesh_assert_valid_dof_ids(const MeshBase &mesh, unsigned int sysnum=libMesh::invalid_uint)
A function for verifying that degree of freedom indexing matches across processors.
Sphere processor_bounding_sphere(const MeshBase &mesh, const processor_id_type pid)
Definition mesh_tools.C:670
void clear_spline_nodes(MeshBase &)
Remove spline node (for IsoGeometric Analysis meshes) elements and nodes and constraints from the mes...
void libmesh_assert_contiguous_dof_ids(const MeshBase &mesh, unsigned int sysnum)
A function for verifying that degree of freedom indexes are contiguous on each processor,...
unsigned int n_active_levels(const MeshBase &mesh)
Definition mesh_tools.C:796
unsigned int n_local_levels(const MeshBase &mesh)
Definition mesh_tools.C:813
Sphere subdomain_bounding_sphere(const MeshBase &mesh, const subdomain_id_type sid)
Definition mesh_tools.C:709
void libmesh_assert_no_links_to_elem(const MeshBase &mesh, const Elem *bad_elem)
The following functions, only available in builds with DEBUG defined, typically have surprisingly slo...
void libmesh_assert_topology_consistent_procids< Elem >(const MeshBase &mesh)
void libmesh_assert_parallel_consistent_procids< Elem >(const MeshBase &mesh)
void get_not_subactive_node_ids(const MeshBase &mesh, std::set< dof_id_type > &not_subactive_node_ids)
Builds a set of node IDs for nodes which belong to non-subactive elements.
Definition mesh_tools.C:993
libMesh::BoundingBox create_nodal_bounding_box(const MeshBase &mesh)
Definition mesh_tools.C:591
void libmesh_assert_connected_nodes(const MeshBase &mesh)
A function for verifying that all nodes are connected to at least one element.
void find_hanging_nodes_and_parents(const MeshBase &mesh, std::map< dof_id_type, std::vector< dof_id_type > > &hanging_nodes)
Given a mesh hanging_nodes will be filled with an associative array keyed off the global id of all th...
void libmesh_assert_valid_amr_elem_ids(const MeshBase &mesh)
A function for verifying that ids of elements are correctly sorted for AMR (parents have lower ids th...
void find_nodal_or_face_neighbors(const MeshBase &mesh, const Node &node, const std::unordered_map< dof_id_type, std::vector< const Elem * > > &nodes_to_elem_map, std::vector< const Node * > &neighbors)
Given a mesh and a node in the mesh, the vector will be filled with every node directly attached to t...
void libmesh_assert_valid_constraint_rows(const MeshBase &mesh)
A function for verifying that all mesh constraint rows express relations between nodes and elements t...
std::unordered_set< dof_id_type > find_block_boundary_nodes(const MeshBase &mesh)
Returns a std::set containing Node IDs for all of the block boundary nodes.
Definition mesh_tools.C:544
void correct_node_proc_ids(MeshBase &)
Changes the processor ids on each node so be the same as the id of the lowest element touching that n...
libMesh::BoundingBox create_processor_bounding_box(const MeshBase &mesh, const processor_id_type pid)
Definition mesh_tools.C:645
void libmesh_assert_canonical_node_procids(const MeshBase &mesh)
A function for verifying that processor assignment of nodes matches the heuristic specified in Node::...
void libmesh_assert_equal_n_systems(const MeshBase &mesh)
The following functions, only available in builds with NDEBUG undefined, are for asserting internal c...
dof_id_type n_elem_of_type(const MeshBase &mesh, const ElemType type)
Definition mesh_tools.C:735
void libmesh_assert_valid_refinement_tree(const MeshBase &mesh)
A function for verifying that elements on this processor have valid descendants and consistent active...
void libmesh_assert_consistent_distributed_nodes(const MeshBase &mesh)
A function for verifying that distribution of nodes is parallel consistent (every processor can see e...
dof_id_type total_weight(const MeshBase &mesh)
Definition mesh_tools.C:421
dof_id_type n_active_elem_of_type(const MeshBase &mesh, const ElemType type)
Definition mesh_tools.C:744
void libmesh_assert_topology_consistent_procids< Node >(const MeshBase &mesh)
std::unordered_set< dof_id_type > find_boundary_nodes(const MeshBase &mesh)
Returns a std::set containing Node IDs for all of the boundary nodes.
Definition mesh_tools.C:524
void libmesh_assert_consistent_distributed(const MeshBase &mesh)
A function for verifying that distribution of dof objects is parallel consistent (every processor can...
unsigned int n_levels(const MeshBase &mesh)
Definition mesh_tools.C:826
void libmesh_assert_equal_points(const MeshBase &mesh)
A function for testing that node locations match across processors.
unsigned int paranoid_n_levels(const MeshBase &mesh)
Definition mesh_tools.C:850
void libmesh_assert_parallel_consistent_procids< Node >(const MeshBase &mesh)
void sync_dofobject_data_by_id(const Communicator &comm, const Iterator &range_begin, const Iterator &range_end, SyncFunctor &sync)
Request data about a range of ghost dofobjects uniquely identified by their id.
void parallel_reduce(const Range &range, Body &body, unsigned int n_threads=libMesh::n_threads())
Execute the provided reduction operation in parallel on the specified range.
std::string enum_to_string(const T e)
The libMesh namespace provides an interface to certain functionality in the library.
uint8_t unique_id_type
Definition id_types.h:86
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition int_range.h:153
ElemType
Defines an enum for geometric element types.
void libmesh_ignore(const Args &...)
@ RATIONAL_BERNSTEIN_MAP
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
const RemoteElem * remote_elem
Definition remote_elem.C:57
uint8_t dof_id_type
Definition id_types.h:67
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
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
The definition of the const_element_iterator struct.
Definition mesh_base.h:2556
The definition of the const_node_iterator struct.
Definition mesh_base.h:2607
const dof_id_type n_nodes
Definition tecplot_io.C:67