core
Shared MPI block decomposition + asynchronous ghost-layer exchange (header-only C++20)
Loading...
Searching...
No Matches
indicators.hpp
Go to the documentation of this file.
1// core — solution-adaptive refinement indicators for a BlockOctree.
2//
3// Geometry-based refinement lives in refine.hpp (refineToSdf). This header adds
4// *solution*-based error indicators: per-leaf scalars derived from a leaf field
5// that flag where the mesh should be refined (large indicator) or may be coarsened
6// (small indicator). They drive the dynamic-AMR cycle in adapt.hpp.
7//
8// The headline criterion is the Löhner (1987) indicator — a dimensionless,
9// normalized second difference that is the standard robust feature detector for
10// AMR (it tracks curvature / steep fronts and self-normalizes so a single
11// threshold works across scales). A raw second-derivative magnitude is also
12// provided for when an un-normalized curvature measure is wanted.
13//
14// Neighbour values come from BlockOctree::faceNeighbor (the covering same/coarser/
15// corner-finer neighbour under 2:1 balance); a face with no neighbour (block /
16// domain edge) contributes nothing on that axis.
17//
18// Header-only, guarded by PECLET_CORE_HAVE_MORTON.
19#ifndef PECLET_CORE_AMR_INDICATORS_HPP
20#define PECLET_CORE_AMR_INDICATORS_HPP
21
22#ifdef PECLET_CORE_HAVE_MORTON
23
24#include <cmath>
25#include <vector>
26
29
30namespace peclet::core::amr {
31
39template <int Dim, unsigned Bits>
40std::vector<double> lohnerIndicator(const BlockOctree<Dim, Bits>& t, const std::vector<double>& u,
41 double eps = 0.01) {
42 const Index n = t.numLeaves();
43 std::vector<double> e(static_cast<std::size_t>(n), 0.0);
44 for (Index i = 0; i < n; ++i) {
45 const double ui = u[static_cast<std::size_t>(i)];
46 double num2 = 0.0, den2 = 0.0;
47 for (int axis = 0; axis < Dim; ++axis) {
48 const Index jp = t.faceNeighbor(i, axis, +1);
49 const Index jm = t.faceNeighbor(i, axis, -1);
50 if (jp < 0 || jm < 0)
51 continue;
52 const double up = u[static_cast<std::size_t>(jp)];
53 const double um = u[static_cast<std::size_t>(jm)];
54 const double d2 = up - 2.0 * ui + um;
55 const double nrm = std::fabs(up - ui) + std::fabs(ui - um) +
56 eps * (std::fabs(up) + 2.0 * std::fabs(ui) + std::fabs(um));
57 num2 += d2 * d2;
58 den2 += nrm * nrm;
59 }
60 e[static_cast<std::size_t>(i)] = (den2 > 0.0) ? std::sqrt(num2 / den2) : 0.0;
61 }
62 return e;
63}
64
69template <int Dim, unsigned Bits>
70std::vector<double> secondDiffIndicator(const BlockOctree<Dim, Bits>& t,
71 const std::vector<double>& u) {
72 const Index n = t.numLeaves();
73 std::vector<double> e(static_cast<std::size_t>(n), 0.0);
74 for (Index i = 0; i < n; ++i) {
75 const double ui = u[static_cast<std::size_t>(i)];
76 double s = 0.0;
77 for (int axis = 0; axis < Dim; ++axis) {
78 const Index jp = t.faceNeighbor(i, axis, +1);
79 const Index jm = t.faceNeighbor(i, axis, -1);
80 if (jp < 0 || jm < 0)
81 continue;
82 const double d2 =
83 u[static_cast<std::size_t>(jp)] - 2.0 * ui + u[static_cast<std::size_t>(jm)];
84 s += d2 * d2;
85 }
86 e[static_cast<std::size_t>(i)] = std::sqrt(s);
87 }
88 return e;
89}
90
91} // namespace peclet::core::amr
92
93#endif // PECLET_CORE_HAVE_MORTON
94#endif // PECLET_CORE_AMR_INDICATORS_HPP
std::vector< double > secondDiffIndicator(const BlockOctree< Dim, Bits > &t, const std::vector< double > &u)
Raw second-derivative magnitude indicator, per leaf: the L2 norm over axes of the undivided second di...
std::vector< double > lohnerIndicator(const BlockOctree< Dim, Bits > &t, const std::vector< double > &u, double eps=0.01)
Löhner normalized second-difference indicator E_i ∈ [0,1], per leaf, for scalar u (indexed by leaf sl...
Kokkos::View< T *, MemSpace > View
1D device array.
Definition view.hpp:26
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
Definition types.hpp:15