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) 2000 - 2025 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
17
19#include <deal.II/grid/tria.h>
22
23#include <deal.II/lac/vector.h>
24
25#include <algorithm>
26#include <cmath>
27#include <fstream>
28#include <functional>
29#include <limits>
30#include <numeric>
31
33
34namespace
35{
43 template <int dim, int spacedim, typename Number>
44 void
45 refine_and_coarsen_fixed_fraction_via_l1_norm(
47 const Vector<Number> &criteria,
48 const double top_fraction,
49 const double bottom_fraction,
50 const unsigned int max_n_cells)
51 {
52 // sort the criteria in descending order in an auxiliary vector, which we
53 // have to sum up and compare with @p{fraction_of_error*total_error}
54 Vector<Number> criteria_sorted = criteria;
55 std::sort(criteria_sorted.begin(),
56 criteria_sorted.end(),
57 std::greater<double>());
58
59 const double total_error = criteria_sorted.l1_norm();
60
61 // compute thresholds
62 typename Vector<Number>::const_iterator pp = criteria_sorted.begin();
63 for (double sum = 0;
64 (sum < top_fraction * total_error) && (pp != criteria_sorted.end());
65 ++pp)
66 sum += *pp;
67 double top_threshold =
68 (pp != criteria_sorted.begin() ? (*pp + *(pp - 1)) / 2 : *pp);
69
70 typename Vector<Number>::const_iterator qq = criteria_sorted.end() - 1;
71 for (double sum = 0; (sum < bottom_fraction * total_error) &&
72 (qq != criteria_sorted.begin() - 1);
73 --qq)
74 sum += *qq;
75 double bottom_threshold =
76 ((qq != criteria_sorted.end() - 1) ? (*qq + *(qq + 1)) / 2 : 0.);
77
78 // we now have an idea how many cells we
79 // are going to refine and coarsen. we use
80 // this information to see whether we are
81 // over the limit and if so use a function
82 // that knows how to deal with this
83 // situation
84
85 // note, that at this point, we have no
86 // information about anisotropically refined
87 // cells, thus use the situation of purely
88 // isotropic refinement as guess for a mixed
89 // refinemnt as well.
90 const unsigned int refine_cells = pp - criteria_sorted.begin(),
91 coarsen_cells = criteria_sorted.end() - 1 - qq;
92
93 if (static_cast<unsigned int>(
94 tria.n_active_cells() +
95 refine_cells * (GeometryInfo<dim>::max_children_per_cell - 1) -
96 (coarsen_cells * (GeometryInfo<dim>::max_children_per_cell - 1) /
98 {
100 criteria,
101 1. * refine_cells /
102 criteria.size(),
103 1. * coarsen_cells /
104 criteria.size(),
105 max_n_cells);
106 return;
107 }
108
109 // in some rare cases it may happen that
110 // both thresholds are the same (e.g. if
111 // there are many cells with the same
112 // error indicator). That would mean that
113 // all cells will be flagged for
114 // refinement or coarsening, but some will
115 // be flagged for both, namely those for
116 // which the indicator equals the
117 // thresholds. This is forbidden, however.
118 //
119 // In some rare cases with very few cells
120 // we also could get integer round off
121 // errors and get problems with
122 // the top and bottom fractions.
123 //
124 // In these case we arbitrarily reduce the
125 // bottom threshold by one permille below
126 // the top threshold
127 //
128 // Finally, in some cases
129 // (especially involving symmetric
130 // solutions) there are many cells
131 // with the same error indicator
132 // values. if there are many with
133 // indicator equal to the top
134 // threshold, no refinement will
135 // take place below; to avoid this
136 // case, we also lower the top
137 // threshold if it equals the
138 // largest indicator and the
139 // top_fraction!=1
140 const double max_criterion = *(criteria_sorted.begin()),
141 min_criterion = *(criteria_sorted.end() - 1);
142
143 if ((top_threshold == max_criterion) && (top_fraction != 1))
144 top_threshold *= 0.999;
145
146 if (bottom_threshold >= top_threshold)
147 bottom_threshold = 0.999 * top_threshold;
148
149 // actually flag cells
150 if (top_threshold < max_criterion)
151 GridRefinement::refine(tria, criteria, top_threshold, refine_cells);
152
153 if (bottom_threshold > min_criterion)
154 GridRefinement::coarsen(tria, criteria, bottom_threshold);
155 }
156} // namespace
157
158
159
160template <int dim, typename Number, int spacedim>
161void
163 const Vector<Number> &criteria,
164 const double threshold,
165 const unsigned int max_to_mark)
166{
167 Assert(criteria.size() == tria.n_active_cells(),
168 ExcDimensionMismatch(criteria.size(), tria.n_active_cells()));
169 Assert(criteria.is_non_negative(), ExcNegativeCriteria());
170
171 // when all indicators are zero we
172 // do not need to refine but only
173 // to coarsen
174 if (criteria.all_zero())
175 return;
176
177 const unsigned int n_cells = criteria.size();
178
179 // TODO: This is undocumented, looks fishy and seems unnecessary
180
181 double new_threshold = threshold;
182 // when threshold==0 find the
183 // smallest value in criteria
184 // greater 0
185 if (new_threshold == 0)
186 {
187 new_threshold = criteria(0);
188 for (unsigned int index = 1; index < n_cells; ++index)
189 if (criteria(index) > 0 && (criteria(index) < new_threshold))
190 new_threshold = criteria(index);
191 }
192
193 unsigned int marked = 0;
194 for (const auto &cell : tria.active_cell_iterators())
196 &tria) == nullptr ||
197 cell->is_locally_owned()) &&
198 std::fabs(criteria(cell->active_cell_index())) >= new_threshold)
199 {
200 if (max_to_mark != numbers::invalid_unsigned_int &&
201 marked >= max_to_mark)
202 break;
203 ++marked;
204 cell->set_refine_flag();
205 }
206}
207
208
209
210template <int dim, typename Number, int spacedim>
211void
213 const Vector<Number> &criteria,
214 const double threshold)
215{
216 Assert(criteria.size() == tria.n_active_cells(),
217 ExcDimensionMismatch(criteria.size(), tria.n_active_cells()));
218 Assert(criteria.is_non_negative(), ExcNegativeCriteria());
219
220 for (const auto &cell : tria.active_cell_iterators())
222 &tria) == nullptr ||
223 cell->is_locally_owned()) &&
224 std::fabs(criteria(cell->active_cell_index())) <= threshold)
225 if (!cell->refine_flag_set())
226 cell->set_coarsen_flag();
227}
228
229
230
231template <int dim>
232std::pair<double, double>
234 const types::global_cell_index current_n_cells,
235 const types::global_cell_index max_n_cells,
236 const double top_fraction,
237 const double bottom_fraction)
238{
239 Assert(top_fraction >= 0, ExcInvalidParameterValue());
240 Assert(top_fraction <= 1, ExcInvalidParameterValue());
241 Assert(bottom_fraction >= 0, ExcInvalidParameterValue());
242 Assert(bottom_fraction <= 1, ExcInvalidParameterValue());
243 Assert(top_fraction + bottom_fraction <=
244 1 + 10 * std::numeric_limits<double>::epsilon(),
245 ExcInvalidParameterValue());
246
247 double refine_cells = current_n_cells * top_fraction;
248 double coarsen_cells = current_n_cells * bottom_fraction;
249
250 const double cell_increase_on_refine =
252 const double cell_decrease_on_coarsen =
254
255 std::pair<double, double> adjusted_fractions(top_fraction, bottom_fraction);
256 // first we have to see whether we
257 // currently already exceed the target
258 // number of cells
259 if (current_n_cells >= max_n_cells)
260 {
261 // if yes, then we need to stop
262 // refining cells and instead try to
263 // only coarsen as many as it would
264 // take to get to the target
265
266 // as we have no information on cells
267 // being refined isotropically or
268 // anisotropically, assume isotropic
269 // refinement here, though that may
270 // result in a worse approximation
271 adjusted_fractions.first = 0;
272 coarsen_cells =
273 (current_n_cells - max_n_cells) / cell_decrease_on_coarsen;
274 adjusted_fractions.second =
275 std::min(coarsen_cells / current_n_cells, 1.0);
276 }
277 // otherwise, see if we would exceed the
278 // maximum desired number of cells with the
279 // number of cells that are likely going to
280 // result from refinement. here, each cell
281 // to be refined is replaced by
282 // C=GeometryInfo<dim>::max_children_per_cell
283 // new cells, i.e. there will be C-1 more
284 // cells than before. similarly, C cells
285 // will be replaced by 1
286
287 // again, this is true for isotropically
288 // refined cells. we take this as an
289 // approximation of a mixed refinement.
290 else if (static_cast<types::global_cell_index>(
291 current_n_cells + refine_cells * cell_increase_on_refine -
292 coarsen_cells * cell_decrease_on_coarsen) > max_n_cells)
293 {
294 // we have to adjust the
295 // fractions. assume we want
296 // alpha*refine_fraction and
297 // alpha*coarsen_fraction as new
298 // fractions and the resulting number
299 // of cells to be equal to
300 // max_n_cells. this leads to the
301 // following equation for alpha
302 const double alpha = 1. * (max_n_cells - current_n_cells) /
303 (refine_cells * cell_increase_on_refine -
304 coarsen_cells * cell_decrease_on_coarsen);
305
306 adjusted_fractions.first = alpha * top_fraction;
307 adjusted_fractions.second = alpha * bottom_fraction;
308 }
309 return (adjusted_fractions);
310}
311
312
313
314template <int dim, typename Number, int spacedim>
315void
318 const Vector<Number> &criteria,
319 const double top_fraction,
320 const double bottom_fraction,
321 const unsigned int max_n_cells)
322{
323 // correct number of cells is
324 // checked in @p{refine}
325 Assert((top_fraction >= 0) && (top_fraction <= 1),
326 ExcInvalidParameterValue());
327 Assert((bottom_fraction >= 0) && (bottom_fraction <= 1),
328 ExcInvalidParameterValue());
329 Assert(top_fraction + bottom_fraction <=
330 1 + 10 * std::numeric_limits<double>::epsilon(),
331 ExcInvalidParameterValue());
332 Assert(criteria.is_non_negative(), ExcNegativeCriteria());
333
334 const std::pair<double, double> adjusted_fractions =
335 adjust_refine_and_coarsen_number_fraction<dim>(criteria.size(),
336 max_n_cells,
337 top_fraction,
338 bottom_fraction);
339
340 const int refine_cells =
341 static_cast<int>(adjusted_fractions.first * criteria.size());
342 const int coarsen_cells =
343 static_cast<int>(adjusted_fractions.second * criteria.size());
344
345 if (refine_cells || coarsen_cells)
346 {
347 Vector<Number> tmp(criteria);
348 if (refine_cells)
349 {
350 if (static_cast<std::size_t>(refine_cells) == criteria.size())
351 refine(tria, criteria, std::numeric_limits<double>::lowest());
352 else
353 {
354 std::nth_element(tmp.begin(),
355 tmp.begin() + refine_cells - 1,
356 tmp.end(),
357 std::greater<double>());
358 refine(tria, criteria, *(tmp.begin() + refine_cells - 1));
359 }
360 }
361
362 if (coarsen_cells)
363 {
364 if (static_cast<std::size_t>(coarsen_cells) == criteria.size())
365 coarsen(tria, criteria, std::numeric_limits<double>::max());
366 else
367 {
368 std::nth_element(tmp.begin(),
369 tmp.begin() + tmp.size() - coarsen_cells,
370 tmp.end(),
371 std::greater<double>());
372 coarsen(tria,
373 criteria,
374 *(tmp.begin() + tmp.size() - coarsen_cells));
375 }
376 }
377 }
378}
379
380
381
382template <int dim, typename Number, int spacedim>
383void
386 const Vector<Number> &criteria,
387 const double top_fraction,
388 const double bottom_fraction,
389 const unsigned int max_n_cells,
390 const VectorTools::NormType norm_type)
391{
392 // correct number of cells is checked in @p{refine}
393 Assert((top_fraction >= 0) && (top_fraction <= 1),
394 ExcInvalidParameterValue());
395 Assert((bottom_fraction >= 0) && (bottom_fraction <= 1),
396 ExcInvalidParameterValue());
397 Assert(top_fraction + bottom_fraction <=
398 1 + 10 * std::numeric_limits<double>::epsilon(),
399 ExcInvalidParameterValue());
400 Assert(criteria.is_non_negative(), ExcNegativeCriteria());
401
402 switch (norm_type)
403 {
405 // evaluate norms on subsets and compare them as
406 // c_0 + c_1 + ... < fraction * l1-norm(c)
407 refine_and_coarsen_fixed_fraction_via_l1_norm(
408 tria, criteria, top_fraction, bottom_fraction, max_n_cells);
409 break;
410
412 {
413 // we do not want to evaluate norms on subsets as:
414 // sqrt(c_0^2 + c_1^2 + ...) < fraction * l2-norm(c)
415 // instead take the square of both sides of the equation
416 // and evaluate:
417 // c_0^2 + c_1^2 + ... < fraction^2 * l1-norm(c.c)
418 // we adjust all parameters accordingly
419 Vector<Number> criteria_squared(criteria.size());
420 std::transform(criteria.begin(),
421 criteria.end(),
422 criteria_squared.begin(),
423 [](Number c) { return c * c; });
424
425 refine_and_coarsen_fixed_fraction_via_l1_norm(tria,
426 criteria_squared,
427 top_fraction *
428 top_fraction,
429 bottom_fraction *
430 bottom_fraction,
431 max_n_cells);
432 }
433 break;
434
435 default:
437 break;
438 }
439}
440
441
442
443template <int dim, typename Number, int spacedim>
444void
446 const Vector<Number> &criteria,
447 const unsigned int order)
448{
449 Assert(criteria.size() == tria.n_active_cells(),
450 ExcDimensionMismatch(criteria.size(), tria.n_active_cells()));
451 Assert(criteria.is_non_negative(), ExcNegativeCriteria());
452
453 // get a decreasing order on the error indicator
454 std::vector<unsigned int> cell_indices(criteria.size());
455 std::iota(cell_indices.begin(), cell_indices.end(), 0u);
456
457 std::sort(cell_indices.begin(),
458 cell_indices.end(),
459 [&criteria](const unsigned int left, const unsigned int right) {
460 return criteria[left] > criteria[right];
461 });
462
463 double expected_error_reduction = 0;
464 const double original_error = criteria.l1_norm();
465
466 const std::size_t N = criteria.size();
467
468 // minimize the cost functional discussed in the documentation
469 double min_cost = std::numeric_limits<double>::max();
470 std::size_t min_arg = 0;
471
472 const double reduction_factor = (1. - std::pow(2., -1. * order));
473 for (std::size_t M = 0; M < criteria.size(); ++M)
474 {
475 expected_error_reduction += reduction_factor * criteria(cell_indices[M]);
476
477 const double cost =
478 std::pow(((Utilities::fixed_power<dim>(2) - 1) * (1 + M) + N),
479 static_cast<double>(order) / dim) *
480 (original_error - expected_error_reduction);
481 if (cost <= min_cost)
482 {
483 min_cost = cost;
484 min_arg = M;
485 }
486 }
487
488 refine(tria, criteria, criteria(cell_indices[min_arg]));
489}
490
491
492// explicit instantiations
493#include "grid/grid_refinement.inst"
494
unsigned int n_active_cells() const
const value_type * const_iterator
Definition vector.h:119
virtual size_type size() const override
iterator end()
bool is_non_negative() const
bool all_zero() const
real_type l1_norm() const
iterator begin()
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_NOT_IMPLEMENTED()
IteratorRange< active_cell_iterator > active_cell_iterators() const
#define Assert(cond, exc)
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 refine_and_coarsen_fixed_number(Triangulation< dim, spacedim > &triangulation, const Vector< Number > &criteria, const double top_fraction_of_cells, const double bottom_fraction_of_cells, const unsigned int max_n_cells=std::numeric_limits< unsigned int >::max())
void refine_and_coarsen_optimize(Triangulation< dim, spacedim > &tria, const Vector< Number > &criteria, const unsigned int order=2)
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)
void refine_and_coarsen_fixed_fraction(Triangulation< dim, spacedim > &tria, const Vector< Number > &criteria, const double top_fraction, const double bottom_fraction, const unsigned int max_n_cells=std::numeric_limits< unsigned int >::max(), const VectorTools::NormType norm_type=VectorTools::L1_norm)
T sum(const T &t, const MPI_Comm mpi_communicator)
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &, const Number p)