1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2020 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 // C++ includes
20 #include <cstdlib> // *must* precede <cmath> for proper std:abs() on PGI, Sun Studio CC
21 #include <cmath> // for isnan(), when it's defined
22 #include <limits>
23
24 // Local includes
25 #include "libmesh/libmesh_config.h"
26
27 // only compile these functions if the user requests AMR support
28 #ifdef LIBMESH_ENABLE_AMR
29
30 #include "libmesh/boundary_info.h"
31 #include "libmesh/error_vector.h"
32 #include "libmesh/libmesh_logging.h"
33 #include "libmesh/mesh_base.h"
34 #include "libmesh/mesh_communication.h"
35 #include "libmesh/mesh_refinement.h"
36 #include "libmesh/parallel.h"
37 #include "libmesh/parallel_ghost_sync.h"
38 #include "libmesh/partitioner.h"
39 #include "libmesh/remote_elem.h"
40 #include "libmesh/sync_refinement_flags.h"
41 #include "libmesh/int_range.h"
42
43 #ifdef DEBUG
44 // Some extra validation for DistributedMesh
45 #include "libmesh/mesh_tools.h"
46 #endif // DEBUG
47
48 #ifdef LIBMESH_ENABLE_PERIODIC
49 #include "libmesh/periodic_boundaries.h"
50 #endif
51
52
53
54 // Anonymous namespace for helper functions
55 // namespace {
56 //
57 // using namespace libMesh;
58 //
59 // struct SyncCoarsenInactive
60 // {
61 // bool operator() (const Elem * elem) const {
62 // // If we're not an ancestor, there's no chance our coarsening
63 // // settings need to be changed.
64 // if (!elem->ancestor())
65 // return false;
66 //
67 // // If we don't have any remote children, we already know enough to
68 // // determine the correct refinement flag ourselves.
69 // //
70 // // If we know we have a child that isn't being coarsened, that
71 // // also forces a specific flag.
72 // //
73 // // Either way there's nothing we need to communicate.
74 // bool found_remote_child = false;
75 // for (auto & child : elem->child_ref_range())
76 // {
77 // if (child.refinement_flag() != Elem::COARSEN)
78 // return false;
79 // if (&child == remote_elem)
80 // found_remote_child = true;
81 // }
82 // return found_remote_child;
83 // }
84 // };
85 // }
86
87
88
89 namespace libMesh
90 {
91
92 //-----------------------------------------------------------------
93 // Mesh refinement methods
MeshRefinement(MeshBase & m)94 MeshRefinement::MeshRefinement (MeshBase & m) :
95 ParallelObject(m),
96 _mesh(m),
97 _use_member_parameters(false),
98 _coarsen_by_parents(false),
99 _refine_fraction(0.3),
100 _coarsen_fraction(0.0),
101 _max_h_level(libMesh::invalid_uint),
102 _coarsen_threshold(10),
103 _nelem_target(0),
104 _absolute_global_tolerance(0.0),
105 _face_level_mismatch_limit(1),
106 _edge_level_mismatch_limit(0),
107 _node_level_mismatch_limit(0),
108 _overrefined_boundary_limit(0),
109 _underrefined_boundary_limit(0),
110 _enforce_mismatch_limit_prior_to_refinement(false)
111 #ifdef LIBMESH_ENABLE_PERIODIC
112 , _periodic_boundaries(nullptr)
113 #endif
114 {
115 }
116
117
118
119 #ifdef LIBMESH_ENABLE_PERIODIC
set_periodic_boundaries_ptr(PeriodicBoundaries * pb_ptr)120 void MeshRefinement::set_periodic_boundaries_ptr(PeriodicBoundaries * pb_ptr)
121 {
122 _periodic_boundaries = pb_ptr;
123 }
124 #endif
125
126
127
~MeshRefinement()128 MeshRefinement::~MeshRefinement ()
129 {
130 this->clear();
131 }
132
133
134
clear()135 void MeshRefinement::clear ()
136 {
137 _new_nodes_map.clear();
138 }
139
140
141
add_node(Elem & parent,unsigned int child,unsigned int node,processor_id_type proc_id)142 Node * MeshRefinement::add_node(Elem & parent,
143 unsigned int child,
144 unsigned int node,
145 processor_id_type proc_id)
146 {
147 LOG_SCOPE("add_node()", "MeshRefinement");
148
149 unsigned int parent_n = parent.as_parent_node(child, node);
150
151 if (parent_n != libMesh::invalid_uint)
152 return parent.node_ptr(parent_n);
153
154 const std::vector<std::pair<dof_id_type, dof_id_type>>
155 bracketing_nodes = parent.bracketing_nodes(child, node);
156
157 // If we're not a parent node, we *must* be bracketed by at least
158 // one pair of parent nodes
159 libmesh_assert(bracketing_nodes.size());
160
161 const dof_id_type new_node_id =
162 _new_nodes_map.find(bracketing_nodes);
163
164 // Return the node if it already exists.
165 //
166 // We'll leave the processor_id untouched in this case - if we're
167 // repartitioning later or if this is a new unpartitioned node,
168 // we'll update it then, and if not then we don't want to update it.
169 if (new_node_id != DofObject::invalid_id)
170 return _mesh.node_ptr(new_node_id);
171
172 // Otherwise we need to add a new node.
173 //
174 // Figure out where to add the point:
175
176 Point p; // defaults to 0,0,0
177
178 for (auto n : parent.node_index_range())
179 {
180 // The value from the embedding matrix
181 const float em_val = parent.embedding_matrix(child,node,n);
182
183 if (em_val != 0.)
184 {
185 p.add_scaled (parent.point(n), em_val);
186
187 // If we'd already found the node we shouldn't be here
188 libmesh_assert_not_equal_to (em_val, 1);
189 }
190 }
191
192 // Although we're leaving new nodes unpartitioned at first, with a
193 // DistributedMesh we would need a default id based on the numbering
194 // scheme for the requested processor_id.
195 Node * new_node = _mesh.add_point (p, DofObject::invalid_id, proc_id);
196
197 libmesh_assert(new_node);
198
199 // But then we'll make sure this node is marked as unpartitioned.
200 new_node->processor_id() = DofObject::invalid_processor_id;
201
202 // Add the node to the map.
203 _new_nodes_map.add_node(*new_node, bracketing_nodes);
204
205 // Return the address of the new node
206 return new_node;
207 }
208
209
210
add_elem(Elem * elem)211 Elem * MeshRefinement::add_elem (Elem * elem)
212 {
213 libmesh_assert(elem);
214 return _mesh.add_elem (elem);
215 }
216
add_elem(std::unique_ptr<Elem> elem)217 Elem * MeshRefinement::add_elem (std::unique_ptr<Elem> elem)
218 {
219 libmesh_assert(elem);
220 return _mesh.add_elem(std::move(elem));
221 }
222
223
create_parent_error_vector(const ErrorVector & error_per_cell,ErrorVector & error_per_parent,Real & parent_error_min,Real & parent_error_max)224 void MeshRefinement::create_parent_error_vector(const ErrorVector & error_per_cell,
225 ErrorVector & error_per_parent,
226 Real & parent_error_min,
227 Real & parent_error_max)
228 {
229 // This function must be run on all processors at once
230 parallel_object_only();
231
232 // Make sure the input error vector is valid
233 #ifdef DEBUG
234 for (const auto & val : error_per_cell)
235 {
236 libmesh_assert_greater_equal (val, 0);
237 // isnan() isn't standard C++ yet
238 #ifdef isnan
239 libmesh_assert(!isnan(val));
240 #endif
241 }
242
243 // Use a reference to std::vector to avoid confusing
244 // this->comm().verify
245 std::vector<ErrorVectorReal> & epc = error_per_parent;
246 libmesh_assert(this->comm().verify(epc));
247 #endif // #ifdef DEBUG
248
249 // error values on uncoarsenable elements will be left at -1
250 error_per_parent.clear();
251 error_per_parent.resize(error_per_cell.size(), 0.0);
252
253 {
254 // Find which elements are uncoarsenable
255 for (auto & elem : _mesh.active_local_element_ptr_range())
256 {
257 Elem * parent = elem->parent();
258
259 // Active elements are uncoarsenable
260 error_per_parent[elem->id()] = -1.0;
261
262 // Grandparents and up are uncoarsenable
263 while (parent)
264 {
265 parent = parent->parent();
266 if (parent)
267 {
268 const dof_id_type parentid = parent->id();
269 libmesh_assert_less (parentid, error_per_parent.size());
270 error_per_parent[parentid] = -1.0;
271 }
272 }
273 }
274
275 // Sync between processors.
276 // Use a reference to std::vector to avoid confusing
277 // this->comm().min
278 std::vector<ErrorVectorReal> & epp = error_per_parent;
279 this->comm().min(epp);
280 }
281
282 // The parent's error is defined as the square root of the
283 // sum of the children's errors squared, so errors that are
284 // Hilbert norms remain Hilbert norms.
285 //
286 // Because the children may be on different processors, we
287 // calculate local contributions to the parents' errors squared
288 // first, then sum across processors and take the square roots
289 // second.
290 for (auto & elem : _mesh.active_local_element_ptr_range())
291 {
292 Elem * parent = elem->parent();
293
294 // Calculate each contribution to parent cells
295 if (parent)
296 {
297 const dof_id_type parentid = parent->id();
298 libmesh_assert_less (parentid, error_per_parent.size());
299
300 // If the parent has grandchildren we won't be able to
301 // coarsen it, so forget it. Otherwise, add this child's
302 // contribution to the sum of the squared child errors
303 if (error_per_parent[parentid] != -1.0)
304 error_per_parent[parentid] += (error_per_cell[elem->id()] *
305 error_per_cell[elem->id()]);
306 }
307 }
308
309 // Sum the vector across all processors
310 this->comm().sum(static_cast<std::vector<ErrorVectorReal> &>(error_per_parent));
311
312 // Calculate the min and max as we loop
313 parent_error_min = std::numeric_limits<double>::max();
314 parent_error_max = 0.;
315
316 for (auto i : index_range(error_per_parent))
317 {
318 // If this element isn't a coarsenable parent with error, we
319 // have nothing to do. Just flag it as -1 and move on
320 // Note that this->comm().sum might have left uncoarsenable
321 // elements with error_per_parent=-n_proc, so reset it to
322 // error_per_parent=-1
323 if (error_per_parent[i] < 0.)
324 {
325 error_per_parent[i] = -1.;
326 continue;
327 }
328
329 // The error estimator might have already given us an
330 // estimate on the coarsenable parent elements; if so then
331 // we want to retain that estimate
332 if (error_per_cell[i])
333 {
334 error_per_parent[i] = error_per_cell[i];
335 continue;
336 }
337 // if not, then e_parent = sqrt(sum(e_child^2))
338 else
339 error_per_parent[i] = std::sqrt(error_per_parent[i]);
340
341 parent_error_min = std::min (parent_error_min,
342 error_per_parent[i]);
343 parent_error_max = std::max (parent_error_max,
344 error_per_parent[i]);
345 }
346 }
347
348
349
update_nodes_map()350 void MeshRefinement::update_nodes_map ()
351 {
352 this->_new_nodes_map.init(_mesh);
353 }
354
355
356
test_level_one(bool libmesh_dbg_var (libmesh_assert_pass))357 bool MeshRefinement::test_level_one (bool libmesh_dbg_var(libmesh_assert_pass))
358 {
359 // This function must be run on all processors at once
360 parallel_object_only();
361
362 // We may need a PointLocator for topological_neighbor() tests
363 // later, which we need to make sure gets constructed on all
364 // processors at once.
365 std::unique_ptr<PointLocatorBase> point_locator;
366
367 #ifdef LIBMESH_ENABLE_PERIODIC
368 bool has_periodic_boundaries =
369 _periodic_boundaries && !_periodic_boundaries->empty();
370 libmesh_assert(this->comm().verify(has_periodic_boundaries));
371
372 if (has_periodic_boundaries)
373 point_locator = _mesh.sub_point_locator();
374 #endif
375
376 bool failure = false;
377
378 #ifndef NDEBUG
379 Elem * failed_elem = nullptr;
380 Elem * failed_neighbor = nullptr;
381 #endif // !NDEBUG
382
383 for (auto & elem : _mesh.active_local_element_ptr_range())
384 for (auto n : elem->side_index_range())
385 {
386 Elem * neighbor =
387 topological_neighbor(elem, point_locator.get(), n);
388
389 if (!neighbor || !neighbor->active() ||
390 neighbor == remote_elem)
391 continue;
392
393 if ((neighbor->level() + 1 < elem->level()) ||
394 (neighbor->p_level() + 1 < elem->p_level()) ||
395 (neighbor->p_level() > elem->p_level() + 1))
396 {
397 failure = true;
398 #ifndef NDEBUG
399 failed_elem = elem;
400 failed_neighbor = neighbor;
401 #endif // !NDEBUG
402 break;
403 }
404 }
405
406 // If any processor failed, we failed globally
407 this->comm().max(failure);
408
409 if (failure)
410 {
411 // We didn't pass the level one test, so libmesh_assert that
412 // we're allowed not to
413 #ifndef NDEBUG
414 if (libmesh_assert_pass)
415 {
416 libMesh::out << "MeshRefinement Level one failure, element: "
417 << *failed_elem
418 << std::endl;
419 libMesh::out << "MeshRefinement Level one failure, neighbor: "
420 << *failed_neighbor
421 << std::endl;
422 }
423 #endif // !NDEBUG
424 libmesh_assert(!libmesh_assert_pass);
425 return false;
426 }
427 return true;
428 }
429
430
431
test_unflagged(bool libmesh_dbg_var (libmesh_assert_pass))432 bool MeshRefinement::test_unflagged (bool libmesh_dbg_var(libmesh_assert_pass))
433 {
434 // This function must be run on all processors at once
435 parallel_object_only();
436
437 bool found_flag = false;
438
439 #ifndef NDEBUG
440 Elem * failed_elem = nullptr;
441 #endif
442
443 // Search for local flags
444 for (auto & elem : _mesh.active_local_element_ptr_range())
445 if (elem->refinement_flag() == Elem::REFINE ||
446 elem->refinement_flag() == Elem::COARSEN ||
447 elem->p_refinement_flag() == Elem::REFINE ||
448 elem->p_refinement_flag() == Elem::COARSEN)
449 {
450 found_flag = true;
451 #ifndef NDEBUG
452 failed_elem = elem;
453 #endif
454 break;
455 }
456
457 // If we found a flag on any processor, it counts
458 this->comm().max(found_flag);
459
460 if (found_flag)
461 {
462 #ifndef NDEBUG
463 if (libmesh_assert_pass)
464 {
465 libMesh::out <<
466 "MeshRefinement test_unflagged failure, element: " <<
467 *failed_elem << std::endl;
468 }
469 #endif
470 // We didn't pass the "elements are unflagged" test,
471 // so libmesh_assert that we're allowed not to
472 libmesh_assert(!libmesh_assert_pass);
473 return false;
474 }
475 return true;
476 }
477
478
479
refine_and_coarsen_elements()480 bool MeshRefinement::refine_and_coarsen_elements ()
481 {
482 // This function must be run on all processors at once
483 parallel_object_only();
484
485 // We can't yet turn a non-level-one mesh into a level-one mesh
486 if (_face_level_mismatch_limit)
487 libmesh_assert(test_level_one(true));
488
489 // Possibly clean up the refinement flags from
490 // a previous step. While we're at it, see if this method should be
491 // a no-op.
492 bool elements_flagged = false;
493
494 for (auto & elem : _mesh.element_ptr_range())
495 {
496 // This might be left over from the last step
497 const Elem::RefinementState flag = elem->refinement_flag();
498
499 // Set refinement flag to INACTIVE if the
500 // element isn't active
501 if ( !elem->active())
502 {
503 elem->set_refinement_flag(Elem::INACTIVE);
504 elem->set_p_refinement_flag(Elem::INACTIVE);
505 }
506 else if (flag == Elem::JUST_REFINED)
507 elem->set_refinement_flag(Elem::DO_NOTHING);
508 else if (!elements_flagged)
509 {
510 if (flag == Elem::REFINE || flag == Elem::COARSEN)
511 elements_flagged = true;
512 else
513 {
514 const Elem::RefinementState pflag =
515 elem->p_refinement_flag();
516 if (pflag == Elem::REFINE || pflag == Elem::COARSEN)
517 elements_flagged = true;
518 }
519 }
520 }
521
522 // Did *any* processor find elements flagged for AMR/C?
523 _mesh.comm().max(elements_flagged);
524
525 // If we have nothing to do, let's not bother verifying that nothing
526 // is compatible with nothing.
527 if (!elements_flagged)
528 return false;
529
530 // Parallel consistency has to come first, or coarsening
531 // along processor boundaries might occasionally be falsely
532 // prevented
533 #ifdef DEBUG
534 bool flags_were_consistent = this->make_flags_parallel_consistent();
535
536 libmesh_assert (flags_were_consistent);
537 #endif
538
539 // Smooth refinement and coarsening flags
540 _smooth_flags(true, true);
541
542 // First coarsen the flagged elements.
543 const bool coarsening_changed_mesh =
544 this->_coarsen_elements ();
545
546 // First coarsen the flagged elements.
547 // FIXME: test_level_one now tests consistency across periodic
548 // boundaries, which requires a point_locator, which just got
549 // invalidated by _coarsen_elements() and hasn't yet been cleared by
550 // prepare_for_use().
551
552 // libmesh_assert(this->make_coarsening_compatible());
553 // libmesh_assert(this->make_refinement_compatible());
554
555 // FIXME: This won't pass unless we add a redundant find_neighbors()
556 // call or replace find_neighbors() with on-the-fly neighbor updating
557 // libmesh_assert(!this->eliminate_unrefined_patches());
558
559 // We can't contract the mesh ourselves anymore - a System might
560 // need to restrict old coefficient vectors first
561 // _mesh.contract();
562
563 // First coarsen the flagged elements.
564 // Now refine the flagged elements. This will
565 // take up some space, maybe more than what was freed.
566 const bool refining_changed_mesh =
567 this->_refine_elements();
568
569 // First coarsen the flagged elements.
570 // Finally, the new mesh needs to be prepared for use
571 if (coarsening_changed_mesh || refining_changed_mesh)
572 {
573 #ifdef DEBUG
574 _mesh.libmesh_assert_valid_parallel_ids();
575 #endif
576
577 _mesh.prepare_for_use ();
578
579 if (_face_level_mismatch_limit)
580 libmesh_assert(test_level_one(true));
581 libmesh_assert(test_unflagged(true));
582 libmesh_assert(this->make_coarsening_compatible());
583 libmesh_assert(this->make_refinement_compatible());
584 // FIXME: This won't pass unless we add a redundant find_neighbors()
585 // call or replace find_neighbors() with on-the-fly neighbor updating
586 // libmesh_assert(!this->eliminate_unrefined_patches());
587
588 return true;
589 }
590 else
591 {
592 if (_face_level_mismatch_limit)
593 libmesh_assert(test_level_one(true));
594 libmesh_assert(test_unflagged(true));
595 libmesh_assert(this->make_coarsening_compatible());
596 libmesh_assert(this->make_refinement_compatible());
597 }
598
599 // Otherwise there was no change in the mesh,
600 // let the user know. Also, there is no need
601 // to prepare the mesh for use since it did not change.
602 return false;
603
604 }
605
606
607
608
609
610
611
coarsen_elements()612 bool MeshRefinement::coarsen_elements ()
613 {
614 // This function must be run on all processors at once
615 parallel_object_only();
616
617 // We can't yet turn a non-level-one mesh into a level-one mesh
618 if (_face_level_mismatch_limit)
619 libmesh_assert(test_level_one(true));
620
621 // Possibly clean up the refinement flags from
622 // a previous step
623 for (auto & elem : _mesh.element_ptr_range())
624 {
625 // Set refinement flag to INACTIVE if the
626 // element isn't active
627 if (!elem->active())
628 {
629 elem->set_refinement_flag(Elem::INACTIVE);
630 elem->set_p_refinement_flag(Elem::INACTIVE);
631 }
632
633 // This might be left over from the last step
634 if (elem->refinement_flag() == Elem::JUST_REFINED)
635 elem->set_refinement_flag(Elem::DO_NOTHING);
636 }
637
638 // Parallel consistency has to come first, or coarsening
639 // along processor boundaries might occasionally be falsely
640 // prevented
641 bool flags_were_consistent = this->make_flags_parallel_consistent();
642
643 // In theory, we should be able to remove the above call, which can
644 // be expensive and should be unnecessary. In practice, doing
645 // consistent flagging in parallel is hard, it's impossible to
646 // verify at the library level if it's being done by user code, and
647 // we don't want to abort large parallel runs in opt mode... but we
648 // do want to warn that they should be fixed.
649 libmesh_assert(flags_were_consistent);
650 if (!flags_were_consistent)
651 {
652 libMesh::out << "Refinement flags were not consistent between processors!\n"
653 << "Correcting and continuing.";
654 }
655
656 // Smooth coarsening flags
657 _smooth_flags(false, true);
658
659 // Coarsen the flagged elements.
660 const bool mesh_changed =
661 this->_coarsen_elements ();
662
663 if (_face_level_mismatch_limit)
664 libmesh_assert(test_level_one(true));
665 libmesh_assert(this->make_coarsening_compatible());
666 // FIXME: This won't pass unless we add a redundant find_neighbors()
667 // call or replace find_neighbors() with on-the-fly neighbor updating
668 // libmesh_assert(!this->eliminate_unrefined_patches());
669
670 // We can't contract the mesh ourselves anymore - a System might
671 // need to restrict old coefficient vectors first
672 // _mesh.contract();
673
674 // Finally, the new mesh may need to be prepared for use
675 if (mesh_changed)
676 _mesh.prepare_for_use ();
677
678 return mesh_changed;
679 }
680
681
682
683
684
685
686
refine_elements()687 bool MeshRefinement::refine_elements ()
688 {
689 // This function must be run on all processors at once
690 parallel_object_only();
691
692 if (_face_level_mismatch_limit)
693 libmesh_assert(test_level_one(true));
694
695 // Possibly clean up the refinement flags from
696 // a previous step
697 for (auto & elem : _mesh.element_ptr_range())
698 {
699 // Set refinement flag to INACTIVE if the
700 // element isn't active
701 if (!elem->active())
702 {
703 elem->set_refinement_flag(Elem::INACTIVE);
704 elem->set_p_refinement_flag(Elem::INACTIVE);
705 }
706
707 // This might be left over from the last step
708 if (elem->refinement_flag() == Elem::JUST_REFINED)
709 elem->set_refinement_flag(Elem::DO_NOTHING);
710 }
711
712
713
714 // Parallel consistency has to come first, or coarsening
715 // along processor boundaries might occasionally be falsely
716 // prevented
717 bool flags_were_consistent = this->make_flags_parallel_consistent();
718
719 // In theory, we should be able to remove the above call, which can
720 // be expensive and should be unnecessary. In practice, doing
721 // consistent flagging in parallel is hard, it's impossible to
722 // verify at the library level if it's being done by user code, and
723 // we don't want to abort large parallel runs in opt mode... but we
724 // do want to warn that they should be fixed.
725 libmesh_assert(flags_were_consistent);
726 if (!flags_were_consistent)
727 {
728 libMesh::out << "Refinement flags were not consistent between processors!\n"
729 << "Correcting and continuing.";
730 }
731
732 // Smooth refinement flags
733 _smooth_flags(true, false);
734
735 // Now refine the flagged elements. This will
736 // take up some space, maybe more than what was freed.
737 const bool mesh_changed =
738 this->_refine_elements();
739
740 if (_face_level_mismatch_limit)
741 libmesh_assert(test_level_one(true));
742 libmesh_assert(this->make_refinement_compatible());
743 // FIXME: This won't pass unless we add a redundant find_neighbors()
744 // call or replace find_neighbors() with on-the-fly neighbor updating
745 // libmesh_assert(!this->eliminate_unrefined_patches());
746
747 // Finally, the new mesh needs to be prepared for use
748 if (mesh_changed)
749 _mesh.prepare_for_use ();
750
751 return mesh_changed;
752 }
753
754
755
make_flags_parallel_consistent()756 bool MeshRefinement::make_flags_parallel_consistent()
757 {
758 // This function must be run on all processors at once
759 parallel_object_only();
760
761 LOG_SCOPE ("make_flags_parallel_consistent()", "MeshRefinement");
762
763 SyncRefinementFlags hsync(_mesh, &Elem::refinement_flag,
764 &Elem::set_refinement_flag);
765 Parallel::sync_dofobject_data_by_id
766 (this->comm(), _mesh.elements_begin(), _mesh.elements_end(), hsync);
767
768 SyncRefinementFlags psync(_mesh, &Elem::p_refinement_flag,
769 &Elem::set_p_refinement_flag);
770 Parallel::sync_dofobject_data_by_id
771 (this->comm(), _mesh.elements_begin(), _mesh.elements_end(), psync);
772
773 // If we weren't consistent in both h and p on every processor then
774 // we weren't globally consistent
775 bool parallel_consistent = hsync.parallel_consistent &&
776 psync.parallel_consistent;
777 this->comm().min(parallel_consistent);
778
779 return parallel_consistent;
780 }
781
782
783
make_coarsening_compatible()784 bool MeshRefinement::make_coarsening_compatible()
785 {
786 // This function must be run on all processors at once
787 parallel_object_only();
788
789 // We may need a PointLocator for topological_neighbor() tests
790 // later, which we need to make sure gets constructed on all
791 // processors at once.
792 std::unique_ptr<PointLocatorBase> point_locator;
793
794 #ifdef LIBMESH_ENABLE_PERIODIC
795 bool has_periodic_boundaries =
796 _periodic_boundaries && !_periodic_boundaries->empty();
797 libmesh_assert(this->comm().verify(has_periodic_boundaries));
798
799 if (has_periodic_boundaries)
800 point_locator = _mesh.sub_point_locator();
801 #endif
802
803 LOG_SCOPE ("make_coarsening_compatible()", "MeshRefinement");
804
805 // Unless we encounter a specific situation level-one
806 // will be satisfied after executing this loop just once
807 bool level_one_satisfied = true;
808
809
810 // Unless we encounter a specific situation we will be compatible
811 // with any selected refinement flags
812 bool compatible_with_refinement = true;
813
814
815 // find the maximum h and p levels in the mesh
816 unsigned int max_level = 0;
817 unsigned int max_p_level = 0;
818
819 {
820 // First we look at all the active level-0 elements. Since it doesn't make
821 // sense to coarsen them we must un-set their coarsen flags if
822 // they are set.
823 for (auto & elem : _mesh.active_element_ptr_range())
824 {
825 max_level = std::max(max_level, elem->level());
826 max_p_level =
827 std::max(max_p_level,
828 static_cast<unsigned int>(elem->p_level()));
829
830 if ((elem->level() == 0) &&
831 (elem->refinement_flag() == Elem::COARSEN))
832 elem->set_refinement_flag(Elem::DO_NOTHING);
833
834 if ((elem->p_level() == 0) &&
835 (elem->p_refinement_flag() == Elem::COARSEN))
836 elem->set_p_refinement_flag(Elem::DO_NOTHING);
837 }
838 }
839
840 // Even if there are no refined elements on this processor then
841 // there may still be work for us to do on e.g. ancestor elements.
842 // At the very least we need to be in the loop if a distributed mesh
843 // needs to synchronize data.
844 #if 0
845 if (max_level == 0 && max_p_level == 0)
846 {
847 // But we still have to check with other processors
848 this->comm().min(compatible_with_refinement);
849
850 return compatible_with_refinement;
851 }
852 #endif
853
854 // Loop over all the active elements. If an element is marked
855 // for coarsening we better check its neighbors. If ANY of these neighbors
856 // are marked for refinement AND are at the same level then there is a
857 // conflict. By convention refinement wins, so we un-mark the element for
858 // coarsening. Level-one would be violated in this case so we need to re-run
859 // the loop.
860 if (_face_level_mismatch_limit)
861 {
862
863 repeat:
864 level_one_satisfied = true;
865
866 do
867 {
868 level_one_satisfied = true;
869
870 for (auto & elem : _mesh.active_element_ptr_range())
871 {
872 bool my_flag_changed = false;
873
874 if (elem->refinement_flag() == Elem::COARSEN) // If the element is active and
875 // the coarsen flag is set
876 {
877 const unsigned int my_level = elem->level();
878
879 for (auto n : elem->side_index_range())
880 {
881 const Elem * neighbor =
882 topological_neighbor(elem, point_locator.get(), n);
883
884 if (neighbor != nullptr && // I have a
885 neighbor != remote_elem) // neighbor here
886 {
887 if (neighbor->active()) // and it is active
888 {
889 if ((neighbor->level() == my_level) &&
890 (neighbor->refinement_flag() == Elem::REFINE)) // the neighbor is at my level
891 // and wants to be refined
892 {
893 elem->set_refinement_flag(Elem::DO_NOTHING);
894 my_flag_changed = true;
895 break;
896 }
897 }
898 else // I have a neighbor and it is not active. That means it has children.
899 { // While it _may_ be possible to coarsen us if all the children of
900 // that element want to be coarsened, it is impossible to know at this
901 // stage. Forget about it for the moment... This can be handled in
902 // two steps.
903 elem->set_refinement_flag(Elem::DO_NOTHING);
904 my_flag_changed = true;
905 break;
906 }
907 }
908 }
909 }
910 if (elem->p_refinement_flag() == Elem::COARSEN) // If
911 // the element is active and the order reduction flag is set
912 {
913 const unsigned int my_p_level = elem->p_level();
914
915 for (auto n : elem->side_index_range())
916 {
917 const Elem * neighbor =
918 topological_neighbor(elem, point_locator.get(), n);
919
920 if (neighbor != nullptr && // I have a
921 neighbor != remote_elem) // neighbor here
922 {
923 if (neighbor->active()) // and it is active
924 {
925 if ((neighbor->p_level() > my_p_level &&
926 neighbor->p_refinement_flag() != Elem::COARSEN)
927 || (neighbor->p_level() == my_p_level &&
928 neighbor->p_refinement_flag() == Elem::REFINE))
929 {
930 elem->set_p_refinement_flag(Elem::DO_NOTHING);
931 my_flag_changed = true;
932 break;
933 }
934 }
935 else // I have a neighbor and it is not active.
936 { // We need to find which of its children
937 // have me as a neighbor, and maintain
938 // level one p compatibility with them.
939 // Because we currently have level one h
940 // compatibility, we don't need to check
941 // grandchildren
942
943 libmesh_assert(neighbor->has_children());
944 for (auto & subneighbor : neighbor->child_ref_range())
945 if (&subneighbor != remote_elem &&
946 subneighbor.active() &&
947 has_topological_neighbor(&subneighbor, point_locator.get(), elem))
948 if ((subneighbor.p_level() > my_p_level &&
949 subneighbor.p_refinement_flag() != Elem::COARSEN)
950 || (subneighbor.p_level() == my_p_level &&
951 subneighbor.p_refinement_flag() == Elem::REFINE))
952 {
953 elem->set_p_refinement_flag(Elem::DO_NOTHING);
954 my_flag_changed = true;
955 break;
956 }
957 if (my_flag_changed)
958 break;
959 }
960 }
961 }
962 }
963
964 // If the current element's flag changed, we hadn't
965 // satisfied the level one rule.
966 if (my_flag_changed)
967 level_one_satisfied = false;
968
969 // Additionally, if it has non-local neighbors, and
970 // we're not in serial, then we'll eventually have to
971 // return compatible_with_refinement = false, because
972 // our change has to propagate to neighboring
973 // processors.
974 if (my_flag_changed && !_mesh.is_serial())
975 for (auto n : elem->side_index_range())
976 {
977 Elem * neigh =
978 topological_neighbor(elem, point_locator.get(), n);
979
980 if (!neigh)
981 continue;
982 if (neigh == remote_elem ||
983 neigh->processor_id() !=
984 this->processor_id())
985 {
986 compatible_with_refinement = false;
987 break;
988 }
989 // FIXME - for non-level one meshes we should
990 // test all descendants
991 if (neigh->has_children())
992 for (auto & child : neigh->child_ref_range())
993 if (&child == remote_elem ||
994 child.processor_id() !=
995 this->processor_id())
996 {
997 compatible_with_refinement = false;
998 break;
999 }
1000 }
1001 }
1002 }
1003 while (!level_one_satisfied);
1004
1005 } // end if (_face_level_mismatch_limit)
1006
1007
1008 // Next we look at all of the ancestor cells.
1009 // If there is a parent cell with all of its children
1010 // wanting to be unrefined then the element is a candidate
1011 // for unrefinement. If all the children don't
1012 // all want to be unrefined then ALL of them need to have their
1013 // unrefinement flags cleared.
1014 for (int level = max_level; level >= 0; level--)
1015 for (auto & elem : as_range(_mesh.level_elements_begin(level), _mesh.level_elements_end(level)))
1016 if (elem->ancestor())
1017 {
1018 // right now the element hasn't been disqualified
1019 // as a candidate for unrefinement
1020 bool is_a_candidate = true;
1021 bool found_remote_child = false;
1022
1023 for (auto & child : elem->child_ref_range())
1024 {
1025 if (&child == remote_elem)
1026 found_remote_child = true;
1027 else if ((child.refinement_flag() != Elem::COARSEN) ||
1028 !child.active() )
1029 is_a_candidate = false;
1030 }
1031
1032 if (!is_a_candidate && !found_remote_child)
1033 {
1034 elem->set_refinement_flag(Elem::INACTIVE);
1035
1036 for (auto & child : elem->child_ref_range())
1037 {
1038 if (&child == remote_elem)
1039 continue;
1040 if (child.refinement_flag() == Elem::COARSEN)
1041 {
1042 level_one_satisfied = false;
1043 child.set_refinement_flag(Elem::DO_NOTHING);
1044 }
1045 }
1046 }
1047 }
1048
1049 if (!level_one_satisfied && _face_level_mismatch_limit) goto repeat;
1050
1051
1052 // If all the children of a parent are set to be coarsened
1053 // then flag the parent so that they can kill their kids.
1054
1055 // On a distributed mesh, we won't always be able to determine this
1056 // on parent elements with remote children, even if we own the
1057 // parent, without communication.
1058 //
1059 // We'll first communicate *to* parents' owners when we determine
1060 // they cannot be coarsened, then we'll sync the final refinement
1061 // flag *from* the parents.
1062
1063 // uncoarsenable_parents[p] live on processor id p
1064 const processor_id_type n_proc = _mesh.n_processors();
1065 const processor_id_type my_proc_id = _mesh.processor_id();
1066 const bool distributed_mesh = !_mesh.is_replicated();
1067
1068 std::vector<std::vector<dof_id_type>>
1069 uncoarsenable_parents(n_proc);
1070
1071 for (auto & elem : as_range(_mesh.ancestor_elements_begin(), _mesh.ancestor_elements_end()))
1072 {
1073 // Presume all the children are flagged for coarsening and
1074 // then look for a contradiction
1075 bool all_children_flagged_for_coarsening = true;
1076
1077 for (auto & child : elem->child_ref_range())
1078 {
1079 if (&child != remote_elem &&
1080 child.refinement_flag() != Elem::COARSEN)
1081 {
1082 all_children_flagged_for_coarsening = false;
1083 if (!distributed_mesh)
1084 break;
1085 if (child.processor_id() != elem->processor_id())
1086 {
1087 uncoarsenable_parents[elem->processor_id()].push_back(elem->id());
1088 break;
1089 }
1090 }
1091 }
1092
1093 if (all_children_flagged_for_coarsening)
1094 elem->set_refinement_flag(Elem::COARSEN_INACTIVE);
1095 else
1096 elem->set_refinement_flag(Elem::INACTIVE);
1097 }
1098
1099 // If we have a distributed mesh, we might need to sync up
1100 // INACTIVE vs. COARSEN_INACTIVE flags.
1101 if (distributed_mesh)
1102 {
1103 // We'd better still be in sync here
1104 parallel_object_only();
1105
1106 Parallel::MessageTag
1107 uncoarsenable_tag = this->comm().get_unique_tag();
1108 std::vector<Parallel::Request> uncoarsenable_push_requests(n_proc-1);
1109
1110 for (processor_id_type p = 0; p != n_proc; ++p)
1111 {
1112 if (p == my_proc_id)
1113 continue;
1114
1115 Parallel::Request &request =
1116 uncoarsenable_push_requests[p - (p > my_proc_id)];
1117
1118 _mesh.comm().send
1119 (p, uncoarsenable_parents[p], request, uncoarsenable_tag);
1120 }
1121
1122 for (processor_id_type p = 1; p != n_proc; ++p)
1123 {
1124 std::vector<dof_id_type> my_uncoarsenable_parents;
1125 _mesh.comm().receive
1126 (Parallel::any_source, my_uncoarsenable_parents,
1127 uncoarsenable_tag);
1128
1129 for (const auto & id : my_uncoarsenable_parents)
1130 {
1131 Elem & elem = _mesh.elem_ref(id);
1132 libmesh_assert(elem.refinement_flag() == Elem::INACTIVE ||
1133 elem.refinement_flag() == Elem::COARSEN_INACTIVE);
1134 elem.set_refinement_flag(Elem::INACTIVE);
1135 }
1136 }
1137
1138 Parallel::wait(uncoarsenable_push_requests);
1139
1140 SyncRefinementFlags hsync(_mesh, &Elem::refinement_flag,
1141 &Elem::set_refinement_flag);
1142 sync_dofobject_data_by_id
1143 (this->comm(), _mesh.not_local_elements_begin(),
1144 _mesh.not_local_elements_end(),
1145 // We'd like a smaller sync, but this leads to bugs?
1146 // SyncCoarsenInactive(),
1147 hsync);
1148 }
1149
1150 // If one processor finds an incompatibility, we're globally
1151 // incompatible
1152 this->comm().min(compatible_with_refinement);
1153
1154 return compatible_with_refinement;
1155 }
1156
1157
1158
1159
1160
1161
1162
1163
make_refinement_compatible()1164 bool MeshRefinement::make_refinement_compatible()
1165 {
1166 // This function must be run on all processors at once
1167 parallel_object_only();
1168
1169 // We may need a PointLocator for topological_neighbor() tests
1170 // later, which we need to make sure gets constructed on all
1171 // processors at once.
1172 std::unique_ptr<PointLocatorBase> point_locator;
1173
1174 #ifdef LIBMESH_ENABLE_PERIODIC
1175 bool has_periodic_boundaries =
1176 _periodic_boundaries && !_periodic_boundaries->empty();
1177 libmesh_assert(this->comm().verify(has_periodic_boundaries));
1178
1179 if (has_periodic_boundaries)
1180 point_locator = _mesh.sub_point_locator();
1181 #endif
1182
1183 LOG_SCOPE ("make_refinement_compatible()", "MeshRefinement");
1184
1185 // Unless we encounter a specific situation we will be compatible
1186 // with any selected coarsening flags
1187 bool compatible_with_coarsening = true;
1188
1189 // This loop enforces the level-1 rule. We should only
1190 // execute it if the user indeed wants level-1 satisfied!
1191 if (_face_level_mismatch_limit)
1192 {
1193 // Unless we encounter a specific situation level-one
1194 // will be satisfied after executing this loop just once
1195 bool level_one_satisfied = true;
1196
1197 do
1198 {
1199 level_one_satisfied = true;
1200
1201 for (auto & elem : _mesh.active_element_ptr_range())
1202 {
1203 const unsigned short n_sides = elem->n_sides();
1204
1205 if (elem->refinement_flag() == Elem::REFINE) // If the element is active and the
1206 // h refinement flag is set
1207 {
1208 const unsigned int my_level = elem->level();
1209
1210 for (unsigned short side = 0; side != n_sides;
1211 ++side)
1212 {
1213 Elem * neighbor =
1214 topological_neighbor(elem, point_locator.get(), side);
1215
1216 if (neighbor != nullptr && // I have a
1217 neighbor != remote_elem && // neighbor here
1218 neighbor->active()) // and it is active
1219 {
1220 // Case 1: The neighbor is at the same level I am.
1221 // 1a: The neighbor will be refined -> NO PROBLEM
1222 // 1b: The neighbor won't be refined -> NO PROBLEM
1223 // 1c: The neighbor wants to be coarsened -> PROBLEM
1224 if (neighbor->level() == my_level)
1225 {
1226 if (neighbor->refinement_flag() == Elem::COARSEN)
1227 {
1228 neighbor->set_refinement_flag(Elem::DO_NOTHING);
1229 if (neighbor->parent())
1230 neighbor->parent()->set_refinement_flag(Elem::INACTIVE);
1231 compatible_with_coarsening = false;
1232 level_one_satisfied = false;
1233 }
1234 }
1235
1236
1237 // Case 2: The neighbor is one level lower than I am.
1238 // The neighbor thus MUST be refined to satisfy
1239 // the level-one rule, regardless of whether it
1240 // was originally flagged for refinement. If it
1241 // wasn't flagged already we need to repeat
1242 // this process.
1243 else if ((neighbor->level()+1) == my_level)
1244 {
1245 if (neighbor->refinement_flag() != Elem::REFINE)
1246 {
1247 neighbor->set_refinement_flag(Elem::REFINE);
1248 if (neighbor->parent())
1249 neighbor->parent()->set_refinement_flag(Elem::INACTIVE);
1250 compatible_with_coarsening = false;
1251 level_one_satisfied = false;
1252 }
1253 }
1254 #ifdef DEBUG
1255 // Note that the only other possibility is that the
1256 // neighbor is already refined, in which case it isn't
1257 // active and we should never get here.
1258 else
1259 libmesh_error_msg("ERROR: Neighbor level must be equal or 1 higher than mine.");
1260 #endif
1261 }
1262 }
1263 }
1264 if (elem->p_refinement_flag() == Elem::REFINE) // If the element is active and the
1265 // p refinement flag is set
1266 {
1267 const unsigned int my_p_level = elem->p_level();
1268
1269 for (unsigned int side=0; side != n_sides; side++)
1270 {
1271 Elem * neighbor =
1272 topological_neighbor(elem, point_locator.get(), side);
1273
1274 if (neighbor != nullptr && // I have a
1275 neighbor != remote_elem) // neighbor here
1276 {
1277 if (neighbor->active()) // and it is active
1278 {
1279 if (neighbor->p_level() < my_p_level &&
1280 neighbor->p_refinement_flag() != Elem::REFINE)
1281 {
1282 neighbor->set_p_refinement_flag(Elem::REFINE);
1283 level_one_satisfied = false;
1284 compatible_with_coarsening = false;
1285 }
1286 if (neighbor->p_level() == my_p_level &&
1287 neighbor->p_refinement_flag() == Elem::COARSEN)
1288 {
1289 neighbor->set_p_refinement_flag(Elem::DO_NOTHING);
1290 level_one_satisfied = false;
1291 compatible_with_coarsening = false;
1292 }
1293 }
1294 else // I have an inactive neighbor
1295 {
1296 libmesh_assert(neighbor->has_children());
1297 for (auto & subneighbor : neighbor->child_ref_range())
1298 if (&subneighbor != remote_elem && subneighbor.active() &&
1299 has_topological_neighbor(&subneighbor, point_locator.get(), elem))
1300 {
1301 if (subneighbor.p_level() < my_p_level &&
1302 subneighbor.p_refinement_flag() != Elem::REFINE)
1303 {
1304 // We should already be level one
1305 // compatible
1306 libmesh_assert_greater (subneighbor.p_level() + 2u,
1307 my_p_level);
1308 subneighbor.set_p_refinement_flag(Elem::REFINE);
1309 level_one_satisfied = false;
1310 compatible_with_coarsening = false;
1311 }
1312 if (subneighbor.p_level() == my_p_level &&
1313 subneighbor.p_refinement_flag() == Elem::COARSEN)
1314 {
1315 subneighbor.set_p_refinement_flag(Elem::DO_NOTHING);
1316 level_one_satisfied = false;
1317 compatible_with_coarsening = false;
1318 }
1319 }
1320 }
1321 }
1322 }
1323 }
1324 }
1325 }
1326
1327 while (!level_one_satisfied);
1328 } // end if (_face_level_mismatch_limit)
1329
1330 // If we're not compatible on one processor, we're globally not
1331 // compatible
1332 this->comm().min(compatible_with_coarsening);
1333
1334 return compatible_with_coarsening;
1335 }
1336
1337
1338
1339
_coarsen_elements()1340 bool MeshRefinement::_coarsen_elements ()
1341 {
1342 // This function must be run on all processors at once
1343 parallel_object_only();
1344
1345 LOG_SCOPE ("_coarsen_elements()", "MeshRefinement");
1346
1347 // Flags indicating if this call actually changes the mesh
1348 bool mesh_changed = false;
1349 bool mesh_p_changed = false;
1350
1351 // Clear the unused_elements data structure.
1352 // The elements have been packed since it was built,
1353 // so there are _no_ unused elements. We cannot trust
1354 // any iterators currently in this data structure.
1355 // _unused_elements.clear();
1356
1357 // Loop over the elements first to determine if the mesh will
1358 // undergo h-coarsening. If it will, then we'll need to communicate
1359 // more ghosted elements. We need to communicate them *before* we
1360 // do the coarsening; otherwise it is possible to coarsen away a
1361 // one-element-thick layer partition and leave the partitions on
1362 // either side unable to figure out how to talk to each other.
1363 for (auto & elem : _mesh.element_ptr_range())
1364 if (elem->refinement_flag() == Elem::COARSEN)
1365 {
1366 mesh_changed = true;
1367 break;
1368 }
1369
1370 // If the mesh changed on any processor, it changed globally
1371 this->comm().max(mesh_changed);
1372
1373 // And then we may need to widen the ghosting layers.
1374 if (mesh_changed)
1375 MeshCommunication().send_coarse_ghosts(_mesh);
1376
1377 for (auto & elem : _mesh.element_ptr_range())
1378 {
1379 // active elements flagged for coarsening will
1380 // no longer be deleted until MeshRefinement::contract()
1381 if (elem->refinement_flag() == Elem::COARSEN)
1382 {
1383 // Huh? no level-0 element should be active
1384 // and flagged for coarsening.
1385 libmesh_assert_not_equal_to (elem->level(), 0);
1386
1387 // Remove this element from any neighbor
1388 // lists that point to it.
1389 elem->nullify_neighbors();
1390
1391 // Remove any boundary information associated
1392 // with this element
1393 _mesh.get_boundary_info().remove (elem);
1394
1395 // Add this iterator to the _unused_elements
1396 // data structure so we might fill it.
1397 // The _unused_elements optimization is currently off.
1398 // _unused_elements.push_back (it);
1399
1400 // Don't delete the element until
1401 // MeshRefinement::contract()
1402 // _mesh.delete_elem(elem);
1403 }
1404
1405 // inactive elements flagged for coarsening
1406 // will become active
1407 else if (elem->refinement_flag() == Elem::COARSEN_INACTIVE)
1408 {
1409 elem->coarsen();
1410 libmesh_assert (elem->active());
1411
1412 // the mesh has certainly changed
1413 mesh_changed = true;
1414 }
1415 if (elem->p_refinement_flag() == Elem::COARSEN)
1416 {
1417 if (elem->p_level() > 0)
1418 {
1419 elem->set_p_refinement_flag(Elem::JUST_COARSENED);
1420 elem->set_p_level(elem->p_level() - 1);
1421 mesh_p_changed = true;
1422 }
1423 else
1424 {
1425 elem->set_p_refinement_flag(Elem::DO_NOTHING);
1426 }
1427 }
1428 }
1429
1430 this->comm().max(mesh_p_changed);
1431
1432 // And we may need to update DistributedMesh values reflecting the changes
1433 if (mesh_changed)
1434 _mesh.update_parallel_id_counts();
1435
1436 // Node processor ids may need to change if an element of that id
1437 // was coarsened away
1438 if (mesh_changed && !_mesh.is_serial())
1439 {
1440 // Update the _new_nodes_map so that processors can
1441 // find requested nodes
1442 this->update_nodes_map ();
1443
1444 MeshCommunication().make_nodes_parallel_consistent (_mesh);
1445
1446 // Clear the _new_nodes_map
1447 this->clear();
1448
1449 #ifdef DEBUG
1450 MeshTools::libmesh_assert_valid_procids<Node>(_mesh);
1451 #endif
1452 }
1453
1454 // If p levels changed all we need to do is make sure that parent p
1455 // levels changed in sync
1456 if (mesh_p_changed && !_mesh.is_serial())
1457 {
1458 MeshCommunication().make_p_levels_parallel_consistent (_mesh);
1459 }
1460
1461 return (mesh_changed || mesh_p_changed);
1462 }
1463
1464
1465
_refine_elements()1466 bool MeshRefinement::_refine_elements ()
1467 {
1468 libmesh_assert(_mesh.is_prepared() || _mesh.is_replicated());
1469
1470 // This function must be run on all processors at once
1471 parallel_object_only();
1472
1473 // Update the _new_nodes_map so that elements can
1474 // find nodes to connect to.
1475 this->update_nodes_map ();
1476
1477 LOG_SCOPE ("_refine_elements()", "MeshRefinement");
1478
1479 // Iterate over the elements, counting the elements
1480 // flagged for h refinement.
1481 dof_id_type n_elems_flagged = 0;
1482
1483 for (auto & elem : _mesh.element_ptr_range())
1484 if (elem->refinement_flag() == Elem::REFINE)
1485 n_elems_flagged++;
1486
1487 // Construct a local vector of Elem * which have been
1488 // previously marked for refinement. We reserve enough
1489 // space to allow for every element to be refined.
1490 std::vector<Elem *> local_copy_of_elements;
1491 local_copy_of_elements.reserve(n_elems_flagged);
1492
1493 // If mesh p levels changed, we might need to synchronize parent p
1494 // levels on a distributed mesh.
1495 bool mesh_p_changed = false;
1496
1497 // Iterate over the elements, looking for elements flagged for
1498 // refinement.
1499
1500 // If we are on a ReplicatedMesh, then we just do the refinement in
1501 // the same order on every processor and everything stays in sync.
1502
1503 // If we are on a DistributedMesh, that's impossible.
1504 //
1505 // If the mesh is distributed, we need to make sure that if we end
1506 // up as the owner of a new node, which might happen if that node is
1507 // attached to one of our own elements, then we have given it a
1508 // legitimate node id and our own processor id. We generate
1509 // legitimate node ids and use our own processor id when we are
1510 // refining our own elements but not when we refine others'
1511 // elements. Therefore we want to refine our own elements *first*,
1512 // thereby generating all nodes which might belong to us, and then
1513 // refine others' elements *after*, thereby generating nodes with
1514 // temporary ids which we know we will discard.
1515 //
1516 // Even if the DistributedMesh is serialized, we can't just treat it
1517 // like a ReplicatedMesh, because DistributedMesh doesn't *trust*
1518 // users to refine partitioned elements in a serialized way, so it
1519 // assigns temporary ids, so we need to synchronize ids afterward to
1520 // be safe anyway, so we might as well use the distributed mesh code
1521 // path.
1522 for (auto & elem : _mesh.is_replicated() ? _mesh.active_element_ptr_range() : _mesh.active_local_element_ptr_range())
1523 {
1524 if (elem->refinement_flag() == Elem::REFINE)
1525 local_copy_of_elements.push_back(elem);
1526 if (elem->p_refinement_flag() == Elem::REFINE &&
1527 elem->active())
1528 {
1529 elem->set_p_level(elem->p_level()+1);
1530 elem->set_p_refinement_flag(Elem::JUST_REFINED);
1531 mesh_p_changed = true;
1532 }
1533 }
1534
1535 if (!_mesh.is_replicated())
1536 {
1537 for (auto & elem : as_range(_mesh.active_not_local_elements_begin(),
1538 _mesh.active_not_local_elements_end()))
1539 {
1540 if (elem->refinement_flag() == Elem::REFINE)
1541 local_copy_of_elements.push_back(elem);
1542 if (elem->p_refinement_flag() == Elem::REFINE &&
1543 elem->active())
1544 {
1545 elem->set_p_level(elem->p_level()+1);
1546 elem->set_p_refinement_flag(Elem::JUST_REFINED);
1547 mesh_p_changed = true;
1548 }
1549 }
1550 }
1551
1552 // Now iterate over the local copies and refine each one.
1553 // This may resize the mesh's internal container and invalidate
1554 // any existing iterators.
1555 for (auto & elem : local_copy_of_elements)
1556 elem->refine(*this);
1557
1558 // The mesh changed if there were elements h refined
1559 bool mesh_changed = !local_copy_of_elements.empty();
1560
1561 // If the mesh changed on any processor, it changed globally
1562 this->comm().max(mesh_changed);
1563 this->comm().max(mesh_p_changed);
1564
1565 // And we may need to update DistributedMesh values reflecting the changes
1566 if (mesh_changed)
1567 _mesh.update_parallel_id_counts();
1568
1569 if (mesh_changed && !_mesh.is_replicated())
1570 {
1571 MeshCommunication().make_elems_parallel_consistent (_mesh);
1572 MeshCommunication().make_new_nodes_parallel_consistent (_mesh);
1573 #ifdef DEBUG
1574 _mesh.libmesh_assert_valid_parallel_ids();
1575 #endif
1576 }
1577
1578 // If we're refining a ReplicatedMesh, then we haven't yet assigned
1579 // node processor ids. But if we're refining a partitioned
1580 // ReplicatedMesh, then we *need* to assign node processor ids.
1581 if (mesh_changed && _mesh.is_replicated() &&
1582 (_mesh.unpartitioned_elements_begin() ==
1583 _mesh.unpartitioned_elements_end()))
1584 Partitioner::set_node_processor_ids(_mesh);
1585
1586 if (mesh_p_changed && !_mesh.is_replicated())
1587 {
1588 MeshCommunication().make_p_levels_parallel_consistent (_mesh);
1589 }
1590
1591 // Clear the _new_nodes_map and _unused_elements data structures.
1592 this->clear();
1593
1594 return (mesh_changed || mesh_p_changed);
1595 }
1596
1597
_smooth_flags(bool refining,bool coarsening)1598 void MeshRefinement::_smooth_flags(bool refining, bool coarsening)
1599 {
1600 // Smoothing can break in weird ways on a mesh with broken topology
1601 #ifdef DEBUG
1602 MeshTools::libmesh_assert_valid_neighbors(_mesh);
1603 #endif
1604
1605 // Repeat until flag changes match on every processor
1606 do
1607 {
1608 // Repeat until coarsening & refinement flags jive
1609 bool satisfied = false;
1610 do
1611 {
1612 // If we're refining or coarsening, hit the corresponding
1613 // face level test code. Short-circuiting || is our friend
1614 const bool coarsening_satisfied =
1615 !coarsening ||
1616 this->make_coarsening_compatible();
1617
1618 const bool refinement_satisfied =
1619 !refining ||
1620 this->make_refinement_compatible();
1621
1622 bool smoothing_satisfied =
1623 !this->eliminate_unrefined_patches();// &&
1624
1625 if (_edge_level_mismatch_limit)
1626 smoothing_satisfied = smoothing_satisfied &&
1627 !this->limit_level_mismatch_at_edge (_edge_level_mismatch_limit);
1628
1629 if (_node_level_mismatch_limit)
1630 smoothing_satisfied = smoothing_satisfied &&
1631 !this->limit_level_mismatch_at_node (_node_level_mismatch_limit);
1632
1633 if (_overrefined_boundary_limit>=0)
1634 smoothing_satisfied = smoothing_satisfied &&
1635 !this->limit_overrefined_boundary(_overrefined_boundary_limit);
1636
1637 if (_underrefined_boundary_limit>=0)
1638 smoothing_satisfied = smoothing_satisfied &&
1639 !this->limit_underrefined_boundary(_underrefined_boundary_limit);
1640
1641 satisfied = (coarsening_satisfied &&
1642 refinement_satisfied &&
1643 smoothing_satisfied);
1644
1645 libmesh_assert(this->comm().verify(satisfied));
1646 }
1647 while (!satisfied);
1648 }
1649 while (!_mesh.is_serial() && !this->make_flags_parallel_consistent());
1650 }
1651
1652
uniformly_p_refine(unsigned int n)1653 void MeshRefinement::uniformly_p_refine (unsigned int n)
1654 {
1655 // Refine n times
1656 for (unsigned int rstep=0; rstep<n; rstep++)
1657 for (auto & elem : _mesh.active_element_ptr_range())
1658 {
1659 // P refine all the active elements
1660 elem->set_p_level(elem->p_level()+1);
1661 elem->set_p_refinement_flag(Elem::JUST_REFINED);
1662 }
1663 }
1664
1665
1666
uniformly_p_coarsen(unsigned int n)1667 void MeshRefinement::uniformly_p_coarsen (unsigned int n)
1668 {
1669 // Coarsen p times
1670 for (unsigned int rstep=0; rstep<n; rstep++)
1671 for (auto & elem : _mesh.active_element_ptr_range())
1672 if (elem->p_level() > 0)
1673 {
1674 // P coarsen all the active elements
1675 elem->set_p_level(elem->p_level()-1);
1676 elem->set_p_refinement_flag(Elem::JUST_COARSENED);
1677 }
1678 }
1679
1680
1681
uniformly_refine(unsigned int n)1682 void MeshRefinement::uniformly_refine (unsigned int n)
1683 {
1684 // Refine n times
1685 // FIXME - this won't work if n>1 and the mesh
1686 // has already been attached to an equation system
1687 for (unsigned int rstep=0; rstep<n; rstep++)
1688 {
1689 // Clean up the refinement flags
1690 this->clean_refinement_flags();
1691
1692 // Flag all the active elements for refinement.
1693 for (auto & elem : _mesh.active_element_ptr_range())
1694 elem->set_refinement_flag(Elem::REFINE);
1695
1696 // Refine all the elements we just flagged.
1697 this->_refine_elements();
1698 }
1699
1700 // Finally, the new mesh probably needs to be prepared for use
1701 if (n > 0)
1702 _mesh.prepare_for_use ();
1703 }
1704
1705
1706
uniformly_coarsen(unsigned int n)1707 void MeshRefinement::uniformly_coarsen (unsigned int n)
1708 {
1709 // Coarsen n times
1710 for (unsigned int rstep=0; rstep<n; rstep++)
1711 {
1712 // Clean up the refinement flags
1713 this->clean_refinement_flags();
1714
1715 // Flag all the active elements for coarsening.
1716 for (auto & elem : _mesh.active_element_ptr_range())
1717 {
1718 elem->set_refinement_flag(Elem::COARSEN);
1719 if (elem->parent())
1720 elem->parent()->set_refinement_flag(Elem::COARSEN_INACTIVE);
1721 }
1722
1723 // On a distributed mesh, we may have parent elements with
1724 // remote active children. To keep flags consistent, we'll need
1725 // a communication step.
1726 if (!_mesh.is_replicated())
1727 {
1728 const processor_id_type n_proc = _mesh.n_processors();
1729 const processor_id_type my_proc_id = _mesh.processor_id();
1730
1731 std::vector<std::vector<dof_id_type>>
1732 parents_to_coarsen(n_proc);
1733
1734 for (const auto & elem : as_range(_mesh.ancestor_elements_begin(), _mesh.ancestor_elements_end()))
1735 if (elem->processor_id() != my_proc_id &&
1736 elem->refinement_flag() == Elem::COARSEN_INACTIVE)
1737 parents_to_coarsen[elem->processor_id()].push_back(elem->id());
1738
1739 Parallel::MessageTag
1740 coarsen_tag = this->comm().get_unique_tag();
1741 std::vector<Parallel::Request> coarsen_push_requests(n_proc-1);
1742
1743 for (processor_id_type p = 0; p != n_proc; ++p)
1744 {
1745 if (p == my_proc_id)
1746 continue;
1747
1748 Parallel::Request &request =
1749 coarsen_push_requests[p - (p > my_proc_id)];
1750
1751 _mesh.comm().send
1752 (p, parents_to_coarsen[p], request, coarsen_tag);
1753 }
1754
1755 for (processor_id_type p = 1; p != n_proc; ++p)
1756 {
1757 std::vector<dof_id_type> my_parents_to_coarsen;
1758 _mesh.comm().receive
1759 (Parallel::any_source, my_parents_to_coarsen,
1760 coarsen_tag);
1761
1762 for (const auto & id : my_parents_to_coarsen)
1763 {
1764 Elem & elem = _mesh.elem_ref(id);
1765 libmesh_assert(elem.refinement_flag() == Elem::INACTIVE ||
1766 elem.refinement_flag() == Elem::COARSEN_INACTIVE);
1767 elem.set_refinement_flag(Elem::COARSEN_INACTIVE);
1768 }
1769 }
1770
1771 Parallel::wait(coarsen_push_requests);
1772
1773 SyncRefinementFlags hsync(_mesh, &Elem::refinement_flag,
1774 &Elem::set_refinement_flag);
1775 sync_dofobject_data_by_id
1776 (this->comm(), _mesh.not_local_elements_begin(),
1777 _mesh.not_local_elements_end(),
1778 // We'd like a smaller sync, but this leads to bugs?
1779 // SyncCoarsenInactive(),
1780 hsync);
1781 }
1782
1783 // Coarsen all the elements we just flagged.
1784 this->_coarsen_elements();
1785 }
1786
1787
1788 // Finally, the new mesh probably needs to be prepared for use
1789 if (n > 0)
1790 _mesh.prepare_for_use ();
1791 }
1792
1793
1794
topological_neighbor(Elem * elem,const PointLocatorBase * point_locator,const unsigned int side)1795 Elem * MeshRefinement::topological_neighbor(Elem * elem,
1796 const PointLocatorBase * point_locator,
1797 const unsigned int side)
1798 {
1799 #ifdef LIBMESH_ENABLE_PERIODIC
1800 if (_periodic_boundaries && !_periodic_boundaries->empty())
1801 {
1802 libmesh_assert(point_locator);
1803 return elem->topological_neighbor(side, _mesh, *point_locator, _periodic_boundaries);
1804 }
1805 #endif
1806 return elem->neighbor_ptr(side);
1807 }
1808
1809
1810
has_topological_neighbor(const Elem * elem,const PointLocatorBase * point_locator,const Elem * neighbor)1811 bool MeshRefinement::has_topological_neighbor(const Elem * elem,
1812 const PointLocatorBase * point_locator,
1813 const Elem * neighbor)
1814 {
1815 #ifdef LIBMESH_ENABLE_PERIODIC
1816 if (_periodic_boundaries && !_periodic_boundaries->empty())
1817 {
1818 libmesh_assert(point_locator);
1819 return elem->has_topological_neighbor(neighbor, _mesh, *point_locator, _periodic_boundaries);
1820 }
1821 #endif
1822 return elem->has_neighbor(neighbor);
1823 }
1824
1825
1826
1827 } // namespace libMesh
1828
1829
1830 #endif
1831