Line data Source code
1 : // The libMesh Finite Element Library.
2 : // Copyright (C) 2002-2025 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
55 : namespace {
56 :
57 : using namespace libMesh;
58 :
59 : /**
60 : * SumElemWeight(Range) sums the number of nodes per element
61 : * for each element in the provided range. The join() method
62 : * defines how to combine the reduction operation from two
63 : * distinct instances of this class which may be executed on
64 : * separate threads.
65 : */
66 : class SumElemWeight
67 : {
68 : public:
69 0 : SumElemWeight () :
70 0 : _weight(0)
71 0 : {}
72 :
73 0 : SumElemWeight (SumElemWeight &, Threads::split) :
74 0 : _weight(0)
75 0 : {}
76 :
77 0 : void operator()(const ConstElemRange & range)
78 : {
79 0 : for (const auto & elem : range)
80 0 : _weight += elem->n_nodes();
81 0 : }
82 :
83 0 : dof_id_type weight() const
84 0 : { 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 0 : void join (const SumElemWeight & other)
90 0 : { _weight += other.weight(); }
91 : #endif
92 :
93 : private:
94 : dof_id_type _weight;
95 : };
96 :
97 :
98 : /**
99 : * FindBBox(Range) computes the bounding box for the objects
100 : * in the specified range. This class may be split and subranges
101 : * can be executed on separate threads. The join() method
102 : * defines how the results from two separate threads are combined.
103 : */
104 : class FindBBox
105 : {
106 : public:
107 2284764 : FindBBox () : _bbox()
108 32454 : {}
109 :
110 40193 : FindBBox (FindBBox & other, Threads::split) :
111 40193 : _bbox(other._bbox)
112 13618 : {}
113 :
114 40636 : void operator()(const ConstNodeRange & range)
115 : {
116 5073100 : for (const auto & node : range)
117 : {
118 457357 : libmesh_assert(node);
119 5032464 : _bbox.union_with(*node);
120 : }
121 40636 : }
122 :
123 2336265 : void operator()(const ConstElemRange & range)
124 : {
125 26847529 : for (const auto & elem : range)
126 : {
127 1266033 : libmesh_assert(elem);
128 24511264 : _bbox.union_with(elem->loose_bounding_box());
129 : }
130 2336265 : }
131 :
132 16508 : Point & min() { return _bbox.min(); }
133 :
134 16508 : 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 13618 : void join (const FindBBox & other)
140 : {
141 40193 : _bbox.union_with(other._bbox);
142 26575 : }
143 : #endif
144 :
145 48400 : libMesh::BoundingBox & bbox ()
146 : {
147 48400 : return _bbox;
148 : }
149 :
150 : private:
151 : BoundingBox _bbox;
152 : };
153 :
154 : #ifdef DEBUG
155 2788854 : void assert_semiverify_dofobj(const Parallel::Communicator & communicator,
156 : const DofObject * d,
157 : unsigned int sysnum = libMesh::invalid_uint)
158 : {
159 2788854 : if (d)
160 : {
161 2712958 : const unsigned int n_sys = d->n_systems();
162 :
163 5425916 : std::vector<unsigned int> n_vars (n_sys, 0);
164 5797998 : for (unsigned int s = 0; s != n_sys; ++s)
165 3085040 : if (sysnum == s ||
166 : sysnum == libMesh::invalid_uint)
167 2712958 : n_vars[s] = d->n_vars(s);
168 :
169 : const unsigned int tot_n_vars =
170 2712958 : std::accumulate(n_vars.begin(), n_vars.end(), 0);
171 :
172 5425916 : std::vector<unsigned int> n_comp (tot_n_vars, 0);
173 5425916 : std::vector<dof_id_type> first_dof (tot_n_vars, 0);
174 :
175 5797998 : for (unsigned int s = 0, i=0; s != n_sys; ++s)
176 : {
177 3085040 : if (sysnum != s &&
178 : sysnum != libMesh::invalid_uint)
179 372082 : continue;
180 :
181 9543000 : for (unsigned int v = 0; v != n_vars[s]; ++v, ++i)
182 : {
183 6830042 : n_comp[i] = d->n_comp(s,v);
184 6830042 : first_dof[i] = n_comp[i] ? d->dof_number(s,v,0) : DofObject::invalid_id;
185 : }
186 : }
187 :
188 2712958 : libmesh_assert(communicator.semiverify(&n_sys));
189 2712958 : libmesh_assert(communicator.semiverify(&n_vars));
190 2712958 : libmesh_assert(communicator.semiverify(&n_comp));
191 2712958 : libmesh_assert(communicator.semiverify(&first_dof));
192 : }
193 : else
194 : {
195 75896 : const unsigned int * p_ui = nullptr;
196 75896 : const std::vector<unsigned int> * p_vui = nullptr;
197 75896 : const std::vector<dof_id_type> * p_vdid = nullptr;
198 :
199 75896 : libmesh_assert(communicator.semiverify(p_ui));
200 75896 : libmesh_assert(communicator.semiverify(p_vui));
201 75896 : libmesh_assert(communicator.semiverify(p_vui));
202 75896 : libmesh_assert(communicator.semiverify(p_vdid));
203 : }
204 2788854 : }
205 :
206 :
207 :
208 : #ifdef LIBMESH_ENABLE_UNIQUE_ID
209 9178658 : void 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 9178658 : if (d)
218 : {
219 8797014 : tempmin = tempmax = d->unique_id();
220 : }
221 : else
222 : {
223 381644 : TIMPI::Attributes<unique_id_type>::set_highest(tempmin);
224 381644 : TIMPI::Attributes<unique_id_type>::set_lowest(tempmax);
225 : }
226 9178658 : comm.min(tempmin);
227 9178658 : comm.max(tempmax);
228 17975672 : bool invalid = d && ((d->unique_id() != tempmin) ||
229 8797014 : (d->unique_id() != tempmax));
230 9178658 : comm.max(invalid);
231 :
232 : // First verify that everything is in sync
233 9178658 : libmesh_assert(!invalid);
234 :
235 : // Then verify that any remote id doesn't duplicate a local one.
236 9178658 : if (!d && tempmin == tempmax)
237 50164 : libmesh_assert(!unique_ids.count(tempmin));
238 9178658 : }
239 : #endif // LIBMESH_ENABLE_UNIQUE_ID
240 : #endif // DEBUG
241 :
242 177784 : void 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 10016 : 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 634953 : for (const auto & elem : node_to_elem_vec)
257 : {
258 : // We only care about active elements...
259 457169 : if (elem->active())
260 : {
261 : // Which local node number is global_id?
262 444291 : unsigned local_node_number = elem->local_node(global_id);
263 :
264 : // Make sure it was found
265 12878 : libmesh_assert_not_equal_to(local_node_number, libMesh::invalid_uint);
266 :
267 457169 : 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 457169 : if (!n_edges)
271 : {
272 3905 : switch (elem->type())
273 : {
274 1562 : case EDGE2:
275 : {
276 44 : switch (local_node_number)
277 : {
278 781 : case 0:
279 : // The other node is a nodal neighbor
280 803 : neighbor_set.insert(elem->node_ptr(1));
281 781 : break;
282 :
283 781 : case 1:
284 : // The other node is a nodal neighbor
285 803 : neighbor_set.insert(elem->node_ptr(0));
286 781 : break;
287 :
288 0 : default:
289 0 : libmesh_error_msg("Invalid local node number: " << local_node_number << " found." << std::endl);
290 : }
291 44 : break;
292 : }
293 :
294 1491 : case EDGE3:
295 : {
296 42 : switch (local_node_number)
297 : {
298 : // The outside nodes have node 2 as a neighbor
299 1136 : case 0:
300 : case 1:
301 1168 : neighbor_set.insert(elem->node_ptr(2));
302 1136 : break;
303 :
304 : // The middle node has the outer nodes as neighbors
305 355 : case 2:
306 365 : neighbor_set.insert(elem->node_ptr(0));
307 365 : neighbor_set.insert(elem->node_ptr(1));
308 355 : break;
309 :
310 0 : default:
311 0 : libmesh_error_msg("Invalid local node number: " << local_node_number << " found." << std::endl);
312 : }
313 42 : break;
314 : }
315 :
316 852 : case EDGE4:
317 : {
318 24 : switch (local_node_number)
319 : {
320 213 : case 0:
321 : // The left-middle node is a nodal neighbor
322 219 : neighbor_set.insert(elem->node_ptr(2));
323 213 : break;
324 :
325 213 : case 1:
326 : // The right-middle node is a nodal neighbor
327 219 : neighbor_set.insert(elem->node_ptr(3));
328 213 : break;
329 :
330 : // The left-middle node
331 213 : case 2:
332 219 : neighbor_set.insert(elem->node_ptr(0));
333 219 : neighbor_set.insert(elem->node_ptr(3));
334 213 : break;
335 :
336 : // The right-middle node
337 213 : case 3:
338 219 : neighbor_set.insert(elem->node_ptr(1));
339 219 : neighbor_set.insert(elem->node_ptr(2));
340 213 : break;
341 :
342 0 : default:
343 0 : libmesh_error_msg("Invalid local node number: " << local_node_number << " found." << std::endl);
344 : }
345 24 : break;
346 : }
347 :
348 0 : default:
349 0 : libmesh_error_msg("Unrecognized ElemType: " << Utility::enum_to_string(elem->type()) << std::endl);
350 : }
351 : }
352 :
353 : // Index of the current edge
354 12878 : unsigned current_edge = 0;
355 :
356 457169 : const unsigned short n_nodes = elem->n_nodes();
357 :
358 1654158 : while (current_edge < n_edges)
359 : {
360 : // Find the edge the node is on
361 33718 : bool found_edge = false;
362 4930311 : for (; current_edge<n_edges; ++current_edge)
363 4594410 : if (elem->is_node_on_edge(local_node_number, current_edge))
364 : {
365 24256 : found_edge = true;
366 24256 : break;
367 : }
368 :
369 : // Did we find one?
370 1196989 : if (found_edge)
371 : {
372 861088 : const Node * node_to_save = nullptr;
373 :
374 : // Find another node in this element on this edge
375 3232914 : for (unsigned other_node_this_edge = 0; other_node_this_edge != n_nodes; other_node_this_edge++)
376 3267118 : if ( (elem->is_node_on_edge(other_node_this_edge, current_edge)) && // On the current edge
377 1214242 : (elem->node_id(other_node_this_edge) != global_id)) // But not the original node
378 : {
379 : // We've found a nodal neighbor! Save a pointer to it..
380 861088 : node_to_save = elem->node_ptr(other_node_this_edge);
381 861088 : break;
382 : }
383 :
384 : // Make sure we found something
385 24256 : libmesh_assert(node_to_save != nullptr);
386 :
387 836832 : neighbor_set.insert(node_to_save);
388 : }
389 :
390 : // Keep looking for edges, node may be on more than one edge
391 1196989 : current_edge++;
392 : }
393 : } // if (elem->active())
394 : } // for
395 :
396 : // Assign the entries from the set to the vector. Note: this
397 : // replaces any existing contents in neighbors and modifies its size
398 : // accordingly.
399 172776 : neighbors.assign(neighbor_set.begin(), neighbor_set.end());
400 177784 : }
401 :
402 : }
403 :
404 :
405 : namespace libMesh
406 : {
407 :
408 : // ------------------------------------------------------------
409 : // MeshTools functions
410 :
411 : namespace MeshTools
412 : {
413 :
414 0 : dof_id_type total_weight(const MeshBase & mesh)
415 : {
416 0 : if (!mesh.is_serial())
417 : {
418 0 : libmesh_parallel_only(mesh.comm());
419 0 : dof_id_type weight = MeshTools::weight (mesh, mesh.processor_id());
420 0 : mesh.comm().sum(weight);
421 : dof_id_type unpartitioned_weight =
422 0 : MeshTools::weight (mesh, DofObject::invalid_processor_id);
423 0 : return weight + unpartitioned_weight;
424 : }
425 :
426 0 : SumElemWeight sew;
427 :
428 0 : Threads::parallel_reduce (ConstElemRange (mesh.elements_begin(),
429 0 : mesh.elements_end()),
430 : sew);
431 0 : return sew.weight();
432 :
433 : }
434 :
435 :
436 :
437 0 : dof_id_type weight(const MeshBase & mesh, const processor_id_type pid)
438 : {
439 0 : SumElemWeight sew;
440 :
441 0 : Threads::parallel_reduce (ConstElemRange (mesh.pid_elements_begin(pid),
442 0 : mesh.pid_elements_end(pid)),
443 : sew);
444 0 : return sew.weight();
445 : }
446 :
447 :
448 :
449 3849 : void build_nodes_to_elem_map (const MeshBase & mesh,
450 : std::vector<std::vector<dof_id_type>> & nodes_to_elem_map)
451 : {
452 : // A vector indexed over all nodes is too inefficient to use for a
453 : // distributed mesh. Use the unordered_map API instead.
454 3849 : if (!mesh.is_serial())
455 : libmesh_deprecated();
456 :
457 3849 : nodes_to_elem_map.resize (mesh.max_node_id());
458 :
459 97324 : for (const auto & elem : mesh.element_ptr_range())
460 186072 : for (auto & node : elem->node_ref_range())
461 : {
462 3960 : libmesh_assert_less (node.id(), nodes_to_elem_map.size());
463 3960 : libmesh_assert_less (elem->id(), mesh.n_elem());
464 :
465 142524 : nodes_to_elem_map[node.id()].push_back(elem->id());
466 3629 : }
467 3849 : }
468 :
469 :
470 :
471 213 : void build_nodes_to_elem_map (const MeshBase & mesh,
472 : std::vector<std::vector<const Elem *>> & nodes_to_elem_map)
473 : {
474 : // A vector indexed over all nodes is too inefficient to use for a
475 : // distributed mesh. Use the unordered_map API instead.
476 213 : if (!mesh.is_serial())
477 : libmesh_deprecated();
478 :
479 213 : nodes_to_elem_map.resize (mesh.max_node_id());
480 :
481 2904 : for (const auto & elem : mesh.element_ptr_range())
482 4651 : for (auto & node : elem->node_ref_range())
483 : {
484 94 : libmesh_assert_less (node.id(), nodes_to_elem_map.size());
485 :
486 3431 : nodes_to_elem_map[node.id()].push_back(elem);
487 201 : }
488 213 : }
489 :
490 :
491 :
492 243232 : void build_nodes_to_elem_map (const MeshBase & mesh,
493 : std::unordered_map<dof_id_type, std::vector<dof_id_type>> & nodes_to_elem_map)
494 : {
495 7220 : nodes_to_elem_map.clear();
496 :
497 52440924 : for (const auto & elem : mesh.element_ptr_range())
498 145262701 : for (auto & node : elem->node_ref_range())
499 116532129 : nodes_to_elem_map[node.id()].push_back(elem->id());
500 243232 : }
501 :
502 :
503 :
504 929 : void build_nodes_to_elem_map (const MeshBase & mesh,
505 : std::unordered_map<dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map)
506 : {
507 28 : nodes_to_elem_map.clear();
508 :
509 147134 : for (const auto & elem : mesh.element_ptr_range())
510 959989 : for (auto & node : elem->node_ref_range())
511 882814 : nodes_to_elem_map[node.id()].push_back(elem);
512 929 : }
513 :
514 :
515 :
516 : std::unordered_set<dof_id_type>
517 3350 : find_boundary_nodes(const MeshBase & mesh)
518 : {
519 100 : std::unordered_set<dof_id_type> boundary_nodes;
520 :
521 : // Loop over elements, find those on boundary, and
522 : // mark them as true in on_boundary.
523 894688 : for (const auto & elem : mesh.active_element_ptr_range())
524 2545814 : for (auto s : elem->side_index_range())
525 2147778 : if (elem->neighbor_ptr(s) == nullptr) // on the boundary
526 : {
527 124972 : auto nodes_on_side = elem->nodes_on_side(s);
528 :
529 717971 : for (auto & local_id : nodes_on_side)
530 618295 : boundary_nodes.insert(elem->node_ptr(local_id)->id());
531 3150 : }
532 :
533 3350 : return boundary_nodes;
534 : }
535 :
536 : std::unordered_set<dof_id_type>
537 1930 : find_block_boundary_nodes(const MeshBase & mesh)
538 : {
539 60 : std::unordered_set<dof_id_type> block_boundary_nodes;
540 :
541 : // Loop over elements, find those on boundary, and
542 : // mark them as true in on_boundary.
543 778728 : for (const auto & elem : mesh.active_element_ptr_range())
544 2186902 : for (auto s : elem->side_index_range())
545 1840302 : if (elem->neighbor_ptr(s) && (elem->neighbor_ptr(s)->subdomain_id() != elem->subdomain_id()))
546 : {
547 0 : auto nodes_on_side = elem->nodes_on_side(s);
548 :
549 0 : for (auto & local_id : nodes_on_side)
550 0 : block_boundary_nodes.insert(elem->node_ptr(local_id)->id());
551 1810 : }
552 :
553 1930 : return block_boundary_nodes;
554 : }
555 :
556 :
557 :
558 : libMesh::BoundingBox
559 1148864 : create_bounding_box (const MeshBase & mesh)
560 : {
561 : // This function must be run on all processors at once
562 15946 : libmesh_parallel_only(mesh.comm());
563 :
564 15946 : FindBBox find_bbox;
565 :
566 : // Start with any unpartitioned elements we know about locally
567 2281782 : Threads::parallel_reduce (ConstElemRange (mesh.pid_elements_begin(DofObject::invalid_processor_id),
568 1164810 : mesh.pid_elements_end(DofObject::invalid_processor_id)),
569 : find_bbox);
570 :
571 : // And combine with our local elements
572 1148864 : find_bbox.bbox().union_with(create_local_bounding_box(mesh));
573 :
574 : // Compare the bounding boxes across processors
575 1148864 : mesh.comm().min(find_bbox.min());
576 1148864 : mesh.comm().max(find_bbox.max());
577 :
578 1148864 : return find_bbox.bbox();
579 : }
580 :
581 :
582 :
583 : libMesh::BoundingBox
584 19490 : create_nodal_bounding_box (const MeshBase & mesh)
585 : {
586 : // This function must be run on all processors at once
587 562 : libmesh_parallel_only(mesh.comm());
588 :
589 562 : FindBBox find_bbox;
590 :
591 : // Start with any unpartitioned nodes we know about locally
592 38418 : Threads::parallel_reduce (ConstNodeRange (mesh.pid_nodes_begin(DofObject::invalid_processor_id),
593 20052 : mesh.pid_nodes_end(DofObject::invalid_processor_id)),
594 : find_bbox);
595 :
596 : // Add our local nodes
597 38418 : Threads::parallel_reduce (ConstNodeRange (mesh.local_nodes_begin(),
598 38418 : mesh.local_nodes_end()),
599 : find_bbox);
600 :
601 : // Compare the bounding boxes across processors
602 19490 : mesh.comm().min(find_bbox.min());
603 19490 : mesh.comm().max(find_bbox.max());
604 :
605 19490 : return find_bbox.bbox();
606 : }
607 :
608 :
609 :
610 : Sphere
611 0 : bounding_sphere(const MeshBase & mesh)
612 : {
613 0 : libMesh::BoundingBox bbox = create_bounding_box(mesh);
614 :
615 0 : const Real diag = (bbox.second - bbox.first).norm();
616 0 : const Point cent = (bbox.second + bbox.first)/2;
617 :
618 0 : return Sphere (cent, .5*diag);
619 : }
620 :
621 :
622 :
623 : libMesh::BoundingBox
624 1148864 : create_local_bounding_box (const MeshBase & mesh)
625 : {
626 15946 : FindBBox find_bbox;
627 :
628 2281782 : Threads::parallel_reduce (ConstElemRange (mesh.local_elements_begin(),
629 1164810 : mesh.local_elements_end()),
630 : find_bbox);
631 :
632 1148864 : return find_bbox.bbox();
633 : }
634 :
635 :
636 :
637 : libMesh::BoundingBox
638 0 : create_processor_bounding_box (const MeshBase & mesh,
639 : const processor_id_type pid)
640 : {
641 : // This can only be run in parallel, with consistent arguments.
642 0 : libmesh_parallel_only(mesh.comm());
643 0 : libmesh_assert(mesh.comm().verify(pid));
644 :
645 0 : libmesh_assert_less (pid, mesh.n_processors());
646 :
647 0 : FindBBox find_bbox;
648 :
649 0 : Threads::parallel_reduce (ConstElemRange (mesh.pid_elements_begin(pid),
650 0 : mesh.pid_elements_end(pid)),
651 : find_bbox);
652 :
653 : // Compare the bounding boxes across processors
654 0 : mesh.comm().min(find_bbox.min());
655 0 : mesh.comm().max(find_bbox.max());
656 :
657 0 : return find_bbox.bbox();
658 : }
659 :
660 :
661 :
662 : Sphere
663 0 : processor_bounding_sphere (const MeshBase & mesh,
664 : const processor_id_type pid)
665 : {
666 : libMesh::BoundingBox bbox =
667 0 : create_processor_bounding_box(mesh, pid);
668 :
669 0 : const Real diag = (bbox.second - bbox.first).norm();
670 0 : const Point cent = (bbox.second + bbox.first)/2;
671 :
672 0 : return Sphere (cent, .5*diag);
673 : }
674 :
675 :
676 :
677 : libMesh::BoundingBox
678 0 : create_subdomain_bounding_box (const MeshBase & mesh,
679 : const subdomain_id_type sid)
680 : {
681 : // This can only be run in parallel, with consistent arguments.
682 0 : libmesh_parallel_only(mesh.comm());
683 0 : libmesh_assert(mesh.comm().verify(sid));
684 :
685 0 : FindBBox find_bbox;
686 :
687 : Threads::parallel_reduce
688 0 : (ConstElemRange (mesh.active_local_subdomain_elements_begin(sid),
689 0 : mesh.active_local_subdomain_elements_end(sid)),
690 : find_bbox);
691 :
692 : // Compare the bounding boxes across processors
693 0 : mesh.comm().min(find_bbox.min());
694 0 : mesh.comm().max(find_bbox.max());
695 :
696 0 : return find_bbox.bbox();
697 : }
698 :
699 :
700 :
701 : Sphere
702 0 : subdomain_bounding_sphere (const MeshBase & mesh,
703 : const subdomain_id_type sid)
704 : {
705 : libMesh::BoundingBox bbox =
706 0 : create_subdomain_bounding_box(mesh, sid);
707 :
708 0 : const Real diag = (bbox.second - bbox.first).norm();
709 0 : const Point cent = (bbox.second + bbox.first)/2;
710 :
711 0 : return Sphere (cent, .5*diag);
712 : }
713 :
714 :
715 :
716 0 : void elem_types (const MeshBase & mesh,
717 : std::vector<ElemType> & et)
718 : {
719 : // Loop over the the elements. If the current element type isn't in
720 : // the vector, insert it.
721 0 : for (const auto & elem : mesh.element_ptr_range())
722 0 : if (!std::count(et.begin(), et.end(), elem->type()))
723 0 : et.push_back(elem->type());
724 0 : }
725 :
726 :
727 :
728 0 : dof_id_type n_elem_of_type (const MeshBase & mesh,
729 : const ElemType type)
730 : {
731 0 : return static_cast<dof_id_type>(std::distance(mesh.type_elements_begin(type),
732 0 : mesh.type_elements_end (type)));
733 : }
734 :
735 :
736 :
737 0 : dof_id_type n_active_elem_of_type (const MeshBase & mesh,
738 : const ElemType type)
739 : {
740 0 : return static_cast<dof_id_type>(std::distance(mesh.active_type_elements_begin(type),
741 0 : mesh.active_type_elements_end (type)));
742 : }
743 :
744 0 : dof_id_type n_non_subactive_elem_of_type_at_level(const MeshBase & mesh,
745 : const ElemType type,
746 : const unsigned int level)
747 : {
748 0 : dof_id_type cnt = 0;
749 :
750 : // iterate over the elements of the specified type
751 0 : for (const auto & elem : as_range(mesh.type_elements_begin(type),
752 0 : mesh.type_elements_end(type)))
753 0 : if ((elem->level() == level) && !elem->subactive())
754 0 : cnt++;
755 :
756 0 : return cnt;
757 : }
758 :
759 :
760 1425 : unsigned int n_active_local_levels(const MeshBase & mesh)
761 : {
762 1425 : unsigned int nl = 0;
763 :
764 40866 : for (auto & elem : mesh.active_local_element_ptr_range())
765 41845 : nl = std::max(elem->level() + 1, nl);
766 :
767 1425 : return nl;
768 : }
769 :
770 :
771 :
772 1425 : unsigned int n_active_levels(const MeshBase & mesh)
773 : {
774 42 : libmesh_parallel_only(mesh.comm());
775 :
776 1425 : unsigned int nl = n_active_local_levels(mesh);
777 :
778 2808 : for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
779 5616 : mesh.unpartitioned_elements_end()))
780 0 : if (elem->active())
781 1341 : nl = std::max(elem->level() + 1, nl);
782 :
783 1425 : mesh.comm().max(nl);
784 1425 : return nl;
785 : }
786 :
787 :
788 :
789 1643398 : unsigned int n_local_levels(const MeshBase & mesh)
790 : {
791 1643398 : unsigned int nl = 0;
792 :
793 4852022 : for (const auto & elem : as_range(mesh.local_elements_begin(),
794 36432604 : mesh.local_elements_end()))
795 33383639 : nl = std::max(elem->level() + 1, nl);
796 :
797 1643398 : return nl;
798 : }
799 :
800 :
801 :
802 1643398 : unsigned int n_levels(const MeshBase & mesh)
803 : {
804 28100 : libmesh_parallel_only(mesh.comm());
805 :
806 1643398 : unsigned int nl = n_local_levels(mesh);
807 :
808 3646534 : for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
809 19858543 : mesh.unpartitioned_elements_end()))
810 14928365 : nl = std::max(elem->level() + 1, nl);
811 :
812 1643398 : mesh.comm().max(nl);
813 :
814 : // n_levels() is only valid and should only be called in cases where
815 : // the mesh is validly distributed (or serialized). Let's run an
816 : // expensive test in debug mode to make sure this is such a case.
817 : #ifdef DEBUG
818 28100 : const unsigned int paranoid_nl = paranoid_n_levels(mesh);
819 28100 : libmesh_assert_equal_to(nl, paranoid_nl);
820 : #endif
821 1643398 : return nl;
822 : }
823 :
824 :
825 :
826 38208 : unsigned int paranoid_n_levels(const MeshBase & mesh)
827 : {
828 28398 : libmesh_parallel_only(mesh.comm());
829 :
830 38208 : unsigned int nl = 0;
831 4036493 : for (const auto & elem : mesh.element_ptr_range())
832 3997987 : nl = std::max(elem->level() + 1, nl);
833 :
834 38208 : mesh.comm().max(nl);
835 38208 : return nl;
836 : }
837 :
838 :
839 :
840 639 : dof_id_type n_connected_components(const MeshBase & mesh,
841 : Real constraint_tol)
842 : {
843 36 : LOG_SCOPE("n_connected_components()", "MeshTools");
844 :
845 : // Yes, I'm being lazy. This is for mesh analysis before a
846 : // simulation, not anything going in any loops.
847 639 : if (!mesh.is_serial_on_zero())
848 0 : libmesh_not_implemented();
849 :
850 639 : dof_id_type n_components = 0;
851 :
852 657 : if (mesh.processor_id())
853 : {
854 522 : mesh.comm().broadcast(n_components);
855 531 : return n_components;
856 : }
857 :
858 : // All nodes in a set here are connected (at least indirectly) to
859 : // all other nodes in the same set, but have not yet been discovered
860 : // to be connected to nodes in other sets.
861 9 : std::vector<std::unordered_set<const Node *>> components;
862 :
863 : // With a typical mesh with few components and somewhat-contiguous
864 : // ordering, vector performance should be fine. With a mesh with
865 : // many components or completely scrambled ordering, performance
866 : // can be a disaster.
867 3204 : auto find_component = [&components](const Node * n) {
868 267 : std::unordered_set<const Node *> * component = nullptr;
869 :
870 8132 : for (auto & c: components)
871 4928 : if (c.find(n) != c.end())
872 : {
873 105 : libmesh_assert(component == nullptr);
874 105 : component = &c;
875 : }
876 :
877 3204 : return component;
878 108 : };
879 :
880 : auto add_to_component =
881 2010 : [&find_component]
882 2412 : (std::unordered_set<const Node *> & component, const Node * n) {
883 :
884 2412 : auto current_component = find_component(n);
885 : // We may already know we're in the desired component
886 2412 : if (&component == current_component)
887 51 : return;
888 :
889 : // If we're unknown, we should be in the desired component
890 1950 : else if (!current_component)
891 147 : component.insert(n);
892 :
893 : // If we think we're in another component, it should actually be
894 : // part of the desired component
895 : else
896 : {
897 3 : component.merge(*current_component);
898 3 : libmesh_assert(current_component->empty());
899 : }
900 99 : };
901 :
902 9 : auto & constraint_rows = mesh.get_constraint_rows();
903 :
904 1659 : for (const auto & elem : mesh.element_ptr_range())
905 : {
906 792 : const Node * first_node = elem->node_ptr(0);
907 :
908 792 : auto component = find_component(first_node);
909 :
910 : // If we didn't find one, make a new one, reusing an existing
911 : // slot if possible or growing our vector if necessary
912 792 : if (!component)
913 684 : for (auto & c: components)
914 354 : if (c.empty())
915 0 : component = &c;
916 :
917 432 : if (!component)
918 219 : component = &components.emplace_back();
919 :
920 3234 : for (const Node & n : elem->node_ref_range())
921 : {
922 2376 : add_to_component(*component, &n);
923 :
924 2376 : auto it = constraint_rows.find(&n);
925 2376 : if (it == constraint_rows.end())
926 195 : continue;
927 :
928 72 : for (const auto & [pr, val] : it->second)
929 : {
930 : // Ignore too-trivial constraint coefficients if
931 : // we get a non-default-0 constraint_tol
932 39 : if (std::abs(val) < constraint_tol)
933 0 : continue;
934 :
935 36 : const Elem * spline_elem = pr.first;
936 3 : libmesh_assert(spline_elem == mesh.elem_ptr(spline_elem->id()));
937 :
938 : const Node * spline_node =
939 36 : spline_elem->node_ptr(pr.second);
940 :
941 36 : add_to_component(*component, spline_node);
942 : }
943 : }
944 90 : }
945 :
946 327 : for (auto & component : components)
947 219 : if (!component.empty())
948 144 : ++n_components;
949 :
950 : // We calculated this on proc 0; now let everyone else know too
951 108 : mesh.comm().broadcast(n_components);
952 :
953 108 : return n_components;
954 90 : }
955 :
956 :
957 :
958 0 : void get_not_subactive_node_ids(const MeshBase & mesh,
959 : std::set<dof_id_type> & not_subactive_node_ids)
960 : {
961 0 : for (const auto & elem : mesh.element_ptr_range())
962 0 : if (!elem->subactive())
963 0 : for (auto & n : elem->node_ref_range())
964 0 : not_subactive_node_ids.insert(n.id());
965 0 : }
966 :
967 :
968 :
969 481352 : dof_id_type n_elem (const MeshBase::const_element_iterator & begin,
970 : const MeshBase::const_element_iterator & end)
971 : {
972 943392 : return cast_int<dof_id_type>(std::distance(begin, end));
973 : }
974 :
975 :
976 :
977 0 : dof_id_type n_nodes (const MeshBase::const_node_iterator & begin,
978 : const MeshBase::const_node_iterator & end)
979 : {
980 0 : return cast_int<dof_id_type>(std::distance(begin, end));
981 : }
982 :
983 :
984 :
985 426 : Real volume (const MeshBase & mesh,
986 : unsigned int dim)
987 : {
988 12 : libmesh_parallel_only(mesh.comm());
989 :
990 426 : if (dim == libMesh::invalid_uint)
991 426 : dim = mesh.mesh_dimension();
992 :
993 426 : Real vol = 0;
994 :
995 : // first my local elements
996 889 : for (const auto & elem : as_range(mesh.local_elements_begin(),
997 2807 : mesh.local_elements_end()))
998 588 : if (elem->dim() == dim)
999 990 : vol += elem->volume();
1000 :
1001 : // then count any unpartitioned objects, once
1002 438 : if (mesh.processor_id() == 0)
1003 138 : for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
1004 276 : mesh.unpartitioned_elements_end()))
1005 0 : if (elem->dim() == dim)
1006 60 : vol += elem->volume();
1007 :
1008 426 : mesh.comm().sum(vol);
1009 426 : return vol;
1010 : }
1011 :
1012 :
1013 :
1014 1425 : unsigned int n_p_levels (const MeshBase & mesh)
1015 : {
1016 42 : libmesh_parallel_only(mesh.comm());
1017 :
1018 1425 : unsigned int max_p_level = 0;
1019 :
1020 : // first my local elements
1021 7068 : for (const auto & elem : as_range(mesh.local_elements_begin(),
1022 48788 : mesh.local_elements_end()))
1023 48773 : max_p_level = std::max(elem->p_level(), max_p_level);
1024 :
1025 : // then any unpartitioned objects
1026 2808 : for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
1027 5616 : mesh.unpartitioned_elements_end()))
1028 1341 : max_p_level = std::max(elem->p_level(), max_p_level);
1029 :
1030 1425 : mesh.comm().max(max_p_level);
1031 1425 : return max_p_level + 1;
1032 : }
1033 :
1034 :
1035 :
1036 2272 : void find_nodal_neighbors(const MeshBase &,
1037 : const Node & node,
1038 : const std::vector<std::vector<const Elem *>> & nodes_to_elem_map,
1039 : std::vector<const Node *> & neighbors)
1040 : {
1041 2336 : find_nodal_neighbors_helper(node.id(), nodes_to_elem_map[node.id()],
1042 : neighbors);
1043 2272 : }
1044 :
1045 :
1046 :
1047 175512 : void find_nodal_neighbors(const MeshBase &,
1048 : const Node & node,
1049 : const std::unordered_map<dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map,
1050 : std::vector<const Node *> & neighbors)
1051 : {
1052 : const std::vector<const Elem *> node_to_elem_vec =
1053 180456 : libmesh_map_find(nodes_to_elem_map, node.id());
1054 175512 : find_nodal_neighbors_helper(node.id(), node_to_elem_vec, neighbors);
1055 175512 : }
1056 :
1057 :
1058 :
1059 0 : void find_hanging_nodes_and_parents(const MeshBase & mesh,
1060 : std::map<dof_id_type, std::vector<dof_id_type>> & hanging_nodes)
1061 : {
1062 : // Loop through all the elements
1063 0 : for (auto & elem : mesh.active_local_element_ptr_range())
1064 0 : if (elem->type() == QUAD4)
1065 0 : for (auto s : elem->side_index_range())
1066 : {
1067 : // Loop over the sides looking for sides that have hanging nodes
1068 : // This code is inspired by compute_proj_constraints()
1069 0 : const Elem * neigh = elem->neighbor_ptr(s);
1070 :
1071 : // If not a boundary side
1072 0 : if (neigh != nullptr)
1073 : {
1074 : // Is there a coarser element next to this one?
1075 0 : if (neigh->level() < elem->level())
1076 : {
1077 0 : const Elem * ancestor = elem;
1078 0 : while (neigh->level() < ancestor->level())
1079 0 : ancestor = ancestor->parent();
1080 0 : unsigned int s_neigh = neigh->which_neighbor_am_i(ancestor);
1081 0 : libmesh_assert_less (s_neigh, neigh->n_neighbors());
1082 :
1083 : // Couple of helper uints...
1084 0 : unsigned int local_node1=0;
1085 0 : unsigned int local_node2=0;
1086 :
1087 0 : bool found_in_neighbor = false;
1088 :
1089 : // Find the two vertices that make up this side
1090 0 : while (!elem->is_node_on_side(local_node1++,s)) { }
1091 0 : local_node1--;
1092 :
1093 : // Start looking for the second one with the next node
1094 0 : local_node2=local_node1+1;
1095 :
1096 : // Find the other one
1097 0 : while (!elem->is_node_on_side(local_node2++,s)) { }
1098 0 : local_node2--;
1099 :
1100 : //Pull out their global ids:
1101 0 : dof_id_type node1 = elem->node_id(local_node1);
1102 0 : dof_id_type node2 = elem->node_id(local_node2);
1103 :
1104 : // Now find which node is present in the neighbor
1105 : // FIXME This assumes a level one rule!
1106 : // The _other_ one is the hanging node
1107 :
1108 : // First look for the first one
1109 : // FIXME could be streamlined a bit
1110 0 : for (unsigned int n=0;n<neigh->n_sides();n++)
1111 0 : if (neigh->node_id(n) == node1)
1112 0 : found_in_neighbor=true;
1113 :
1114 0 : dof_id_type hanging_node=0;
1115 :
1116 0 : if (!found_in_neighbor)
1117 0 : hanging_node=node1;
1118 : else // If it wasn't node1 then it must be node2!
1119 0 : hanging_node=node2;
1120 :
1121 : // Reset for reuse
1122 0 : local_node1=0;
1123 :
1124 : // Find the first node that makes up the side in the neighbor (these should be the parent nodes)
1125 0 : while (!neigh->is_node_on_side(local_node1++,s_neigh)) { }
1126 0 : local_node1--;
1127 :
1128 0 : local_node2=local_node1+1;
1129 :
1130 : // Find the second node...
1131 0 : while (!neigh->is_node_on_side(local_node2++,s_neigh)) { }
1132 0 : local_node2--;
1133 :
1134 : // Save them if we haven't already found the parents for this one
1135 0 : if (hanging_nodes[hanging_node].size()<2)
1136 : {
1137 0 : hanging_nodes[hanging_node].push_back(neigh->node_id(local_node1));
1138 0 : hanging_nodes[hanging_node].push_back(neigh->node_id(local_node2));
1139 : }
1140 : }
1141 : }
1142 0 : }
1143 0 : }
1144 :
1145 :
1146 :
1147 36 : void clear_spline_nodes(MeshBase & mesh)
1148 : {
1149 6 : std::vector<Elem *> nodeelem_to_delete;
1150 :
1151 8737 : for (auto & elem : mesh.element_ptr_range())
1152 5073 : if (elem->type() == NODEELEM &&
1153 4140 : elem->mapping_type() == RATIONAL_BERNSTEIN_MAP)
1154 4170 : nodeelem_to_delete.push_back(elem);
1155 :
1156 3 : auto & constraint_rows = mesh.get_constraint_rows();
1157 :
1158 : // All our constraint_rows ought to be for spline constraints we're
1159 : // about to get rid of.
1160 : #ifndef NDEBUG
1161 762 : for (auto & node_row : constraint_rows)
1162 2064 : for (auto pr : node_row.second)
1163 : {
1164 1305 : const Elem * elem = pr.first.first;
1165 1305 : libmesh_assert(elem->type() == NODEELEM);
1166 1305 : libmesh_assert(elem->mapping_type() == RATIONAL_BERNSTEIN_MAP);
1167 : }
1168 : #endif
1169 :
1170 3 : constraint_rows.clear();
1171 :
1172 4176 : for (Elem * elem : nodeelem_to_delete)
1173 : {
1174 690 : Node * node = elem->node_ptr(0);
1175 4140 : mesh.delete_elem(elem);
1176 4140 : mesh.delete_node(node);
1177 : }
1178 36 : }
1179 :
1180 :
1181 :
1182 936 : bool valid_is_prepared (const MeshBase & mesh)
1183 : {
1184 84 : LOG_SCOPE("libmesh_assert_valid_is_prepared()", "MeshTools");
1185 :
1186 936 : if (!mesh.is_prepared())
1187 0 : return true;
1188 :
1189 978 : std::unique_ptr<MeshBase> mesh_clone = mesh.clone();
1190 :
1191 : // Try preparing (without allowing repartitioning or renumbering, to
1192 : // avoid false assertion failures)
1193 68 : bool old_allow_renumbering = mesh_clone->allow_renumbering();
1194 42 : mesh_clone->allow_renumbering(false);
1195 : bool old_allow_remote_element_removal =
1196 42 : mesh_clone->allow_remote_element_removal();
1197 68 : bool old_skip_partitioning = mesh_clone->skip_partitioning();
1198 42 : mesh_clone->skip_partitioning(true);
1199 42 : mesh_clone->allow_remote_element_removal(false);
1200 936 : mesh_clone->prepare_for_use();
1201 42 : mesh_clone->allow_renumbering(old_allow_renumbering);
1202 42 : mesh_clone->allow_remote_element_removal(old_allow_remote_element_removal);
1203 42 : mesh_clone->skip_partitioning(old_skip_partitioning);
1204 :
1205 936 : return (mesh == *mesh_clone);
1206 868 : }
1207 :
1208 :
1209 :
1210 : #ifndef NDEBUG
1211 :
1212 122 : void libmesh_assert_equal_n_systems (const MeshBase & mesh)
1213 : {
1214 244 : LOG_SCOPE("libmesh_assert_equal_n_systems()", "MeshTools");
1215 :
1216 122 : unsigned int n_sys = libMesh::invalid_uint;
1217 :
1218 10842 : for (const auto & elem : mesh.element_ptr_range())
1219 : {
1220 10720 : if (n_sys == libMesh::invalid_uint)
1221 122 : n_sys = elem->n_systems();
1222 : else
1223 10598 : libmesh_assert_equal_to (elem->n_systems(), n_sys);
1224 : }
1225 :
1226 29237 : for (const auto & node : mesh.node_ptr_range())
1227 : {
1228 29115 : if (n_sys == libMesh::invalid_uint)
1229 0 : n_sys = node->n_systems();
1230 : else
1231 29115 : libmesh_assert_equal_to (node->n_systems(), n_sys);
1232 : }
1233 122 : }
1234 :
1235 :
1236 :
1237 : #ifdef LIBMESH_ENABLE_AMR
1238 0 : void libmesh_assert_old_dof_objects (const MeshBase & mesh)
1239 : {
1240 0 : LOG_SCOPE("libmesh_assert_old_dof_objects()", "MeshTools");
1241 :
1242 0 : for (const auto & elem : mesh.element_ptr_range())
1243 : {
1244 0 : if (elem->refinement_flag() == Elem::JUST_REFINED ||
1245 0 : elem->refinement_flag() == Elem::INACTIVE)
1246 0 : continue;
1247 :
1248 0 : if (elem->has_dofs())
1249 0 : libmesh_assert(elem->get_old_dof_object());
1250 :
1251 0 : for (auto & node : elem->node_ref_range())
1252 0 : if (node.has_dofs())
1253 0 : libmesh_assert(node.get_old_dof_object());
1254 : }
1255 0 : }
1256 : #else
1257 : void libmesh_assert_old_dof_objects (const MeshBase &) {}
1258 : #endif // LIBMESH_ENABLE_AMR
1259 :
1260 :
1261 :
1262 0 : void libmesh_assert_valid_node_pointers(const MeshBase & mesh)
1263 : {
1264 0 : LOG_SCOPE("libmesh_assert_valid_node_pointers()", "MeshTools");
1265 :
1266 : // Here we specifically do not want "auto &" because we need to
1267 : // reseat the (temporary) pointer variable in the loop below,
1268 : // without modifying the original.
1269 0 : for (const Elem * elem : mesh.element_ptr_range())
1270 : {
1271 0 : libmesh_assert (elem);
1272 0 : while (elem)
1273 : {
1274 0 : elem->libmesh_assert_valid_node_pointers();
1275 0 : for (auto n : elem->neighbor_ptr_range())
1276 0 : if (n && n != remote_elem)
1277 0 : n->libmesh_assert_valid_node_pointers();
1278 :
1279 0 : libmesh_assert_not_equal_to (elem->parent(), remote_elem);
1280 0 : elem = elem->parent();
1281 : }
1282 : }
1283 0 : }
1284 :
1285 :
1286 :
1287 8596 : void libmesh_assert_valid_remote_elems(const MeshBase & mesh)
1288 : {
1289 17192 : LOG_SCOPE("libmesh_assert_valid_remote_elems()", "MeshTools");
1290 :
1291 641616 : for (const auto & elem : as_range(mesh.local_elements_begin(),
1292 1283232 : mesh.local_elements_end()))
1293 : {
1294 633020 : libmesh_assert (elem);
1295 :
1296 : // We currently don't allow active_local_elements to have
1297 : // remote_elem neighbors
1298 633020 : if (elem->active())
1299 2764000 : for (auto n : elem->neighbor_ptr_range())
1300 2224445 : libmesh_assert_not_equal_to (n, remote_elem);
1301 :
1302 : #ifdef LIBMESH_ENABLE_AMR
1303 633020 : const Elem * parent = elem->parent();
1304 633020 : if (parent)
1305 355744 : libmesh_assert_not_equal_to (parent, remote_elem);
1306 :
1307 : // We can only be strict about active elements' subactive
1308 : // children
1309 633020 : if (elem->active() && elem->has_children())
1310 26661 : for (auto & child : elem->child_ref_range())
1311 21348 : libmesh_assert_not_equal_to (&child, remote_elem);
1312 : #endif
1313 : }
1314 8596 : }
1315 :
1316 :
1317 :
1318 388 : void libmesh_assert_valid_elem_ids(const MeshBase & mesh)
1319 : {
1320 776 : LOG_SCOPE("libmesh_assert_valid_elem_ids()", "MeshTools");
1321 :
1322 388 : processor_id_type lastprocid = 0;
1323 388 : dof_id_type lastelemid = 0;
1324 :
1325 13378 : for (const auto & elem : mesh.active_element_ptr_range())
1326 : {
1327 12990 : libmesh_assert (elem);
1328 12990 : processor_id_type elemprocid = elem->processor_id();
1329 12990 : dof_id_type elemid = elem->id();
1330 :
1331 12990 : libmesh_assert_greater_equal (elemid, lastelemid);
1332 12990 : libmesh_assert_greater_equal (elemprocid, lastprocid);
1333 :
1334 12990 : lastelemid = elemid;
1335 12990 : lastprocid = elemprocid;
1336 : }
1337 388 : }
1338 :
1339 :
1340 :
1341 776 : void libmesh_assert_valid_amr_elem_ids(const MeshBase & mesh)
1342 : {
1343 1552 : LOG_SCOPE("libmesh_assert_valid_amr_elem_ids()", "MeshTools");
1344 :
1345 318986 : for (const auto & elem : mesh.element_ptr_range())
1346 : {
1347 318210 : libmesh_assert (elem);
1348 :
1349 318210 : const Elem * parent = elem->parent();
1350 :
1351 318210 : if (parent)
1352 : {
1353 24548 : libmesh_assert_greater_equal (elem->id(), parent->id());
1354 24548 : libmesh_assert_greater_equal (elem->processor_id(), parent->processor_id());
1355 : }
1356 : }
1357 776 : }
1358 :
1359 :
1360 :
1361 12124 : void libmesh_assert_valid_amr_interior_parents(const MeshBase & mesh)
1362 : {
1363 24248 : LOG_SCOPE("libmesh_assert_valid_amr_interior_parents()", "MeshTools");
1364 :
1365 1388319 : for (const auto & elem : mesh.element_ptr_range())
1366 : {
1367 1376195 : libmesh_assert (elem);
1368 :
1369 : // We can skip to the next element if we're full-dimension
1370 : // and therefore don't have any interior parents
1371 1376195 : if (elem->dim() >= LIBMESH_DIM)
1372 616440 : continue;
1373 :
1374 759755 : const Elem * ip = elem->interior_parent();
1375 :
1376 759755 : const Elem * parent = elem->parent();
1377 :
1378 759755 : if (ip && (ip != remote_elem) && parent)
1379 : {
1380 1000 : libmesh_assert_equal_to (ip->top_parent(),
1381 : elem->top_parent()->interior_parent());
1382 :
1383 1000 : if (ip->level() == elem->level())
1384 1000 : libmesh_assert_equal_to (ip->parent(),
1385 : parent->interior_parent());
1386 : else
1387 : {
1388 0 : libmesh_assert_less (ip->level(), elem->level());
1389 0 : libmesh_assert_equal_to (ip, parent->interior_parent());
1390 : }
1391 : }
1392 : }
1393 12124 : }
1394 :
1395 :
1396 :
1397 0 : void libmesh_assert_contiguous_dof_ids(const MeshBase & mesh, unsigned int sysnum)
1398 : {
1399 0 : LOG_SCOPE("libmesh_assert_contiguous_dof_ids()", "MeshTools");
1400 :
1401 0 : if (mesh.n_processors() == 1)
1402 0 : return;
1403 :
1404 0 : libmesh_parallel_only(mesh.comm());
1405 :
1406 0 : dof_id_type min_dof_id = std::numeric_limits<dof_id_type>::max(),
1407 0 : max_dof_id = std::numeric_limits<dof_id_type>::min();
1408 :
1409 : // Figure out what our local dof id range is
1410 0 : for (const auto * node : mesh.local_node_ptr_range())
1411 : {
1412 0 : for (auto v : make_range(node->n_vars(sysnum)))
1413 0 : for (auto c : make_range(node->n_comp(sysnum, v)))
1414 : {
1415 0 : dof_id_type id = node->dof_number(sysnum, v, c);
1416 0 : min_dof_id = std::min (min_dof_id, id);
1417 0 : max_dof_id = std::max (max_dof_id, id);
1418 : }
1419 : }
1420 :
1421 : // Make sure no other processors' ids are inside it
1422 0 : for (const auto * node : mesh.node_ptr_range())
1423 : {
1424 0 : if (node->processor_id() == mesh.processor_id())
1425 0 : continue;
1426 0 : for (auto v : make_range(node->n_vars(sysnum)))
1427 0 : for (auto c : make_range(node->n_comp(sysnum, v)))
1428 : {
1429 0 : dof_id_type id = node->dof_number(sysnum, v, c);
1430 0 : libmesh_assert (id < min_dof_id ||
1431 : id > max_dof_id);
1432 : }
1433 : }
1434 : }
1435 :
1436 :
1437 :
1438 : template <>
1439 17490 : void libmesh_assert_topology_consistent_procids<Elem>(const MeshBase & mesh)
1440 : {
1441 34980 : LOG_SCOPE("libmesh_assert_topology_consistent_procids()", "MeshTools");
1442 :
1443 : // This parameter is not used when !LIBMESH_ENABLE_AMR
1444 17490 : libmesh_ignore(mesh);
1445 :
1446 : // If we're adaptively refining, check processor ids for consistency
1447 : // between parents and children.
1448 : #ifdef LIBMESH_ENABLE_AMR
1449 :
1450 : // Ancestor elements we won't worry about, but subactive and active
1451 : // elements ought to have parents with consistent processor ids
1452 2628478 : for (const auto & elem : mesh.element_ptr_range())
1453 : {
1454 2610988 : libmesh_assert(elem);
1455 :
1456 2610988 : if (!elem->active() && !elem->subactive())
1457 288060 : continue;
1458 :
1459 2322928 : const Elem * parent = elem->parent();
1460 :
1461 2322928 : if (parent)
1462 : {
1463 1157592 : libmesh_assert(parent->has_children());
1464 1157592 : processor_id_type parent_procid = parent->processor_id();
1465 1157592 : bool matching_child_id = false;
1466 : // If we've got a remote_elem then we don't know whether
1467 : // it's responsible for the parent's processor id; all
1468 : // we can do is assume it is and let its processor fail
1469 : // an assert if there's something wrong.
1470 7222104 : for (auto & child : parent->child_ref_range())
1471 12129024 : if (&child == remote_elem ||
1472 6064512 : child.processor_id() == parent_procid)
1473 5909872 : matching_child_id = true;
1474 1157592 : libmesh_assert(matching_child_id);
1475 : }
1476 : }
1477 : #endif
1478 17490 : }
1479 :
1480 :
1481 :
1482 : template <>
1483 26716 : void libmesh_assert_topology_consistent_procids<Node>(const MeshBase & mesh)
1484 : {
1485 26716 : LOG_SCOPE("libmesh_assert_topology_consistent_procids()", "MeshTools");
1486 :
1487 26716 : if (mesh.n_processors() == 1)
1488 0 : return;
1489 :
1490 26716 : libmesh_parallel_only(mesh.comm());
1491 :
1492 : // We want this test to be valid even when called after nodes have
1493 : // been added asynchronously but before they're renumbered.
1494 : //
1495 : // Plus, some code (looking at you, stitch_meshes) modifies
1496 : // DofObject ids without keeping max_elem_id()/max_node_id()
1497 : // consistent, but that's done in a safe way for performance
1498 : // reasons, so we'll play along and just figure out new max ids
1499 : // ourselves.
1500 26716 : dof_id_type parallel_max_node_id = 0;
1501 7284115 : for (const auto & node : mesh.node_ptr_range())
1502 7257399 : parallel_max_node_id = std::max<dof_id_type>(parallel_max_node_id,
1503 7257399 : node->id()+1);
1504 26716 : mesh.comm().max(parallel_max_node_id);
1505 :
1506 :
1507 53432 : std::vector<bool> node_touched_by_me(parallel_max_node_id, false);
1508 :
1509 2218224 : for (const auto & elem : as_range(mesh.local_elements_begin(),
1510 4436448 : mesh.local_elements_end()))
1511 : {
1512 2191508 : libmesh_assert (elem);
1513 :
1514 16541217 : for (auto & node : elem->node_ref_range())
1515 : {
1516 14349709 : dof_id_type nodeid = node.id();
1517 14349709 : node_touched_by_me[nodeid] = true;
1518 : }
1519 : }
1520 53432 : std::vector<bool> node_touched_by_anyone(node_touched_by_me);
1521 26716 : mesh.comm().max(node_touched_by_anyone);
1522 :
1523 3643344 : for (const auto & node : mesh.local_node_ptr_range())
1524 : {
1525 3616628 : libmesh_assert(node);
1526 3616628 : dof_id_type nodeid = node->id();
1527 3616628 : libmesh_assert(!node_touched_by_anyone[nodeid] ||
1528 : node_touched_by_me[nodeid]);
1529 : }
1530 : }
1531 :
1532 :
1533 :
1534 0 : void libmesh_assert_canonical_node_procids (const MeshBase & mesh)
1535 : {
1536 0 : for (const auto & elem : mesh.active_element_ptr_range())
1537 0 : for (auto & node : elem->node_ref_range())
1538 0 : libmesh_assert_equal_to
1539 : (node.processor_id(),
1540 : node.choose_processor_id(node.processor_id(),
1541 : elem->processor_id()));
1542 0 : }
1543 :
1544 :
1545 :
1546 : #ifdef LIBMESH_ENABLE_AMR
1547 1226 : void libmesh_assert_valid_refinement_tree(const MeshBase & mesh)
1548 : {
1549 2452 : LOG_SCOPE("libmesh_assert_valid_refinement_tree()", "MeshTools");
1550 :
1551 59722 : for (const auto & elem : mesh.element_ptr_range())
1552 : {
1553 58496 : libmesh_assert(elem);
1554 58496 : if (elem->has_children())
1555 17748 : for (auto & child : elem->child_ref_range())
1556 14928 : if (&child != remote_elem)
1557 14000 : libmesh_assert_equal_to (child.parent(), elem);
1558 58496 : if (elem->active())
1559 : {
1560 55676 : libmesh_assert(!elem->ancestor());
1561 55676 : libmesh_assert(!elem->subactive());
1562 : }
1563 2820 : else if (elem->ancestor())
1564 : {
1565 2820 : libmesh_assert(!elem->subactive());
1566 : }
1567 : else
1568 0 : libmesh_assert(elem->subactive());
1569 :
1570 58496 : if (elem->p_refinement_flag() == Elem::JUST_REFINED)
1571 0 : libmesh_assert_greater(elem->p_level(), 0);
1572 : }
1573 1226 : }
1574 : #else
1575 : void libmesh_assert_valid_refinement_tree(const MeshBase &)
1576 : {
1577 : }
1578 : #endif // LIBMESH_ENABLE_AMR
1579 :
1580 : #endif // !NDEBUG
1581 :
1582 :
1583 :
1584 : #ifdef DEBUG
1585 :
1586 0 : void libmesh_assert_no_links_to_elem(const MeshBase & mesh,
1587 : const Elem * bad_elem)
1588 : {
1589 0 : for (const auto & elem : mesh.element_ptr_range())
1590 : {
1591 0 : libmesh_assert (elem);
1592 0 : libmesh_assert_not_equal_to (elem->parent(), bad_elem);
1593 0 : for (auto n : elem->neighbor_ptr_range())
1594 0 : libmesh_assert_not_equal_to (n, bad_elem);
1595 :
1596 : #ifdef LIBMESH_ENABLE_AMR
1597 0 : if (elem->has_children())
1598 0 : for (auto & child : elem->child_ref_range())
1599 0 : libmesh_assert_not_equal_to (&child, bad_elem);
1600 : #endif
1601 : }
1602 0 : }
1603 :
1604 :
1605 566 : void libmesh_assert_equal_points (const MeshBase & mesh)
1606 : {
1607 1132 : LOG_SCOPE("libmesh_assert_equal_points()", "MeshTools");
1608 :
1609 566 : dof_id_type pmax_node_id = mesh.max_node_id();
1610 566 : mesh.comm().max(pmax_node_id);
1611 :
1612 915766 : for (dof_id_type i=0; i != pmax_node_id; ++i)
1613 : {
1614 915200 : const Point * p = mesh.query_node_ptr(i);
1615 :
1616 915200 : libmesh_assert(mesh.comm().semiverify(p));
1617 : }
1618 566 : }
1619 :
1620 :
1621 566 : void libmesh_assert_equal_connectivity (const MeshBase & mesh)
1622 : {
1623 1132 : LOG_SCOPE("libmesh_assert_equal_connectivity()", "MeshTools");
1624 :
1625 566 : dof_id_type pmax_elem_id = mesh.max_elem_id();
1626 566 : mesh.comm().max(pmax_elem_id);
1627 :
1628 1142058 : for (dof_id_type i=0; i != pmax_elem_id; ++i)
1629 : {
1630 1141492 : const Elem * e = mesh.query_elem_ptr(i);
1631 :
1632 2282984 : std::vector<dof_id_type> nodes;
1633 1141492 : if (e)
1634 5771288 : for (auto n : e->node_index_range())
1635 4629912 : nodes.push_back(e->node_id(n));
1636 :
1637 1141492 : libmesh_assert(mesh.comm().semiverify(e ? &nodes : nullptr));
1638 : }
1639 566 : }
1640 :
1641 :
1642 0 : void libmesh_assert_connected_nodes (const MeshBase & mesh)
1643 : {
1644 0 : LOG_SCOPE("libmesh_assert_connected_nodes()", "MeshTools");
1645 :
1646 0 : std::set<const Node *> used_nodes;
1647 :
1648 0 : for (const auto & elem : mesh.element_ptr_range())
1649 : {
1650 0 : libmesh_assert (elem);
1651 :
1652 0 : for (auto & n : elem->node_ref_range())
1653 0 : used_nodes.insert(&n);
1654 : }
1655 :
1656 0 : for (const auto & node : mesh.node_ptr_range())
1657 : {
1658 0 : libmesh_assert(node);
1659 0 : libmesh_assert(used_nodes.count(node));
1660 : }
1661 0 : }
1662 :
1663 :
1664 :
1665 13076 : void libmesh_assert_valid_constraint_rows (const MeshBase & mesh)
1666 : {
1667 13076 : libmesh_parallel_only(mesh.comm());
1668 :
1669 13076 : const auto & constraint_rows = mesh.get_constraint_rows();
1670 :
1671 13076 : bool have_constraint_rows = !constraint_rows.empty();
1672 13076 : mesh.comm().max(have_constraint_rows);
1673 13076 : if (!have_constraint_rows)
1674 13036 : return;
1675 :
1676 12700 : for (auto & row : constraint_rows)
1677 : {
1678 12660 : const Node * node = row.first;
1679 12660 : libmesh_assert(node == mesh.node_ptr(node->id()));
1680 :
1681 44126 : for (auto & pr : row.second)
1682 : {
1683 31466 : const Elem * spline_elem = pr.first.first;
1684 31466 : libmesh_assert(spline_elem == mesh.elem_ptr(spline_elem->id()));
1685 : }
1686 : }
1687 :
1688 40 : dof_id_type pmax_node_id = mesh.max_node_id();
1689 40 : mesh.comm().max(pmax_node_id);
1690 :
1691 16990 : for (dof_id_type i=0; i != pmax_node_id; ++i)
1692 : {
1693 16950 : const Node * node = mesh.query_node_ptr(i);
1694 :
1695 16950 : bool have_constraint = constraint_rows.count(node);
1696 :
1697 16950 : const std::size_t my_n_constraints = have_constraint ?
1698 12660 : libmesh_map_find(constraint_rows, node).size() : std::size_t(-1);
1699 16950 : const std::size_t * n_constraints = node ?
1700 16950 : &my_n_constraints : nullptr;
1701 :
1702 16950 : libmesh_assert(mesh.comm().semiverify(n_constraints));
1703 : }
1704 : }
1705 :
1706 :
1707 :
1708 32824 : void libmesh_assert_valid_boundary_ids(const MeshBase & mesh)
1709 : {
1710 32824 : LOG_SCOPE("libmesh_assert_valid_boundary_ids()", "MeshTools");
1711 :
1712 32824 : if (mesh.n_processors() == 1)
1713 680 : return;
1714 :
1715 32144 : libmesh_parallel_only(mesh.comm());
1716 :
1717 32144 : const BoundaryInfo & boundary_info = mesh.get_boundary_info();
1718 :
1719 32144 : dof_id_type pmax_elem_id = mesh.max_elem_id();
1720 32144 : mesh.comm().max(pmax_elem_id);
1721 :
1722 4403680 : for (dof_id_type i=0; i != pmax_elem_id; ++i)
1723 : {
1724 4371536 : const Elem * elem = mesh.query_elem_ptr(i);
1725 4371536 : const unsigned int my_n_nodes = elem ? elem->n_nodes() : 0;
1726 4371536 : const unsigned int my_n_edges = elem ? elem->n_edges() : 0;
1727 4371536 : const unsigned int my_n_sides = elem ? elem->n_sides() : 0;
1728 : unsigned int
1729 4371536 : n_nodes = my_n_nodes,
1730 4371536 : n_edges = my_n_edges,
1731 4371536 : n_sides = my_n_sides;
1732 :
1733 4371536 : mesh.comm().max(n_nodes);
1734 4371536 : mesh.comm().max(n_edges);
1735 4371536 : mesh.comm().max(n_sides);
1736 :
1737 4371536 : if (elem)
1738 : {
1739 4293769 : libmesh_assert_equal_to(my_n_nodes, n_nodes);
1740 4293769 : libmesh_assert_equal_to(my_n_edges, n_edges);
1741 4293769 : libmesh_assert_equal_to(my_n_sides, n_sides);
1742 : }
1743 :
1744 : // Let's test all IDs on the element with one communication
1745 : // rather than n_nodes + n_edges + n_sides communications, to
1746 : // cut down on latency in dbg modes.
1747 8743072 : std::vector<boundary_id_type> all_bcids;
1748 :
1749 33575412 : for (unsigned int n=0; n != n_nodes; ++n)
1750 : {
1751 58407752 : std::vector<boundary_id_type> bcids;
1752 29203876 : if (elem)
1753 : {
1754 29040082 : boundary_info.boundary_ids(elem->node_ptr(n), bcids);
1755 :
1756 : // Ordering of boundary ids shouldn't matter
1757 29040082 : std::sort(bcids.begin(), bcids.end());
1758 : }
1759 : // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1760 :
1761 29203876 : all_bcids.insert(all_bcids.end(), bcids.begin(),
1762 58407752 : bcids.end());
1763 : // Separator
1764 29203876 : all_bcids.push_back(BoundaryInfo::invalid_id);
1765 : }
1766 :
1767 27666100 : for (unsigned short e=0; e != n_edges; ++e)
1768 : {
1769 46589128 : std::vector<boundary_id_type> bcids;
1770 :
1771 23294564 : if (elem)
1772 : {
1773 23193300 : boundary_info.edge_boundary_ids(elem, e, bcids);
1774 :
1775 : // Ordering of boundary ids shouldn't matter
1776 23193300 : std::sort(bcids.begin(), bcids.end());
1777 : }
1778 :
1779 : // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1780 :
1781 23294564 : all_bcids.insert(all_bcids.end(), bcids.begin(),
1782 46589128 : bcids.end());
1783 : // Separator
1784 23294564 : all_bcids.push_back(BoundaryInfo::invalid_id);
1785 :
1786 23294564 : if (elem)
1787 : {
1788 23193300 : boundary_info.raw_edge_boundary_ids(elem, e, bcids);
1789 :
1790 : // Ordering of boundary ids shouldn't matter
1791 23193300 : std::sort(bcids.begin(), bcids.end());
1792 :
1793 23193300 : all_bcids.insert(all_bcids.end(), bcids.begin(),
1794 46386600 : bcids.end());
1795 : // Separator
1796 23193300 : all_bcids.push_back(BoundaryInfo::invalid_id);
1797 : }
1798 :
1799 : // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1800 : }
1801 :
1802 21707304 : for (unsigned short s=0; s != n_sides; ++s)
1803 : {
1804 34671536 : std::vector<boundary_id_type> bcids;
1805 :
1806 17335768 : if (elem)
1807 : {
1808 17261812 : boundary_info.boundary_ids(elem, s, bcids);
1809 :
1810 : // Ordering of boundary ids shouldn't matter
1811 17261812 : std::sort(bcids.begin(), bcids.end());
1812 :
1813 17261812 : all_bcids.insert(all_bcids.end(), bcids.begin(),
1814 34523624 : bcids.end());
1815 : // Separator
1816 17261812 : all_bcids.push_back(BoundaryInfo::invalid_id);
1817 : }
1818 :
1819 : // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1820 :
1821 17335768 : if (elem)
1822 : {
1823 17261812 : boundary_info.raw_boundary_ids(elem, s, bcids);
1824 :
1825 : // Ordering of boundary ids shouldn't matter
1826 17261812 : std::sort(bcids.begin(), bcids.end());
1827 :
1828 17261812 : all_bcids.insert(all_bcids.end(), bcids.begin(),
1829 34523624 : bcids.end());
1830 : // Separator
1831 17261812 : all_bcids.push_back(BoundaryInfo::invalid_id);
1832 : }
1833 :
1834 : // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1835 : }
1836 :
1837 13114608 : for (unsigned short sf=0; sf != 2; ++sf)
1838 : {
1839 17486144 : std::vector<boundary_id_type> bcids;
1840 :
1841 8743072 : if (elem)
1842 : {
1843 8587538 : boundary_info.shellface_boundary_ids(elem, sf, bcids);
1844 :
1845 : // Ordering of boundary ids shouldn't matter
1846 8587538 : std::sort(bcids.begin(), bcids.end());
1847 :
1848 8587538 : all_bcids.insert(all_bcids.end(), bcids.begin(),
1849 17175076 : bcids.end());
1850 : // Separator
1851 8587538 : all_bcids.push_back(BoundaryInfo::invalid_id);
1852 : }
1853 :
1854 : // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1855 :
1856 8743072 : if (elem)
1857 : {
1858 8587538 : boundary_info.raw_shellface_boundary_ids(elem, sf, bcids);
1859 :
1860 : // Ordering of boundary ids shouldn't matter
1861 8587538 : std::sort(bcids.begin(), bcids.end());
1862 :
1863 8587538 : all_bcids.insert(all_bcids.end(), bcids.begin(),
1864 17175076 : bcids.end());
1865 : // Separator
1866 8587538 : all_bcids.push_back(BoundaryInfo::invalid_id);
1867 : }
1868 :
1869 : // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1870 : }
1871 :
1872 4371536 : libmesh_assert(mesh.comm().semiverify
1873 : (elem ? &all_bcids : nullptr));
1874 : }
1875 : }
1876 :
1877 :
1878 7600 : void libmesh_assert_valid_dof_ids(const MeshBase & mesh, unsigned int sysnum)
1879 : {
1880 7600 : LOG_SCOPE("libmesh_assert_valid_dof_ids()", "MeshTools");
1881 :
1882 7600 : if (mesh.n_processors() == 1)
1883 0 : return;
1884 :
1885 7600 : libmesh_parallel_only(mesh.comm());
1886 :
1887 7600 : dof_id_type pmax_elem_id = mesh.max_elem_id();
1888 7600 : mesh.comm().max(pmax_elem_id);
1889 :
1890 972250 : for (dof_id_type i=0; i != pmax_elem_id; ++i)
1891 964650 : assert_semiverify_dofobj(mesh.comm(),
1892 964650 : mesh.query_elem_ptr(i),
1893 : sysnum);
1894 :
1895 7600 : dof_id_type pmax_node_id = mesh.max_node_id();
1896 7600 : mesh.comm().max(pmax_node_id);
1897 :
1898 1831804 : for (dof_id_type i=0; i != pmax_node_id; ++i)
1899 1824204 : assert_semiverify_dofobj(mesh.comm(),
1900 1824204 : mesh.query_node_ptr(i),
1901 : sysnum);
1902 : }
1903 :
1904 :
1905 : #ifdef LIBMESH_ENABLE_UNIQUE_ID
1906 25176 : void libmesh_assert_valid_unique_ids(const MeshBase & mesh)
1907 : {
1908 50352 : LOG_SCOPE("libmesh_assert_valid_unique_ids()", "MeshTools");
1909 :
1910 25176 : libmesh_parallel_only(mesh.comm());
1911 :
1912 : // First collect all the unique_ids we can see and make sure there's
1913 : // no duplicates
1914 50352 : std::unordered_set<unique_id_type> semilocal_unique_ids;
1915 :
1916 2897749 : for (auto const & elem : mesh.active_element_ptr_range())
1917 : {
1918 2872573 : libmesh_assert (!semilocal_unique_ids.count(elem->unique_id()));
1919 2872573 : semilocal_unique_ids.insert(elem->unique_id());
1920 : }
1921 :
1922 5482415 : for (auto const & node : mesh.node_ptr_range())
1923 : {
1924 5457239 : libmesh_assert (!semilocal_unique_ids.count(node->unique_id()));
1925 5457239 : semilocal_unique_ids.insert(node->unique_id());
1926 : }
1927 :
1928 : // Then make sure elements are all in sync and remote elements don't
1929 : // duplicate semilocal
1930 :
1931 25176 : dof_id_type pmax_elem_id = mesh.max_elem_id();
1932 25176 : mesh.comm().max(pmax_elem_id);
1933 :
1934 3440320 : for (dof_id_type i=0; i != pmax_elem_id; ++i)
1935 : {
1936 3415144 : const Elem * elem = mesh.query_elem_ptr(i);
1937 3415144 : assert_dofobj_unique_id(mesh.comm(), elem, semilocal_unique_ids);
1938 : }
1939 :
1940 : // Then make sure nodes are all in sync and remote elements don't
1941 : // duplicate semilocal
1942 :
1943 25176 : dof_id_type pmax_node_id = mesh.max_node_id();
1944 25176 : mesh.comm().max(pmax_node_id);
1945 :
1946 5788690 : for (dof_id_type i=0; i != pmax_node_id; ++i)
1947 : {
1948 5763514 : const Node * node = mesh.query_node_ptr(i);
1949 5763514 : assert_dofobj_unique_id(mesh.comm(), node, semilocal_unique_ids);
1950 : }
1951 25176 : }
1952 : #endif
1953 :
1954 0 : void libmesh_assert_consistent_distributed(const MeshBase & mesh)
1955 : {
1956 0 : libmesh_parallel_only(mesh.comm());
1957 :
1958 0 : dof_id_type parallel_max_elem_id = mesh.max_elem_id();
1959 0 : mesh.comm().max(parallel_max_elem_id);
1960 :
1961 0 : for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
1962 : {
1963 0 : const Elem * elem = mesh.query_elem_ptr(i);
1964 : processor_id_type pid =
1965 0 : elem ? elem->processor_id() : DofObject::invalid_processor_id;
1966 0 : mesh.comm().min(pid);
1967 0 : libmesh_assert(elem || pid != mesh.processor_id());
1968 : }
1969 :
1970 0 : dof_id_type parallel_max_node_id = mesh.max_node_id();
1971 0 : mesh.comm().max(parallel_max_node_id);
1972 :
1973 0 : for (dof_id_type i=0; i != parallel_max_node_id; ++i)
1974 : {
1975 0 : const Node * node = mesh.query_node_ptr(i);
1976 : processor_id_type pid =
1977 0 : node ? node->processor_id() : DofObject::invalid_processor_id;
1978 0 : mesh.comm().min(pid);
1979 0 : libmesh_assert(node || pid != mesh.processor_id());
1980 : }
1981 0 : }
1982 :
1983 :
1984 0 : void libmesh_assert_consistent_distributed_nodes(const MeshBase & mesh)
1985 : {
1986 0 : libmesh_parallel_only(mesh.comm());
1987 0 : auto locator = mesh.sub_point_locator();
1988 :
1989 0 : dof_id_type parallel_max_elem_id = mesh.max_elem_id();
1990 0 : mesh.comm().max(parallel_max_elem_id);
1991 :
1992 0 : for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
1993 : {
1994 0 : const Elem * elem = mesh.query_elem_ptr(i);
1995 :
1996 0 : const unsigned int my_n_nodes = elem ? elem->n_nodes() : 0;
1997 0 : unsigned int n_nodes = my_n_nodes;
1998 0 : mesh.comm().max(n_nodes);
1999 :
2000 0 : if (n_nodes)
2001 0 : libmesh_assert(mesh.comm().semiverify(elem ? &my_n_nodes : nullptr));
2002 :
2003 0 : for (unsigned int n=0; n != n_nodes; ++n)
2004 : {
2005 0 : const Node * node = elem ? elem->node_ptr(n) : nullptr;
2006 : processor_id_type pid =
2007 0 : node ? node->processor_id() : DofObject::invalid_processor_id;
2008 0 : mesh.comm().min(pid);
2009 0 : libmesh_assert(node || pid != mesh.processor_id());
2010 : }
2011 : }
2012 0 : }
2013 :
2014 :
2015 :
2016 : template <>
2017 17490 : void libmesh_assert_parallel_consistent_procids<Elem>(const MeshBase & mesh)
2018 : {
2019 17490 : LOG_SCOPE("libmesh_assert_parallel_consistent_procids()", "MeshTools");
2020 :
2021 17490 : if (mesh.n_processors() == 1)
2022 0 : return;
2023 :
2024 17490 : libmesh_parallel_only(mesh.comm());
2025 :
2026 : // Some code (looking at you, stitch_meshes) modifies DofObject ids
2027 : // without keeping max_elem_id()/max_node_id() consistent, but
2028 : // that's done in a safe way for performance reasons, so we'll play
2029 : // along and just figure out new max ids ourselves.
2030 17490 : dof_id_type parallel_max_elem_id = 0;
2031 2628478 : for (const auto & elem : mesh.element_ptr_range())
2032 2610988 : parallel_max_elem_id = std::max<dof_id_type>(parallel_max_elem_id,
2033 2610988 : elem->id()+1);
2034 17490 : mesh.comm().max(parallel_max_elem_id);
2035 :
2036 : // Check processor ids for consistency between processors
2037 :
2038 2697192 : for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
2039 : {
2040 2679702 : const Elem * elem = mesh.query_elem_ptr(i);
2041 :
2042 : processor_id_type min_id =
2043 2610988 : elem ? elem->processor_id() :
2044 2679702 : std::numeric_limits<processor_id_type>::max();
2045 2679702 : mesh.comm().min(min_id);
2046 :
2047 : processor_id_type max_id =
2048 2610988 : elem ? elem->processor_id() :
2049 2679702 : std::numeric_limits<processor_id_type>::min();
2050 2679702 : mesh.comm().max(max_id);
2051 :
2052 2679702 : if (elem)
2053 : {
2054 2610988 : libmesh_assert_equal_to (min_id, elem->processor_id());
2055 2610988 : libmesh_assert_equal_to (max_id, elem->processor_id());
2056 : }
2057 :
2058 2679702 : if (min_id == mesh.processor_id())
2059 1266117 : libmesh_assert(elem);
2060 : }
2061 : }
2062 :
2063 :
2064 :
2065 76 : void libmesh_assert_parallel_consistent_new_node_procids(const MeshBase & mesh)
2066 : {
2067 76 : LOG_SCOPE("libmesh_assert_parallel_consistent_new_node_procids()", "MeshTools");
2068 :
2069 76 : if (mesh.n_processors() == 1)
2070 0 : return;
2071 :
2072 76 : libmesh_parallel_only(mesh.comm());
2073 :
2074 : // We want this test to hit every node when called even after nodes
2075 : // have been added asynchronously but before everything has been
2076 : // renumbered.
2077 76 : dof_id_type parallel_max_elem_id = mesh.max_elem_id();
2078 76 : mesh.comm().max(parallel_max_elem_id);
2079 :
2080 152 : std::vector<bool> elem_touched_by_anyone(parallel_max_elem_id, false);
2081 :
2082 43248 : for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
2083 : {
2084 43172 : const Elem * elem = mesh.query_elem_ptr(i);
2085 :
2086 43172 : const unsigned int my_n_nodes = elem ? elem->n_nodes() : 0;
2087 43172 : unsigned int n_nodes = my_n_nodes;
2088 43172 : mesh.comm().max(n_nodes);
2089 :
2090 43172 : if (n_nodes)
2091 15840 : libmesh_assert(mesh.comm().semiverify(elem ? &my_n_nodes : nullptr));
2092 :
2093 173404 : for (unsigned int n=0; n != n_nodes; ++n)
2094 : {
2095 130232 : const Node * node = elem ? elem->node_ptr(n) : nullptr;
2096 130232 : const processor_id_type pid = node ? node->processor_id() : 0;
2097 130232 : libmesh_assert(mesh.comm().semiverify (node ? &pid : nullptr));
2098 : }
2099 : }
2100 : }
2101 :
2102 : template <>
2103 26938 : void libmesh_assert_parallel_consistent_procids<Node>(const MeshBase & mesh)
2104 : {
2105 26938 : LOG_SCOPE("libmesh_assert_parallel_consistent_procids()", "MeshTools");
2106 :
2107 26938 : if (mesh.n_processors() == 1)
2108 0 : return;
2109 :
2110 26938 : libmesh_parallel_only(mesh.comm());
2111 :
2112 : // We want this test to be valid even when called even after nodes
2113 : // have been added asynchronously but before they're renumbered
2114 : //
2115 : // Plus, some code (looking at you, stitch_meshes) modifies
2116 : // DofObject ids without keeping max_elem_id()/max_node_id()
2117 : // consistent, but that's done in a safe way for performance
2118 : // reasons, so we'll play along and just figure out new max ids
2119 : // ourselves.
2120 26938 : dof_id_type parallel_max_node_id = 0;
2121 7534717 : for (const auto & node : mesh.node_ptr_range())
2122 7507779 : parallel_max_node_id = std::max<dof_id_type>(parallel_max_node_id,
2123 7507779 : node->id()+1);
2124 26938 : mesh.comm().max(parallel_max_node_id);
2125 :
2126 53876 : std::vector<bool> node_touched_by_anyone(parallel_max_node_id, false);
2127 :
2128 2264397 : for (const auto & elem : as_range(mesh.local_elements_begin(),
2129 4528794 : mesh.local_elements_end()))
2130 : {
2131 2237459 : libmesh_assert (elem);
2132 :
2133 16990701 : for (auto & node : elem->node_ref_range())
2134 : {
2135 14753242 : dof_id_type nodeid = node.id();
2136 14753242 : node_touched_by_anyone[nodeid] = true;
2137 : }
2138 : }
2139 26938 : mesh.comm().max(node_touched_by_anyone);
2140 :
2141 : // Check processor ids for consistency between processors
2142 : // on any node an element touches
2143 7953792 : for (dof_id_type i=0; i != parallel_max_node_id; ++i)
2144 : {
2145 7926854 : if (!node_touched_by_anyone[i])
2146 421360 : continue;
2147 :
2148 7505494 : const Node * node = mesh.query_node_ptr(i);
2149 7505494 : const processor_id_type pid = node ? node->processor_id() : 0;
2150 :
2151 7505494 : libmesh_assert(mesh.comm().semiverify (node ? &pid : nullptr));
2152 : }
2153 : }
2154 :
2155 :
2156 :
2157 : #ifdef LIBMESH_ENABLE_AMR
2158 0 : void libmesh_assert_valid_refinement_flags(const MeshBase & mesh)
2159 : {
2160 0 : LOG_SCOPE("libmesh_assert_valid_refinement_flags()", "MeshTools");
2161 :
2162 0 : libmesh_parallel_only(mesh.comm());
2163 0 : if (mesh.n_processors() == 1)
2164 0 : return;
2165 :
2166 0 : dof_id_type pmax_elem_id = mesh.max_elem_id();
2167 0 : mesh.comm().max(pmax_elem_id);
2168 :
2169 0 : std::vector<unsigned char> my_elem_h_state(pmax_elem_id, 255);
2170 0 : std::vector<unsigned char> my_elem_p_state(pmax_elem_id, 255);
2171 :
2172 0 : for (const auto & elem : mesh.element_ptr_range())
2173 : {
2174 0 : libmesh_assert (elem);
2175 0 : dof_id_type elemid = elem->id();
2176 :
2177 0 : my_elem_h_state[elemid] =
2178 0 : static_cast<unsigned char>(elem->refinement_flag());
2179 :
2180 0 : my_elem_p_state[elemid] =
2181 0 : static_cast<unsigned char>(elem->p_refinement_flag());
2182 : }
2183 0 : std::vector<unsigned char> min_elem_h_state(my_elem_h_state);
2184 0 : mesh.comm().min(min_elem_h_state);
2185 :
2186 0 : std::vector<unsigned char> min_elem_p_state(my_elem_p_state);
2187 0 : mesh.comm().min(min_elem_p_state);
2188 :
2189 0 : for (dof_id_type i=0; i!= pmax_elem_id; ++i)
2190 : {
2191 0 : libmesh_assert(my_elem_h_state[i] == 255 ||
2192 : my_elem_h_state[i] == min_elem_h_state[i]);
2193 0 : libmesh_assert(my_elem_p_state[i] == 255 ||
2194 : my_elem_p_state[i] == min_elem_p_state[i]);
2195 : }
2196 : }
2197 : #else
2198 : void libmesh_assert_valid_refinement_flags(const MeshBase &)
2199 : {
2200 : }
2201 : #endif // LIBMESH_ENABLE_AMR
2202 :
2203 :
2204 :
2205 15030 : void libmesh_assert_valid_neighbors(const MeshBase & mesh,
2206 : bool assert_valid_remote_elems)
2207 : {
2208 15030 : LOG_SCOPE("libmesh_assert_valid_neighbors()", "MeshTools");
2209 :
2210 2670760 : for (const auto & elem : mesh.element_ptr_range())
2211 : {
2212 2655730 : libmesh_assert (elem);
2213 2655730 : elem->libmesh_assert_valid_neighbors();
2214 : }
2215 :
2216 15030 : if (mesh.n_processors() == 1)
2217 570 : return;
2218 :
2219 14460 : libmesh_parallel_only(mesh.comm());
2220 :
2221 14460 : dof_id_type pmax_elem_id = mesh.max_elem_id();
2222 14460 : mesh.comm().max(pmax_elem_id);
2223 :
2224 2772384 : for (dof_id_type i=0; i != pmax_elem_id; ++i)
2225 : {
2226 2757924 : const Elem * elem = mesh.query_elem_ptr(i);
2227 :
2228 2757924 : const unsigned int my_n_neigh = elem ? elem->n_neighbors() : 0;
2229 2757924 : unsigned int n_neigh = my_n_neigh;
2230 2757924 : mesh.comm().max(n_neigh);
2231 2757924 : if (elem)
2232 2653630 : libmesh_assert_equal_to (my_n_neigh, n_neigh);
2233 :
2234 13290958 : for (unsigned int n = 0; n != n_neigh; ++n)
2235 : {
2236 10533034 : dof_id_type my_neighbor = DofObject::invalid_id;
2237 10533034 : dof_id_type * p_my_neighbor = nullptr;
2238 :
2239 : // If we have a non-remote_elem neighbor link, then we can
2240 : // verify it.
2241 10533034 : if (elem && elem->neighbor_ptr(n) != remote_elem)
2242 : {
2243 10455280 : p_my_neighbor = &my_neighbor;
2244 10455280 : if (elem->neighbor_ptr(n))
2245 9896420 : my_neighbor = elem->neighbor_ptr(n)->id();
2246 :
2247 : // But wait - if we haven't set remote_elem links yet then
2248 : // some nullptr links on ghost elements might be
2249 : // future-remote_elem links, so we can't verify those.
2250 20917464 : if (!assert_valid_remote_elems &&
2251 10456894 : !elem->neighbor_ptr(n) &&
2252 1614 : elem->processor_id() != mesh.processor_id())
2253 753 : p_my_neighbor = nullptr;
2254 : }
2255 10533034 : libmesh_assert(mesh.comm().semiverify(p_my_neighbor));
2256 : }
2257 : }
2258 : }
2259 : #endif // DEBUG
2260 :
2261 :
2262 :
2263 : // Functors for correct_node_proc_ids
2264 : namespace {
2265 :
2266 : typedef std::unordered_map<dof_id_type, processor_id_type> proc_id_map_type;
2267 :
2268 : struct SyncNodeSet
2269 : {
2270 : typedef unsigned char datum; // bool but without bit twiddling issues
2271 :
2272 158 : SyncNodeSet(std::unordered_set<const Node *> & _set,
2273 8347 : MeshBase & _mesh) :
2274 8347 : node_set(_set), mesh(_mesh) {}
2275 :
2276 : std::unordered_set<const Node *> & node_set;
2277 :
2278 : MeshBase & mesh;
2279 :
2280 : // ------------------------------------------------------------
2281 37825 : void gather_data (const std::vector<dof_id_type> & ids,
2282 : std::vector<datum> & data)
2283 : {
2284 : // Find whether each requested node belongs in the set
2285 37825 : data.resize(ids.size());
2286 :
2287 1830683 : for (auto i : index_range(ids))
2288 : {
2289 1792858 : const dof_id_type id = ids[i];
2290 :
2291 : // We'd better have every node we're asked for
2292 1792858 : Node * node = mesh.node_ptr(id);
2293 :
2294 : // Return if the node is in the set.
2295 2888844 : data[i] = node_set.count(node);
2296 : }
2297 37825 : }
2298 :
2299 : // ------------------------------------------------------------
2300 37825 : bool act_on_data (const std::vector<dof_id_type> & ids,
2301 : const std::vector<datum> in_set)
2302 : {
2303 143 : bool data_changed = false;
2304 :
2305 : // Add nodes we've been informed of to our own set
2306 1830683 : for (auto i : index_range(ids))
2307 : {
2308 1792858 : if (in_set[i])
2309 : {
2310 1162999 : Node * node = mesh.node_ptr(ids[i]);
2311 1162999 : if (!node_set.count(node))
2312 : {
2313 256 : node_set.insert(node);
2314 256 : data_changed = true;
2315 : }
2316 : }
2317 : }
2318 :
2319 37825 : return data_changed;
2320 : }
2321 : };
2322 :
2323 :
2324 8031 : struct NodesNotInSet
2325 : {
2326 158 : NodesNotInSet(const std::unordered_set<const Node *> _set)
2327 8189 : : node_set(_set) {}
2328 :
2329 473220 : bool operator() (const Node * node) const
2330 : {
2331 946440 : if (node_set.count(node))
2332 280716 : return false;
2333 192504 : return true;
2334 : }
2335 :
2336 : const std::unordered_set<const Node *> node_set;
2337 : };
2338 :
2339 :
2340 : struct SyncProcIdsFromMap
2341 : {
2342 : typedef processor_id_type datum;
2343 :
2344 194 : SyncProcIdsFromMap(const proc_id_map_type & _map,
2345 34711 : MeshBase & _mesh) :
2346 34711 : new_proc_ids(_map), mesh(_mesh) {}
2347 :
2348 : const proc_id_map_type & new_proc_ids;
2349 :
2350 : MeshBase & mesh;
2351 :
2352 : // ------------------------------------------------------------
2353 121429 : void gather_data (const std::vector<dof_id_type> & ids,
2354 : std::vector<datum> & data)
2355 : {
2356 : // Find the new processor id of each requested node
2357 121429 : data.resize(ids.size());
2358 :
2359 6983925 : for (auto i : index_range(ids))
2360 : {
2361 6862496 : const dof_id_type id = ids[i];
2362 :
2363 : // Return the node's new processor id if it has one, or its
2364 : // old processor id if not.
2365 6862496 : if (const auto it = new_proc_ids.find(id);
2366 47012 : it != new_proc_ids.end())
2367 6231416 : data[i] = it->second;
2368 : else
2369 : {
2370 : // We'd better find every node we're asked for
2371 631080 : const Node & node = mesh.node_ref(id);
2372 631080 : data[i] = node.processor_id();
2373 : }
2374 : }
2375 121429 : }
2376 :
2377 : // ------------------------------------------------------------
2378 121429 : void act_on_data (const std::vector<dof_id_type> & ids,
2379 : const std::vector<datum> proc_ids)
2380 : {
2381 : // Set the node processor ids we've now been informed of
2382 6983925 : for (auto i : index_range(ids))
2383 : {
2384 6862496 : Node & node = mesh.node_ref(ids[i]);
2385 6862496 : node.processor_id() = proc_ids[i];
2386 : }
2387 121429 : }
2388 : };
2389 : }
2390 :
2391 :
2392 :
2393 34711 : void correct_node_proc_ids (MeshBase & mesh)
2394 : {
2395 388 : LOG_SCOPE("correct_node_proc_ids()","MeshTools");
2396 :
2397 : // This function must be run on all processors at once
2398 194 : libmesh_parallel_only(mesh.comm());
2399 :
2400 : // We require all processors to agree on nodal processor ids before
2401 : // going into this algorithm.
2402 : #ifdef DEBUG
2403 194 : libmesh_assert_parallel_consistent_procids<Node>(mesh);
2404 : #endif
2405 :
2406 : // If we have any unpartitioned elements at this
2407 : // stage there is a problem
2408 194 : libmesh_assert (n_elem(mesh.unpartitioned_elements_begin(),
2409 : mesh.unpartitioned_elements_end()) == 0);
2410 :
2411 : // Fix nodes' processor ids. Coarsening may have left us with nodes
2412 : // which are no longer touched by any elements of the same processor
2413 : // id, and for DofMap to work we need to fix that.
2414 :
2415 : // This is harder now that libMesh no longer requires a distributed
2416 : // mesh to ghost all nodal neighbors: it is possible for two active
2417 : // elements on two different processors to share the same node in
2418 : // such a way that neither processor knows the others' element
2419 : // exists!
2420 :
2421 : // While we're at it, if this mesh is configured to allow
2422 : // repartitioning, we'll repartition *all* the nodes' processor ids
2423 : // using the canonical Node heuristic, to try and improve DoF load
2424 : // balancing. But if the mesh is disallowing repartitioning, we
2425 : // won't touch processor_id on any node where it's valid, regardless
2426 : // of whether or not it's canonical.
2427 194 : bool repartition_all_nodes = !mesh.skip_noncritical_partitioning();
2428 388 : std::unordered_set<const Node *> valid_nodes;
2429 :
2430 : // If we aren't allowed to repartition, then we're going to leave
2431 : // every node we can at its current processor_id, and *only*
2432 : // repartition the nodes whose current processor id is incompatible
2433 : // with DoFMap (because it doesn't touch an active element, e.g. due
2434 : // to coarsening)
2435 34711 : if (!repartition_all_nodes)
2436 : {
2437 1127692 : for (const auto & elem : mesh.active_element_ptr_range())
2438 5812313 : for (const auto & node : elem->node_ref_range())
2439 5188291 : if (elem->processor_id() == node.processor_id())
2440 4818885 : valid_nodes.insert(&node);
2441 :
2442 158 : SyncNodeSet syncv(valid_nodes, mesh);
2443 :
2444 : Parallel::sync_dofobject_data_by_id
2445 16536 : (mesh.comm(), mesh.nodes_begin(), mesh.nodes_end(), syncv);
2446 : }
2447 :
2448 : // We build up a set of compatible processor ids for each node
2449 388 : proc_id_map_type new_proc_ids;
2450 :
2451 8783526 : for (auto & elem : mesh.active_element_ptr_range())
2452 : {
2453 4395135 : processor_id_type pid = elem->processor_id();
2454 :
2455 42755957 : for (auto & node : elem->node_ref_range())
2456 : {
2457 38360822 : const dof_id_type id = node.id();
2458 38360822 : if (auto it = new_proc_ids.find(id);
2459 342932 : it == new_proc_ids.end())
2460 152592 : new_proc_ids.emplace(id, pid);
2461 : else
2462 27494680 : it->second = node.choose_processor_id(it->second, pid);
2463 : }
2464 34323 : }
2465 :
2466 : // Sort the new pids to push to each processor
2467 : std::map<processor_id_type, std::vector<std::pair<dof_id_type, processor_id_type>>>
2468 388 : ids_to_push;
2469 :
2470 24904628 : for (const auto & node : mesh.node_ptr_range())
2471 12666780 : if (const auto it = std::as_const(new_proc_ids).find(node->id());
2472 12666780 : it != new_proc_ids.end() && node->processor_id() != DofObject::invalid_processor_id)
2473 10900465 : ids_to_push[node->processor_id()].emplace_back(node->id(), /*pid=*/it->second);
2474 :
2475 : auto action_functor =
2476 156350 : [& mesh, & new_proc_ids]
2477 : (processor_id_type,
2478 11324280 : const std::vector<std::pair<dof_id_type, processor_id_type>> & data)
2479 : {
2480 11023216 : for (const auto & [id, pid] : data)
2481 : {
2482 10866142 : if (const auto it = new_proc_ids.find(id);
2483 152592 : it == new_proc_ids.end())
2484 0 : new_proc_ids.emplace(id, pid);
2485 : else
2486 : {
2487 10866142 : const Node & node = mesh.node_ref(id);
2488 10866142 : it->second = node.choose_processor_id(it->second, pid);
2489 : }
2490 : }
2491 35241 : };
2492 :
2493 : Parallel::push_parallel_vector_data
2494 34711 : (mesh.comm(), ids_to_push, action_functor);
2495 :
2496 : // Now new_proc_ids is correct for every node we used to own. Let's
2497 : // ask every other processor about the nodes they used to own. But
2498 : // first we'll need to keep track of which nodes we used to own,
2499 : // lest we get them confused with nodes we newly own.
2500 388 : std::unordered_set<Node *> ex_local_nodes;
2501 8853638 : for (auto & node : mesh.local_node_ptr_range())
2502 4527260 : if (const auto it = new_proc_ids.find(node->id());
2503 4527260 : it != new_proc_ids.end() && it->second != mesh.processor_id())
2504 34372 : ex_local_nodes.insert(node);
2505 :
2506 194 : SyncProcIdsFromMap sync(new_proc_ids, mesh);
2507 34711 : if (repartition_all_nodes)
2508 : Parallel::sync_dofobject_data_by_id
2509 52692 : (mesh.comm(), mesh.nodes_begin(), mesh.nodes_end(), sync);
2510 : else
2511 : {
2512 158 : NodesNotInSet nnis(valid_nodes);
2513 :
2514 : Parallel::sync_dofobject_data_by_id
2515 16536 : (mesh.comm(), mesh.nodes_begin(), mesh.nodes_end(), nnis, sync);
2516 : }
2517 :
2518 : // And finally let's update the nodes we used to own.
2519 39298 : for (const auto & node : ex_local_nodes)
2520 : {
2521 2103 : if (valid_nodes.count(node))
2522 2083 : continue;
2523 :
2524 2504 : const dof_id_type id = node->id();
2525 10 : const proc_id_map_type::iterator it = new_proc_ids.find(id);
2526 10 : libmesh_assert(it != new_proc_ids.end());
2527 2504 : node->processor_id() = it->second;
2528 : }
2529 :
2530 : // We should still have consistent nodal processor ids coming out of
2531 : // this algorithm, but if we're allowed to repartition the mesh then
2532 : // they should be canonically correct too.
2533 : #ifdef DEBUG
2534 194 : libmesh_assert_valid_procids<Node>(mesh);
2535 : //if (repartition_all_nodes)
2536 : // libmesh_assert_canonical_node_procids(mesh);
2537 : #endif
2538 34711 : }
2539 :
2540 :
2541 :
2542 19490 : void Private::globally_renumber_nodes_and_elements (MeshBase & mesh)
2543 : {
2544 19490 : MeshCommunication().assign_global_indices(mesh);
2545 19490 : }
2546 :
2547 : } // namespace MeshTools
2548 :
2549 : } // namespace libMesh
|