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
grid_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) 2010 - 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
15
19#include <deal.II/lac/vector.h>
20
21#ifdef DEAL_II_WITH_P4EST
23
26# include <deal.II/grid/tria.h>
29
30# include <algorithm>
31# include <functional>
32# include <limits>
33# include <numeric>
34#endif
35
37
38#ifdef DEAL_II_WITH_P4EST
39namespace
40{
41 template <int dim, int spacedim>
42 unsigned int
43 n_locally_owned_active_cells(const Triangulation<dim, spacedim> &tria)
44 {
45 if (const auto parallel_tria =
47 &tria))
48 return parallel_tria->n_locally_owned_active_cells();
49 else
50 return tria.n_active_cells();
51 }
52
53 template <typename number>
54 number
55 max_element(const ::Vector<number> &criteria)
56 {
57 return (criteria.size() > 0) ?
58 (*std::max_element(criteria.begin(), criteria.end())) :
59 std::numeric_limits<number>::min();
60 }
61
62
63
64 template <typename number>
65 number
66 min_element(const ::Vector<number> &criteria)
67 {
68 return (criteria.size() > 0) ?
69 (*std::min_element(criteria.begin(), criteria.end())) :
70 std::numeric_limits<number>::max();
71 }
72
73
74
80 template <typename number>
81 double
82 compute_global_sum(const ::Vector<number> &criteria,
83 const MPI_Comm mpi_communicator)
84 {
85 double my_sum =
86 std::accumulate(criteria.begin(),
87 criteria.end(),
88 /* do accumulation in the correct data type: */
89 number());
90
91 double result = 0;
92 // compute the minimum on processor zero
93 const int ierr =
94 MPI_Reduce(&my_sum, &result, 1, MPI_DOUBLE, MPI_SUM, 0, mpi_communicator);
95 AssertThrowMPI(ierr);
96
97 // make sure only processor zero got something
98 if (Utilities::MPI::this_mpi_process(mpi_communicator) != 0)
99 Assert(result == 0, ExcInternalError());
100
101 return result;
102 }
103
104
105
110 template <int dim, int spacedim, typename Number>
111 void
112 get_locally_owned_indicators(const ::Triangulation<dim, spacedim> &tria,
113 const ::Vector<Number> &criteria,
114 Vector<Number> &locally_owned_indicators)
115 {
116 Assert(locally_owned_indicators.size() ==
117 n_locally_owned_active_cells(tria),
119
120 unsigned int owned_index = 0;
121 for (const auto &cell :
122 tria.active_cell_iterators() | IteratorFilters::LocallyOwnedCell())
123 {
124 locally_owned_indicators(owned_index) =
125 criteria(cell->active_cell_index());
126 ++owned_index;
127 }
128 Assert(owned_index == n_locally_owned_active_cells(tria),
130 }
131
132
133 // we compute refinement thresholds by bisection of the interval spanned by
134 // the smallest and largest error indicator. this leads to a small problem:
135 // if, for example, we want to coarsen zero per cent of the cells, then we
136 // need to pick a threshold equal to the smallest indicator, but of course
137 // the bisection algorithm can never find a threshold equal to one of the
138 // end points of the interval. So we slightly increase the interval before
139 // we even start
140 void
141 adjust_interesting_range(double (&interesting_range)[2])
142 {
143 Assert(interesting_range[0] <= interesting_range[1], ExcInternalError());
144
145 if (interesting_range[0] > 0)
146 {
147 // In this case, we calculate the first interval split point `m` in the
148 // `compute_threshold` functions in the optimized way: We exploit that
149 // the logarithms of all criteria are more uniformly distributed than
150 // their actual values, i.e. m=sqrt(b*e).
151 //
152 // Both factors will modify the split point only slightly by a factor of
153 // sqrt(1.01*0.99) = sqrt(0.9999) ~ 0.9950.
154 interesting_range[0] *= 0.99;
155 interesting_range[1] *= 1.01;
156 }
157 else
158 {
159 // In all other cases, we begin with an the arithmetic mean as the
160 // standard interval split point, i.e. m=(b+e)/2.
161 //
162 // Both increments will add up to zero when calculating the initial
163 // split point in the `compute_threshold` functions.
164 const double difference =
165 std::abs(interesting_range[1] - interesting_range[0]);
166 interesting_range[0] -= 0.01 * difference;
167 interesting_range[1] += 0.01 * difference;
168 }
169 }
170
171
172
178 template <int dim, int spacedim, typename Number>
179 void
180 mark_cells(::Triangulation<dim, spacedim> &tria,
181 const ::Vector<Number> &criteria,
182 const double top_threshold,
183 const double bottom_threshold)
184 {
185 ::GridRefinement::refine(tria, criteria, top_threshold);
186 ::GridRefinement::coarsen(tria, criteria, bottom_threshold);
187
188 // as a final good measure, delete all flags again from cells that we don't
189 // locally own
190 for (const auto &cell : tria.active_cell_iterators())
191 if (cell->subdomain_id() != tria.locally_owned_subdomain())
192 {
193 cell->clear_refine_flag();
194 cell->clear_coarsen_flag();
195 }
196 }
197
198
199
207 template <int dim, int spacedim, typename Number>
208 void
209 refine_and_coarsen_fixed_fraction_via_l1_norm(
211 const ::Vector<Number> &criteria,
212 const double top_fraction_of_error,
213 const double bottom_fraction_of_error)
214 {
215 // first extract from the vector of indicators the ones that correspond
216 // to cells that we locally own
217 Vector<Number> locally_owned_indicators(n_locally_owned_active_cells(tria));
218 get_locally_owned_indicators(tria, criteria, locally_owned_indicators);
219
220 MPI_Comm mpi_communicator = tria.get_mpi_communicator();
221
222 // figure out the global max and min of the indicators. we don't need it
223 // here, but it's a collective communication call
224 const std::pair<double, double> global_min_and_max =
226 compute_global_min_and_max_at_root(locally_owned_indicators,
227 mpi_communicator);
228
229 const double total_error =
230 compute_global_sum(locally_owned_indicators, mpi_communicator);
231
232 double top_target_error = top_fraction_of_error * total_error,
233 bottom_target_error = (1. - bottom_fraction_of_error) * total_error;
234
235 double top_threshold, bottom_threshold;
238 global_min_and_max,
239 top_target_error,
240 mpi_communicator);
241
242 // compute bottom threshold only if necessary. otherwise use the lowest
243 // threshold possible
244 if (bottom_fraction_of_error > 0)
245 bottom_threshold = ::internal::parallel::distributed::
247 locally_owned_indicators,
248 global_min_and_max,
249 bottom_target_error,
250 mpi_communicator);
251 else
252 bottom_threshold = std::numeric_limits<Number>::lowest();
253
254 // now refine the mesh
255 mark_cells(tria, criteria, top_threshold, bottom_threshold);
256 }
257} // namespace
258
259
260
261namespace internal
262{
263 namespace parallel
264 {
265 namespace distributed
266 {
267 namespace GridRefinement
268 {
269 template <typename number>
270 std::pair<number, number>
272 const ::Vector<number> &criteria,
273 const MPI_Comm mpi_communicator)
274 {
275 // we'd like to compute the global max and min from the local ones in
276 // one MPI communication. we can do that by taking the elementwise
277 // minimum of the local min and the negative maximum over all
278 // processors
279
280 const double local_min = min_element(criteria),
281 local_max = max_element(criteria);
282 double comp[2] = {local_min, -local_max};
283 double result[2] = {0, 0};
284
285 // compute the minimum on processor zero
286 const int ierr = MPI_Reduce(
287 comp, result, 2, MPI_DOUBLE, MPI_MIN, 0, mpi_communicator);
288 AssertThrowMPI(ierr);
289
290 // make sure only processor zero got something
291 if (Utilities::MPI::this_mpi_process(mpi_communicator) != 0)
292 Assert((result[0] == 0) && (result[1] == 0), ExcInternalError());
293
294 return std::make_pair(result[0], -result[1]);
295 }
296
297
298
299 namespace RefineAndCoarsenFixedNumber
300 {
301 template <typename number>
302 number
303 compute_threshold(const ::Vector<number> &criteria,
304 const std::pair<double, double> &global_min_and_max,
305 const types::global_cell_index n_target_cells,
306 const MPI_Comm mpi_communicator)
307 {
308 double interesting_range[2] = {global_min_and_max.first,
309 global_min_and_max.second};
310 adjust_interesting_range(interesting_range);
311
312 const unsigned int root_mpi_rank = 0;
313 unsigned int iteration = 0;
314
315 do
316 {
317 int ierr = MPI_Bcast(interesting_range,
318 2,
319 MPI_DOUBLE,
320 root_mpi_rank,
321 mpi_communicator);
322 AssertThrowMPI(ierr);
323
324 if (interesting_range[0] == interesting_range[1])
325 return interesting_range[0];
326
327 const double test_threshold =
328 (interesting_range[0] > 0 ?
329 std::sqrt(interesting_range[0] * interesting_range[1]) :
330 (interesting_range[0] + interesting_range[1]) / 2);
331
332 // Count how many of our own elements would be above this
333 // threshold. Use a 64bit result type if we are compiling with
334 // 64bit indices to avoid an overflow when computing the sum
335 // below.
336 const types::global_cell_index my_count =
337 std::count_if(criteria.begin(),
338 criteria.end(),
339 [test_threshold](const double c) {
340 return c > test_threshold;
341 });
342 const types::global_cell_index total_count =
343 Utilities::MPI::sum(my_count, mpi_communicator);
344
345 // now adjust the range. if we have too many cells, we take the
346 // upper half of the previous range, otherwise the lower half.
347 // if we have hit the right number, then set the range to the
348 // exact value. non-root nodes also update their own
349 // interesting_range, however their results are not significant
350 // since the values will be overwritten by MPI_Bcast from the
351 // root node in next loop.
352 if (total_count > n_target_cells)
353 interesting_range[0] = test_threshold;
354 else if (total_count < n_target_cells)
355 interesting_range[1] = test_threshold;
356 else
357 interesting_range[0] = interesting_range[1] = test_threshold;
358
359 // terminate the iteration after 25 go-arounds. this is
360 // necessary because oftentimes error indicators on cells have
361 // exactly the same value, and so there may not be a particular
362 // value that cuts the indicators in such a way that we can
363 // achieve the desired number of cells. using a maximal number
364 // of iterations means that we terminate the iteration after a
365 // fixed number N of steps if the indicators were perfectly
366 // badly distributed, and we make at most a mistake of 1/2^N in
367 // the number of cells flagged if indicators are perfectly
368 // equidistributed
369 ++iteration;
370 if (iteration == 25)
371 interesting_range[0] = interesting_range[1] = test_threshold;
372 }
373 while (true);
374
376 return -1;
377 }
378 } // namespace RefineAndCoarsenFixedNumber
379
380
381
382 namespace RefineAndCoarsenFixedFraction
383 {
384 template <typename number>
385 number
386 compute_threshold(const ::Vector<number> &criteria,
387 const std::pair<double, double> &global_min_and_max,
388 const double target_error,
389 const MPI_Comm mpi_communicator)
390 {
391 double interesting_range[2] = {global_min_and_max.first,
392 global_min_and_max.second};
393 adjust_interesting_range(interesting_range);
394
395 const unsigned int root_mpi_rank = 0;
396 unsigned int iteration = 0;
397
398 do
399 {
400 int ierr = MPI_Bcast(interesting_range,
401 2,
402 MPI_DOUBLE,
403 root_mpi_rank,
404 mpi_communicator);
405 AssertThrowMPI(ierr);
406
407 if (interesting_range[0] == interesting_range[1])
408 {
409 // so we have found our threshold. since we adjust the range
410 // at the top of the function to be slightly larger than the
411 // actual extremes of the refinement criteria values, we can
412 // end up in a situation where the threshold is in fact
413 // larger than the maximal refinement indicator. in such
414 // cases, we get no refinement at all. thus, cap the
415 // threshold by the actual largest value
416 double final_threshold =
417 std::min(interesting_range[0], global_min_and_max.second);
418 ierr = MPI_Bcast(&final_threshold,
419 1,
420 MPI_DOUBLE,
421 root_mpi_rank,
422 mpi_communicator);
423 AssertThrowMPI(ierr);
424
425 return final_threshold;
426 }
427
428 const double test_threshold =
429 (interesting_range[0] > 0 ?
430 std::sqrt(interesting_range[0] * interesting_range[1]) :
431 (interesting_range[0] + interesting_range[1]) / 2);
432
433 // accumulate the error of those our own elements above this
434 // threshold and then add to it the number for all the others
435 double my_error = 0;
436 for (unsigned int i = 0; i < criteria.size(); ++i)
437 if (criteria(i) > test_threshold)
438 my_error += criteria(i);
439
440 double total_error = 0.;
441
442 ierr = MPI_Reduce(&my_error,
443 &total_error,
444 1,
445 MPI_DOUBLE,
446 MPI_SUM,
447 root_mpi_rank,
448 mpi_communicator);
449 AssertThrowMPI(ierr);
450
451 // now adjust the range. if we have too many cells, we take the
452 // upper half of the previous range, otherwise the lower half.
453 // if we have hit the right number, then set the range to the
454 // exact value. non-root nodes also update their own
455 // interesting_range, however their results are not significant
456 // since the values will be overwritten by MPI_Bcast from the
457 // root node in next loop.
458 if (total_error > target_error)
459 interesting_range[0] = test_threshold;
460 else if (total_error < target_error)
461 interesting_range[1] = test_threshold;
462 else
463 interesting_range[0] = interesting_range[1] = test_threshold;
464
465 // terminate the iteration after 25 go-arounds. this is
466 // necessary because oftentimes error indicators on cells
467 // have exactly the same value, and so there may not be a
468 // particular value that cuts the indicators in such a way
469 // that we can achieve the desired number of cells. using a
470 // max of 25 iterations means that we terminate the
471 // iteration after 25 steps if the indicators were perfectly
472 // badly distributed, and we make at most a mistake of
473 // 1/2^25 in the number of cells flagged if indicators are
474 // perfectly equidistributed
475 ++iteration;
476 if (iteration == 25)
477 interesting_range[0] = interesting_range[1] = test_threshold;
478 }
479 while (true);
480
482 return -1;
483 }
484 } // namespace RefineAndCoarsenFixedFraction
485 } // namespace GridRefinement
486 } // namespace distributed
487 } // namespace parallel
488} // namespace internal
489
490
491
492namespace parallel
493{
494 namespace distributed
495 {
496 namespace GridRefinement
497 {
498 template <int dim, typename Number, int spacedim>
499 void
502 const ::Vector<Number> &criteria,
503 const double top_fraction_of_cells,
504 const double bottom_fraction_of_cells,
505 const types::global_cell_index max_n_cells)
506 {
507 Assert(criteria.size() == tria.n_active_cells(),
508 ExcDimensionMismatch(criteria.size(), tria.n_active_cells()));
509 Assert((top_fraction_of_cells >= 0) && (top_fraction_of_cells <= 1),
511 Assert((bottom_fraction_of_cells >= 0) &&
512 (bottom_fraction_of_cells <= 1),
514 Assert(top_fraction_of_cells + bottom_fraction_of_cells <= 1,
516 Assert(criteria.is_non_negative(),
518
519 const std::pair<double, double> adjusted_fractions =
521 dim>(tria.n_global_active_cells(),
522 max_n_cells,
523 top_fraction_of_cells,
524 bottom_fraction_of_cells);
525
526 // first extract from the vector of indicators the ones that correspond
527 // to cells that we locally own
528 Vector<Number> locally_owned_indicators(
529 n_locally_owned_active_cells(tria));
530 get_locally_owned_indicators(tria, criteria, locally_owned_indicators);
531
532 MPI_Comm mpi_communicator = tria.get_mpi_communicator();
533
534 // figure out the global max and min of the indicators. we don't need it
535 // here, but it's a collective communication call
536 const std::pair<Number, Number> global_min_and_max =
538 compute_global_min_and_max_at_root(locally_owned_indicators,
539 mpi_communicator);
540
541
542 double top_threshold, bottom_threshold;
545 locally_owned_indicators,
546 global_min_and_max,
547 static_cast<types::global_cell_index>(adjusted_fractions.first *
548 tria.n_global_active_cells()),
549 mpi_communicator);
550
551 // compute bottom threshold only if necessary. otherwise use the lowest
552 // threshold possible
553 if (adjusted_fractions.second > 0)
554 bottom_threshold = ::internal::parallel::distributed::
556 locally_owned_indicators,
557 global_min_and_max,
558 static_cast<types::global_cell_index>(
559 std::ceil((1. - adjusted_fractions.second) *
560 tria.n_global_active_cells())),
561 mpi_communicator);
562 else
563 bottom_threshold = std::numeric_limits<Number>::lowest();
564
565 // now refine the mesh
566 mark_cells(tria, criteria, top_threshold, bottom_threshold);
567 }
568
569
570
571 template <int dim, typename Number, int spacedim>
572 void
575 const ::Vector<Number> &criteria,
576 const double top_fraction_of_error,
577 const double bottom_fraction_of_error,
578 const VectorTools::NormType norm_type)
579 {
580 Assert(criteria.size() == tria.n_active_cells(),
581 ExcDimensionMismatch(criteria.size(), tria.n_active_cells()));
582 Assert((top_fraction_of_error >= 0) && (top_fraction_of_error <= 1),
584 Assert((bottom_fraction_of_error >= 0) &&
585 (bottom_fraction_of_error <= 1),
587 Assert(top_fraction_of_error + bottom_fraction_of_error <= 1,
589 Assert(criteria.is_non_negative(),
591
592 switch (norm_type)
593 {
595 // evaluate norms on subsets and compare them as
596 // c_0 + c_1 + ... < fraction * l1-norm(c)
597 refine_and_coarsen_fixed_fraction_via_l1_norm(
598 tria,
599 criteria,
600 top_fraction_of_error,
601 bottom_fraction_of_error);
602 break;
603
605 {
606 // we do not want to evaluate norms on subsets as:
607 // sqrt(c_0^2 + c_1^2 + ...) < fraction * l2-norm(c)
608 // instead take the square of both sides of the equation
609 // and evaluate:
610 // c_0^2 + c_1^2 + ... < fraction^2 * l1-norm(c.c)
611 // we adjust all parameters accordingly
612 Vector<Number> criteria_squared(criteria.size());
613 std::transform(criteria.begin(),
614 criteria.end(),
615 criteria_squared.begin(),
616 [](Number c) { return c * c; });
617
618 refine_and_coarsen_fixed_fraction_via_l1_norm(
619 tria,
620 criteria_squared,
621 top_fraction_of_error * top_fraction_of_error,
622 bottom_fraction_of_error * bottom_fraction_of_error);
623 }
624 break;
625
626 default:
628 break;
629 }
630 }
631 } // namespace GridRefinement
632 } // namespace distributed
633} // namespace parallel
634
635
636// explicit instantiations
637# include "distributed/grid_refinement.inst"
638#endif
639
virtual types::global_cell_index n_global_active_cells() const
virtual MPI_Comm get_mpi_communicator() const
unsigned int n_active_cells() const
virtual size_type size() const override
iterator begin()
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ASSERT_UNREACHABLE()
#define DEAL_II_NOT_IMPLEMENTED()
#define Assert(cond, exc)
#define AssertThrowMPI(error_code)
static ::ExceptionBase & ExcNegativeCriteria()
static ::ExceptionBase & ExcInvalidParameterValue()
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
void refine(Triangulation< dim, spacedim > &tria, const Vector< Number > &criteria, const double threshold, const unsigned int max_to_mark=numbers::invalid_unsigned_int)
void coarsen(Triangulation< dim, spacedim > &tria, const Vector< Number > &criteria, const double threshold)
std::pair< double, double > adjust_refine_and_coarsen_number_fraction(const types::global_cell_index current_n_cells, const types::global_cell_index max_n_cells, const double top_fraction_of_cells, const double bottom_fraction_of_cells)
*  *  if(update_pressure &update_flags) *  compute_pressure(constitutive_request
T sum(const T &t, const MPI_Comm mpi_communicator)
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:118
number compute_threshold(const ::Vector< number > &criteria, const std::pair< double, double > &global_min_and_max, const double target_error, const MPI_Comm mpi_communicator)
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 refine_and_coarsen_fixed_fraction(::Triangulation< dim, spacedim > &tria, const ::Vector< Number > &criteria, const double top_fraction_of_error, const double bottom_fraction_of_error, const VectorTools::NormType norm_type=VectorTools::L1_norm)
void refine_and_coarsen_fixed_number(::Triangulation< dim, spacedim > &tria, const ::Vector< Number > &criteria, const double top_fraction_of_cells, const double bottom_fraction_of_cells, const types::global_cell_index max_n_cells=std::numeric_limits< types::global_cell_index >::max())
STL namespace.
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
unsigned int subdomain_id
Definition types.h:50