MAIA bb96820c
Multiphysics at AIA
Loading...
Searching...
No Matches
hilbert.h
Go to the documentation of this file.
1// Copyright (C) 2024 The m-AIA AUTHORS
2//
3// This file is part of m-AIA (https://git.rwth-aachen.de/aia/m-AIA/m-AIA)
4//
5// SPDX-License-Identifier: LGPL-3.0-only
6
7#ifndef HILBERT_H_
8#define HILBERT_H_
9
10#include <algorithm>
11#include <array>
12#include <climits>
14#include "INCLUDE/maiatypes.h"
15
16namespace maia {
17namespace grid {
18namespace hilbert {
19
20// Detail_ namespace contains implementation details and should never be used
21// directly from outside this namespace. For the actual Hilbert index function
22// and comments refer to the method "index(...)" below.
23namespace detail_ {
24
25// Empty class template to produce error if not specialized below
26template <MInt nDim, typename FloatType, typename IdType>
27struct Impl;
28
29
30// Partial specialization for 2-dimensional case (for info on algorithm, see
31// "index(...)" method below)
32template <typename FloatType, typename IdType>
33struct Impl<2, FloatType, IdType> {
34 static IdType f(const FloatType* const coordinates, const IdType level) {
35 // Define dimension variable for easy reference
36 const IdType nDim = 2;
37
38 // Reset index and save coordinates into working array that will be used in
39 // the algorithm
40 IdType index = 0;
41 std::array<FloatType, nDim> x;
42 std::copy_n(coordinates, nDim, x.begin());
43
44 // Calculate contribution to Hilbert index iteratively for each level
45 for(IdType l = 0; l < level; l++) {
46 // SX and SY determine the quadrant in which the coordinates are
47 const IdType SX = x[0] < 0.5 ? 0 : 1;
48 const IdType SY = x[1] < 0.5 ? 0 : 1;
49
50 // Sn is 1 if coordinates are in quadrant n and 0 otherwise
51 // Some quadrants n,m are treated equally and a Snm variable is used
52 const IdType S0 = (1 - SX) * (1 - SY);
53 const IdType S12 = SY;
54 const IdType S3 = SX * (1 - SY);
55
56 // Multiplier is the weight by which a contribution to the Hilbert index
57 // is scaled. Higher levels have higher weights and thus a higher impact
58 // on the final index.
59 const IdType multiplier = IPOW2(nDim * (level - 1 - l));
60
61 // Update index by determining the local quadrand and scaling it
62 index += ((1 + SX) * S12 + 3 * S3) * multiplier;
63
64 // Finally, new transformed (mirrored, scaled and translated) coordinates
65 // are determined for the next step in the algorithm
66 std::array<FloatType, nDim> transformed;
67 transformed[0] = 2.0 * x[1] * S0 + (2.0 * x[0] - SX) * S12 + (-2.0 * x[1] + 1.0) * S3;
68 transformed[1] = 2.0 * x[0] * S0 + (2.0 * x[1] - 1.0) * S12 + 2.0 * (-x[0] + 1.0) * S3;
69 x = transformed;
70 }
71
72 return index;
73 }
74};
75
76
77// Partial specialization for 3-dimensional case (for info on algorithm, see
78// "index(...)" method below)
79template <typename FloatType, typename IdType>
80struct Impl<3, FloatType, IdType> {
81 static IdType f(const FloatType* const coordinates, const IdType level) {
82 // Define dimension variable for easy reference
83 const IdType nDim = 3;
84
85 // Reset index and save coordinates into working array that will be used in
86 // the algorithm
87 IdType index = 0;
88 std::array<FloatType, nDim> x;
89 std::copy_n(coordinates, nDim, x.begin());
90
91 // Calculate contribution to Hilbert index iteratively for each level
92 for(IdType l = 0; l < level; l++) {
93 // SX and SY determine the quadrant in which the coordinates are
94 const IdType SX = x[0] < 0.5 ? 0 : 1;
95 const IdType SY = x[1] < 0.5 ? 0 : 1;
96 const IdType SZ = x[2] < 0.5 ? 0 : 1;
97
98 // Sn is 1 if coordinates are in quadrant n and 0 otherwise
99 // Some quadrants n,m are treated equally and a Snm variable is used
100 const IdType S0 = (1 - SX) * (1 - SY) * (1 - SZ);
101 const IdType S12 = SY * (1 - SZ);
102 const IdType S34 = SX * (1 - SY);
103 const IdType S56 = SY * SZ;
104 const IdType S7 = (1 - SX) * (1 - SY) * SZ;
105
106 // Multiplier is the weight by which a contribution to the Hilbert index
107 // is scaled. Higher levels have higher weights and thus a higher impact
108 // on the final index.
109 const IdType multiplier = IPOW2(nDim * (level - 1 - l));
110
111 // Update index by determining the local quadrand and scaling it
112 index += ((1 + SX) * S12 + (3 + SZ) * S34 + (6 - SX) * S56 + 7 * S7) * multiplier;
113
114 // Finally, new transformed (mirrored, scaled and translated) coordinates
115 // are determined for the next step in the algorithm
116 std::array<FloatType, nDim> transformed;
117 transformed[0] =
118 2.0 * x[0] * S0 + 2.0 * x[2] * S12 + (-2.0 * x[1] + 1.0) * S34 + 2.0 * (-x[2] + 1.0) * S56 + 2.0 * x[0] * S7;
119 transformed[1] = 2.0 * x[2] * S0 + (2.0 * x[1] - 1.0) * S12 + 2.0 * (-x[0] + 1.0) * S34 + (2.0 * x[1] - 1.0) * S56
120 + 2.0 * (-x[2] + 1.0) * S7;
121 transformed[2] = 2.0 * x[1] * S0 + (2.0 * x[0] - SX) * S12 + (2.0 * x[2] - SZ) * S34
122 + (-2.0 * x[0] + 1.0 + SX) * S56 + (-2.0 * x[1] + 1.0) * S7;
123 x = transformed;
124 }
125
126 return index;
127 }
128};
129
130} // namespace detail_
131
132
164template <MInt nDim, typename FloatType, typename IdType>
165IdType index(const FloatType* const x, const IdType level) {
166 // Check that level is at least 0
167 ASSERT(level >= 0, "Level must be at least zero");
168
169 // Check for integer overflow ("- 1" to account for signed integers, cast to
170 // avoid "unsigned/signed comparison" warning)
171 ASSERT(nDim * level <= CHAR_BIT * static_cast<IdType>(sizeof(IdType)) - 1, "Integer overflow: level too large");
172
173 // Assert that coordinates are in unit hypercube
174 ASSERT(x[0] >= 0.0 && x[0] <= 1.0, "Coordinate 0 out of bounds.");
175 ASSERT(x[1] >= 0.0 && x[1] <= 1.0, "Coordinate 1 out of bounds.");
176 IF_CONSTEXPR(nDim == 3) { ASSERT(x[2] >= 0.0 && x[2] <= 1.0, "Coordinate 2 out of bounds."); }
177
178 // Call template function
180}
181
182
187template <MInt nDim, typename FloatType, typename IdType>
189 const FloatType* const x,
190 const IdType level,
191 const FloatType* const centerOfGravity,
192 FloatType const lengthOnLevel0) {
193 // Check that level is at least 0
194 ASSERT(level >= 0, "Level must be at least zero");
195
196 // Check for integer overflow ("- 1" to account for signed integers, cast to
197 // avoid "unsigned/signed comparison" warning)
198 ASSERT(nDim * level <= CHAR_BIT * static_cast<IdType>(sizeof(IdType)) - 1, "Integer overflow: level too large");
199
200 MBool exists = true;
201 treeId = 0;
202 for(IdType k = 0; k < nDim; k++) {
203 if(x[k] < centerOfGravity[k] - 0.5 * lengthOnLevel0 || x[k] > centerOfGravity[k] + 0.5 * lengthOnLevel0) {
204 exists = false;
205 }
206 }
207 if(!exists) {
208 treeId = std::numeric_limits<IdType>::is_signed ? -1 : ~0;
209 } else {
210 IdType bitCount = 0;
211 IdType coord[3];
212 constexpr IdType id2 = (IdType)2;
213 for(IdType k = 0; k < nDim; k++) {
214 coord[k] = (IdType)((x[k] - centerOfGravity[k] + 0.5 * lengthOnLevel0) * FPOW2(level) / lengthOnLevel0);
215 }
216 for(IdType lvl = level - 1; lvl >= 0; lvl--) {
217 for(IdType k = 0; k < nDim; k++) {
218 IdType bit = (coord[k] / (IdType)IPOW2(lvl)) % id2;
219 treeId |= bit << bitCount;
220 bitCount++;
221 }
222 }
223 }
224 return exists;
225}
226
231template <MInt nDim, typename FloatType, typename IdType>
232void treeIdToCoordinates(FloatType* const x,
233 const IdType treeId,
234 const IdType level,
235 const FloatType* const centerOfGravity,
236 const FloatType lengthOnLevel0) {
237 // Check that level is at least 0
238 ASSERT(level >= 0, "Level must be at least zero");
239
240 // Check for integer overflow ("- 1" to account for signed integers, cast to
241 // avoid "unsigned/signed comparison" warning)
242 ASSERT(nDim * level <= CHAR_BIT * static_cast<IdType>(sizeof(IdType)) - 1, "Integer overflow: level too large");
243
244 IdType bitCount = 0;
245 constexpr IdType id1 = (IdType)1;
246 for(IdType j = 0; j < nDim; j++) {
247 x[j] = centerOfGravity[j];
248 }
249 for(IdType lvl = 1; lvl <= level; lvl++) {
250 for(IdType j = 0; j < nDim; j++) {
251 IdType tmpBit = (treeId >> bitCount) & id1;
252 x[j] += (tmpBit ? 1.0 : -1.0) * lengthOnLevel0 * FFPOW2(lvl + 1);
253 bitCount++;
254 }
255 }
256}
257
258} // namespace hilbert
259} // namespace grid
260} // namespace maia
261
262#endif // HILBERT_H_
constexpr MLong IPOW2(MInt x)
constexpr MFloat FPOW2(MInt x)
constexpr MFloat FFPOW2(MInt x)
bool MBool
Definition: maiatypes.h:58
void treeIdToCoordinates(FloatType *const x, const IdType treeId, const IdType level, const FloatType *const centerOfGravity, const FloatType lengthOnLevel0)
Definition: hilbert.h:232
IdType index(const FloatType *const x, const IdType level)
Return Hilbert index for given location and level in 2D or 3D.
Definition: hilbert.h:165
MBool coordinatesToTreeId(IdType &treeId, const FloatType *const x, const IdType level, const FloatType *const centerOfGravity, FloatType const lengthOnLevel0)
Definition: hilbert.h:188
Namespace for auxiliary functions/classes.
static IdType f(const FloatType *const coordinates, const IdType level)
Definition: hilbert.h:34
static IdType f(const FloatType *const coordinates, const IdType level)
Definition: hilbert.h:81