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
24template <class T> inline T clampLevelSetPhi(T v) {
25 return v > T(1) ? T(1) : (v < T(-1) ? T(-1) : v);
26}
27
28template <class T>
29inline T levelSetCrossingDistance(T insidePhi, T outsidePhi,
30 T minBoundaryFraction, T gridDelta) {
31 const T denom = std::abs(insidePhi) + std::abs(outsidePhi);
32 if (denom <= std::numeric_limits<T>::epsilon())
33 return gridDelta;
34 return std::max(minBoundaryFraction * gridDelta,
35 gridDelta * std::abs(insidePhi) / denom);
36}
37
38} // namespace detail
39
43template <class T, int D> class OxidationSolverBase {
44protected:
45 using IndexType = viennahrle::Index<D>;
47 viennahrle::ConstSparseIterator<typename Domain<T, D>::DomainType>;
48
49 static constexpr std::size_t noNode = std::numeric_limits<std::size_t>::max();
50 std::vector<std::size_t> nodeLookupFlat;
53 std::array<std::size_t, D> extents{};
54 std::array<std::size_t, D> strides{};
56
62
63 bool crosses(T a, T b) const {
64 // Use a small tolerance consistent with isInsideOxide so that grid points
65 // that land exactly on an interface (phi ≈ 0 from floating-point rounding
66 // in GeometricAdvect) are treated as surface points in boundary detection.
67 constexpr T eps = T(1e-9);
68 return (a <= eps && b >= -eps) || (a >= -eps && b <= eps);
69 }
70
71 T valueAt(ConstSparseIterator &it, const IndexType &index) const {
72 it.goToIndices(index);
73 return it.getValue();
74 }
75
76 bool inBounds(const IndexType &index) const {
77 for (unsigned i = 0; i < D; ++i)
78 if (index[i] < minIndex[i] || index[i] > maxIndex[i])
79 return false;
80 return true;
81 }
82
84 std::size_t total = 1;
85 for (unsigned i = 0; i < D; ++i)
86 total *= extents[i];
87 nodeLookupFlat.assign(total, noNode);
88 }
89
90 std::size_t lookupNode(const IndexType &index) const {
91 if (!inBounds(index))
92 return noNode;
93 return nodeLookupFlat[linearIndex(index)];
94 }
95
96 std::size_t linearIndex(const IndexType &index) const {
97 if (!inBounds(index))
98 return std::numeric_limits<std::size_t>::max();
99 std::size_t result = 0;
100 for (unsigned i = 0; i < D; ++i)
101 result += static_cast<std::size_t>(index[i] - minIndex[i]) * strides[i];
102 return result;
103 }
104
105 bool increment(IndexType &index) const {
106 for (unsigned i = 0; i < D; ++i) {
107 if (index[i] < maxIndex[i]) {
108 ++index[i];
109 return true;
110 }
111 index[i] = minIndex[i];
112 }
113 return false;
114 }
115
116 // Searches with expanding radius shells so that RK2 Stage 2 can find oxide
117 // nodes even after the surface has advanced several grid cells beyond the
118 // band.
119 std::size_t findNearbyNode(const IndexType &index) const {
120 for (int radius = 1; radius <= 4; ++radius) {
121 T bestDistance2 = std::numeric_limits<T>::max();
122 std::size_t bestNode = std::numeric_limits<std::size_t>::max();
123
124 IndexType offset{};
125 offset.fill(-radius);
126 while (true) {
127 IndexType candidate = index;
128 T distance2 = 0.;
129 for (unsigned i = 0; i < D; ++i) {
130 candidate[i] += offset[i];
131 distance2 += static_cast<T>(offset[i] * offset[i]);
132 }
133
134 if (distance2 > 0 && inBounds(candidate)) {
135 const std::size_t foundId = nodeLookupFlat[linearIndex(candidate)];
136 if (foundId != noNode && distance2 < bestDistance2) {
137 bestDistance2 = distance2;
138 bestNode = foundId;
139 }
140 }
141
142 unsigned dim = 0;
143 for (; dim < D; ++dim) {
144 if (offset[dim] < radius) {
145 ++offset[dim];
146 break;
147 }
148 offset[dim] = -radius;
149 }
150 if (dim == D)
151 break;
152 }
153
154 if (bestNode != std::numeric_limits<std::size_t>::max())
155 return bestNode;
156 }
157 return std::numeric_limits<std::size_t>::max();
158 }
159
161 SmartPointer<Domain<T, D>> reactionInterface,
162 SmartPointer<Domain<T, D>> ambientInterface,
163 SmartPointer<Domain<T, D>> maskInterface, bool useRequestedBounds,
164 const IndexType &requestedMinIndex, const IndexType &requestedMaxIndex,
165 std::size_t maxGridPoints, const std::string &solverName) {
166 auto &reactionGrid = reactionInterface->getGrid();
167 auto &ambientGrid = ambientInterface->getGrid();
168 gridDelta = reactionGrid.getGridDelta();
169
170 if (std::abs(gridDelta - ambientGrid.getGridDelta()) >
171 std::numeric_limits<T>::epsilon() ||
172 (maskInterface != nullptr &&
173 std::abs(gridDelta - maskInterface->getGrid().getGridDelta()) >
174 std::numeric_limits<T>::epsilon())) {
175 Logger::getInstance()
176 .addError(solverName + ": Interface grid deltas must match.")
177 .print();
178 return false;
179 }
180
181 IndexType gridMin = reactionGrid.getMinGridPoint();
182 IndexType gridMax = reactionGrid.getMaxGridPoint();
183 for (unsigned i = 0; i < D; ++i) {
184 gridMin[i] = std::max(gridMin[i], ambientGrid.getMinGridPoint(i));
185 gridMax[i] = std::min(gridMax[i], ambientGrid.getMaxGridPoint(i));
186 if (maskInterface != nullptr) {
187 gridMin[i] =
188 std::max(gridMin[i], maskInterface->getGrid().getMinGridPoint(i));
189 gridMax[i] =
190 std::min(gridMax[i], maskInterface->getGrid().getMaxGridPoint(i));
191 }
192 }
193
194 auto bounds = definedPointBounds(reactionInterface);
195 mergeDefinedPointBounds(bounds, ambientInterface);
196 if (maskInterface != nullptr)
197 mergeDefinedPointBounds(bounds, maskInterface);
198
199 minIndex = bounds.foundDefinedPoints ? bounds.min : gridMin;
200 maxIndex = bounds.foundDefinedPoints ? bounds.max : gridMax;
201 applyBoundsPaddingAndClamp(minIndex, maxIndex, gridMin, gridMax);
202
203 std::size_t numGridPoints = 1;
204 for (unsigned i = 0; i < D; ++i) {
205 if (useRequestedBounds) {
206 minIndex[i] = std::max(minIndex[i], requestedMinIndex[i]);
207 maxIndex[i] = std::min(maxIndex[i], requestedMaxIndex[i]);
208 }
209 if (maxIndex[i] < minIndex[i]) {
210 Logger::getInstance()
211 .addError(solverName + ": Cartesian solve region is empty.")
212 .print();
213 return false;
214 }
215 extents[i] = static_cast<std::size_t>(maxIndex[i] - minIndex[i] + 1);
216 numGridPoints *= extents[i];
217 strides[i] = (i == 0) ? 1 : strides[i - 1] * extents[i - 1];
218 }
219
220 if (numGridPoints > maxGridPoints) {
221 Logger::getInstance()
222 .addError(solverName +
223 ": Cartesian solve region exceeds maxGridPoints.")
224 .print();
225 return false;
226 }
227 return true;
228 }
229
230 bool initializeGridFromMask(SmartPointer<Domain<T, D>> maskInterface,
231 bool useRequestedBounds,
232 const IndexType &requestedMinIndex,
233 const IndexType &requestedMaxIndex,
234 std::size_t maxGridPoints,
235 const std::string &solverName) {
236 auto &grid = maskInterface->getGrid();
237 gridDelta = grid.getGridDelta();
238
239 auto bounds = definedPointBounds(maskInterface);
240 minIndex = bounds.foundDefinedPoints ? bounds.min : grid.getMinGridPoint();
241 maxIndex = bounds.foundDefinedPoints ? bounds.max : grid.getMaxGridPoint();
242 applyBoundsPaddingAndClamp(minIndex, maxIndex, grid.getMinGridPoint(),
243 grid.getMaxGridPoint());
244
245 std::size_t numGridPoints = 1;
246 for (unsigned i = 0; i < D; ++i) {
247 if (useRequestedBounds) {
248 minIndex[i] = std::max(minIndex[i], requestedMinIndex[i]);
249 maxIndex[i] = std::min(maxIndex[i], requestedMaxIndex[i]);
250 }
251 if (maxIndex[i] < minIndex[i]) {
252 Logger::getInstance()
253 .addError(solverName + ": Cartesian solve region is empty.")
254 .print();
255 return false;
256 }
257 extents[i] = static_cast<std::size_t>(maxIndex[i] - minIndex[i] + 1);
258 numGridPoints *= extents[i];
259 strides[i] = (i == 0) ? 1 : strides[i - 1] * extents[i - 1];
260 }
261
262 if (numGridPoints > maxGridPoints) {
263 Logger::getInstance()
264 .addError(solverName +
265 ": Cartesian solve region exceeds maxGridPoints.")
266 .print();
267 return false;
268 }
269 return true;
270 }
271
272private:
273 GridBounds definedPointBounds(SmartPointer<Domain<T, D>> levelSet) const {
274 GridBounds bounds;
275 ConstSparseIterator it(levelSet->getDomain());
276 for (; !it.isFinished(); ++it) {
277 if (!it.isDefined())
278 continue;
279
280 const auto &index = it.getStartIndices();
281 if (!bounds.foundDefinedPoints) {
282 bounds.min = index;
283 bounds.max = index;
284 bounds.foundDefinedPoints = true;
285 } else {
286 for (unsigned i = 0; i < D; ++i) {
287 bounds.min[i] = std::min(bounds.min[i], index[i]);
288 bounds.max[i] = std::max(bounds.max[i], index[i]);
289 }
290 }
291 }
292 return bounds;
293 }
294
295 void mergeDefinedPointBounds(GridBounds &target,
296 SmartPointer<Domain<T, D>> levelSet) const {
297 const auto source = definedPointBounds(levelSet);
298 if (!source.foundDefinedPoints)
299 return;
300 if (!target.foundDefinedPoints) {
301 target = source;
302 return;
303 }
304 for (unsigned i = 0; i < D; ++i) {
305 target.min[i] = std::min(target.min[i], source.min[i]);
306 target.max[i] = std::max(target.max[i], source.max[i]);
307 }
308 }
309
310 static void applyBoundsPaddingAndClamp(IndexType &minIndex,
312 const IndexType &gridMin,
313 const IndexType &gridMax) {
314 constexpr int padding = 4;
315 for (unsigned i = 0; i < D; ++i) {
316 // Clamp to the physical grid before adding/subtracting padding.
317 // lsInterior on an INFINITE_BOUNDARY level-set can leave far-field
318 // sentinel entries (near std::numeric_limits<IndexType>::min/max) in
319 // the HRLE structure. definedPointBounds() visits them and records
320 // the extreme index. Subtracting padding from that sentinel directly
321 // overflows, turning the tiny sentinel into a large positive value and
322 // making extents[i] enormous. Clamping first makes the arithmetic safe.
323 minIndex[i] =
324 std::max(gridMin[i], std::max(gridMin[i], minIndex[i]) - padding);
325 maxIndex[i] =
326 std::min(gridMax[i], std::min(gridMax[i], maxIndex[i]) + padding);
327 }
328 }
329};
330
331} // 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:43
static constexpr std::size_t noNode
Definition lsOxidationSolverBase.hpp:49
bool crosses(T a, T b) const
Definition lsOxidationSolverBase.hpp:63
std::size_t lookupNode(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:90
std::size_t linearIndex(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:96
void initNodeLookup()
Definition lsOxidationSolverBase.hpp:83
bool inBounds(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:76
std::array< std::size_t, D > strides
Definition lsOxidationSolverBase.hpp:54
T gridDelta
Definition lsOxidationSolverBase.hpp:55
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:230
std::vector< std::size_t > nodeLookupFlat
Definition lsOxidationSolverBase.hpp:50
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:160
std::array< std::size_t, D > extents
Definition lsOxidationSolverBase.hpp:53
viennahrle::ConstSparseIterator< typename Domain< T, D >::DomainType > ConstSparseIterator
Definition lsOxidationSolverBase.hpp:46
T valueAt(ConstSparseIterator &it, const IndexType &index) const
Definition lsOxidationSolverBase.hpp:71
bool increment(IndexType &index) const
Definition lsOxidationSolverBase.hpp:105
IndexType minIndex
Definition lsOxidationSolverBase.hpp:51
viennahrle::Index< D > IndexType
Definition lsOxidationSolverBase.hpp:45
std::size_t findNearbyNode(const IndexType &index) const
Definition lsOxidationSolverBase.hpp:119
IndexType maxIndex
Definition lsOxidationSolverBase.hpp:52
tuple bounds
Definition AirGapDeposition.py:63
Definition lsOxidationSolverBase.hpp:20
T levelSetCrossingDistance(T insidePhi, T outsidePhi, T minBoundaryFraction, T gridDelta)
Definition lsOxidationSolverBase.hpp:29
T clampLevelSetPhi(T v)
Clamp HRLE far-field sentinels (±DBL_MAX) to ±1 before differencing to prevent DBL_MAX² overflow that...
Definition lsOxidationSolverBase.hpp:24
Definition lsAdvect.hpp:41
Definition lsOxidationSolverBase.hpp:57
IndexType max
Definition lsOxidationSolverBase.hpp:59
bool foundDefinedPoints
Definition lsOxidationSolverBase.hpp:60
IndexType min
Definition lsOxidationSolverBase.hpp:58