Line data Source code
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 and subroutines
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 2583974 : FindBBox () : _bbox()
108 36822 : {}
109 :
110 870 : FindBBox (FindBBox & other, Threads::split) :
111 870 : _bbox(other._bbox)
112 298 : {}
113 :
114 39872 : void operator()(const ConstNodeRange & range)
115 : {
116 5108830 : for (const auto & node : range)
117 : {
118 460679 : libmesh_assert(node);
119 5068958 : _bbox.union_with(*node);
120 : }
121 39872 : }
122 :
123 2600856 : void operator()(const ConstElemRange & range)
124 : {
125 28868534 : for (const auto & elem : range)
126 : {
127 1413140 : libmesh_assert(elem);
128 26267678 : _bbox.union_with(elem->loose_bounding_box());
129 : }
130 2600856 : }
131 :
132 18694 : Point & min() { return _bbox.min(); }
133 :
134 18694 : 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 298 : void join (const FindBBox & other)
140 : {
141 870 : _bbox.union_with(other._bbox);
142 580 : }
143 : #endif
144 :
145 54950 : libMesh::BoundingBox & bbox ()
146 : {
147 54950 : return _bbox;
148 : }
149 :
150 : private:
151 : BoundingBox _bbox;
152 : };
153 :
154 : #ifdef DEBUG
155 2980902 : void assert_semiverify_dofobj(const Parallel::Communicator & communicator,
156 : const DofObject * d,
157 : unsigned int sysnum = libMesh::invalid_uint)
158 : {
159 2980902 : if (d)
160 : {
161 2905442 : const unsigned int n_sys = d->n_systems();
162 :
163 5810884 : std::vector<unsigned int> n_vars (n_sys, 0);
164 6509094 : for (unsigned int s = 0; s != n_sys; ++s)
165 3603652 : if (sysnum == s ||
166 : sysnum == libMesh::invalid_uint)
167 2905442 : n_vars[s] = d->n_vars(s);
168 :
169 : const unsigned int tot_n_vars =
170 2905442 : std::accumulate(n_vars.begin(), n_vars.end(), 0);
171 :
172 5810884 : std::vector<unsigned int> n_comp (tot_n_vars, 0);
173 5810884 : std::vector<dof_id_type> first_dof (tot_n_vars, 0);
174 :
175 6509094 : for (unsigned int s = 0, i=0; s != n_sys; ++s)
176 : {
177 3603652 : if (sysnum != s &&
178 : sysnum != libMesh::invalid_uint)
179 698210 : continue;
180 :
181 9769010 : for (unsigned int v = 0; v != n_vars[s]; ++v, ++i)
182 : {
183 6863568 : n_comp[i] = d->n_comp(s,v);
184 6863568 : first_dof[i] = n_comp[i] ? d->dof_number(s,v,0) : DofObject::invalid_id;
185 : }
186 : }
187 :
188 2905442 : libmesh_assert(communicator.semiverify(&n_sys));
189 2905442 : libmesh_assert(communicator.semiverify(&n_vars));
190 2905442 : libmesh_assert(communicator.semiverify(&n_comp));
191 2905442 : libmesh_assert(communicator.semiverify(&first_dof));
192 : }
193 : else
194 : {
195 75460 : const unsigned int * p_ui = nullptr;
196 75460 : const std::vector<unsigned int> * p_vui = nullptr;
197 75460 : const std::vector<dof_id_type> * p_vdid = nullptr;
198 :
199 75460 : libmesh_assert(communicator.semiverify(p_ui));
200 75460 : libmesh_assert(communicator.semiverify(p_vui));
201 75460 : libmesh_assert(communicator.semiverify(p_vui));
202 75460 : libmesh_assert(communicator.semiverify(p_vdid));
203 : }
204 2980902 : }
205 :
206 :
207 :
208 : #ifdef LIBMESH_ENABLE_UNIQUE_ID
209 16890522 : 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 16890522 : if (d)
218 : {
219 16223082 : tempmin = tempmax = d->unique_id();
220 : }
221 : else
222 : {
223 667440 : TIMPI::Attributes<unique_id_type>::set_highest(tempmin);
224 667440 : TIMPI::Attributes<unique_id_type>::set_lowest(tempmax);
225 : }
226 16890522 : comm.min(tempmin);
227 16890522 : comm.max(tempmax);
228 33113604 : bool invalid = d && ((d->unique_id() != tempmin) ||
229 16223082 : (d->unique_id() != tempmax));
230 16890522 : comm.max(invalid);
231 :
232 : // First verify that everything is in sync
233 16890522 : libmesh_assert(!invalid);
234 :
235 : // Then verify that any remote id doesn't duplicate a local one.
236 16890522 : if (!d && tempmin == tempmax)
237 81256 : libmesh_assert(!unique_ids.count(tempmin));
238 16890522 : }
239 : #endif // LIBMESH_ENABLE_UNIQUE_ID
240 : #endif // DEBUG
241 :
242 225994 : 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 16036 : 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 1460302 : for (const auto & elem : node_to_elem_vec)
257 : {
258 : // We only care about active elements...
259 1234308 : if (elem->active())
260 : {
261 : // Which local node number is global_id?
262 1193204 : unsigned local_node_number = elem->local_node(global_id);
263 :
264 : // Make sure it was found
265 41104 : libmesh_assert_not_equal_to(local_node_number, libMesh::invalid_uint);
266 :
267 1234308 : 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 1234308 : if (!n_edges)
271 : {
272 1374 : switch (elem->type())
273 : {
274 592 : case EDGE2:
275 : {
276 18 : switch (local_node_number)
277 : {
278 296 : case 0:
279 : // The other node is a nodal neighbor
280 305 : neighbor_set.insert(elem->node_ptr(1));
281 296 : break;
282 :
283 296 : case 1:
284 : // The other node is a nodal neighbor
285 305 : neighbor_set.insert(elem->node_ptr(0));
286 296 : break;
287 :
288 0 : default:
289 0 : libmesh_error_msg("Invalid local node number: " << local_node_number << " found." << std::endl);
290 : }
291 18 : break;
292 : }
293 :
294 498 : case EDGE3:
295 : {
296 18 : switch (local_node_number)
297 : {
298 : // The outside nodes have node 2 as a neighbor
299 356 : case 0:
300 : case 1:
301 370 : neighbor_set.insert(elem->node_ptr(2));
302 356 : break;
303 :
304 : // The middle node has the outer nodes as neighbors
305 142 : case 2:
306 146 : neighbor_set.insert(elem->node_ptr(0));
307 146 : neighbor_set.insert(elem->node_ptr(1));
308 142 : break;
309 :
310 0 : default:
311 0 : libmesh_error_msg("Invalid local node number: " << local_node_number << " found." << std::endl);
312 : }
313 18 : break;
314 : }
315 :
316 284 : case EDGE4:
317 : {
318 8 : switch (local_node_number)
319 : {
320 71 : case 0:
321 : // The left-middle node is a nodal neighbor
322 73 : neighbor_set.insert(elem->node_ptr(2));
323 71 : break;
324 :
325 71 : case 1:
326 : // The right-middle node is a nodal neighbor
327 73 : neighbor_set.insert(elem->node_ptr(3));
328 71 : break;
329 :
330 : // The left-middle node
331 71 : case 2:
332 73 : neighbor_set.insert(elem->node_ptr(0));
333 73 : neighbor_set.insert(elem->node_ptr(3));
334 71 : break;
335 :
336 : // The right-middle node
337 71 : case 3:
338 73 : neighbor_set.insert(elem->node_ptr(1));
339 73 : neighbor_set.insert(elem->node_ptr(2));
340 71 : break;
341 :
342 0 : default:
343 0 : libmesh_error_msg("Invalid local node number: " << local_node_number << " found." << std::endl);
344 : }
345 8 : 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 1234308 : const auto elem_order = Elem::type_to_default_order_map[elem->type()];
354 :
355 : // Index of the current edge
356 41104 : unsigned current_edge = 0;
357 :
358 1234308 : const unsigned short n_nodes = elem->n_nodes();
359 :
360 5024403 : while (current_edge < n_edges)
361 : {
362 : // Find the edge the node is on
363 125794 : bool found_edge = false;
364 9166991 : for (; current_edge<n_edges; ++current_edge)
365 8404404 : if (elem->is_node_on_edge(local_node_number, current_edge))
366 : {
367 99678 : found_edge = true;
368 99678 : break;
369 : }
370 :
371 : // Did we find one?
372 3790095 : if (found_edge)
373 : {
374 3027508 : const Node * node_to_save = nullptr;
375 :
376 : // Find another node in this element on this edge
377 22530524 : for (unsigned other_node_this_edge = 0; other_node_this_edge != n_nodes; other_node_this_edge++)
378 : {
379 35692072 : const bool both_vertices = elem->is_vertex(local_node_number) &&
380 16189056 : elem->is_vertex(other_node_this_edge);
381 26015146 : if ( elem->is_node_on_edge(other_node_this_edge, current_edge) && // On the current edge
382 19551572 : 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 3632856 : (elem_order == 1 || !both_vertices))
385 : {
386 : // We've found a nodal neighbor! Save a pointer to it..
387 3288908 : node_to_save = elem->node_ptr(other_node_this_edge);
388 :
389 : // Make sure we found something
390 108954 : libmesh_assert(node_to_save != nullptr);
391 :
392 3179954 : neighbor_set.insert(node_to_save);
393 : }
394 : }
395 : }
396 :
397 : // Keep looking for edges, node may be on more than one edge
398 3790095 : 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 217976 : neighbors.assign(neighbor_set.begin(), neighbor_set.end());
407 225994 : }
408 :
409 :
410 :
411 14138 : std::unique_ptr<MeshBase> reprepared_mesh_clone (const MeshBase & mesh)
412 : {
413 14138 : const MeshBase::Preparation prep = mesh.preparation();
414 :
415 : // If the mesh thinks it's prepared in some way, *re*-preparing in
416 : // that way shouldn't change a clone of it, as long as we disallow
417 : // repartitioning or renumbering or remote element removal.
418 14138 : std::unique_ptr<MeshBase> mesh_clone = mesh.clone();
419 :
420 12466 : const bool old_allow_renumbering = mesh_clone->allow_renumbering();
421 : const bool old_allow_remote_element_removal =
422 12466 : mesh_clone->allow_remote_element_removal();
423 12466 : const bool old_skip_partitioning = mesh_clone->skip_partitioning();
424 12416 : mesh_clone->allow_renumbering(false);
425 12416 : mesh_clone->allow_remote_element_removal(false);
426 12416 : mesh_clone->skip_partitioning(true);
427 :
428 : // If the mesh thinks it's already completely prepared, test that
429 14138 : if (prep)
430 14138 : mesh_clone->prepare_for_use();
431 : // If the mesh thinks it's somewhat prepared, test each way it
432 : // thinks so.
433 : else
434 : {
435 0 : if (prep.has_synched_id_counts)
436 0 : mesh_clone->update_parallel_id_counts();
437 :
438 0 : if (prep.has_neighbor_ptrs)
439 0 : mesh_clone->find_neighbors();
440 :
441 0 : if (prep.has_cached_elem_data)
442 0 : mesh_clone->cache_elem_data();
443 :
444 0 : if (mesh.allow_detect_interior_parents() &&
445 0 : prep.has_interior_parent_ptrs)
446 0 : mesh_clone->detect_interior_parents();
447 :
448 0 : if (old_allow_remote_element_removal &&
449 0 : prep.has_removed_remote_elements)
450 0 : mesh_clone->delete_remote_elements();
451 :
452 0 : if (prep.has_removed_orphaned_nodes)
453 0 : mesh_clone->remove_orphaned_nodes();
454 :
455 0 : if (prep.has_boundary_id_sets)
456 0 : mesh_clone->get_boundary_info().regenerate_id_sets();
457 :
458 : // I don't know how we'll tell if this changes anything, but
459 : // we'll do it for completeness
460 0 : if (prep.has_reinit_ghosting_functors)
461 0 : mesh_clone->reinit_ghosting_functors();
462 : }
463 :
464 : // Restore original flag values
465 12416 : mesh_clone->allow_renumbering(old_allow_renumbering);
466 12416 : mesh_clone->allow_remote_element_removal(old_allow_remote_element_removal);
467 12416 : mesh_clone->skip_partitioning(old_skip_partitioning);
468 :
469 26554 : return mesh_clone;
470 0 : }
471 :
472 :
473 : }
474 :
475 :
476 : namespace libMesh
477 : {
478 :
479 : // ------------------------------------------------------------
480 : // MeshTools functions
481 :
482 : namespace MeshTools
483 : {
484 :
485 0 : dof_id_type total_weight(const MeshBase & mesh)
486 : {
487 0 : if (!mesh.is_serial())
488 : {
489 0 : libmesh_parallel_only(mesh.comm());
490 0 : dof_id_type weight = MeshTools::weight (mesh, mesh.processor_id());
491 0 : mesh.comm().sum(weight);
492 : dof_id_type unpartitioned_weight =
493 0 : MeshTools::weight (mesh, DofObject::invalid_processor_id);
494 0 : return weight + unpartitioned_weight;
495 : }
496 :
497 0 : SumElemWeight sew;
498 :
499 0 : Threads::parallel_reduce (ConstElemRange (mesh.elements_begin(),
500 0 : mesh.elements_end()),
501 : sew);
502 0 : return sew.weight();
503 :
504 : }
505 :
506 :
507 :
508 0 : dof_id_type weight(const MeshBase & mesh, const processor_id_type pid)
509 : {
510 0 : SumElemWeight sew;
511 :
512 0 : Threads::parallel_reduce (ConstElemRange (mesh.pid_elements_begin(pid),
513 0 : mesh.pid_elements_end(pid)),
514 : sew);
515 0 : return sew.weight();
516 : }
517 :
518 :
519 :
520 6289 : void build_nodes_to_elem_map (const MeshBase & mesh,
521 : std::vector<std::vector<dof_id_type>> & nodes_to_elem_map)
522 : {
523 : // A vector indexed over all nodes is too inefficient to use for a
524 : // distributed mesh. Use the unordered_map API instead.
525 6289 : if (!mesh.is_serial())
526 : libmesh_deprecated();
527 :
528 6289 : nodes_to_elem_map.resize (mesh.max_node_id());
529 :
530 159220 : for (const auto & elem : mesh.element_ptr_range())
531 308664 : for (auto & node : elem->node_ref_range())
532 : {
533 12504 : libmesh_assert_less (node.id(), nodes_to_elem_map.size());
534 12504 : libmesh_assert_less (elem->id(), mesh.n_elem());
535 :
536 236268 : nodes_to_elem_map[node.id()].push_back(elem->id());
537 5773 : }
538 6289 : }
539 :
540 :
541 :
542 0 : void build_nodes_to_elem_map (const MeshBase & mesh,
543 : std::vector<std::vector<const Elem *>> & nodes_to_elem_map)
544 : {
545 : // A vector indexed over all nodes is too inefficient to use for a
546 : // distributed mesh. Use the unordered_map API instead.
547 0 : if (!mesh.is_serial())
548 : libmesh_deprecated();
549 :
550 0 : nodes_to_elem_map.resize (mesh.max_node_id());
551 :
552 0 : for (const auto & elem : mesh.element_ptr_range())
553 0 : for (auto & node : elem->node_ref_range())
554 : {
555 0 : libmesh_assert_less (node.id(), nodes_to_elem_map.size());
556 :
557 0 : nodes_to_elem_map[node.id()].push_back(elem);
558 0 : }
559 0 : }
560 :
561 :
562 :
563 256003 : void build_nodes_to_elem_map (const MeshBase & mesh,
564 : std::unordered_map<dof_id_type, std::vector<dof_id_type>> & nodes_to_elem_map)
565 : {
566 7584 : nodes_to_elem_map.clear();
567 :
568 62694564 : for (const auto & elem : mesh.element_ptr_range())
569 190598307 : for (auto & node : elem->node_ref_range())
570 156500029 : nodes_to_elem_map[node.id()].push_back(elem->id());
571 256003 : }
572 :
573 :
574 :
575 5823 : void build_nodes_to_elem_map (const MeshBase & mesh,
576 : std::unordered_map<dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map)
577 : {
578 164 : nodes_to_elem_map.clear();
579 :
580 769248 : for (const auto & elem : mesh.element_ptr_range())
581 2735901 : for (auto & node : elem->node_ref_range())
582 2328997 : nodes_to_elem_map[node.id()].push_back(elem);
583 5823 : }
584 :
585 :
586 :
587 : std::unordered_set<dof_id_type>
588 6119 : find_boundary_nodes(const MeshBase & mesh)
589 : {
590 178 : std::unordered_set<dof_id_type> boundary_nodes;
591 :
592 : // Loop over elements, find those on boundary, and
593 : // mark them as true in on_boundary.
594 1088800 : for (const auto & elem : mesh.active_element_ptr_range())
595 3109744 : for (auto s : elem->side_index_range())
596 2636242 : if (elem->neighbor_ptr(s) == nullptr) // on the boundary
597 : {
598 143276 : auto nodes_on_side = elem->nodes_on_side(s);
599 :
600 742200 : for (auto & local_id : nodes_on_side)
601 634704 : boundary_nodes.insert(elem->node_ptr(local_id)->id());
602 5763 : }
603 :
604 6119 : return boundary_nodes;
605 : }
606 :
607 : std::unordered_set<dof_id_type>
608 3137 : find_block_boundary_nodes(const MeshBase & mesh)
609 : {
610 94 : std::unordered_set<dof_id_type> block_boundary_nodes;
611 :
612 : // Loop over elements, find those on boundary, and
613 : // mark them as true in on_boundary.
614 864512 : for (const auto & elem : mesh.active_element_ptr_range())
615 2469648 : for (auto s : elem->side_index_range())
616 2090882 : if (elem->neighbor_ptr(s) && (elem->neighbor_ptr(s)->subdomain_id() != elem->subdomain_id()))
617 : {
618 0 : auto nodes_on_side = elem->nodes_on_side(s);
619 :
620 0 : for (auto & local_id : nodes_on_side)
621 0 : block_boundary_nodes.insert(elem->node_ptr(local_id)->id());
622 2949 : }
623 :
624 3137 : return block_boundary_nodes;
625 : }
626 :
627 :
628 :
629 : libMesh::BoundingBox
630 1300299 : create_bounding_box (const MeshBase & mesh)
631 : {
632 : // This function must be run on all processors at once
633 18128 : libmesh_parallel_only(mesh.comm());
634 :
635 18128 : FindBBox find_bbox;
636 :
637 : // Start with any unpartitioned elements we know about locally
638 2600598 : Threads::parallel_reduce (ConstElemRange (mesh.pid_elements_begin(DofObject::invalid_processor_id),
639 1318427 : mesh.pid_elements_end(DofObject::invalid_processor_id)),
640 : find_bbox);
641 :
642 : // And combine with our local elements
643 1300299 : find_bbox.bbox().union_with(create_local_bounding_box(mesh));
644 :
645 : // Compare the bounding boxes across processors
646 1300299 : mesh.comm().min(find_bbox.min());
647 1300299 : mesh.comm().max(find_bbox.max());
648 :
649 1300299 : return find_bbox.bbox();
650 : }
651 :
652 :
653 :
654 : libMesh::BoundingBox
655 19630 : create_nodal_bounding_box (const MeshBase & mesh)
656 : {
657 : // This function must be run on all processors at once
658 566 : libmesh_parallel_only(mesh.comm());
659 :
660 566 : FindBBox find_bbox;
661 :
662 : // Start with any unpartitioned nodes we know about locally
663 39260 : Threads::parallel_reduce (ConstNodeRange (mesh.pid_nodes_begin(DofObject::invalid_processor_id),
664 39260 : mesh.pid_nodes_end(DofObject::invalid_processor_id)),
665 : find_bbox);
666 :
667 : // Add our local nodes
668 39260 : Threads::parallel_reduce (ConstNodeRange (mesh.local_nodes_begin(),
669 38694 : mesh.local_nodes_end()),
670 : find_bbox);
671 :
672 : // Compare the bounding boxes across processors
673 19630 : mesh.comm().min(find_bbox.min());
674 19630 : mesh.comm().max(find_bbox.max());
675 :
676 19630 : return find_bbox.bbox();
677 : }
678 :
679 :
680 :
681 : Sphere
682 0 : bounding_sphere(const MeshBase & mesh)
683 : {
684 0 : libMesh::BoundingBox bbox = create_bounding_box(mesh);
685 :
686 0 : const Real diag = (bbox.second - bbox.first).norm();
687 0 : const Point cent = (bbox.second + bbox.first)/2;
688 :
689 0 : return Sphere (cent, .5*diag);
690 : }
691 :
692 :
693 :
694 : libMesh::BoundingBox
695 1300299 : create_local_bounding_box (const MeshBase & mesh)
696 : {
697 18128 : FindBBox find_bbox;
698 :
699 2600598 : Threads::parallel_reduce (ConstElemRange (mesh.local_elements_begin(),
700 1318427 : mesh.local_elements_end()),
701 : find_bbox);
702 :
703 1300299 : return find_bbox.bbox();
704 : }
705 :
706 :
707 :
708 : libMesh::BoundingBox
709 0 : create_processor_bounding_box (const MeshBase & mesh,
710 : const processor_id_type pid)
711 : {
712 : // This can only be run in parallel, with consistent arguments.
713 0 : libmesh_parallel_only(mesh.comm());
714 0 : libmesh_assert(mesh.comm().verify(pid));
715 :
716 0 : libmesh_assert_less (pid, mesh.n_processors());
717 :
718 0 : FindBBox find_bbox;
719 :
720 0 : Threads::parallel_reduce (ConstElemRange (mesh.pid_elements_begin(pid),
721 0 : mesh.pid_elements_end(pid)),
722 : find_bbox);
723 :
724 : // Compare the bounding boxes across processors
725 0 : mesh.comm().min(find_bbox.min());
726 0 : mesh.comm().max(find_bbox.max());
727 :
728 0 : return find_bbox.bbox();
729 : }
730 :
731 :
732 :
733 : Sphere
734 0 : processor_bounding_sphere (const MeshBase & mesh,
735 : const processor_id_type pid)
736 : {
737 : libMesh::BoundingBox bbox =
738 0 : create_processor_bounding_box(mesh, pid);
739 :
740 0 : const Real diag = (bbox.second - bbox.first).norm();
741 0 : const Point cent = (bbox.second + bbox.first)/2;
742 :
743 0 : return Sphere (cent, .5*diag);
744 : }
745 :
746 :
747 :
748 : libMesh::BoundingBox
749 0 : create_subdomain_bounding_box (const MeshBase & mesh,
750 : const subdomain_id_type sid)
751 : {
752 : // This can only be run in parallel, with consistent arguments.
753 0 : libmesh_parallel_only(mesh.comm());
754 0 : libmesh_assert(mesh.comm().verify(sid));
755 :
756 0 : FindBBox find_bbox;
757 :
758 : Threads::parallel_reduce
759 0 : (ConstElemRange (mesh.active_local_subdomain_elements_begin(sid),
760 0 : mesh.active_local_subdomain_elements_end(sid)),
761 : find_bbox);
762 :
763 : // Compare the bounding boxes across processors
764 0 : mesh.comm().min(find_bbox.min());
765 0 : mesh.comm().max(find_bbox.max());
766 :
767 0 : return find_bbox.bbox();
768 : }
769 :
770 :
771 :
772 : Sphere
773 0 : subdomain_bounding_sphere (const MeshBase & mesh,
774 : const subdomain_id_type sid)
775 : {
776 : libMesh::BoundingBox bbox =
777 0 : create_subdomain_bounding_box(mesh, sid);
778 :
779 0 : const Real diag = (bbox.second - bbox.first).norm();
780 0 : const Point cent = (bbox.second + bbox.first)/2;
781 :
782 0 : return Sphere (cent, .5*diag);
783 : }
784 :
785 :
786 :
787 0 : void elem_types (const MeshBase & mesh,
788 : std::vector<ElemType> & et)
789 : {
790 : // Loop over the the elements. If the current element type isn't in
791 : // the vector, insert it.
792 0 : for (const auto & elem : mesh.element_ptr_range())
793 0 : if (!std::count(et.begin(), et.end(), elem->type()))
794 0 : et.push_back(elem->type());
795 0 : }
796 :
797 :
798 :
799 0 : dof_id_type n_elem_of_type (const MeshBase & mesh,
800 : const ElemType type)
801 : {
802 0 : return static_cast<dof_id_type>(std::distance(mesh.type_elements_begin(type),
803 0 : mesh.type_elements_end (type)));
804 : }
805 :
806 :
807 :
808 0 : dof_id_type n_active_elem_of_type (const MeshBase & mesh,
809 : const ElemType type)
810 : {
811 0 : return static_cast<dof_id_type>(std::distance(mesh.active_type_elements_begin(type),
812 0 : mesh.active_type_elements_end (type)));
813 : }
814 :
815 0 : dof_id_type n_non_subactive_elem_of_type_at_level(const MeshBase & mesh,
816 : const ElemType type,
817 : const unsigned int level)
818 : {
819 0 : dof_id_type cnt = 0;
820 :
821 : // iterate over the elements of the specified type
822 0 : for (const auto & elem : as_range(mesh.type_elements_begin(type),
823 0 : mesh.type_elements_end(type)))
824 0 : if ((elem->level() == level) && !elem->subactive())
825 0 : cnt++;
826 :
827 0 : return cnt;
828 : }
829 :
830 :
831 5471 : unsigned int n_active_local_levels(const MeshBase & mesh)
832 : {
833 : struct LevelCounter {
834 : unsigned int nl;
835 :
836 5471 : LevelCounter () : nl(0) {}
837 :
838 0 : LevelCounter (LevelCounter &, Threads::split) :
839 0 : nl(0) {}
840 :
841 5471 : void operator()(const ConstElemRange & range) {
842 70636 : for (const Elem * elem : range)
843 69004 : nl = std::max(elem->level() + 1, nl);
844 5471 : }
845 :
846 0 : void join(const LevelCounter & other) {
847 0 : nl = std::max(nl, other.nl);
848 0 : }
849 : };
850 :
851 156 : LevelCounter counter;
852 :
853 5471 : Threads::parallel_reduce(mesh.active_local_element_stored_range(), counter);
854 :
855 5471 : return counter.nl;
856 : }
857 :
858 :
859 :
860 5471 : unsigned int n_active_levels(const MeshBase & mesh)
861 : {
862 156 : libmesh_parallel_only(mesh.comm());
863 :
864 5471 : unsigned int nl = n_active_local_levels(mesh);
865 :
866 10786 : for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
867 21572 : mesh.unpartitioned_elements_end()))
868 0 : if (elem->active())
869 5159 : nl = std::max(elem->level() + 1, nl);
870 :
871 5471 : mesh.comm().max(nl);
872 5471 : return nl;
873 : }
874 :
875 :
876 :
877 1889446 : unsigned int n_local_levels(const MeshBase & mesh)
878 : {
879 1889446 : unsigned int nl = 0;
880 :
881 6081360 : for (const auto & elem : as_range(mesh.local_elements_begin(),
882 41222961 : mesh.local_elements_end()))
883 37727250 : nl = std::max(elem->level() + 1, nl);
884 :
885 1889446 : return nl;
886 : }
887 :
888 :
889 :
890 1889446 : unsigned int n_levels(const MeshBase & mesh)
891 : {
892 43300 : libmesh_parallel_only(mesh.comm());
893 :
894 1889446 : unsigned int nl = n_local_levels(mesh);
895 :
896 4120560 : for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
897 21533345 : mesh.unpartitioned_elements_end()))
898 15877541 : nl = std::max(elem->level() + 1, nl);
899 :
900 1889446 : mesh.comm().max(nl);
901 :
902 : // n_levels() is only valid and should only be called in cases where
903 : // the mesh is validly distributed (or serialized). Let's run an
904 : // expensive test in debug mode to make sure this is such a case.
905 : #ifdef DEBUG
906 43300 : const unsigned int paranoid_nl = paranoid_n_levels(mesh);
907 43300 : libmesh_assert_equal_to(nl, paranoid_nl);
908 : #endif
909 1889446 : return nl;
910 : }
911 :
912 :
913 :
914 57479 : unsigned int paranoid_n_levels(const MeshBase & mesh)
915 : {
916 43716 : libmesh_parallel_only(mesh.comm());
917 :
918 57479 : unsigned int nl = 0;
919 5565130 : for (const auto & elem : mesh.element_ptr_range())
920 5507235 : nl = std::max(elem->level() + 1, nl);
921 :
922 57479 : mesh.comm().max(nl);
923 57479 : return nl;
924 : }
925 :
926 :
927 :
928 639 : dof_id_type n_connected_components(const MeshBase & mesh,
929 : Real constraint_tol)
930 : {
931 36 : LOG_SCOPE("n_connected_components()", "MeshTools");
932 :
933 : // Yes, I'm being lazy. This is for mesh analysis before a
934 : // simulation, not anything going in any loops.
935 639 : if (!mesh.is_serial_on_zero())
936 0 : libmesh_not_implemented();
937 :
938 639 : dof_id_type n_components = 0;
939 :
940 657 : if (mesh.processor_id())
941 : {
942 522 : mesh.comm().broadcast(n_components);
943 531 : return n_components;
944 : }
945 :
946 : // All nodes in a set here are connected (at least indirectly) to
947 : // all other nodes in the same set, but have not yet been discovered
948 : // to be connected to nodes in other sets.
949 : //
950 : // Using an unordered_set of ids rather than a set of pointers seems
951 : // to be roughly 150% faster?
952 : // typedef const Node * node_entry_type
953 : typedef dof_id_type node_entry_type;
954 9 : std::vector<std::unordered_set<node_entry_type>> components;
955 :
956 : // With a typical mesh with few components and somewhat-contiguous
957 : // ordering, vector performance should be fine. With a mesh with
958 : // many components or completely scrambled ordering, performance
959 : // can be a disaster.
960 2742 : auto find_component = [&components](node_entry_type n) {
961 6158 : for (auto & c: components)
962 4064 : if (c.find(n) != c.end())
963 54 : return &c;
964 :
965 162 : return (std::unordered_set<node_entry_type> *)(nullptr);
966 108 : };
967 :
968 : auto add_to_component =
969 2010 : [&find_component]
970 2202 : (std::unordered_set<node_entry_type> & component, node_entry_type n) {
971 : // We may already be in the desired component
972 2412 : if (component.find(n) != component.end())
973 51 : return;
974 :
975 1950 : auto current_component = find_component(n);
976 :
977 : // Didn't we *just* check this?
978 150 : libmesh_assert (&component != current_component);
979 :
980 : // If we're unknown, we should be in the desired component
981 1950 : if (!current_component)
982 147 : component.insert(n);
983 :
984 : // If we think we're in another component, it should actually be
985 : // part of the desired component
986 : else
987 : {
988 : // Merge the component likely to be smaller into the one
989 : // likely to be larger - this is orders of magnitude faster
990 : // than the other way around!
991 3 : current_component->merge(component);
992 3 : current_component->swap(component);
993 3 : libmesh_assert(current_component->empty());
994 : }
995 108 : };
996 :
997 9 : auto & constraint_rows = mesh.get_constraint_rows();
998 :
999 1659 : for (const auto & elem : mesh.element_ptr_range())
1000 : {
1001 : // const node_entry_type first_node = elem->node_ptr(0);
1002 792 : const node_entry_type first_node = elem->node_id(0);
1003 :
1004 792 : auto component = find_component(first_node);
1005 :
1006 : // If we didn't find one, make a new one, reusing an existing
1007 : // slot if possible or growing our vector if necessary
1008 792 : if (!component)
1009 684 : for (auto & c: components)
1010 354 : if (c.empty())
1011 0 : component = &c;
1012 :
1013 432 : if (!component)
1014 219 : component = &components.emplace_back();
1015 :
1016 3234 : for (const Node & node : elem->node_ref_range())
1017 : {
1018 : // const node_entry_type n = &node;
1019 396 : const node_entry_type n = node.id();
1020 2376 : add_to_component(*component, n);
1021 :
1022 2376 : auto it = constraint_rows.find(&node);
1023 2376 : if (it == constraint_rows.end())
1024 195 : continue;
1025 :
1026 72 : for (const auto & [pr, val] : it->second)
1027 : {
1028 : // Ignore too-trivial constraint coefficients if
1029 : // we get a non-default-0 constraint_tol
1030 39 : if (std::abs(val) < constraint_tol)
1031 0 : continue;
1032 :
1033 36 : const Elem * spline_elem = pr.first;
1034 3 : libmesh_assert(spline_elem == mesh.elem_ptr(spline_elem->id()));
1035 :
1036 : const Node * spline_node =
1037 36 : spline_elem->node_ptr(pr.second);
1038 :
1039 : // add_to_component(*component, spline_node);
1040 36 : add_to_component(*component, spline_node->id());
1041 : }
1042 : }
1043 90 : }
1044 :
1045 327 : for (auto & component : components)
1046 219 : if (!component.empty())
1047 144 : ++n_components;
1048 :
1049 : // We calculated this on proc 0; now let everyone else know too
1050 108 : mesh.comm().broadcast(n_components);
1051 :
1052 108 : return n_components;
1053 90 : }
1054 :
1055 :
1056 :
1057 0 : void get_not_subactive_node_ids(const MeshBase & mesh,
1058 : std::set<dof_id_type> & not_subactive_node_ids)
1059 : {
1060 0 : for (const auto & elem : mesh.element_ptr_range())
1061 0 : if (!elem->subactive())
1062 0 : for (auto & n : elem->node_ref_range())
1063 0 : not_subactive_node_ids.insert(n.id());
1064 0 : }
1065 :
1066 :
1067 :
1068 547143 : dof_id_type n_elem (const MeshBase::const_element_iterator & begin,
1069 : const MeshBase::const_element_iterator & end)
1070 : {
1071 1072472 : return cast_int<dof_id_type>(std::distance(begin, end));
1072 : }
1073 :
1074 :
1075 :
1076 0 : dof_id_type n_nodes (const MeshBase::const_node_iterator & begin,
1077 : const MeshBase::const_node_iterator & end)
1078 : {
1079 0 : return cast_int<dof_id_type>(std::distance(begin, end));
1080 : }
1081 :
1082 :
1083 :
1084 4050 : Real volume (const MeshBase & mesh,
1085 : unsigned int dim)
1086 : {
1087 114 : libmesh_parallel_only(mesh.comm());
1088 :
1089 4050 : if (dim == libMesh::invalid_uint)
1090 4050 : dim = mesh.mesh_dimension();
1091 :
1092 4050 : Real vol = 0;
1093 :
1094 : // first my local elements
1095 9733 : for (const auto & elem : as_range(mesh.local_elements_begin(),
1096 56215 : mesh.local_elements_end()))
1097 20995 : if (elem->dim() == dim)
1098 24817 : vol += elem->volume();
1099 :
1100 : // then count any unpartitioned objects, once
1101 4164 : if (mesh.processor_id() == 0)
1102 1317 : for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
1103 2634 : mesh.unpartitioned_elements_end()))
1104 0 : if (elem->dim() == dim)
1105 573 : vol += elem->volume();
1106 :
1107 4050 : mesh.comm().sum(vol);
1108 4050 : return vol;
1109 : }
1110 :
1111 :
1112 :
1113 5471 : unsigned int n_p_levels (const MeshBase & mesh)
1114 : {
1115 156 : libmesh_parallel_only(mesh.comm());
1116 :
1117 5471 : unsigned int max_p_level = 0;
1118 :
1119 : // first my local elements
1120 17480 : for (const auto & elem : as_range(mesh.local_elements_begin(),
1121 93378 : mesh.local_elements_end()))
1122 83659 : max_p_level = std::max(elem->p_level(), max_p_level);
1123 :
1124 : // then any unpartitioned objects
1125 10786 : for (const auto & elem : as_range(mesh.unpartitioned_elements_begin(),
1126 21572 : mesh.unpartitioned_elements_end()))
1127 5159 : max_p_level = std::max(elem->p_level(), max_p_level);
1128 :
1129 5471 : mesh.comm().max(max_p_level);
1130 5471 : return max_p_level + 1;
1131 : }
1132 :
1133 :
1134 :
1135 0 : void find_nodal_neighbors(const MeshBase &,
1136 : const Node & node,
1137 : const std::vector<std::vector<const Elem *>> & nodes_to_elem_map,
1138 : std::vector<const Node *> & neighbors)
1139 : {
1140 0 : find_nodal_neighbors_helper(node.id(), nodes_to_elem_map[node.id()],
1141 : neighbors);
1142 0 : }
1143 :
1144 :
1145 :
1146 225994 : void find_nodal_neighbors(const MeshBase &,
1147 : const Node & node,
1148 : const std::unordered_map<dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map,
1149 : std::vector<const Node *> & neighbors)
1150 : {
1151 : const std::vector<const Elem *> node_to_elem_vec =
1152 234012 : libmesh_map_find(nodes_to_elem_map, node.id());
1153 225994 : find_nodal_neighbors_helper(node.id(), node_to_elem_vec, neighbors);
1154 225994 : }
1155 :
1156 49559 : void find_nodal_or_face_neighbors(
1157 : const MeshBase & mesh,
1158 : const Node & node,
1159 : const std::unordered_map<dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map,
1160 : std::vector<const Node *> & neighbors)
1161 : {
1162 : // Find all the nodal neighbors... that is the nodes directly connected
1163 : // to this node through one edge.
1164 49559 : find_nodal_neighbors(mesh, node, nodes_to_elem_map, neighbors);
1165 :
1166 : // If no neighbors are found, use all nodes on the containing side as
1167 : // neighbors.
1168 49559 : if (!neighbors.size())
1169 : {
1170 : // Grab the element containing node
1171 6804 : const auto * elem = libmesh_map_find(nodes_to_elem_map, node.id()).front();
1172 : // Find the element side containing node
1173 17503 : for (const auto &side : elem->side_index_range())
1174 : {
1175 17503 : const auto &nodes_on_side = elem->nodes_on_side(side);
1176 : const auto it =
1177 23074 : std::find_if(nodes_on_side.begin(), nodes_on_side.end(), [&](auto local_node_id) {
1178 64593 : return elem->node_id(local_node_id) == node.id();
1179 1946 : });
1180 :
1181 18476 : if (it != nodes_on_side.end())
1182 : {
1183 58008 : for (const auto &local_node_id : nodes_on_side)
1184 : // No need to add node itself as a neighbor
1185 53931 : if (const auto *node_ptr = elem->node_ptr(local_node_id);
1186 2727 : *node_ptr != node)
1187 44400 : neighbors.push_back(node_ptr);
1188 347 : break;
1189 : }
1190 : }
1191 : }
1192 3048 : libmesh_assert(neighbors.size());
1193 49559 : }
1194 :
1195 :
1196 :
1197 0 : void find_hanging_nodes_and_parents(const MeshBase & mesh,
1198 : std::map<dof_id_type, std::vector<dof_id_type>> & hanging_nodes)
1199 : {
1200 : // Loop through all the elements
1201 0 : for (auto & elem : mesh.active_local_element_ptr_range())
1202 0 : if (elem->type() == QUAD4)
1203 0 : for (auto s : elem->side_index_range())
1204 : {
1205 : // Loop over the sides looking for sides that have hanging nodes
1206 : // This code is inspired by compute_proj_constraints()
1207 0 : const Elem * neigh = elem->neighbor_ptr(s);
1208 :
1209 : // If not a boundary side
1210 0 : if (neigh != nullptr)
1211 : {
1212 : // Is there a coarser element next to this one?
1213 0 : if (neigh->level() < elem->level())
1214 : {
1215 0 : const Elem * ancestor = elem;
1216 0 : while (neigh->level() < ancestor->level())
1217 0 : ancestor = ancestor->parent();
1218 0 : unsigned int s_neigh = neigh->which_neighbor_am_i(ancestor);
1219 0 : libmesh_assert_less (s_neigh, neigh->n_neighbors());
1220 :
1221 : // Couple of helper uints...
1222 0 : unsigned int local_node1=0;
1223 0 : unsigned int local_node2=0;
1224 :
1225 0 : bool found_in_neighbor = false;
1226 :
1227 : // Find the two vertices that make up this side
1228 0 : while (!elem->is_node_on_side(local_node1++,s)) { }
1229 0 : local_node1--;
1230 :
1231 : // Start looking for the second one with the next node
1232 0 : local_node2=local_node1+1;
1233 :
1234 : // Find the other one
1235 0 : while (!elem->is_node_on_side(local_node2++,s)) { }
1236 0 : local_node2--;
1237 :
1238 : //Pull out their global ids:
1239 0 : dof_id_type node1 = elem->node_id(local_node1);
1240 0 : dof_id_type node2 = elem->node_id(local_node2);
1241 :
1242 : // Now find which node is present in the neighbor
1243 : // FIXME This assumes a level one rule!
1244 : // The _other_ one is the hanging node
1245 :
1246 : // First look for the first one
1247 : // FIXME could be streamlined a bit
1248 0 : for (unsigned int n=0;n<neigh->n_sides();n++)
1249 0 : if (neigh->node_id(n) == node1)
1250 0 : found_in_neighbor=true;
1251 :
1252 0 : dof_id_type hanging_node=0;
1253 :
1254 0 : if (!found_in_neighbor)
1255 0 : hanging_node=node1;
1256 : else // If it wasn't node1 then it must be node2!
1257 0 : hanging_node=node2;
1258 :
1259 : // Reset for reuse
1260 0 : local_node1=0;
1261 :
1262 : // Find the first node that makes up the side in the neighbor (these should be the parent nodes)
1263 0 : while (!neigh->is_node_on_side(local_node1++,s_neigh)) { }
1264 0 : local_node1--;
1265 :
1266 0 : local_node2=local_node1+1;
1267 :
1268 : // Find the second node...
1269 0 : while (!neigh->is_node_on_side(local_node2++,s_neigh)) { }
1270 0 : local_node2--;
1271 :
1272 : // Save them if we haven't already found the parents for this one
1273 0 : if (hanging_nodes[hanging_node].size()<2)
1274 : {
1275 0 : hanging_nodes[hanging_node].push_back(neigh->node_id(local_node1));
1276 0 : hanging_nodes[hanging_node].push_back(neigh->node_id(local_node2));
1277 : }
1278 : }
1279 : }
1280 0 : }
1281 0 : }
1282 :
1283 :
1284 :
1285 36 : void clear_spline_nodes(MeshBase & mesh)
1286 : {
1287 6 : std::vector<Elem *> nodeelem_to_delete;
1288 :
1289 8737 : for (auto & elem : mesh.element_ptr_range())
1290 5073 : if (elem->type() == NODEELEM &&
1291 4140 : elem->mapping_type() == RATIONAL_BERNSTEIN_MAP)
1292 4170 : nodeelem_to_delete.push_back(elem);
1293 :
1294 3 : auto & constraint_rows = mesh.get_constraint_rows();
1295 :
1296 : // All our constraint_rows ought to be for spline constraints we're
1297 : // about to get rid of.
1298 : #ifndef NDEBUG
1299 762 : for (auto & node_row : constraint_rows)
1300 2064 : for (auto pr : node_row.second)
1301 : {
1302 1305 : const Elem * elem = pr.first.first;
1303 1305 : libmesh_assert(elem->type() == NODEELEM);
1304 1305 : libmesh_assert(elem->mapping_type() == RATIONAL_BERNSTEIN_MAP);
1305 : }
1306 : #endif
1307 :
1308 3 : constraint_rows.clear();
1309 :
1310 4176 : for (Elem * elem : nodeelem_to_delete)
1311 : {
1312 690 : Node * node = elem->node_ptr(0);
1313 4140 : mesh.delete_elem(elem);
1314 4140 : mesh.delete_node(node);
1315 : }
1316 36 : }
1317 :
1318 :
1319 :
1320 1772 : bool valid_is_prepared (const MeshBase & mesh)
1321 : {
1322 100 : LOG_SCOPE("valid_is_prepared()", "MeshTools");
1323 :
1324 1772 : const MeshBase::Preparation prep = mesh.preparation();
1325 :
1326 : // If the mesh doesn't think *anything* has been prepared, we have
1327 : // nothing to check.
1328 1772 : if (prep == MeshBase::Preparation())
1329 0 : return true;
1330 :
1331 : // If the mesh thinks it's partitioned, check. These are counts,
1332 : // not caches, so it's a real check.
1333 1772 : if (prep.is_partitioned)
1334 3544 : if (mesh.n_unpartitioned_elem() ||
1335 50 : mesh.n_unpartitioned_nodes())
1336 0 : return false;
1337 :
1338 : // If the mesh thinks it's prepared in some way, *re*-preparing in
1339 : // that way shouldn't change a clone of it, as long as we disallow
1340 : // repartitioning or renumbering or remote element removal.
1341 1822 : std::unique_ptr<MeshBase> mesh_clone = reprepared_mesh_clone(mesh);
1342 :
1343 : // Check whether the original and clone compare equal
1344 1772 : return (mesh == *mesh_clone);
1345 1672 : }
1346 :
1347 :
1348 :
1349 : #ifndef NDEBUG
1350 :
1351 :
1352 12368 : void libmesh_assert_valid_is_prepared (const MeshBase & mesh)
1353 : {
1354 12368 : LOG_SCOPE("libmesh_assert_valid_is_prepared()", "MeshTools");
1355 :
1356 12368 : const MeshBase::Preparation prep = mesh.preparation();
1357 :
1358 : // If the mesh doesn't think *anything* has been prepared, we have
1359 : // nothing to check.
1360 12368 : if (prep == MeshBase::Preparation())
1361 2 : return;
1362 :
1363 : // If the mesh thinks it's partitioned, check. These are counts,
1364 : // not caches, so it's a real check.
1365 12366 : if (prep.is_partitioned)
1366 12366 : libmesh_assert_msg
1367 : (!mesh.n_unpartitioned_elem() && !mesh.n_unpartitioned_nodes(),
1368 : "Mesh preparation().is_partitioned does not reflect mesh data.");
1369 :
1370 : // If the mesh thinks it's prepared in some way, *re*-preparing in
1371 : // that way shouldn't change a clone of it, as long as we disallow
1372 : // repartitioning or renumbering or remote element removal.
1373 12366 : std::unique_ptr<MeshBase> mesh_clone = reprepared_mesh_clone(mesh);
1374 :
1375 : mesh.assert_equal_to
1376 12366 : (*mesh_clone,
1377 : "Mesh data does not match mesh preparation().\n"
1378 12366 : "Efficiently-prepared mesh != exhaustively-prepared clone.");
1379 : }
1380 :
1381 :
1382 :
1383 :
1384 :
1385 136 : void libmesh_assert_equal_n_systems (const MeshBase & mesh)
1386 : {
1387 272 : LOG_SCOPE("libmesh_assert_equal_n_systems()", "MeshTools");
1388 :
1389 136 : unsigned int n_sys = libMesh::invalid_uint;
1390 :
1391 12632 : for (const auto & elem : mesh.element_ptr_range())
1392 : {
1393 12496 : if (n_sys == libMesh::invalid_uint)
1394 136 : n_sys = elem->n_systems();
1395 : else
1396 12360 : libmesh_assert_equal_to (elem->n_systems(), n_sys);
1397 : }
1398 :
1399 31283 : for (const auto & node : mesh.node_ptr_range())
1400 : {
1401 31147 : if (n_sys == libMesh::invalid_uint)
1402 0 : n_sys = node->n_systems();
1403 : else
1404 31147 : libmesh_assert_equal_to (node->n_systems(), n_sys);
1405 : }
1406 136 : }
1407 :
1408 :
1409 :
1410 : #ifdef LIBMESH_ENABLE_AMR
1411 0 : void libmesh_assert_old_dof_objects (const MeshBase & mesh)
1412 : {
1413 0 : LOG_SCOPE("libmesh_assert_old_dof_objects()", "MeshTools");
1414 :
1415 0 : for (const auto & elem : mesh.element_ptr_range())
1416 : {
1417 0 : if (elem->refinement_flag() == Elem::JUST_REFINED ||
1418 0 : elem->refinement_flag() == Elem::INACTIVE)
1419 0 : continue;
1420 :
1421 0 : if (elem->has_dofs())
1422 0 : libmesh_assert(elem->get_old_dof_object());
1423 :
1424 0 : for (auto & node : elem->node_ref_range())
1425 0 : if (node.has_dofs())
1426 0 : libmesh_assert(node.get_old_dof_object());
1427 : }
1428 0 : }
1429 : #else
1430 : void libmesh_assert_old_dof_objects (const MeshBase &) {}
1431 : #endif // LIBMESH_ENABLE_AMR
1432 :
1433 :
1434 :
1435 0 : void libmesh_assert_valid_node_pointers(const MeshBase & mesh)
1436 : {
1437 0 : LOG_SCOPE("libmesh_assert_valid_node_pointers()", "MeshTools");
1438 :
1439 : // Here we specifically do not want "auto &" because we need to
1440 : // reseat the (temporary) pointer variable in the loop below,
1441 : // without modifying the original.
1442 0 : for (const Elem * elem : mesh.element_ptr_range())
1443 : {
1444 0 : libmesh_assert (elem);
1445 0 : while (elem)
1446 : {
1447 0 : elem->libmesh_assert_valid_node_pointers();
1448 0 : for (auto n : elem->neighbor_ptr_range())
1449 0 : if (n && n != remote_elem)
1450 0 : n->libmesh_assert_valid_node_pointers();
1451 :
1452 0 : libmesh_assert_not_equal_to (elem->parent(), remote_elem);
1453 0 : elem = elem->parent();
1454 : }
1455 : }
1456 0 : }
1457 :
1458 :
1459 :
1460 9580 : void libmesh_assert_valid_remote_elems(const MeshBase & mesh)
1461 : {
1462 19160 : LOG_SCOPE("libmesh_assert_valid_remote_elems()", "MeshTools");
1463 :
1464 667815 : for (const auto & elem : as_range(mesh.local_elements_begin(),
1465 1335630 : mesh.local_elements_end()))
1466 : {
1467 658235 : libmesh_assert (elem);
1468 :
1469 : // We currently don't allow active_local_elements to have
1470 : // remote_elem neighbors
1471 658235 : if (elem->active())
1472 2851080 : for (auto n : elem->neighbor_ptr_range())
1473 2293813 : libmesh_assert_not_equal_to (n, remote_elem);
1474 :
1475 : #ifdef LIBMESH_ENABLE_AMR
1476 658235 : const Elem * parent = elem->parent();
1477 658235 : if (parent)
1478 377230 : libmesh_assert_not_equal_to (parent, remote_elem);
1479 :
1480 : // We can only be strict about active elements' subactive
1481 : // children
1482 658235 : if (elem->active() && elem->has_children())
1483 30156 : for (auto & child : elem->child_ref_range())
1484 24144 : libmesh_assert_not_equal_to (&child, remote_elem);
1485 : #endif
1486 : }
1487 9580 : }
1488 :
1489 :
1490 :
1491 392 : void libmesh_assert_valid_elem_ids(const MeshBase & mesh)
1492 : {
1493 784 : LOG_SCOPE("libmesh_assert_valid_elem_ids()", "MeshTools");
1494 :
1495 392 : processor_id_type lastprocid = 0;
1496 392 : dof_id_type lastelemid = 0;
1497 :
1498 13631 : for (const auto & elem : mesh.active_element_ptr_range())
1499 : {
1500 13239 : libmesh_assert (elem);
1501 13239 : processor_id_type elemprocid = elem->processor_id();
1502 13239 : dof_id_type elemid = elem->id();
1503 :
1504 13239 : libmesh_assert_greater_equal (elemid, lastelemid);
1505 13239 : libmesh_assert_greater_equal (elemprocid, lastprocid);
1506 :
1507 13239 : lastelemid = elemid;
1508 13239 : lastprocid = elemprocid;
1509 : }
1510 392 : }
1511 :
1512 :
1513 :
1514 13204 : void libmesh_assert_valid_amr_elem_ids(const MeshBase & mesh)
1515 : {
1516 26408 : LOG_SCOPE("libmesh_assert_valid_amr_elem_ids()", "MeshTools");
1517 :
1518 1676408 : for (const auto & elem : mesh.element_ptr_range())
1519 : {
1520 1663204 : libmesh_assert (elem);
1521 :
1522 1663204 : const Elem * parent = elem->parent();
1523 :
1524 1663204 : if (parent)
1525 : {
1526 838354 : libmesh_assert_greater_equal (elem->id(), parent->id());
1527 838354 : libmesh_assert_greater_equal (elem->processor_id(), parent->processor_id());
1528 : }
1529 : }
1530 13204 : }
1531 :
1532 :
1533 :
1534 25796 : void libmesh_assert_valid_amr_interior_parents(const MeshBase & mesh)
1535 : {
1536 51592 : LOG_SCOPE("libmesh_assert_valid_amr_interior_parents()", "MeshTools");
1537 :
1538 2792707 : for (const auto & elem : mesh.element_ptr_range())
1539 : {
1540 2766911 : libmesh_assert (elem);
1541 :
1542 : // We can skip to the next element if we're full-dimension
1543 : // and therefore don't have any interior parents
1544 2766911 : if (elem->dim() >= LIBMESH_DIM)
1545 1202018 : continue;
1546 :
1547 1564893 : const Elem * ip = elem->interior_parent();
1548 :
1549 1564893 : const Elem * parent = elem->parent();
1550 :
1551 1564893 : if (ip && (ip != remote_elem) && parent)
1552 : {
1553 1952 : libmesh_assert_equal_to (ip->top_parent(),
1554 : elem->top_parent()->interior_parent());
1555 :
1556 1952 : if (ip->level() == elem->level())
1557 1952 : libmesh_assert_equal_to (ip->parent(),
1558 : parent->interior_parent());
1559 : else
1560 : {
1561 0 : libmesh_assert_less (ip->level(), elem->level());
1562 0 : libmesh_assert_equal_to (ip, parent->interior_parent());
1563 : }
1564 : }
1565 : }
1566 25796 : }
1567 :
1568 :
1569 :
1570 0 : void libmesh_assert_contiguous_dof_ids(const MeshBase & mesh, unsigned int sysnum)
1571 : {
1572 0 : LOG_SCOPE("libmesh_assert_contiguous_dof_ids()", "MeshTools");
1573 :
1574 0 : if (mesh.n_processors() == 1)
1575 0 : return;
1576 :
1577 0 : libmesh_parallel_only(mesh.comm());
1578 :
1579 0 : dof_id_type min_dof_id = std::numeric_limits<dof_id_type>::max(),
1580 0 : max_dof_id = std::numeric_limits<dof_id_type>::min();
1581 :
1582 : // Figure out what our local dof id range is
1583 0 : for (const auto * node : mesh.local_node_ptr_range())
1584 : {
1585 0 : for (auto v : make_range(node->n_vars(sysnum)))
1586 0 : for (auto c : make_range(node->n_comp(sysnum, v)))
1587 : {
1588 0 : dof_id_type id = node->dof_number(sysnum, v, c);
1589 0 : min_dof_id = std::min (min_dof_id, id);
1590 0 : max_dof_id = std::max (max_dof_id, id);
1591 : }
1592 : }
1593 :
1594 : // Make sure no other processors' ids are inside it
1595 0 : for (const auto * node : mesh.node_ptr_range())
1596 : {
1597 0 : if (node->processor_id() == mesh.processor_id())
1598 0 : continue;
1599 0 : for (auto v : make_range(node->n_vars(sysnum)))
1600 0 : for (auto c : make_range(node->n_comp(sysnum, v)))
1601 : {
1602 0 : dof_id_type id = node->dof_number(sysnum, v, c);
1603 0 : libmesh_assert (id < min_dof_id ||
1604 : id > max_dof_id);
1605 : }
1606 : }
1607 : }
1608 :
1609 :
1610 :
1611 : template <>
1612 19576 : void libmesh_assert_topology_consistent_procids<Elem>(const MeshBase & mesh)
1613 : {
1614 39152 : LOG_SCOPE("libmesh_assert_topology_consistent_procids()", "MeshTools");
1615 :
1616 : // This parameter is not used when !LIBMESH_ENABLE_AMR
1617 19576 : libmesh_ignore(mesh);
1618 :
1619 : // If we're adaptively refining, check processor ids for consistency
1620 : // between parents and children.
1621 : #ifdef LIBMESH_ENABLE_AMR
1622 :
1623 : // Ancestor elements we won't worry about, but subactive and active
1624 : // elements ought to have parents with consistent processor ids
1625 2733790 : for (const auto & elem : mesh.element_ptr_range())
1626 : {
1627 2714214 : libmesh_assert(elem);
1628 :
1629 2714214 : if (!elem->active() && !elem->subactive())
1630 306752 : continue;
1631 :
1632 2407462 : const Elem * parent = elem->parent();
1633 :
1634 2407462 : if (parent)
1635 : {
1636 1225116 : libmesh_assert(parent->has_children());
1637 1225116 : processor_id_type parent_procid = parent->processor_id();
1638 1225116 : bool matching_child_id = false;
1639 : // If we've got a remote_elem then we don't know whether
1640 : // it's responsible for the parent's processor id; all
1641 : // we can do is assume it is and let its processor fail
1642 : // an assert if there's something wrong.
1643 7551620 : for (auto & child : parent->child_ref_range())
1644 12650248 : if (&child == remote_elem ||
1645 6323744 : child.processor_id() == parent_procid)
1646 6156932 : matching_child_id = true;
1647 1225116 : libmesh_assert(matching_child_id);
1648 : }
1649 : }
1650 : #endif
1651 19576 : }
1652 :
1653 :
1654 :
1655 : template <>
1656 28044 : void libmesh_assert_topology_consistent_procids<Node>(const MeshBase & mesh)
1657 : {
1658 28044 : LOG_SCOPE("libmesh_assert_topology_consistent_procids()", "MeshTools");
1659 :
1660 28044 : if (mesh.n_processors() == 1)
1661 0 : return;
1662 :
1663 28044 : libmesh_parallel_only(mesh.comm());
1664 :
1665 : // We want this test to be valid even when called after nodes have
1666 : // been added asynchronously but before they're renumbered.
1667 : //
1668 : // Plus, some code (looking at you, stitch_meshes) modifies
1669 : // DofObject ids without keeping max_elem_id()/max_node_id()
1670 : // consistent, but that's done in a safe way for performance
1671 : // reasons, so we'll play along and just figure out new max ids
1672 : // ourselves.
1673 28044 : dof_id_type parallel_max_node_id = 0;
1674 7567583 : for (const auto & node : mesh.node_ptr_range())
1675 7539539 : parallel_max_node_id = std::max<dof_id_type>(parallel_max_node_id,
1676 7539539 : node->id()+1);
1677 28044 : mesh.comm().max(parallel_max_node_id);
1678 :
1679 :
1680 56088 : std::vector<bool> node_touched_by_me(parallel_max_node_id, false);
1681 :
1682 2188471 : for (const auto & elem : as_range(mesh.local_elements_begin(),
1683 4376942 : mesh.local_elements_end()))
1684 : {
1685 2160427 : libmesh_assert (elem);
1686 :
1687 16486694 : for (auto & node : elem->node_ref_range())
1688 : {
1689 14326267 : dof_id_type nodeid = node.id();
1690 14326267 : node_touched_by_me[nodeid] = true;
1691 : }
1692 : }
1693 56088 : std::vector<bool> node_touched_by_anyone(node_touched_by_me);
1694 28044 : mesh.comm().max(node_touched_by_anyone);
1695 :
1696 3785713 : for (const auto & node : mesh.local_node_ptr_range())
1697 : {
1698 3757669 : libmesh_assert(node);
1699 3757669 : dof_id_type nodeid = node->id();
1700 3757669 : libmesh_assert(!node_touched_by_anyone[nodeid] ||
1701 : node_touched_by_me[nodeid]);
1702 : }
1703 : }
1704 :
1705 :
1706 :
1707 0 : void libmesh_assert_canonical_node_procids (const MeshBase & mesh)
1708 : {
1709 0 : for (const auto & elem : mesh.active_element_ptr_range())
1710 0 : for (auto & node : elem->node_ref_range())
1711 0 : libmesh_assert_equal_to
1712 : (node.processor_id(),
1713 : node.choose_processor_id(node.processor_id(),
1714 : elem->processor_id()));
1715 0 : }
1716 :
1717 :
1718 :
1719 : #ifdef LIBMESH_ENABLE_AMR
1720 1282 : void libmesh_assert_valid_refinement_tree(const MeshBase & mesh)
1721 : {
1722 2564 : LOG_SCOPE("libmesh_assert_valid_refinement_tree()", "MeshTools");
1723 :
1724 67616 : for (const auto & elem : mesh.element_ptr_range())
1725 : {
1726 66334 : libmesh_assert(elem);
1727 66334 : if (elem->has_children())
1728 27268 : for (auto & child : elem->child_ref_range())
1729 23216 : if (&child != remote_elem)
1730 20800 : libmesh_assert_equal_to (child.parent(), elem);
1731 66334 : if (elem->active())
1732 : {
1733 62282 : libmesh_assert(!elem->ancestor());
1734 62282 : libmesh_assert(!elem->subactive());
1735 : }
1736 4052 : else if (elem->ancestor())
1737 : {
1738 4052 : libmesh_assert(!elem->subactive());
1739 : }
1740 : else
1741 0 : libmesh_assert(elem->subactive());
1742 :
1743 66334 : if (elem->p_refinement_flag() == Elem::JUST_REFINED)
1744 0 : libmesh_assert_greater(elem->p_level(), 0);
1745 : }
1746 1282 : }
1747 : #else
1748 : void libmesh_assert_valid_refinement_tree(const MeshBase &)
1749 : {
1750 : }
1751 : #endif // LIBMESH_ENABLE_AMR
1752 :
1753 : #endif // !NDEBUG
1754 :
1755 :
1756 :
1757 : #ifdef DEBUG
1758 :
1759 0 : void libmesh_assert_no_links_to_elem(const MeshBase & mesh,
1760 : const Elem * bad_elem)
1761 : {
1762 0 : for (const auto & elem : mesh.element_ptr_range())
1763 : {
1764 0 : libmesh_assert (elem);
1765 0 : libmesh_assert_not_equal_to (elem->parent(), bad_elem);
1766 0 : for (auto n : elem->neighbor_ptr_range())
1767 0 : libmesh_assert_not_equal_to (n, bad_elem);
1768 :
1769 : #ifdef LIBMESH_ENABLE_AMR
1770 0 : if (elem->has_children())
1771 0 : for (auto & child : elem->child_ref_range())
1772 0 : libmesh_assert_not_equal_to (&child, bad_elem);
1773 : #endif
1774 : }
1775 0 : }
1776 :
1777 :
1778 570 : void libmesh_assert_equal_points (const MeshBase & mesh)
1779 : {
1780 1140 : LOG_SCOPE("libmesh_assert_equal_points()", "MeshTools");
1781 :
1782 570 : dof_id_type pmax_node_id = mesh.max_node_id();
1783 570 : mesh.comm().max(pmax_node_id);
1784 :
1785 922414 : for (dof_id_type i=0; i != pmax_node_id; ++i)
1786 : {
1787 921844 : const Point * p = mesh.query_node_ptr(i);
1788 :
1789 921844 : libmesh_assert(mesh.comm().semiverify(p));
1790 : }
1791 570 : }
1792 :
1793 :
1794 570 : void libmesh_assert_equal_connectivity (const MeshBase & mesh)
1795 : {
1796 1140 : LOG_SCOPE("libmesh_assert_equal_connectivity()", "MeshTools");
1797 :
1798 570 : dof_id_type pmax_elem_id = mesh.max_elem_id();
1799 570 : mesh.comm().max(pmax_elem_id);
1800 :
1801 1144326 : for (dof_id_type i=0; i != pmax_elem_id; ++i)
1802 : {
1803 1143756 : const Elem * e = mesh.query_elem_ptr(i);
1804 :
1805 2287512 : std::vector<dof_id_type> nodes;
1806 1143756 : if (e)
1807 5792488 : for (auto n : e->node_index_range())
1808 4648848 : nodes.push_back(e->node_id(n));
1809 :
1810 1143756 : libmesh_assert(mesh.comm().semiverify(e ? &nodes : nullptr));
1811 : }
1812 570 : }
1813 :
1814 :
1815 0 : void libmesh_assert_connected_nodes (const MeshBase & mesh)
1816 : {
1817 0 : LOG_SCOPE("libmesh_assert_connected_nodes()", "MeshTools");
1818 :
1819 0 : std::set<const Node *> used_nodes;
1820 :
1821 0 : for (const auto & elem : mesh.element_ptr_range())
1822 : {
1823 0 : libmesh_assert (elem);
1824 :
1825 0 : for (auto & n : elem->node_ref_range())
1826 0 : used_nodes.insert(&n);
1827 : }
1828 :
1829 0 : for (const auto & node : mesh.node_ptr_range())
1830 : {
1831 0 : libmesh_assert(node);
1832 0 : libmesh_assert(used_nodes.count(node));
1833 : }
1834 0 : }
1835 :
1836 :
1837 :
1838 31618 : void libmesh_assert_valid_constraint_rows (const MeshBase & mesh)
1839 : {
1840 31618 : libmesh_parallel_only(mesh.comm());
1841 :
1842 31618 : const auto & constraint_rows = mesh.get_constraint_rows();
1843 :
1844 31618 : bool have_constraint_rows = !constraint_rows.empty();
1845 31618 : mesh.comm().max(have_constraint_rows);
1846 31618 : if (!have_constraint_rows)
1847 31548 : return;
1848 :
1849 24910 : for (auto & row : constraint_rows)
1850 : {
1851 24840 : const Node * node = row.first;
1852 24840 : libmesh_assert(node == mesh.node_ptr(node->id()));
1853 :
1854 84254 : for (auto & pr : row.second)
1855 : {
1856 59414 : const Elem * spline_elem = pr.first.first;
1857 59414 : libmesh_assert(spline_elem == mesh.elem_ptr(spline_elem->id()));
1858 : }
1859 : }
1860 :
1861 70 : dof_id_type pmax_node_id = mesh.max_node_id();
1862 70 : mesh.comm().max(pmax_node_id);
1863 :
1864 33172 : for (dof_id_type i=0; i != pmax_node_id; ++i)
1865 : {
1866 33102 : const Node * node = mesh.query_node_ptr(i);
1867 :
1868 33102 : bool have_constraint = constraint_rows.count(node);
1869 :
1870 33102 : const std::size_t my_n_constraints = have_constraint ?
1871 24840 : libmesh_map_find(constraint_rows, node).size() : std::size_t(-1);
1872 33102 : const std::size_t * n_constraints = node ?
1873 33102 : &my_n_constraints : nullptr;
1874 :
1875 33102 : libmesh_assert(mesh.comm().semiverify(n_constraints));
1876 : }
1877 : }
1878 :
1879 :
1880 :
1881 70164 : void libmesh_assert_valid_boundary_ids(const MeshBase & mesh)
1882 : {
1883 70164 : LOG_SCOPE("libmesh_assert_valid_boundary_ids()", "MeshTools");
1884 :
1885 70164 : if (mesh.n_processors() == 1)
1886 1828 : return;
1887 :
1888 68336 : libmesh_parallel_only(mesh.comm());
1889 :
1890 68336 : const BoundaryInfo & boundary_info = mesh.get_boundary_info();
1891 :
1892 68336 : dof_id_type pmax_elem_id = mesh.max_elem_id();
1893 68336 : mesh.comm().max(pmax_elem_id);
1894 :
1895 6820136 : for (dof_id_type i=0; i != pmax_elem_id; ++i)
1896 : {
1897 6751800 : const Elem * elem = mesh.query_elem_ptr(i);
1898 6751800 : const unsigned int my_n_nodes = elem ? elem->n_nodes() : 0;
1899 6751800 : const unsigned int my_n_edges = elem ? elem->n_edges() : 0;
1900 6751800 : const unsigned int my_n_sides = elem ? elem->n_sides() : 0;
1901 : unsigned int
1902 6751800 : n_nodes = my_n_nodes,
1903 6751800 : n_edges = my_n_edges,
1904 6751800 : n_sides = my_n_sides;
1905 :
1906 6751800 : mesh.comm().max(n_nodes);
1907 6751800 : mesh.comm().max(n_edges);
1908 6751800 : mesh.comm().max(n_sides);
1909 :
1910 6751800 : if (elem)
1911 : {
1912 6603715 : libmesh_assert_equal_to(my_n_nodes, n_nodes);
1913 6603715 : libmesh_assert_equal_to(my_n_edges, n_edges);
1914 6603715 : libmesh_assert_equal_to(my_n_sides, n_sides);
1915 : }
1916 :
1917 : // Let's test all IDs on the element with one communication
1918 : // rather than n_nodes + n_edges + n_sides communications, to
1919 : // cut down on latency in dbg modes.
1920 13503600 : std::vector<boundary_id_type> all_bcids;
1921 :
1922 54321782 : for (unsigned int n=0; n != n_nodes; ++n)
1923 : {
1924 95139964 : std::vector<boundary_id_type> bcids;
1925 47569982 : if (elem)
1926 : {
1927 47299718 : boundary_info.boundary_ids(elem->node_ptr(n), bcids);
1928 :
1929 : // Ordering of boundary ids shouldn't matter
1930 47299718 : std::sort(bcids.begin(), bcids.end());
1931 : }
1932 : // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1933 :
1934 47569982 : all_bcids.insert(all_bcids.end(), bcids.begin(),
1935 95139964 : bcids.end());
1936 : // Separator
1937 47569982 : all_bcids.push_back(BoundaryInfo::invalid_id);
1938 : }
1939 :
1940 42034188 : for (unsigned short e=0; e != n_edges; ++e)
1941 : {
1942 70564776 : std::vector<boundary_id_type> bcids;
1943 :
1944 35282388 : if (elem)
1945 : {
1946 35079474 : boundary_info.edge_boundary_ids(elem, e, bcids);
1947 :
1948 : // Ordering of boundary ids shouldn't matter
1949 35079474 : std::sort(bcids.begin(), bcids.end());
1950 : }
1951 :
1952 : // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1953 :
1954 35282388 : all_bcids.insert(all_bcids.end(), bcids.begin(),
1955 70564776 : bcids.end());
1956 : // Separator
1957 35282388 : all_bcids.push_back(BoundaryInfo::invalid_id);
1958 :
1959 35282388 : if (elem)
1960 : {
1961 35079474 : boundary_info.raw_edge_boundary_ids(elem, e, bcids);
1962 :
1963 : // Ordering of boundary ids shouldn't matter
1964 35079474 : std::sort(bcids.begin(), bcids.end());
1965 :
1966 35079474 : all_bcids.insert(all_bcids.end(), bcids.begin(),
1967 70158948 : bcids.end());
1968 : // Separator
1969 35079474 : all_bcids.push_back(BoundaryInfo::invalid_id);
1970 : }
1971 :
1972 : // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1973 : }
1974 :
1975 33432560 : for (unsigned short s=0; s != n_sides; ++s)
1976 : {
1977 53361520 : std::vector<boundary_id_type> bcids;
1978 :
1979 26680760 : if (elem)
1980 : {
1981 26545434 : boundary_info.boundary_ids(elem, s, bcids);
1982 :
1983 : // Ordering of boundary ids shouldn't matter
1984 26545434 : std::sort(bcids.begin(), bcids.end());
1985 :
1986 26545434 : all_bcids.insert(all_bcids.end(), bcids.begin(),
1987 53090868 : bcids.end());
1988 : // Separator
1989 26545434 : all_bcids.push_back(BoundaryInfo::invalid_id);
1990 : }
1991 :
1992 : // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
1993 :
1994 26680760 : if (elem)
1995 : {
1996 26545434 : boundary_info.raw_boundary_ids(elem, s, bcids);
1997 :
1998 : // Ordering of boundary ids shouldn't matter
1999 26545434 : std::sort(bcids.begin(), bcids.end());
2000 :
2001 26545434 : all_bcids.insert(all_bcids.end(), bcids.begin(),
2002 53090868 : bcids.end());
2003 : // Separator
2004 26545434 : all_bcids.push_back(BoundaryInfo::invalid_id);
2005 : }
2006 :
2007 : // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
2008 : }
2009 :
2010 20255400 : for (unsigned short sf=0; sf != 2; ++sf)
2011 : {
2012 27007200 : std::vector<boundary_id_type> bcids;
2013 :
2014 13503600 : if (elem)
2015 : {
2016 13207430 : boundary_info.shellface_boundary_ids(elem, sf, bcids);
2017 :
2018 : // Ordering of boundary ids shouldn't matter
2019 13207430 : std::sort(bcids.begin(), bcids.end());
2020 :
2021 13207430 : all_bcids.insert(all_bcids.end(), bcids.begin(),
2022 26414860 : bcids.end());
2023 : // Separator
2024 13207430 : all_bcids.push_back(BoundaryInfo::invalid_id);
2025 : }
2026 :
2027 : // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
2028 :
2029 13503600 : if (elem)
2030 : {
2031 13207430 : boundary_info.raw_shellface_boundary_ids(elem, sf, bcids);
2032 :
2033 : // Ordering of boundary ids shouldn't matter
2034 13207430 : std::sort(bcids.begin(), bcids.end());
2035 :
2036 13207430 : all_bcids.insert(all_bcids.end(), bcids.begin(),
2037 26414860 : bcids.end());
2038 : // Separator
2039 13207430 : all_bcids.push_back(BoundaryInfo::invalid_id);
2040 : }
2041 :
2042 : // libmesh_assert(mesh.comm().semiverify (elem ? &bcids : nullptr));
2043 : }
2044 :
2045 6751800 : libmesh_assert(mesh.comm().semiverify
2046 : (elem ? &all_bcids : nullptr));
2047 : }
2048 : }
2049 :
2050 :
2051 7910 : void libmesh_assert_valid_dof_ids(const MeshBase & mesh, unsigned int sysnum)
2052 : {
2053 7910 : LOG_SCOPE("libmesh_assert_valid_dof_ids()", "MeshTools");
2054 :
2055 7910 : if (mesh.n_processors() == 1)
2056 0 : return;
2057 :
2058 7910 : libmesh_parallel_only(mesh.comm());
2059 :
2060 7910 : dof_id_type pmax_elem_id = mesh.max_elem_id();
2061 7910 : mesh.comm().max(pmax_elem_id);
2062 :
2063 1045320 : for (dof_id_type i=0; i != pmax_elem_id; ++i)
2064 1037410 : assert_semiverify_dofobj(mesh.comm(),
2065 1037410 : mesh.query_elem_ptr(i),
2066 : sysnum);
2067 :
2068 7910 : dof_id_type pmax_node_id = mesh.max_node_id();
2069 7910 : mesh.comm().max(pmax_node_id);
2070 :
2071 1951402 : for (dof_id_type i=0; i != pmax_node_id; ++i)
2072 1943492 : assert_semiverify_dofobj(mesh.comm(),
2073 1943492 : mesh.query_node_ptr(i),
2074 : sysnum);
2075 : }
2076 :
2077 :
2078 : #ifdef LIBMESH_ENABLE_UNIQUE_ID
2079 62204 : void libmesh_assert_valid_unique_ids(const MeshBase & mesh)
2080 : {
2081 124408 : LOG_SCOPE("libmesh_assert_valid_unique_ids()", "MeshTools");
2082 :
2083 62204 : libmesh_parallel_only(mesh.comm());
2084 :
2085 : // Storage for semi-local DofObject ids.
2086 124408 : std::unordered_set<unique_id_type> semilocal_unique_ids;
2087 :
2088 62204 : auto gather_elem_ids = [&]()
2089 : {
2090 4699191 : for (auto const & elem : mesh.active_element_ptr_range())
2091 : {
2092 4636987 : auto [it, inserted] = semilocal_unique_ids.insert(elem->unique_id());
2093 4636987 : libmesh_assert(inserted);
2094 4636987 : libmesh_ignore(it);
2095 : }
2096 62204 : };
2097 :
2098 62204 : auto gather_node_ids = [&]()
2099 : {
2100 10694297 : for (auto const & node : mesh.node_ptr_range())
2101 : {
2102 10632093 : auto [it, inserted] = semilocal_unique_ids.insert(node->unique_id());
2103 10632093 : libmesh_assert(inserted);
2104 10632093 : libmesh_ignore(it);
2105 : }
2106 62204 : };
2107 :
2108 62204 : auto verify_elems = [&]()
2109 : {
2110 62204 : dof_id_type pmax_elem_id = mesh.max_elem_id();
2111 62204 : mesh.comm().max(pmax_elem_id);
2112 :
2113 5798880 : for (auto i : make_range(pmax_elem_id))
2114 : {
2115 5736676 : const Elem * elem = mesh.query_elem_ptr(i);
2116 5736676 : assert_dofobj_unique_id(mesh.comm(), elem, semilocal_unique_ids);
2117 : }
2118 62204 : };
2119 :
2120 62204 : auto verify_nodes = [&]()
2121 : {
2122 62204 : dof_id_type pmax_node_id = mesh.max_node_id();
2123 62204 : mesh.comm().max(pmax_node_id);
2124 :
2125 11216050 : for (auto i : make_range(pmax_node_id))
2126 : {
2127 11153846 : const Node * node = mesh.query_node_ptr(i);
2128 11153846 : assert_dofobj_unique_id(mesh.comm(), node, semilocal_unique_ids);
2129 : }
2130 62204 : };
2131 :
2132 62204 : if (!mesh.allow_node_and_elem_unique_id_overlap())
2133 : {
2134 : // First collect all the unique_ids we can see and make sure there's
2135 : // no duplicates
2136 62204 : gather_elem_ids();
2137 62204 : gather_node_ids();
2138 :
2139 : // Then make sure elements/nodes are all in sync and remote
2140 : // elements don't duplicate semilocal
2141 62204 : verify_elems();
2142 62204 : verify_nodes();
2143 : }
2144 : else
2145 : {
2146 : // If the mesh allows Node and Elem unique_ids to overlap, then we only
2147 : // check for validity and uniqueness of an Elem (resp. Node) unique id
2148 : // within the set of Elem (resp. Node) unique_ids.
2149 0 : gather_elem_ids();
2150 0 : verify_elems();
2151 :
2152 : // Clear id list before checking Nodes
2153 0 : semilocal_unique_ids.clear();
2154 :
2155 : // Finally, check Nodes
2156 0 : gather_node_ids();
2157 0 : verify_nodes();
2158 : }
2159 62204 : }
2160 : #endif
2161 :
2162 0 : void libmesh_assert_consistent_distributed(const MeshBase & mesh)
2163 : {
2164 0 : libmesh_parallel_only(mesh.comm());
2165 :
2166 0 : dof_id_type parallel_max_elem_id = mesh.max_elem_id();
2167 0 : mesh.comm().max(parallel_max_elem_id);
2168 :
2169 0 : for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
2170 : {
2171 0 : const Elem * elem = mesh.query_elem_ptr(i);
2172 : processor_id_type pid =
2173 0 : elem ? elem->processor_id() : DofObject::invalid_processor_id;
2174 0 : mesh.comm().min(pid);
2175 0 : libmesh_assert(elem || pid != mesh.processor_id());
2176 : }
2177 :
2178 0 : dof_id_type parallel_max_node_id = mesh.max_node_id();
2179 0 : mesh.comm().max(parallel_max_node_id);
2180 :
2181 0 : for (dof_id_type i=0; i != parallel_max_node_id; ++i)
2182 : {
2183 0 : const Node * node = mesh.query_node_ptr(i);
2184 : processor_id_type pid =
2185 0 : node ? node->processor_id() : DofObject::invalid_processor_id;
2186 0 : mesh.comm().min(pid);
2187 0 : libmesh_assert(node || pid != mesh.processor_id());
2188 : }
2189 0 : }
2190 :
2191 :
2192 0 : void libmesh_assert_consistent_distributed_nodes(const MeshBase & mesh)
2193 : {
2194 0 : libmesh_parallel_only(mesh.comm());
2195 0 : auto locator = mesh.sub_point_locator();
2196 :
2197 0 : dof_id_type parallel_max_elem_id = mesh.max_elem_id();
2198 0 : mesh.comm().max(parallel_max_elem_id);
2199 :
2200 0 : for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
2201 : {
2202 0 : const Elem * elem = mesh.query_elem_ptr(i);
2203 :
2204 0 : const unsigned int my_n_nodes = elem ? elem->n_nodes() : 0;
2205 0 : unsigned int n_nodes = my_n_nodes;
2206 0 : mesh.comm().max(n_nodes);
2207 :
2208 0 : if (n_nodes)
2209 0 : libmesh_assert(mesh.comm().semiverify(elem ? &my_n_nodes : nullptr));
2210 :
2211 0 : for (unsigned int n=0; n != n_nodes; ++n)
2212 : {
2213 0 : const Node * node = elem ? elem->node_ptr(n) : nullptr;
2214 : processor_id_type pid =
2215 0 : node ? node->processor_id() : DofObject::invalid_processor_id;
2216 0 : mesh.comm().min(pid);
2217 0 : libmesh_assert(node || pid != mesh.processor_id());
2218 : }
2219 : }
2220 0 : }
2221 :
2222 :
2223 :
2224 : template <>
2225 19576 : void libmesh_assert_parallel_consistent_procids<Elem>(const MeshBase & mesh)
2226 : {
2227 19576 : LOG_SCOPE("libmesh_assert_parallel_consistent_procids()", "MeshTools");
2228 :
2229 19576 : if (mesh.n_processors() == 1)
2230 0 : return;
2231 :
2232 19576 : libmesh_parallel_only(mesh.comm());
2233 :
2234 : // Some code (looking at you, stitch_meshes) modifies DofObject ids
2235 : // without keeping max_elem_id()/max_node_id() consistent, but
2236 : // that's done in a safe way for performance reasons, so we'll play
2237 : // along and just figure out new max ids ourselves.
2238 19576 : dof_id_type parallel_max_elem_id = 0;
2239 2733790 : for (const auto & elem : mesh.element_ptr_range())
2240 2714214 : parallel_max_elem_id = std::max<dof_id_type>(parallel_max_elem_id,
2241 2714214 : elem->id()+1);
2242 19576 : mesh.comm().max(parallel_max_elem_id);
2243 :
2244 : // Check processor ids for consistency between processors
2245 :
2246 2804840 : for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
2247 : {
2248 2785264 : const Elem * elem = mesh.query_elem_ptr(i);
2249 :
2250 : processor_id_type min_id =
2251 2714214 : elem ? elem->processor_id() :
2252 2785264 : std::numeric_limits<processor_id_type>::max();
2253 2785264 : mesh.comm().min(min_id);
2254 :
2255 : processor_id_type max_id =
2256 2714214 : elem ? elem->processor_id() :
2257 2785264 : std::numeric_limits<processor_id_type>::min();
2258 2785264 : mesh.comm().max(max_id);
2259 :
2260 2785264 : if (elem)
2261 : {
2262 2714214 : libmesh_assert_equal_to (min_id, elem->processor_id());
2263 2714214 : libmesh_assert_equal_to (max_id, elem->processor_id());
2264 : }
2265 :
2266 2785264 : if (min_id == mesh.processor_id())
2267 1318415 : libmesh_assert(elem);
2268 : }
2269 : }
2270 :
2271 :
2272 :
2273 76 : void libmesh_assert_parallel_consistent_new_node_procids(const MeshBase & mesh)
2274 : {
2275 76 : LOG_SCOPE("libmesh_assert_parallel_consistent_new_node_procids()", "MeshTools");
2276 :
2277 76 : if (mesh.n_processors() == 1)
2278 0 : return;
2279 :
2280 76 : libmesh_parallel_only(mesh.comm());
2281 :
2282 : // We want this test to hit every node when called even after nodes
2283 : // have been added asynchronously but before everything has been
2284 : // renumbered.
2285 76 : dof_id_type parallel_max_elem_id = mesh.max_elem_id();
2286 76 : mesh.comm().max(parallel_max_elem_id);
2287 :
2288 152 : std::vector<bool> elem_touched_by_anyone(parallel_max_elem_id, false);
2289 :
2290 43200 : for (dof_id_type i=0; i != parallel_max_elem_id; ++i)
2291 : {
2292 43124 : const Elem * elem = mesh.query_elem_ptr(i);
2293 :
2294 43124 : const unsigned int my_n_nodes = elem ? elem->n_nodes() : 0;
2295 43124 : unsigned int n_nodes = my_n_nodes;
2296 43124 : mesh.comm().max(n_nodes);
2297 :
2298 43124 : if (n_nodes)
2299 15840 : libmesh_assert(mesh.comm().semiverify(elem ? &my_n_nodes : nullptr));
2300 :
2301 173356 : for (unsigned int n=0; n != n_nodes; ++n)
2302 : {
2303 130232 : const Node * node = elem ? elem->node_ptr(n) : nullptr;
2304 130232 : const processor_id_type pid = node ? node->processor_id() : 0;
2305 130232 : libmesh_assert(mesh.comm().semiverify (node ? &pid : nullptr));
2306 : }
2307 : }
2308 : }
2309 :
2310 : template <>
2311 41494 : void libmesh_assert_parallel_consistent_procids<Node>(const MeshBase & mesh)
2312 : {
2313 41494 : LOG_SCOPE("libmesh_assert_parallel_consistent_procids()", "MeshTools");
2314 :
2315 41494 : if (mesh.n_processors() == 1)
2316 342 : return;
2317 :
2318 41152 : libmesh_parallel_only(mesh.comm());
2319 :
2320 : // We want this test to be valid even when called even after nodes
2321 : // have been added asynchronously but before they're renumbered
2322 : //
2323 : // Plus, some code (looking at you, stitch_meshes) modifies
2324 : // DofObject ids without keeping max_elem_id()/max_node_id()
2325 : // consistent, but that's done in a safe way for performance
2326 : // reasons, so we'll play along and just figure out new max ids
2327 : // ourselves.
2328 41152 : dof_id_type parallel_max_node_id = 0;
2329 10505165 : for (const auto & node : mesh.node_ptr_range())
2330 10464013 : parallel_max_node_id = std::max<dof_id_type>(parallel_max_node_id,
2331 10464013 : node->id()+1);
2332 41152 : mesh.comm().max(parallel_max_node_id);
2333 :
2334 82304 : std::vector<bool> node_touched_by_anyone(parallel_max_node_id, false);
2335 :
2336 3080421 : for (const auto & elem : as_range(mesh.local_elements_begin(),
2337 6160842 : mesh.local_elements_end()))
2338 : {
2339 3039269 : libmesh_assert (elem);
2340 :
2341 23359846 : for (auto & node : elem->node_ref_range())
2342 : {
2343 20320577 : dof_id_type nodeid = node.id();
2344 20320577 : node_touched_by_anyone[nodeid] = true;
2345 : }
2346 : }
2347 41152 : mesh.comm().max(node_touched_by_anyone);
2348 :
2349 : // Check processor ids for consistency between processors
2350 : // on any node an element touches
2351 11030202 : for (dof_id_type i=0; i != parallel_max_node_id; ++i)
2352 : {
2353 10989050 : if (!node_touched_by_anyone[i])
2354 518400 : continue;
2355 :
2356 10470650 : const Node * node = mesh.query_node_ptr(i);
2357 10470650 : const processor_id_type pid = node ? node->processor_id() : 0;
2358 :
2359 10470650 : libmesh_assert(mesh.comm().semiverify (node ? &pid : nullptr));
2360 : }
2361 : }
2362 :
2363 :
2364 :
2365 : #ifdef LIBMESH_ENABLE_AMR
2366 0 : void libmesh_assert_valid_refinement_flags(const MeshBase & mesh)
2367 : {
2368 0 : LOG_SCOPE("libmesh_assert_valid_refinement_flags()", "MeshTools");
2369 :
2370 0 : libmesh_parallel_only(mesh.comm());
2371 0 : if (mesh.n_processors() == 1)
2372 0 : return;
2373 :
2374 0 : dof_id_type pmax_elem_id = mesh.max_elem_id();
2375 0 : mesh.comm().max(pmax_elem_id);
2376 :
2377 0 : std::vector<unsigned char> my_elem_h_state(pmax_elem_id, 255);
2378 0 : std::vector<unsigned char> my_elem_p_state(pmax_elem_id, 255);
2379 :
2380 0 : for (const auto & elem : mesh.element_ptr_range())
2381 : {
2382 0 : libmesh_assert (elem);
2383 0 : dof_id_type elemid = elem->id();
2384 :
2385 0 : my_elem_h_state[elemid] =
2386 0 : static_cast<unsigned char>(elem->refinement_flag());
2387 :
2388 0 : my_elem_p_state[elemid] =
2389 0 : static_cast<unsigned char>(elem->p_refinement_flag());
2390 : }
2391 0 : std::vector<unsigned char> min_elem_h_state(my_elem_h_state);
2392 0 : mesh.comm().min(min_elem_h_state);
2393 :
2394 0 : std::vector<unsigned char> min_elem_p_state(my_elem_p_state);
2395 0 : mesh.comm().min(min_elem_p_state);
2396 :
2397 0 : for (dof_id_type i=0; i!= pmax_elem_id; ++i)
2398 : {
2399 0 : libmesh_assert(my_elem_h_state[i] == 255 ||
2400 : my_elem_h_state[i] == min_elem_h_state[i]);
2401 0 : libmesh_assert(my_elem_p_state[i] == 255 ||
2402 : my_elem_p_state[i] == min_elem_p_state[i]);
2403 : }
2404 : }
2405 : #else
2406 : void libmesh_assert_valid_refinement_flags(const MeshBase &)
2407 : {
2408 : }
2409 : #endif // LIBMESH_ENABLE_AMR
2410 :
2411 :
2412 :
2413 29044 : void libmesh_assert_valid_neighbors(const MeshBase & mesh,
2414 : bool assert_valid_remote_elems)
2415 : {
2416 29044 : LOG_SCOPE("libmesh_assert_valid_neighbors()", "MeshTools");
2417 :
2418 4200168 : for (const auto & elem : mesh.element_ptr_range())
2419 : {
2420 4171124 : libmesh_assert (elem);
2421 4171124 : elem->libmesh_assert_valid_neighbors();
2422 : }
2423 :
2424 29044 : if (mesh.n_processors() == 1)
2425 1144 : return;
2426 :
2427 27900 : libmesh_parallel_only(mesh.comm());
2428 :
2429 27900 : dof_id_type pmax_elem_id = mesh.max_elem_id();
2430 27900 : mesh.comm().max(pmax_elem_id);
2431 :
2432 4331582 : for (dof_id_type i=0; i != pmax_elem_id; ++i)
2433 : {
2434 4303682 : const Elem * elem = mesh.query_elem_ptr(i);
2435 :
2436 4303682 : const unsigned int my_n_neigh = elem ? elem->n_neighbors() : 0;
2437 4303682 : unsigned int n_neigh = my_n_neigh;
2438 4303682 : mesh.comm().max(n_neigh);
2439 4303682 : if (elem)
2440 4162008 : libmesh_assert_equal_to (my_n_neigh, n_neigh);
2441 :
2442 20899710 : for (unsigned int n = 0; n != n_neigh; ++n)
2443 : {
2444 16596028 : dof_id_type my_neighbor = DofObject::invalid_id;
2445 16596028 : dof_id_type * p_my_neighbor = nullptr;
2446 :
2447 : // If we have a non-remote_elem neighbor link, then we can
2448 : // verify it.
2449 16596028 : if (elem && elem->neighbor_ptr(n) != remote_elem)
2450 : {
2451 16473255 : p_my_neighbor = &my_neighbor;
2452 16473255 : if (elem->neighbor_ptr(n))
2453 15507102 : my_neighbor = elem->neighbor_ptr(n)->id();
2454 :
2455 : // But wait - if we haven't set remote_elem links yet then
2456 : // some nullptr links on ghost elements might be
2457 : // future-remote_elem links, so we can't verify those.
2458 32953430 : if (!assert_valid_remote_elems &&
2459 16474881 : !elem->neighbor_ptr(n) &&
2460 1626 : elem->processor_id() != mesh.processor_id())
2461 759 : p_my_neighbor = nullptr;
2462 : }
2463 16596028 : libmesh_assert(mesh.comm().semiverify(p_my_neighbor));
2464 : }
2465 : }
2466 : }
2467 : #endif // DEBUG
2468 :
2469 :
2470 :
2471 : // Functors for correct_node_proc_ids
2472 : namespace {
2473 :
2474 : typedef std::unordered_map<dof_id_type, processor_id_type> proc_id_map_type;
2475 :
2476 : struct SyncNodeSet
2477 : {
2478 : typedef unsigned char datum; // bool but without bit twiddling issues
2479 :
2480 182 : SyncNodeSet(std::unordered_set<const Node *> & _set,
2481 9199 : MeshBase & _mesh) :
2482 9199 : node_set(_set), mesh(_mesh) {}
2483 :
2484 : std::unordered_set<const Node *> & node_set;
2485 :
2486 : MeshBase & mesh;
2487 :
2488 : // ------------------------------------------------------------
2489 41122 : void gather_data (const std::vector<dof_id_type> & ids,
2490 : std::vector<datum> & data)
2491 : {
2492 : // Find whether each requested node belongs in the set
2493 41122 : data.resize(ids.size());
2494 :
2495 1867830 : for (auto i : index_range(ids))
2496 : {
2497 1826708 : const dof_id_type id = ids[i];
2498 :
2499 : // We'd better have every node we're asked for
2500 1826708 : Node * node = mesh.node_ptr(id);
2501 :
2502 : // Return if the node is in the set.
2503 2954958 : data[i] = node_set.count(node);
2504 : }
2505 41122 : }
2506 :
2507 : // ------------------------------------------------------------
2508 41122 : bool act_on_data (const std::vector<dof_id_type> & ids,
2509 : const std::vector<datum> in_set)
2510 : {
2511 167 : bool data_changed = false;
2512 :
2513 : // Add nodes we've been informed of to our own set
2514 1867830 : for (auto i : index_range(ids))
2515 : {
2516 1826708 : if (in_set[i])
2517 : {
2518 1196849 : Node * node = mesh.node_ptr(ids[i]);
2519 1196849 : if (!node_set.count(node))
2520 : {
2521 256 : node_set.insert(node);
2522 256 : data_changed = true;
2523 : }
2524 : }
2525 : }
2526 :
2527 41122 : return data_changed;
2528 : }
2529 : };
2530 :
2531 :
2532 8835 : struct NodesNotInSet
2533 : {
2534 182 : NodesNotInSet(const std::unordered_set<const Node *> _set)
2535 9017 : : node_set(_set) {}
2536 :
2537 479564 : bool operator() (const Node * node) const
2538 : {
2539 959128 : if (node_set.count(node))
2540 287060 : return false;
2541 192504 : return true;
2542 : }
2543 :
2544 : const std::unordered_set<const Node *> node_set;
2545 : };
2546 :
2547 :
2548 : struct SyncProcIdsFromMap
2549 : {
2550 : typedef processor_id_type datum;
2551 :
2552 218 : SyncProcIdsFromMap(const proc_id_map_type & _map,
2553 37675 : MeshBase & _mesh) :
2554 37675 : new_proc_ids(_map), mesh(_mesh) {}
2555 :
2556 : const proc_id_map_type & new_proc_ids;
2557 :
2558 : MeshBase & mesh;
2559 :
2560 : // ------------------------------------------------------------
2561 131006 : void gather_data (const std::vector<dof_id_type> & ids,
2562 : std::vector<datum> & data)
2563 : {
2564 : // Find the new processor id of each requested node
2565 131006 : data.resize(ids.size());
2566 :
2567 7260567 : for (auto i : index_range(ids))
2568 : {
2569 7129561 : const dof_id_type id = ids[i];
2570 :
2571 : // Return the node's new processor id if it has one, or its
2572 : // old processor id if not.
2573 7129561 : if (const auto it = new_proc_ids.find(id);
2574 47012 : it != new_proc_ids.end())
2575 6498481 : data[i] = it->second;
2576 : else
2577 : {
2578 : // We'd better find every node we're asked for
2579 631080 : const Node & node = mesh.node_ref(id);
2580 631080 : data[i] = node.processor_id();
2581 : }
2582 : }
2583 131006 : }
2584 :
2585 : // ------------------------------------------------------------
2586 131006 : void act_on_data (const std::vector<dof_id_type> & ids,
2587 : const std::vector<datum> proc_ids)
2588 : {
2589 : // Set the node processor ids we've now been informed of
2590 7260567 : for (auto i : index_range(ids))
2591 : {
2592 7129561 : Node & node = mesh.node_ref(ids[i]);
2593 7129561 : node.processor_id() = proc_ids[i];
2594 : }
2595 131006 : }
2596 : };
2597 : }
2598 :
2599 :
2600 :
2601 37675 : void correct_node_proc_ids (MeshBase & mesh)
2602 : {
2603 436 : LOG_SCOPE("correct_node_proc_ids()","MeshTools");
2604 :
2605 : // This function must be run on all processors at once
2606 218 : libmesh_parallel_only(mesh.comm());
2607 :
2608 : // We require all processors to agree on nodal processor ids before
2609 : // going into this algorithm.
2610 : #ifdef DEBUG
2611 218 : libmesh_assert_parallel_consistent_procids<Node>(mesh);
2612 : #endif
2613 :
2614 : // If we have any unpartitioned elements at this
2615 : // stage there is a problem
2616 218 : libmesh_assert (n_elem(mesh.unpartitioned_elements_begin(),
2617 : mesh.unpartitioned_elements_end()) == 0);
2618 :
2619 : // Fix nodes' processor ids. Coarsening may have left us with nodes
2620 : // which are no longer touched by any elements of the same processor
2621 : // id, and for DofMap to work we need to fix that.
2622 :
2623 : // This is harder now that libMesh no longer requires a distributed
2624 : // mesh to ghost all nodal neighbors: it is possible for two active
2625 : // elements on two different processors to share the same node in
2626 : // such a way that neither processor knows the others' element
2627 : // exists!
2628 :
2629 : // While we're at it, if this mesh is configured to allow
2630 : // repartitioning, we'll repartition *all* the nodes' processor ids
2631 : // using the canonical Node heuristic, to try and improve DoF load
2632 : // balancing. But if the mesh is disallowing repartitioning, we
2633 : // won't touch processor_id on any node where it's valid, regardless
2634 : // of whether or not it's canonical.
2635 218 : bool repartition_all_nodes = !mesh.skip_noncritical_partitioning();
2636 436 : std::unordered_set<const Node *> valid_nodes;
2637 :
2638 : // If we aren't allowed to repartition, then we're going to leave
2639 : // every node we can at its current processor_id, and *only*
2640 : // repartition the nodes whose current processor id is incompatible
2641 : // with DoFMap (because it doesn't touch an active element, e.g. due
2642 : // to coarsening)
2643 37675 : if (!repartition_all_nodes)
2644 : {
2645 1158938 : for (const auto & elem : mesh.active_element_ptr_range())
2646 5946053 : for (const auto & node : elem->node_ref_range())
2647 5305120 : if (elem->processor_id() == node.processor_id())
2648 4909802 : valid_nodes.insert(&node);
2649 :
2650 182 : SyncNodeSet syncv(valid_nodes, mesh);
2651 :
2652 : Parallel::sync_dofobject_data_by_id
2653 18216 : (mesh.comm(), mesh.nodes_begin(), mesh.nodes_end(), syncv);
2654 : }
2655 :
2656 : // We build up a set of compatible processor ids for each node
2657 436 : proc_id_map_type new_proc_ids;
2658 :
2659 8996070 : for (auto & elem : mesh.active_element_ptr_range())
2660 : {
2661 4499519 : processor_id_type pid = elem->processor_id();
2662 :
2663 44096702 : for (auto & node : elem->node_ref_range())
2664 : {
2665 39597183 : const dof_id_type id = node.id();
2666 39597183 : if (auto it = new_proc_ids.find(id);
2667 350660 : it == new_proc_ids.end())
2668 155764 : new_proc_ids.emplace(id, pid);
2669 : else
2670 28313309 : it->second = node.choose_processor_id(it->second, pid);
2671 : }
2672 37239 : }
2673 :
2674 : // Sort the new pids to push to each processor
2675 : std::map<processor_id_type, std::vector<std::pair<dof_id_type, processor_id_type>>>
2676 436 : ids_to_push;
2677 :
2678 25740532 : for (const auto & node : mesh.node_ptr_range())
2679 13084952 : if (const auto it = std::as_const(new_proc_ids).find(node->id());
2680 13084952 : it != new_proc_ids.end() && node->processor_id() != DofObject::invalid_processor_id)
2681 11321113 : ids_to_push[node->processor_id()].emplace_back(node->id(), /*pid=*/it->second);
2682 :
2683 : auto action_functor =
2684 171277 : [& mesh, & new_proc_ids]
2685 : (processor_id_type,
2686 11751576 : const std::vector<std::pair<dof_id_type, processor_id_type>> & data)
2687 : {
2688 11455971 : for (const auto & [id, pid] : data)
2689 : {
2690 11283874 : if (const auto it = new_proc_ids.find(id);
2691 155764 : it == new_proc_ids.end())
2692 0 : new_proc_ids.emplace(id, pid);
2693 : else
2694 : {
2695 11283874 : const Node & node = mesh.node_ref(id);
2696 11283874 : it->second = node.choose_processor_id(it->second, pid);
2697 : }
2698 : }
2699 38277 : };
2700 :
2701 : Parallel::push_parallel_vector_data
2702 37675 : (mesh.comm(), ids_to_push, action_functor);
2703 :
2704 : // Now new_proc_ids is correct for every node we used to own. Let's
2705 : // ask every other processor about the nodes they used to own. But
2706 : // first we'll need to keep track of which nodes we used to own,
2707 : // lest we get them confused with nodes we newly own.
2708 436 : std::unordered_set<Node *> ex_local_nodes;
2709 9087712 : for (auto & node : mesh.local_node_ptr_range())
2710 4642931 : if (const auto it = new_proc_ids.find(node->id());
2711 4642931 : it != new_proc_ids.end() && it->second != mesh.processor_id())
2712 37288 : ex_local_nodes.insert(node);
2713 :
2714 218 : SyncProcIdsFromMap sync(new_proc_ids, mesh);
2715 37675 : if (repartition_all_nodes)
2716 : Parallel::sync_dofobject_data_by_id
2717 56916 : (mesh.comm(), mesh.nodes_begin(), mesh.nodes_end(), sync);
2718 : else
2719 : {
2720 182 : NodesNotInSet nnis(valid_nodes);
2721 :
2722 : Parallel::sync_dofobject_data_by_id
2723 18216 : (mesh.comm(), mesh.nodes_begin(), mesh.nodes_end(), nnis, sync);
2724 : }
2725 :
2726 : // And finally let's update the nodes we used to own.
2727 42262 : for (const auto & node : ex_local_nodes)
2728 : {
2729 2103 : if (valid_nodes.count(node))
2730 2083 : continue;
2731 :
2732 2504 : const dof_id_type id = node->id();
2733 10 : const proc_id_map_type::iterator it = new_proc_ids.find(id);
2734 10 : libmesh_assert(it != new_proc_ids.end());
2735 2504 : node->processor_id() = it->second;
2736 : }
2737 :
2738 : // We should still have consistent nodal processor ids coming out of
2739 : // this algorithm, but if we're allowed to repartition the mesh then
2740 : // they should be canonically correct too.
2741 : #ifdef DEBUG
2742 218 : libmesh_assert_valid_procids<Node>(mesh);
2743 : //if (repartition_all_nodes)
2744 : // libmesh_assert_canonical_node_procids(mesh);
2745 : #endif
2746 37675 : }
2747 :
2748 :
2749 :
2750 19630 : void Private::globally_renumber_nodes_and_elements (MeshBase & mesh)
2751 : {
2752 19630 : MeshCommunication().assign_global_indices(mesh);
2753 19630 : }
2754 :
2755 : } // namespace MeshTools
2756 :
2757 : } // namespace libMesh
|