deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
refinement.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2019 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13
14#include <deal.II/base/config.h>
15
16#include <deal.II/base/mpi.h>
17
22
25
28
30
32#include <deal.II/lac/vector.h>
33
34#include <limits>
35
37
38namespace hp
39{
40 namespace Refinement
41 {
45 template <int dim, int spacedim>
46 void
48 {
49 if (dof_handler.get_fe_collection().empty())
50 // nothing to do
51 return;
52
53 Assert(dof_handler.has_hp_capabilities(),
55
56 std::vector<bool> p_flags(
57 dof_handler.get_triangulation().n_active_cells(), true);
58
59 p_adaptivity_from_flags(dof_handler, p_flags);
60 }
61
62
63
64 template <int dim, int spacedim>
65 void
67 const std::vector<bool> &p_flags)
68 {
69 if (dof_handler.get_fe_collection().empty())
70 // nothing to do
71 return;
72
73 Assert(dof_handler.has_hp_capabilities(),
76 p_flags.size());
77
78 for (const auto &cell : dof_handler.active_cell_iterators())
79 if (cell->is_locally_owned() && p_flags[cell->active_cell_index()])
80 {
81 if (cell->refine_flag_set())
82 {
83 const unsigned int super_fe_index =
85 cell->active_fe_index());
86
87 // Reject update if already most superordinate element.
88 if (super_fe_index != cell->active_fe_index())
89 cell->set_future_fe_index(super_fe_index);
90 }
91 else if (cell->coarsen_flag_set())
92 {
93 const unsigned int sub_fe_index =
95 cell->active_fe_index());
96
97 // Reject update if already least subordinate element.
98 if (sub_fe_index != cell->active_fe_index())
99 cell->set_future_fe_index(sub_fe_index);
100 }
101 }
102 }
103
104
105
106 template <int dim, typename Number, int spacedim>
107 void
109 const DoFHandler<dim, spacedim> &dof_handler,
110 const Vector<Number> &criteria,
111 const Number p_refine_threshold,
112 const Number p_coarsen_threshold,
114 &compare_refine,
116 &compare_coarsen)
117 {
118 if (dof_handler.get_fe_collection().empty())
119 // nothing to do
120 return;
121
122 Assert(dof_handler.has_hp_capabilities(),
125 criteria.size());
126
127 std::vector<bool> p_flags(
128 dof_handler.get_triangulation().n_active_cells(), false);
129
130 for (const auto &cell : dof_handler.active_cell_iterators())
131 if (cell->is_locally_owned() &&
132 ((cell->refine_flag_set() &&
133 compare_refine(criteria[cell->active_cell_index()],
134 p_refine_threshold)) ||
135 (cell->coarsen_flag_set() &&
136 compare_coarsen(criteria[cell->active_cell_index()],
137 p_coarsen_threshold))))
138 p_flags[cell->active_cell_index()] = true;
139
140 p_adaptivity_from_flags(dof_handler, p_flags);
141 }
142
143
144
145 template <int dim, typename Number, int spacedim>
146 void
148 const DoFHandler<dim, spacedim> &dof_handler,
149 const Vector<Number> &criteria,
150 const double p_refine_fraction,
151 const double p_coarsen_fraction,
153 &compare_refine,
155 &compare_coarsen)
156 {
157 if (dof_handler.get_fe_collection().empty())
158 // nothing to do
159 return;
160
161 Assert(dof_handler.has_hp_capabilities(),
164 criteria.size());
165 Assert((p_refine_fraction >= 0) && (p_refine_fraction <= 1),
167 Assert((p_coarsen_fraction >= 0) && (p_coarsen_fraction <= 1),
169
170 // We first have to determine the maximal and minimal values of the
171 // criteria of all flagged cells.
172 Number max_criterion_refine = std::numeric_limits<Number>::lowest(),
173 min_criterion_refine = std::numeric_limits<Number>::max();
174 Number max_criterion_coarsen = max_criterion_refine,
175 min_criterion_coarsen = min_criterion_refine;
176
177 for (const auto &cell : dof_handler.active_cell_iterators())
178 if (cell->is_locally_owned())
179 {
180 if (cell->refine_flag_set())
181 {
182 max_criterion_refine =
183 std::max(max_criterion_refine,
184 criteria(cell->active_cell_index()));
185 min_criterion_refine =
186 std::min(min_criterion_refine,
187 criteria(cell->active_cell_index()));
188 }
189 else if (cell->coarsen_flag_set())
190 {
191 max_criterion_coarsen =
192 std::max(max_criterion_coarsen,
193 criteria(cell->active_cell_index()));
194 min_criterion_coarsen =
195 std::min(min_criterion_coarsen,
196 criteria(cell->active_cell_index()));
197 }
198 }
199
200 const parallel::TriangulationBase<dim, spacedim> *parallel_tria =
201 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
202 &dof_handler.get_triangulation());
203 if (parallel_tria != nullptr &&
205 &dof_handler.get_triangulation()) == nullptr)
206 {
207 max_criterion_refine =
208 Utilities::MPI::max(max_criterion_refine,
209 parallel_tria->get_mpi_communicator());
210 min_criterion_refine =
211 Utilities::MPI::min(min_criterion_refine,
212 parallel_tria->get_mpi_communicator());
213 max_criterion_coarsen =
214 Utilities::MPI::max(max_criterion_coarsen,
215 parallel_tria->get_mpi_communicator());
216 min_criterion_coarsen =
217 Utilities::MPI::min(min_criterion_coarsen,
218 parallel_tria->get_mpi_communicator());
219 }
220
221 // Absent any better strategies, we will set the threshold by linear
222 // interpolation for both classes of cells individually.
223 const Number threshold_refine =
224 min_criterion_refine +
225 p_refine_fraction *
226 (max_criterion_refine - min_criterion_refine),
227 threshold_coarsen =
228 min_criterion_coarsen +
229 p_coarsen_fraction *
230 (max_criterion_coarsen - min_criterion_coarsen);
231
233 criteria,
234 threshold_refine,
235 threshold_coarsen,
236 compare_refine,
237 compare_coarsen);
238 }
239
240
241
242 template <int dim, typename Number, int spacedim>
243 void
245 const DoFHandler<dim, spacedim> &dof_handler,
246 const Vector<Number> &criteria,
247 const double p_refine_fraction,
248 const double p_coarsen_fraction,
250 &compare_refine,
252 &compare_coarsen)
253 {
254 if (dof_handler.get_fe_collection().empty())
255 // nothing to do
256 return;
257
258 Assert(dof_handler.has_hp_capabilities(),
261 criteria.size());
262 Assert((p_refine_fraction >= 0) && (p_refine_fraction <= 1),
264 Assert((p_coarsen_fraction >= 0) && (p_coarsen_fraction <= 1),
266
267 // ComparisonFunction returning 'true' or 'false' for any set of
268 // parameters. These will be used to overwrite user-provided comparison
269 // functions whenever no actual comparison is required in the decision
270 // process, i.e. when no or all cells will be refined or coarsened.
271 const ComparisonFunction<Number> compare_false =
272 [](const Number &, const Number &) { return false; };
273 const ComparisonFunction<Number> compare_true =
274 [](const Number &, const Number &) { return true; };
275
276 // 1.) First extract from the vector of indicators the ones that
277 // correspond to cells that we locally own.
278 unsigned int n_flags_refinement = 0;
279 unsigned int n_flags_coarsening = 0;
280 Vector<Number> indicators_refinement(
281 dof_handler.get_triangulation().n_active_cells());
282 Vector<Number> indicators_coarsening(
283 dof_handler.get_triangulation().n_active_cells());
284 for (const auto &cell :
286 if (!cell->is_artificial() && cell->is_locally_owned())
287 {
288 if (cell->refine_flag_set())
289 indicators_refinement(n_flags_refinement++) =
290 criteria(cell->active_cell_index());
291 else if (cell->coarsen_flag_set())
292 indicators_coarsening(n_flags_coarsening++) =
293 criteria(cell->active_cell_index());
294 }
295 indicators_refinement.grow_or_shrink(n_flags_refinement);
296 indicators_coarsening.grow_or_shrink(n_flags_coarsening);
297
298 // 2.) Determine the number of cells for p-refinement and p-coarsening on
299 // basis of the flagged cells.
300 //
301 // 3.) Find thresholds for p-refinement and p-coarsening on only those
302 // cells flagged for adaptation.
303 //
304 // For cases in which no or all cells flagged for refinement and/or
305 // coarsening are subject to p-adaptation, we usually pick thresholds
306 // that apply to all or none of the cells at once. However here, we
307 // do not know which threshold would suffice for this task because the
308 // user could provide any comparison function. Thus if necessary, we
309 // overwrite the user's choice with suitable functions simply
310 // returning 'true' and 'false' for any cell with reference wrappers.
311 // Thus, no function object copies are stored.
312 //
313 // 4.) Perform p-adaptation with absolute thresholds.
314 Number threshold_refinement = 0.;
315 Number threshold_coarsening = 0.;
316 auto reference_compare_refine = std::cref(compare_refine);
317 auto reference_compare_coarsen = std::cref(compare_coarsen);
318
319 const parallel::TriangulationBase<dim, spacedim> *parallel_tria =
320 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
321 &dof_handler.get_triangulation());
322 if (parallel_tria != nullptr &&
324 &dof_handler.get_triangulation()) == nullptr)
325 {
326#ifndef DEAL_II_WITH_P4EST
328#else
329 //
330 // parallel implementation with distributed memory
331 //
332
333 MPI_Comm mpi_communicator = parallel_tria->get_mpi_communicator();
334
335 // 2.) Communicate the number of cells scheduled for p-adaptation
336 // globally.
337 const unsigned int n_global_flags_refinement =
338 Utilities::MPI::sum(n_flags_refinement, mpi_communicator);
339 const unsigned int n_global_flags_coarsening =
340 Utilities::MPI::sum(n_flags_coarsening, mpi_communicator);
341
342 const unsigned int target_index_refinement =
343 static_cast<unsigned int>(
344 std::floor(p_refine_fraction * n_global_flags_refinement));
345 const unsigned int target_index_coarsening =
346 static_cast<unsigned int>(
347 std::ceil((1 - p_coarsen_fraction) * n_global_flags_coarsening));
348
349 // 3.) Figure out the global max and min of the criteria. We don't
350 // need it here, but it's a collective communication call.
351 const std::pair<Number, Number> global_min_max_refinement =
353 compute_global_min_and_max_at_root(indicators_refinement,
354 mpi_communicator);
355
356 const std::pair<Number, Number> global_min_max_coarsening =
358 compute_global_min_and_max_at_root(indicators_coarsening,
359 mpi_communicator);
360
361 // 3.) Compute thresholds if necessary.
362 if (target_index_refinement == 0)
363 reference_compare_refine = std::cref(compare_false);
364 else if (target_index_refinement == n_global_flags_refinement)
365 reference_compare_refine = std::cref(compare_true);
366 else
367 threshold_refinement = internal::parallel::distributed::
369 indicators_refinement,
370 global_min_max_refinement,
371 target_index_refinement,
372 mpi_communicator);
373
374 if (target_index_coarsening == n_global_flags_coarsening)
375 reference_compare_coarsen = std::cref(compare_false);
376 else if (target_index_coarsening == 0)
377 reference_compare_coarsen = std::cref(compare_true);
378 else
379 threshold_coarsening = internal::parallel::distributed::
381 indicators_coarsening,
382 global_min_max_coarsening,
383 target_index_coarsening,
384 mpi_communicator);
385#endif
386 }
387 else
388 {
389 //
390 // serial implementation (and parallel::shared implementation)
391 //
392
393 // 2.) Determine the number of cells scheduled for p-adaptation.
394 const unsigned int n_p_refine_cells = static_cast<unsigned int>(
395 std::floor(p_refine_fraction * n_flags_refinement));
396 const unsigned int n_p_coarsen_cells = static_cast<unsigned int>(
397 std::floor(p_coarsen_fraction * n_flags_coarsening));
398
399 // 3.) Compute thresholds if necessary.
400 if (n_p_refine_cells == 0)
401 reference_compare_refine = std::cref(compare_false);
402 else if (n_p_refine_cells == n_flags_refinement)
403 reference_compare_refine = std::cref(compare_true);
404 else
405 {
406 std::nth_element(indicators_refinement.begin(),
407 indicators_refinement.begin() +
408 n_p_refine_cells - 1,
409 indicators_refinement.end(),
410 std::greater<Number>());
411 threshold_refinement =
412 *(indicators_refinement.begin() + n_p_refine_cells - 1);
413 }
414
415 if (n_p_coarsen_cells == 0)
416 reference_compare_coarsen = std::cref(compare_false);
417 else if (n_p_coarsen_cells == n_flags_coarsening)
418 reference_compare_coarsen = std::cref(compare_true);
419 else
420 {
421 std::nth_element(indicators_coarsening.begin(),
422 indicators_coarsening.begin() +
423 n_p_coarsen_cells - 1,
424 indicators_coarsening.end(),
425 std::less<Number>());
426 threshold_coarsening =
427 *(indicators_coarsening.begin() + n_p_coarsen_cells - 1);
428 }
429 }
430
431 // 4.) Finally perform adaptation.
433 criteria,
434 threshold_refinement,
435 threshold_coarsening,
436 std::cref(reference_compare_refine),
437 std::cref(
438 reference_compare_coarsen));
439 }
440
441
442
443 template <int dim, typename Number, int spacedim>
444 void
446 const Vector<Number> &sobolev_indices)
447 {
448 if (dof_handler.get_fe_collection().empty())
449 // nothing to do
450 return;
451
452 Assert(dof_handler.has_hp_capabilities(),
455 sobolev_indices.size());
456
457 for (const auto &cell : dof_handler.active_cell_iterators())
458 if (cell->is_locally_owned())
459 {
460 if (cell->refine_flag_set())
461 {
462 const unsigned int super_fe_index =
464 cell->active_fe_index());
465
466 // Reject update if already most superordinate element.
467 if (super_fe_index != cell->active_fe_index())
468 {
469 const unsigned int super_fe_degree =
470 dof_handler.get_fe_collection()[super_fe_index].degree;
471
472 if (sobolev_indices[cell->active_cell_index()] >
473 super_fe_degree)
474 cell->set_future_fe_index(super_fe_index);
475 }
476 }
477 else if (cell->coarsen_flag_set())
478 {
479 const unsigned int sub_fe_index =
481 cell->active_fe_index());
482
483 // Reject update if already least subordinate element.
484 if (sub_fe_index != cell->active_fe_index())
485 {
486 const unsigned int sub_fe_degree =
487 dof_handler.get_fe_collection()[sub_fe_index].degree;
488
489 if (sobolev_indices[cell->active_cell_index()] <
490 sub_fe_degree)
491 cell->set_future_fe_index(sub_fe_index);
492 }
493 }
494 }
495 }
496
497
498
499 template <int dim, typename Number, int spacedim>
500 void
502 const DoFHandler<dim, spacedim> &dof_handler,
503 const Vector<Number> &criteria,
504 const Vector<Number> &references,
506 &compare_refine,
508 &compare_coarsen)
509 {
510 if (dof_handler.get_fe_collection().empty())
511 // nothing to do
512 return;
513
514 Assert(dof_handler.has_hp_capabilities(),
517 criteria.size());
519 references.size());
520
521 std::vector<bool> p_flags(
522 dof_handler.get_triangulation().n_active_cells(), false);
523
524 for (const auto &cell : dof_handler.active_cell_iterators())
525 if (cell->is_locally_owned() &&
526 ((cell->refine_flag_set() &&
527 compare_refine(criteria[cell->active_cell_index()],
528 references[cell->active_cell_index()])) ||
529 (cell->coarsen_flag_set() &&
530 compare_coarsen(criteria[cell->active_cell_index()],
531 references[cell->active_cell_index()]))))
532 p_flags[cell->active_cell_index()] = true;
533
534 p_adaptivity_from_flags(dof_handler, p_flags);
535 }
536
537
538
542 template <int dim, typename Number, int spacedim>
543 void
545 const Vector<Number> &error_indicators,
546 Vector<Number> &predicted_errors,
547 const double gamma_p,
548 const double gamma_h,
549 const double gamma_n)
550 {
551 if (dof_handler.get_fe_collection().empty())
552 // nothing to do
553 return;
554
556 error_indicators.size());
558 predicted_errors.size());
559 Assert(0 < gamma_p && gamma_p < 1,
563
564 // auxiliary variables
565 unsigned int future_fe_degree = numbers::invalid_unsigned_int;
566 unsigned int parent_future_fe_index = numbers::invalid_unsigned_int;
567 // store all determined future finite element indices on parent cells for
568 // coarsening
569 std::map<typename DoFHandler<dim, spacedim>::cell_iterator, unsigned int>
570 future_fe_indices_on_coarsened_cells;
571
572 // deep copy error indicators
573 predicted_errors = error_indicators;
574
575 for (const auto &cell : dof_handler.active_cell_iterators() |
577 {
578 // current cell will not be adapted
579 if (!(cell->future_fe_index_set()) && !(cell->refine_flag_set()) &&
580 !(cell->coarsen_flag_set()))
581 {
582 predicted_errors[cell->active_cell_index()] *= gamma_n;
583 continue;
584 }
585
586 // current cell will be adapted
587 // determine degree of its future finite element
588 if (cell->coarsen_flag_set())
589 {
590 Assert(cell->level() > 0,
591 ExcMessage("A coarse cell is flagged for coarsening. "
592 "Please read the note in the documentation "
593 "of predict_error()."));
594
595 // cell will be coarsened, thus determine future finite element
596 // on parent cell
597 const auto &parent = cell->parent();
598 if (future_fe_indices_on_coarsened_cells.find(parent) ==
599 future_fe_indices_on_coarsened_cells.end())
600 {
601 if constexpr (running_in_debug_mode())
602 {
603 for (const auto &child : parent->child_iterators())
604 Assert(
605 child->is_active() && child->coarsen_flag_set(),
607 }
608
609 parent_future_fe_index =
610 internal::hp::DoFHandlerImplementation::
611 dominated_future_fe_on_children<dim, spacedim>(parent);
612
613 future_fe_indices_on_coarsened_cells.insert(
614 {parent, parent_future_fe_index});
615 }
616 else
617 {
618 parent_future_fe_index =
619 future_fe_indices_on_coarsened_cells[parent];
620 }
621
622 future_fe_degree =
623 dof_handler.get_fe_collection()[parent_future_fe_index].degree;
624 }
625 else
626 {
627 // future finite element on current cell is already set
628 future_fe_degree =
629 dof_handler.get_fe_collection()[cell->future_fe_index()].degree;
630 }
631
632 // step 1: exponential decay with p-adaptation
633 if (cell->future_fe_index_set())
634 {
635 if (future_fe_degree > cell->get_fe().degree)
636 predicted_errors[cell->active_cell_index()] *=
637 Utilities::pow(gamma_p,
638 future_fe_degree - cell->get_fe().degree);
639 else if (future_fe_degree < cell->get_fe().degree)
640 predicted_errors[cell->active_cell_index()] /=
641 Utilities::pow(gamma_p,
642 cell->get_fe().degree - future_fe_degree);
643 else
644 {
645 // The two degrees are the same; we do not need to
646 // adapt the predicted error
647 }
648 }
649
650 // step 2: algebraic decay with h-adaptation
651 if (cell->refine_flag_set())
652 {
653 predicted_errors[cell->active_cell_index()] *=
654 (gamma_h * Utilities::pow(.5, future_fe_degree));
655
656 // predicted error will be split on children cells
657 // after adaptation via CellDataTransfer
658 }
659 else if (cell->coarsen_flag_set())
660 {
661 predicted_errors[cell->active_cell_index()] /=
662 (gamma_h * Utilities::pow(.5, future_fe_degree));
663
664 // predicted error will be summed up on parent cell
665 // after adaptation via CellDataTransfer
666 }
667 }
668 }
669
670
671
675 template <int dim, int spacedim>
676 void
678 {
679 if (dof_handler.get_fe_collection().empty())
680 // nothing to do
681 return;
682
683 Assert(dof_handler.has_hp_capabilities(),
685
686 for (const auto &cell : dof_handler.active_cell_iterators())
687 if (cell->is_locally_owned() && cell->future_fe_index_set())
688 {
689 cell->clear_refine_flag();
690 cell->clear_coarsen_flag();
691 }
692 }
693
694
695
696 template <int dim, int spacedim>
697 void
699 {
700 if (dof_handler.get_fe_collection().empty())
701 // nothing to do
702 return;
703
704 Assert(dof_handler.has_hp_capabilities(),
706
707 // Ghost siblings might occur on parallel Triangulation objects.
708 // We need information about refinement flags and future FE indices
709 // on all locally relevant cells here, and thus communicate them.
711 dynamic_cast<
713 const_cast<::Triangulation<dim, spacedim> *>(
714 &dof_handler.get_triangulation())))
715 {
718 }
719
721 const_cast<DoFHandler<dim, spacedim> &>(dof_handler));
722
723 // Now: choose p-adaptation over h-adaptation.
724 for (const auto &cell : dof_handler.active_cell_iterators())
725 if (cell->is_locally_owned() && cell->future_fe_index_set())
726 {
727 // This cell is flagged for p-adaptation.
728
729 // Remove any h-refinement flags.
730 cell->clear_refine_flag();
731
732 // A cell will only be coarsened into its parent if all of its
733 // siblings are flagged for h-coarsening as well. We must take this
734 // into account for our decision whether we would like to impose h-
735 // or p-adaptivity.
736 if (cell->coarsen_flag_set())
737 {
738 if (cell->level() == 0)
739 {
740 // This cell is a coarse cell and has neither parent nor
741 // siblings, thus it cannot be h-coarsened.
742 // Clear the flag and move on to the next cell.
743 cell->clear_coarsen_flag();
744 continue;
745 }
746
747 const auto &parent = cell->parent();
748 const unsigned int n_children = parent->n_children();
749
750 unsigned int h_flagged_children = 0, p_flagged_children = 0;
751 for (const auto &child : parent->child_iterators())
752 {
753 if (child->is_active())
754 {
755 Assert(child->is_artificial() == false,
757
758 if (child->coarsen_flag_set())
759 ++h_flagged_children;
760
761 // The public interface does not allow to access
762 // future FE indices on ghost cells. However, we
763 // need this information here and thus call the
764 // internal function that does not check for cell
765 // ownership.
767 Implementation::
768 future_fe_index_set<dim, spacedim, false>(
769 *child))
770 ++p_flagged_children;
771 }
772 }
773
774 if (h_flagged_children == n_children &&
775 p_flagged_children != n_children)
776 {
777 // Perform pure h-coarsening and
778 // drop all p-adaptation flags.
779 for (const auto &child : parent->child_iterators())
780 {
781 // h_flagged_children == n_children implies
782 // that all children are active
783 Assert(child->is_active(), ExcInternalError());
784 if (child->is_locally_owned())
785 child->clear_future_fe_index();
786 }
787 }
788 else
789 {
790 // Perform p-adaptation (if scheduled) and
791 // drop all h-coarsening flags.
792 for (const auto &child : parent->child_iterators())
793 {
794 if (child->is_active() && child->is_locally_owned())
795 child->clear_coarsen_flag();
796 }
797 }
798 }
799 }
800 }
801
802
803
807 template <int dim, int spacedim>
808 bool
810 const unsigned int max_difference,
811 const unsigned int contains_fe_index)
812 {
813 if (dof_handler.get_fe_collection().empty())
814 // nothing to do
815 return false;
816
817 Assert(dof_handler.has_hp_capabilities(),
819 Assert(
820 max_difference > 0,
822 "This function does not serve any purpose for max_difference = 0."));
823 AssertIndexRange(contains_fe_index,
824 dof_handler.get_fe_collection().size());
825
826 //
827 // establish hierarchy
828 //
829 // - create bidirectional map between hierarchy levels and FE indices
830
831 // there can be as many levels in the hierarchy as active FE indices are
832 // possible
833 using level_type = types::fe_index;
834 const auto invalid_level = static_cast<level_type>(-1);
835
836 // map from FE index to level in hierarchy
837 // FE indices that are not covered in the hierarchy are not in the map
838 const std::vector<unsigned int> fe_index_for_hierarchy_level =
840 contains_fe_index);
841
842 // map from level in hierarchy to FE index
843 // FE indices that are not covered in the hierarchy will be mapped to
844 // invalid_level
845 std::vector<unsigned int> hierarchy_level_for_fe_index(
846 dof_handler.get_fe_collection().size(), invalid_level);
847 for (unsigned int l = 0; l < fe_index_for_hierarchy_level.size(); ++l)
848 hierarchy_level_for_fe_index[fe_index_for_hierarchy_level[l]] = l;
849
850
851 //
852 // parallelization
853 //
854 // - create distributed vector of level indices
855 // - update ghost values in each iteration (see later)
856 // - no need to compress, since the owning processor will have the correct
857 // level index
858
859 // HOTFIX: ::Vector does not accept integral types
861 if (const auto parallel_tria =
862 dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
863 &(dof_handler.get_triangulation())))
864 {
865 future_levels.reinit(
866 parallel_tria->global_active_cell_index_partitioner().lock());
867 }
868 else
869 {
870 future_levels.reinit(
871 dof_handler.get_triangulation().n_active_cells());
872 }
873
874 for (const auto &cell : dof_handler.active_cell_iterators() |
876 future_levels[cell->global_active_cell_index()] =
877 hierarchy_level_for_fe_index[cell->future_fe_index()];
878
879
880 //
881 // limit level difference of neighboring cells
882 //
883 // - go over all locally relevant cells, and adjust the level indices of
884 // locally owned neighbors to match the level difference (as a
885 // consequence, indices on ghost cells will be updated only on the
886 // owning processor)
887 // - always raise levels to match criterion, never lower them
888 // - exchange level indices on ghost cells
889
890 // Function that updates the level of neighbor to fulfill difference
891 // criterion, and returns whether it was changed.
892 const auto update_neighbor_level =
893 [&future_levels, max_difference, invalid_level](
894 const auto &neighbor, const level_type cell_level) -> bool {
895 Assert(neighbor->is_active(), ExcInternalError());
896 // We only care about locally owned neighbors. If neighbor is a ghost
897 // cell, its future FE index will be updated on the owning process and
898 // communicated at the next loop iteration.
899 if (neighbor->is_locally_owned())
900 {
901 const level_type neighbor_level = static_cast<level_type>(
902 future_levels[neighbor->global_active_cell_index()]);
903
904 // ignore neighbors that are not part of the hierarchy
905 if (neighbor_level == invalid_level)
906 return false;
907
908 if ((cell_level - max_difference) > neighbor_level)
909 {
910 future_levels[neighbor->global_active_cell_index()] =
911 cell_level - max_difference;
912
913 return true;
914 }
915 }
916
917 return false;
918 };
919
920 // For cells to be h-coarsened, we need to determine a future FE for the
921 // parent cell, which will be the dominated FE among all children
922 // However, if we want to enforce the max_difference criterion on all
923 // cells on the updated mesh, we will need to simulate the updated mesh on
924 // the current mesh.
925 //
926 // As we are working on p-levels, we will set all siblings that will be
927 // coarsened to the highest p-level among them. The parent cell will be
928 // assigned exactly this level in form of the corresponding FE index in
929 // the adaptation process in
930 // Triangulation::execute_coarsening_and_refinement().
931 //
932 // This function takes a cell and sets all its siblings to the highest
933 // p-level among them. Returns whether any future levels have been
934 // changed.
935 const auto prepare_level_for_parent = [&](const auto &neighbor) -> bool {
936 Assert(neighbor->is_active(), ExcInternalError());
937 if (neighbor->coarsen_flag_set() && neighbor->is_locally_owned())
938 {
939 const auto parent = neighbor->parent();
940
941 std::vector<unsigned int> future_levels_children;
942 future_levels_children.reserve(parent->n_children());
943 for (const auto &child : parent->child_iterators())
944 {
945 Assert(child->is_active() && child->coarsen_flag_set(),
947
948 const level_type child_level = static_cast<level_type>(
949 future_levels[child->global_active_cell_index()]);
950 Assert(child_level != invalid_level,
952 "The FiniteElement on one of the siblings of "
953 "a cell you are trying to coarsen is not part "
954 "of the registered p-adaptation hierarchy."));
955 future_levels_children.push_back(child_level);
956 }
957 Assert(!future_levels_children.empty(), ExcInternalError());
958
959 const unsigned int max_level_children =
960 *std::max_element(future_levels_children.begin(),
961 future_levels_children.end());
962
963 bool children_changed = false;
964 for (const auto &child : parent->child_iterators())
965 // We only care about locally owned children. If child is a ghost
966 // cell, its future FE index will be updated on the owning process
967 // and communicated at the next loop iteration.
968 if (child->is_locally_owned() &&
969 future_levels[child->global_active_cell_index()] !=
970 max_level_children)
971 {
972 future_levels[child->global_active_cell_index()] =
973 max_level_children;
974 children_changed = true;
975 }
976 return children_changed;
977 }
978
979 return false;
980 };
981
982 bool levels_changed = false;
983 bool levels_changed_in_cycle;
984 do
985 {
986 levels_changed_in_cycle = false;
987
988 future_levels.update_ghost_values();
989
990 for (const auto &cell : dof_handler.active_cell_iterators())
991 if (!cell->is_artificial())
992 {
993 const level_type cell_level = static_cast<level_type>(
994 future_levels[cell->global_active_cell_index()]);
995
996 // ignore cells that are not part of the hierarchy
997 if (cell_level == invalid_level)
998 continue;
999
1000 // ignore lowest levels of the hierarchy that always fulfill the
1001 // max_difference criterion
1002 if (cell_level <= max_difference)
1003 continue;
1004
1005 for (unsigned int f = 0; f < cell->n_faces(); ++f)
1006 if (cell->face(f)->at_boundary() == false)
1007 {
1008 if (cell->face(f)->has_children())
1009 {
1010 for (unsigned int sf = 0;
1011 sf < cell->face(f)->n_children();
1012 ++sf)
1013 {
1014 const auto neighbor =
1015 cell->neighbor_child_on_subface(f, sf);
1016
1017 levels_changed_in_cycle |=
1018 update_neighbor_level(neighbor, cell_level);
1019
1020 levels_changed_in_cycle |=
1021 prepare_level_for_parent(neighbor);
1022 }
1023 }
1024 else
1025 {
1026 const auto neighbor = cell->neighbor(f);
1027
1028 levels_changed_in_cycle |=
1029 update_neighbor_level(neighbor, cell_level);
1030
1031 levels_changed_in_cycle |=
1032 prepare_level_for_parent(neighbor);
1033 }
1034 }
1035 }
1036
1037 levels_changed_in_cycle =
1038 Utilities::MPI::logical_or(levels_changed_in_cycle,
1039 dof_handler.get_mpi_communicator());
1040 levels_changed |= levels_changed_in_cycle;
1041 }
1042 while (levels_changed_in_cycle);
1043
1044 // update future FE indices on locally owned cells
1045 for (const auto &cell : dof_handler.active_cell_iterators() |
1047 {
1048 const level_type cell_level = static_cast<level_type>(
1049 future_levels[cell->global_active_cell_index()]);
1050
1051 if (cell_level != invalid_level)
1052 {
1053 const unsigned int fe_index =
1054 fe_index_for_hierarchy_level[cell_level];
1055
1056 if (fe_index != cell->active_fe_index())
1057 cell->set_future_fe_index(fe_index);
1058 else
1059 cell->clear_future_fe_index();
1060 }
1061 }
1062
1063 return levels_changed;
1064 }
1065 } // namespace Refinement
1066} // namespace hp
1067
1068
1069// explicit instantiations
1070#include "hp/refinement.inst"
1071
const hp::FECollection< dim, spacedim > & get_fe_collection() const
const Triangulation< dim, spacedim > & get_triangulation() const
bool has_hp_capabilities() const
MPI_Comm get_mpi_communicator() const
void reinit(const size_type size, const bool omit_zeroing_entries=false)
unsigned int n_active_cells() const
virtual size_type size() const override
iterator end()
void grow_or_shrink(const size_type N)
iterator begin()
unsigned int size() const
Definition collection.h:314
bool empty() const
Definition collection.h:323
unsigned int previous_in_hierarchy(const unsigned int fe_index) const
std::vector< unsigned int > get_hierarchy_sequence(const unsigned int fe_index=0) const
unsigned int next_in_hierarchy(const unsigned int fe_index) const
virtual MPI_Comm get_mpi_communicator() const override
Definition tria_base.cc:158
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ASSERT_UNREACHABLE()
IteratorRange< active_cell_iterator > active_cell_iterators() const
IteratorRange< active_cell_iterator > active_cell_iterators() const
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
static ::ExceptionBase & ExcInvalidParameterValue()
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
static ::ExceptionBase & ExcInconsistentCoarseningFlags()
T sum(const T &t, const MPI_Comm mpi_communicator)
T logical_or(const T &t, const MPI_Comm mpi_communicator)
T max(const T &t, const MPI_Comm mpi_communicator)
T min(const T &t, const MPI_Comm mpi_communicator)
constexpr T pow(const T base, const int iexp)
Definition utilities.h:966
void force_p_over_h(const DoFHandler< dim, spacedim > &dof_handler)
void p_adaptivity_from_reference(const DoFHandler< dim, spacedim > &dof_handler, const Vector< Number > &criteria, const Vector< Number > &references, const ComparisonFunction< std_cxx20::type_identity_t< Number > > &compare_refine, const ComparisonFunction< std_cxx20::type_identity_t< Number > > &compare_coarsen)
void full_p_adaptivity(const DoFHandler< dim, spacedim > &dof_handler)
Definition refinement.cc:47
void p_adaptivity_from_regularity(const DoFHandler< dim, spacedim > &dof_handler, const Vector< Number > &sobolev_indices)
bool limit_p_level_difference(const DoFHandler< dim, spacedim > &dof_handler, const unsigned int max_difference=1, const unsigned int contains_fe_index=0)
void predict_error(const DoFHandler< dim, spacedim > &dof_handler, const Vector< Number > &error_indicators, Vector< Number > &predicted_errors, const double gamma_p=std::sqrt(0.4), const double gamma_h=2., const double gamma_n=1.)
void p_adaptivity_from_absolute_threshold(const DoFHandler< dim, spacedim > &dof_handler, const Vector< Number > &criteria, const Number p_refine_threshold, const Number p_coarsen_threshold, const ComparisonFunction< std_cxx20::type_identity_t< Number > > &compare_refine=std::greater_equal< Number >(), const ComparisonFunction< std_cxx20::type_identity_t< Number > > &compare_coarsen=std::less_equal< Number >())
void choose_p_over_h(const DoFHandler< dim, spacedim > &dof_handler)
void p_adaptivity_fixed_number(const DoFHandler< dim, spacedim > &dof_handler, const Vector< Number > &criteria, const double p_refine_fraction=0.5, const double p_coarsen_fraction=0.5, const ComparisonFunction< std_cxx20::type_identity_t< Number > > &compare_refine=std::greater_equal< Number >(), const ComparisonFunction< std_cxx20::type_identity_t< Number > > &compare_coarsen=std::less_equal< Number >())
std::function< bool(const Number &, const Number &)> ComparisonFunction
Definition refinement.h:141
void p_adaptivity_from_relative_threshold(const DoFHandler< dim, spacedim > &dof_handler, const Vector< Number > &criteria, const double p_refine_fraction=0.5, const double p_coarsen_fraction=0.5, const ComparisonFunction< std_cxx20::type_identity_t< Number > > &compare_refine=std::greater_equal< Number >(), const ComparisonFunction< std_cxx20::type_identity_t< Number > > &compare_coarsen=std::less_equal< Number >())
void p_adaptivity_from_flags(const DoFHandler< dim, spacedim > &dof_handler, const std::vector< bool > &p_flags)
Definition refinement.cc:66
Definition hp.h:115
void communicate_future_fe_indices(DoFHandler< dim, spacedim > &dof_handler)
number compute_threshold(const ::Vector< number > &criteria, const std::pair< double, double > &global_min_and_max, const types::global_cell_index n_target_cells, const MPI_Comm mpi_communicator)
std::pair< number, number > compute_global_min_and_max_at_root(const ::Vector< number > &criteria, const MPI_Comm mpi_communicator)
void exchange_refinement_flags(::parallel::distributed::Triangulation< dim, spacedim > &tria)
Definition tria.cc:54
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
typename type_identity< T >::type type_identity_t
Definition type_traits.h:93
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
unsigned short int fe_index
Definition types.h:70