ViennaLS
Loading...
Searching...
No Matches
lsOxidationSolverBase.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <lsDomain.hpp>
4
5#include <hrleSparseIterator.hpp>
6
7#include <algorithm>
8#include <array>
9#include <cmath>
10#include <cstddef>
11#include <functional>
12#include <limits>
13#include <string>
14#include <vector>
15
16namespace viennals {
17
18using namespace viennacore;
19
20namespace detail {
21
22template <int D>
23inline std::size_t gridIndexHash(const viennahrle::Index<D> &index) {
24 std::size_t seed = 0;
25 for (unsigned i = 0; i < static_cast<unsigned>(D); ++i) {
26 seed ^= std::hash<long long>{}(static_cast<long long>(index[i])) +
27 std::size_t(0x9e3779b97f4a7c15ULL) + (seed << 6) + (seed >> 2);
28 }
29 return seed;
30}
31
32// Hash functor for viennahrle::Index<D> — safe to use as unordered_map key
33// because collisions are resolved by operator== on the full index.
34template <int D> struct IndexTypeHasher {
35 std::size_t operator()(const viennahrle::Index<D> &idx) const {
36 return gridIndexHash<D>(idx);
37 }
38};
39
40template <class T> inline Vec3D<T> vecScaled(const Vec3D<T> &source, T factor) {
41 Vec3D<T> result{0., 0., 0.};
42 for (unsigned i = 0; i < 3; ++i)
43 result[i] = source[i] * factor;
44 return result;
45}
46
47template <class T>
48inline Vec3D<T> vecAdd(const Vec3D<T> &a, const Vec3D<T> &b) {
49 Vec3D<T> result{0., 0., 0.};
50 for (unsigned i = 0; i < 3; ++i)
51 result[i] = a[i] + b[i];
52 return result;
53}
54
55template <class T>
56inline Vec3D<T> vecSubtract(const Vec3D<T> &a, const Vec3D<T> &b) {
57 Vec3D<T> result{0., 0., 0.};
58 for (unsigned i = 0; i < 3; ++i)
59 result[i] = a[i] - b[i];
60 return result;
61}
62
63template <class T>
64inline void vecAddTo(Vec3D<T> &target, const Vec3D<T> &source) {
65 for (unsigned i = 0; i < 3; ++i)
66 target[i] += source[i];
67}
68
71template <class T> inline T clampLevelSetPhi(T v) {
72 return v > T(1) ? T(1) : (v < T(-1) ? T(-1) : v);
73}
74
75template <class T>
76inline T levelSetCrossingDistance(T insidePhi, T outsidePhi,
77 T minBoundaryFraction, T gridDelta) {
78 const T denom = std::abs(insidePhi) + std::abs(outsidePhi);
79 if (denom <= std::numeric_limits<T>::epsilon())
80 return gridDelta;
81 return std::max(minBoundaryFraction * gridDelta,
82 gridDelta * std::abs(insidePhi) / denom);
83}
84
85} // namespace detail
86
90template <class T, int D> class OxidationSolverBase {
91protected:
92 using IndexType = viennahrle::Index<D>;
94 viennahrle::ConstSparseIterator<typename Domain<T, D>::DomainType>;
95
96 static constexpr std::size_t noNode = std::numeric_limits<std::size_t>::max();
97 std::vector<std::size_t> nodeLookupFlat;
100 std::array<std::size_t, D> extents{};
101 std::array<std::size_t, D> strides{};
103
109
110 bool crosses(T a, T b) const {
111 // Use a small tolerance consistent with isInsideOxide so that grid points
112 // that land exactly on an interface (phi ≈ 0 from floating-point rounding
113 // in GeometricAdvect) are treated as surface points in boundary detection.
114 constexpr T eps = T(1e-9);
115 return (a <= eps && b >= -eps) || (a >= -eps && b <= eps);
116 }
117
118 T valueAt(ConstSparseIterator &it, const IndexType &index) const {
119 it.goToIndices(index);
120 return it.getValue();
121 }
122
123 bool inBounds(const IndexType &index) const {
124 for (unsigned i = 0; i < D; ++i)
125 if (index[i] < minIndex[i] || index[i] > maxIndex[i])
126 return false;
127 return true;
128 }
129
131 std::size_t total = 1;
132 for (unsigned i = 0; i < D; ++i)
133 total *= extents[i];
134 nodeLookupFlat.assign(total, noNode);
135 }
136
137 std::size_t lookupNode(const IndexType &index) const {
138 if (!inBounds(index))
139 return noNode;
140 return nodeLookupFlat[linearIndex(index)];
141 }
142
143 std::size_t linearIndex(const IndexType &index) const {
144 if (!inBounds(index))
145 return std::numeric_limits<std::size_t>::max();
146 std::size_t result = 0;
147 for (unsigned i = 0; i < D; ++i)
148 result += static_cast<std::size_t>(index[i] - minIndex[i]) * strides[i];
149 return result;
150 }
151
152 bool increment(IndexType &index) const {
153 for (unsigned i = 0; i < D; ++i) {
154 if (index[i] < maxIndex[i]) {
155 ++index[i];
156 return true;
157 }
158 index[i] = minIndex[i];
159 }
160 return false;
161 }
162
163 // Searches with expanding radius shells so that RK2 Stage 2 can find oxide
164 // nodes even after the surface has advanced several grid cells beyond the
165 // band.
166 std::size_t findNearbyNode(const IndexType &index) const {
167 for (int radius = 1; radius <= 4; ++radius) {
168 T bestDistance2 = std::numeric_limits<T>::max();
169 std::size_t bestNode = std::numeric_limits<std::size_t>::max();
170
171 IndexType offset{};
172 offset.fill(-radius);
173 while (true) {
174 IndexType candidate = index;
175 T distance2 = 0.;
176 for (unsigned i = 0; i < D; ++i) {
177 candidate[i] += offset[i];
178 distance2 += static_cast<T>(offset[i] * offset[i]);
179 }
180
181 if (distance2 > 0 && inBounds(candidate)) {
182 const std::size_t foundId = nodeLookupFlat[linearIndex(candidate)];
183 if (foundId != noNode && distance2 < bestDistance2) {
184 bestDistance2 = distance2;
185 bestNode = foundId;
186 }
187 }
188
189 unsigned dim = 0;
190 for (; dim < D; ++dim) {
191 if (offset[dim] < radius) {
192 ++offset[dim];
193 break;
194 }
195 offset[dim] = -radius;
196 }
197 if (dim == D)
198 break;
199 }
200
201 if (bestNode != std::numeric_limits<std::size_t>::max())
202 return bestNode;
203 }
204 return std::numeric_limits<std::size_t>::max();
205 }
206
208 SmartPointer<Domain<T, D>> reactionInterface,
209 SmartPointer<Domain<T, D>> ambientInterface,
210 SmartPointer<Domain<T, D>> maskInterface, bool useRequestedBounds,
211 const IndexType &requestedMinIndex, const IndexType &requestedMaxIndex,
212 std::size_t maxGridPoints, const std::string &solverName) {
213 auto &reactionGrid = reactionInterface->getGrid();
214 auto &ambientGrid = ambientInterface->getGrid();
215 gridDelta = reactionGrid.getGridDelta();
216
217 if (std::abs(gridDelta - ambientGrid.getGridDelta()) >
218 std::numeric_limits<T>::epsilon() ||
219 (maskInterface != nullptr &&
220 std::abs(gridDelta - maskInterface->getGrid().getGridDelta()) >
221 std::numeric_limits<T>::epsilon())) {
222 Logger::getInstance()
223 .addError(solverName + ": Interface grid deltas must match.")
224 .print();
225 return false;
226 }
227
228 IndexType gridMin = reactionGrid.getMinGridPoint();
229 IndexType gridMax = reactionGrid.getMaxGridPoint();
230 for (unsigned i = 0; i < D; ++i) {
231 gridMin[i] = std::max(gridMin[i], ambientGrid.getMinGridPoint(i));
232 gridMax[i] = std::min(gridMax[i], ambientGrid.getMaxGridPoint(i));
233 if (maskInterface != nullptr) {
234 gridMin[i] =
235 std::max(gridMin[i], maskInterface->getGrid().getMinGridPoint(i));
236 gridMax[i] =
237 std::min(gridMax[i], maskInterface->getGrid().getMaxGridPoint(i));
238 }
239 }
240
241 auto bounds = definedPointBounds(reactionInterface);
242 mergeDefinedPointBounds(bounds, ambientInterface);
243 if (maskInterface != nullptr)
244 mergeDefinedPointBounds(bounds, maskInterface);
245
246 minIndex = bounds.foundDefinedPoints ? bounds.min : gridMin;
247 maxIndex = bounds.foundDefinedPoints ? bounds.max : gridMax;
248 applyBoundsPaddingAndClamp(minIndex, maxIndex, gridMin, gridMax);
249
250 std::size_t numGridPoints = 1;
251 for (unsigned i = 0; i < D; ++i) {
252 if (useRequestedBounds) {
253 minIndex[i] = std::max(minIndex[i], requestedMinIndex[i]);
254 maxIndex[i] = std::min(maxIndex[i], requestedMaxIndex[i]);
255 }
256 if (maxIndex[i] < minIndex[i]) {
257 Logger::getInstance()
258 .addError(solverName + ": Cartesian solve region is empty.")
259 .print();
260 return false;
261 }
262 extents[i] = static_cast<std::size_t>(maxIndex[i] - minIndex[i] + 1);
263 numGridPoints *= extents[i];
264 strides[i] = (i == 0) ? 1 : strides[i - 1] * extents[i - 1];
265 }
266
267 if (numGridPoints > maxGridPoints) {
268 Logger::getInstance()
269 .addError(solverName +
270 ": Cartesian solve region exceeds maxGridPoints.")
271 .print();
272 return false;
273 }
274 return true;
275 }
276
277 bool initializeGridFromMask(SmartPointer<Domain<T, D>> maskInterface,
278 bool useRequestedBounds,
279 const IndexType &requestedMinIndex,
280 const IndexType &requestedMaxIndex,
281 std::size_t maxGridPoints,
282 const std::string &solverName) {
283 auto &grid = maskInterface->getGrid();
284 gridDelta = grid.getGridDelta();
285
286 auto bounds = definedPointBounds(maskInterface);
287 minIndex = bounds.foundDefinedPoints ? bounds.min : grid.getMinGridPoint();
288 maxIndex = bounds.foundDefinedPoints ? bounds.max : grid.getMaxGridPoint();
289 applyBoundsPaddingAndClamp(minIndex, maxIndex, grid.getMinGridPoint(),
290 grid.getMaxGridPoint());
291
292 std::size_t numGridPoints = 1;
293 for (unsigned i = 0; i < D; ++i) {
294 if (useRequestedBounds) {
295 minIndex[i] = std::max(minIndex[i], requestedMinIndex[i]);
296 maxIndex[i] = std::min(maxIndex[i], requestedMaxIndex[i]);
297 }
298 if (maxIndex[i] < minIndex[i]) {
299 Logger::getInstance()
300 .addError(solverName + ": Cartesian solve region is empty.")
301 .print();
302 return false;
303 }
304 extents[i] = static_cast<std::size_t>(maxIndex[i] - minIndex[i] + 1);
305 numGridPoints *= extents[i];
306 strides[i] = (i == 0) ? 1 : strides[i - 1] * extents[i - 1];
307 }
308
309 if (numGridPoints > maxGridPoints) {
310 Logger::getInstance()
311 .addError(solverName +
312 ": Cartesian solve region exceeds maxGridPoints.")
313 .print();
314 return false;
315 }
316 return true;
317 }
318
319private:
320 GridBounds definedPointBounds(SmartPointer<Domain<T, D>> levelSet) const {
321 GridBounds bounds;
322 ConstSparseIterator it(levelSet->getDomain());
323 for (; !it.isFinished(); ++it) {
324 if (!it.isDefined())
325 continue;
326
327 const auto &index = it.getStartIndices();
328 if (!bounds.foundDefinedPoints) {
329 bounds.min = index;
330 bounds.max = index;
331 bounds.foundDefinedPoints = true;
332 } else {
333 for (unsigned i = 0; i < D; ++i) {
334 bounds.min[i] = std::min(bounds.min[i], index[i]);
335 bounds.max[i] = std::max(bounds.max[i], index[i]);
336 }
337 }
338 }
339 return bounds;
340 }
341
342 void mergeDefinedPointBounds(GridBounds &target,
343 SmartPointer<Domain<T, D>> levelSet) const {
344 const auto source = definedPointBounds(levelSet);
345 if (!source.foundDefinedPoints)
346 return;
347 if (!target.foundDefinedPoints) {
348 target = source;
349 return;
350 }
351 for (unsigned i = 0; i < D; ++i) {
352 target.min[i] = std::min(target.min[i], source.min[i]);
353 target.max[i] = std::max(target.max[i], source.max[i]);
354 }
355 }
356
357 static void applyBoundsPaddingAndClamp(IndexType &minIndex,
359 const IndexType &gridMin,
360 const IndexType &gridMax) {
361 constexpr int padding = 4;
362 for (unsigned i = 0; i < D; ++i) {
363 // Clamp to the physical grid before adding/subtracting padding.
364 // lsInterior on an INFINITE_BOUNDARY level-set can leave far-field
365 // sentinel entries (near std::numeric_limits<IndexType>::min/max) in
366 // the HRLE structure. definedPointBounds() visits them and records
367 // the extreme index. Subtracting padding from that sentinel directly
368 // overflows, turning the tiny sentinel into a large positive value and
369 // making extents[i] enormous. Clamping first makes the arithmetic safe.
370 minIndex[i] =
371 std::max(gridMin[i], std::max(gridMin[i], minIndex[i]) - padding);
372 maxIndex[i] =
373 std::min(gridMax[i], std::min(gridMax[i], maxIndex[i]) + padding);
374 }
375 }
376};
377
378} // namespace viennals
constexpr int D
Definition Epitaxy.cpp:12
double T
Definition Epitaxy.cpp:13
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:27
Common Cartesian-grid infrastructure shared by the three oxidation solver classes (diffusion,...
Definition lsOxidationSolverBase.hpp:90
static constexpr std::size_t noNode
Definition lsOxidationSolverBase.hpp:96
bool crosses(T a, T b) const
Definition lsOxidationSolverBase.hpp:110
std::size_t lookupNode(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:137
std::size_t linearIndex(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:143
void initNodeLookup()
Definition lsOxidationSolverBase.hpp:130
bool inBounds(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:123
std::array< std::size_t, D > strides
Definition lsOxidationSolverBase.hpp:101
T gridDelta
Definition lsOxidationSolverBase.hpp:102
bool initializeGridFromMask(SmartPointer< Domain< T, D > > maskInterface, bool useRequestedBounds, const IndexType &requestedMinIndex, const IndexType &requestedMaxIndex, std::size_t maxGridPoints, const std::string &solverName)
Definition lsOxidationSolverBase.hpp:277
std::vector< std::size_t > nodeLookupFlat
Definition lsOxidationSolverBase.hpp:97
bool initializeGridFromInterfaces(SmartPointer< Domain< T, D > > reactionInterface, SmartPointer< Domain< T, D > > ambientInterface, SmartPointer< Domain< T, D > > maskInterface, bool useRequestedBounds, const IndexType &requestedMinIndex, const IndexType &requestedMaxIndex, std::size_t maxGridPoints, const std::string &solverName)
Definition lsOxidationSolverBase.hpp:207
std::array< std::size_t, D > extents
Definition lsOxidationSolverBase.hpp:100
viennahrle::ConstSparseIterator< typename Domain< T, D >::DomainType > ConstSparseIterator
Definition lsOxidationSolverBase.hpp:93
T valueAt(ConstSparseIterator &it, const IndexType &index) const
Definition lsOxidationSolverBase.hpp:118
bool increment(IndexType &index) const
Definition lsOxidationSolverBase.hpp:152
IndexType minIndex
Definition lsOxidationSolverBase.hpp:98
viennahrle::Index< D > IndexType
Definition lsOxidationSolverBase.hpp:92
std::size_t findNearbyNode(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:166
IndexType maxIndex
Definition lsOxidationSolverBase.hpp:99
tuple bounds
Definition AirGapDeposition.py:63
Definition lsOxidationSolverBase.hpp:20
Vec3D< T > vecAdd(const Vec3D< T > &a, const Vec3D< T > &b)
Definition lsOxidationSolverBase.hpp:48
T levelSetCrossingDistance(T insidePhi, T outsidePhi, T minBoundaryFraction, T gridDelta)
Definition lsOxidationSolverBase.hpp:76
Vec3D< T > vecSubtract(const Vec3D< T > &a, const Vec3D< T > &b)
Definition lsOxidationSolverBase.hpp:56
T clampLevelSetPhi(T v)
Clamp HRLE far-field sentinels (±DBL_MAX) to ±1 before differencing to prevent DBL_MAX² overflow that...
Definition lsOxidationSolverBase.hpp:71
Vec3D< T > vecScaled(const Vec3D< T > &source, T factor)
Definition lsOxidationSolverBase.hpp:40
std::size_t gridIndexHash(const viennahrle::Index< D > &index)
Definition lsOxidationSolverBase.hpp:23
void vecAddTo(Vec3D< T > &target, const Vec3D< T > &source)
Definition lsOxidationSolverBase.hpp:64
Definition lsAdvect.hpp:41
Definition lsOxidationSolverBase.hpp:104
IndexType max
Definition lsOxidationSolverBase.hpp:106
bool foundDefinedPoints
Definition lsOxidationSolverBase.hpp:107
IndexType min
Definition lsOxidationSolverBase.hpp:105
Definition lsOxidationSolverBase.hpp:34
std::size_t operator()(const viennahrle::Index< D > &idx) const
Definition lsOxidationSolverBase.hpp:35