ViennaLS
Loading...
Searching...
No Matches
lsStencilLocalLaxFriedrichsScalar.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <hrleSparseBoxIterator.hpp>
4
5#include <lsDomain.hpp>
6#include <lsExpand.hpp>
8#include <lsVelocityField.hpp>
9
10#include <vcVectorType.hpp>
11
12// Choose adaptive epsilon strategy
13// 0: Original fixed epsilon
14// 1: Simple adaptive epsilon (recommended)
15// 2: Full adaptive epsilon (most accurate but slower)
16#ifndef ADAPTIVE_EPSILON_STRATEGY
17#define ADAPTIVE_EPSILON_STRATEGY 1
18#endif
19
20namespace lsInternal {
21
22using namespace viennacore;
23
30template <class T, int D, int order,
31 DifferentiationSchemeEnum finiteDifferenceScheme =
34 using LevelSetType = SmartPointer<viennals::Domain<T, D>>;
35
36 LevelSetType levelSet;
37 SmartPointer<viennals::VelocityField<T>> velocities;
38 viennahrle::ConstSparseBoxIterator<viennahrle::Domain<T, D>,
39 static_cast<int>(finiteDifferenceScheme) +
40 1 + order>
41 neighborIterator;
42 const double gridDelta;
43 const double alphaFactor;
44 const double baseNormalEpsilon =
45 std::cbrt(std::numeric_limits<double>::epsilon());
46
47 // Final dissipation coefficients that are used by the time integrator.
48 VectorType<T, D> finalAlphas{};
49 static constexpr unsigned numStencilPoints = hrleUtil::pow(2 * order + 1, D);
50 static double maxDissipation; // default: std::numeric_limits<double>::max();
51
52 // Adaptive epsilon control parameters
53 static constexpr T velocityScaleFactor =
54 0.5; // Controls velocity-based scaling
55 static constexpr T smoothnessThreshold =
56 0.1; // Threshold for smoothness detection
57 static constexpr T epsilonMinScale = 0.1; // Minimum epsilon scale factor
58 static constexpr T epsilonMaxScale = 100.0; // Maximum epsilon scale factor
59
60#if ADAPTIVE_EPSILON_STRATEGY == 1
61 // Simple adaptive epsilon calculation for better performance
62 T calculateSimpleAdaptiveEpsilon(T velocityMagnitude) const {
63 T absVel = std::abs(velocityMagnitude);
64 T baseEps = baseNormalEpsilon * gridDelta;
65
66 // Simple velocity-based scaling to avoid numerical issues
67 if (absVel > 1e-10) {
68 // Scale epsilon with velocity magnitude (clamped to reasonable range)
69 T scale = std::max(T(0.1), std::min(T(10.0), absVel));
70 return baseEps * scale;
71 }
72
73 return baseEps;
74 }
75#endif
76
77#if ADAPTIVE_EPSILON_STRATEGY == 2
78 // Calculate adaptive epsilon based on velocity magnitude, grid resolution,
79 // and local velocity smoothness
80 T calculateAdaptiveEpsilon(T velocityMagnitude,
81 const Vec3D<T> &localCoordArray, int material,
82 const Vec3D<T> &normal) const {
83 // Base epsilon scaled by grid resolution
84 T adaptiveEpsilon = baseNormalEpsilon * gridDelta;
85
86 // Scale with velocity magnitude to maintain numerical stability
87 // Use larger epsilon for larger velocities to avoid catastrophic
88 // cancellation
89 T absVelocity = std::abs(velocityMagnitude);
90 if (absVelocity > 1e-12) {
91 T velocityScale = 1.0 + velocityScaleFactor * std::log(1.0 + absVelocity);
92 adaptiveEpsilon *= velocityScale;
93 }
94
95 // Estimate local velocity smoothness using a simplified approach
96 // Only check smoothness if velocity is significant
97 if (absVelocity > 1e-10) {
98 T maxRelativeVariation = 0.0;
99
100 // Sample velocity in each coordinate direction with small perturbation
101 for (int dir = 0; dir < D; ++dir) {
102 Vec3D<T> perturbedNormal = normal;
103 perturbedNormal[dir] += smoothnessThreshold;
104
105 // Normalize the perturbed normal
106 T normSq = 0.0;
107 for (int i = 0; i < D; ++i) {
108 normSq += perturbedNormal[i] * perturbedNormal[i];
109 }
110 if (normSq > 1e-12) {
111 T invNorm = T(1) / std::sqrt(normSq);
112 for (int i = 0; i < D; ++i) {
113 perturbedNormal[i] *= invNorm;
114 }
115
116 T perturbedVel = velocities->getScalarVelocity(
117 localCoordArray, material, perturbedNormal,
118 neighborIterator.getCenter().getPointId());
119
120 T relativeVariation =
121 std::abs(perturbedVel - velocityMagnitude) / absVelocity;
122 maxRelativeVariation =
123 std::max(maxRelativeVariation, relativeVariation);
124 }
125 }
126
127 // If velocity varies significantly, use larger epsilon for stability
128 if (maxRelativeVariation > smoothnessThreshold) {
129 T smoothnessFactor = 1.0 + maxRelativeVariation;
130 adaptiveEpsilon *= smoothnessFactor;
131 }
132 }
133
134 // Clamp epsilon to reasonable bounds
135 T minEpsilon = baseNormalEpsilon * gridDelta * epsilonMinScale;
136 T maxEpsilon = baseNormalEpsilon * gridDelta * epsilonMaxScale;
137
138 return std::max(minEpsilon, std::min(maxEpsilon, adaptiveEpsilon));
139 }
140#endif
141
142 Vec3D<T> calculateNormal(const viennahrle::Index<D> &offset) const {
143 Vec3D<T> normal{};
144 constexpr int startIndex = -1;
145 T modulus = 0.;
146
147 for (unsigned i = 0; i < D; ++i) {
148 viennahrle::Index<D> index(offset);
149 std::array<T, 3> values;
150 for (unsigned j = 0; j < 3; ++j) {
151 index[i] = startIndex + j;
152 values[j] = neighborIterator.getNeighbor(index).getValue();
153 }
154 normal[i] =
155 FiniteDifferences<T>::calculateGradient(values.data(), gridDelta);
156 modulus += normal[i] * normal[i];
157 }
158 modulus = T(1) / std::sqrt(modulus);
159 for (unsigned i = 0; i < D; ++i) {
160 normal[i] *= modulus;
161 }
162 return normal;
163 }
164
165 VectorType<T, D> calculateGradient(const viennahrle::Index<D> &offset) const {
166 VectorType<T, D> gradient;
167
168 constexpr unsigned numValues =
170 const int startIndex = -std::floor(numValues / 2);
171
172 for (unsigned i = 0; i < D; ++i) {
173 viennahrle::Index<D> index(offset);
174 std::array<T, numValues> values;
175 for (unsigned j = 0; j < numValues; ++j) {
176 index[i] = startIndex + j;
177 values[j] = neighborIterator.getNeighbor(index).getValue();
178 }
179
180 gradient[i] =
182 values.data(), gridDelta);
183 }
184
185 return gradient;
186 }
187
188 VectorType<T, D> calculateGradientDiff() const {
189 VectorType<T, D> gradient;
190
191 constexpr unsigned numValues =
193 const int startIndex =
194 -std::floor(numValues / 2); // std::floor constexpr in C++23
195
196 for (unsigned i = 0; i < D; ++i) {
197 viennahrle::Index<D> index(0);
198 std::array<T, numValues> values;
199 for (unsigned j = 0; j < numValues; ++j) {
200 index[i] = startIndex + j;
201 values[j] = neighborIterator.getNeighbor(index).getValue();
202 }
203
204 gradient[i] =
206 values.data(), gridDelta);
207 }
208
209 return gradient;
210 }
211
212 static void incrementIndex(viennahrle::Index<D> &index) {
213 int dim = 0;
214 for (; dim < D - 1; ++dim) {
215 if (index[dim] < order)
216 break;
217 index[dim] = -order;
218 }
219 ++index[dim];
220 }
221
222public:
223 static void prepareLS(LevelSetType passedlsDomain) {
224 // Expansion of sparse field must depend on spatial derivative order
225 // AND slf stencil order! --> currently assume scheme = 3rd order always
226 viennals::Expand<T, D>(passedlsDomain, 2 * (order + 1) + 4).apply();
227 }
228
229 StencilLocalLaxFriedrichsScalar(LevelSetType passedlsDomain,
230 SmartPointer<viennals::VelocityField<T>> vel,
231 double a = 1.0)
232 : levelSet(passedlsDomain), velocities(vel),
233 neighborIterator(levelSet->getDomain()),
234 gridDelta(levelSet->getGrid().getGridDelta()), alphaFactor(a) {}
235
236 static void setMaxDissipation(double maxDiss) { maxDissipation = maxDiss; }
237
238 std::pair<T, T> operator()(const viennahrle::Index<D> &indices,
239 int material) {
240 // center coordinate of the local stencil
241 Vec3D<T> coordinate{};
242 for (unsigned i = 0; i < D; ++i) {
243 coordinate[i] = indices[i] * gridDelta;
244 }
245 const auto pointId = neighborIterator.getCenter().getPointId();
246
247 // move neighborIterator to current position
248 neighborIterator.goToIndicesSequential(indices);
249
250 // if there is a vector velocity, we need to project it onto a scalar
251 // velocity first using its normal vector
252 Vec3D<T> normalVector = calculateNormal(viennahrle::Index<D>(0));
253
254 double scalarVelocity = velocities->getScalarVelocity(
255 coordinate, material, normalVector, pointId);
256 auto vectorVelocity = velocities->getVectorVelocity(coordinate, material,
257 normalVector, pointId);
258
259 // now calculate scalar product of normal vector with velocity
260 for (unsigned i = 0; i < D; ++i) {
261 scalarVelocity += vectorVelocity[i] * normalVector[i];
262 }
263
264 if (scalarVelocity == T(0)) {
265 return {0, 0};
266 }
267
268 T hamiltonian =
269 Norm(calculateGradient(viennahrle::Index<D>(0))) * scalarVelocity;
270 T dissipation = 0.; // dissipation
271
272 // dissipation block
273 // reserve alphas for all points in local stencil
274 std::array<VectorType<T, D>, numStencilPoints> alphas{};
275
276 viennahrle::Index<D> currentIndex(-order);
277 // iterate over all points in local stencil and calculate alpha values
278 for (auto &alpha : alphas) {
279 Vec3D<T> localNormal = calculateNormal(currentIndex);
280
281 // Check for corrupted normal
282 if ((std::abs(localNormal[0]) < 1e-6) &&
283 (std::abs(localNormal[1]) < 1e-6) &&
284 (std::abs(localNormal[2]) < 1e-6)) {
285 alpha.fill(0);
286 continue;
287 }
288
289 // get local velocity
290 Vec3D<T> localCoordArray = coordinate;
291 for (unsigned dir = 0; dir < D; ++dir)
292 localCoordArray[dir] += currentIndex[dir] * gridDelta;
293
294 T localScalarVelocity = velocities->getScalarVelocity(
295 localCoordArray, material, localNormal, pointId);
296 Vec3D<T> localVectorVelocity = velocities->getVectorVelocity(
297 localCoordArray, material, localNormal, pointId);
298 // now calculate scalar product of normal vector with velocity
299 for (unsigned dir = 0; dir < D; ++dir) {
300 localScalarVelocity += localVectorVelocity[dir] * localNormal[dir];
301 }
302
303 // Calculate epsilon based on selected strategy
304 T DN;
305#if ADAPTIVE_EPSILON_STRATEGY == 0
306 // Original fixed epsilon approach
307 DN = std::abs(baseNormalEpsilon * localScalarVelocity);
308#elif ADAPTIVE_EPSILON_STRATEGY == 1
309 // Simple adaptive epsilon (recommended)
310 DN = calculateSimpleAdaptiveEpsilon(localScalarVelocity);
311#elif ADAPTIVE_EPSILON_STRATEGY == 2
312 // Full adaptive epsilon (most accurate but slower)
313 DN = calculateAdaptiveEpsilon(localScalarVelocity, localCoordArray,
314 material, localNormal);
315#else
316 // Fallback to original method
317 DN = std::abs(baseNormalEpsilon * localScalarVelocity);
318#endif
319
320 Vec3D<T> normal_p = localNormal; // p=previous
321 Vec3D<T> normal_n = localNormal; // n=next
322
323 VectorType<T, D> velocityDelta;
324 for (int k = 0; k < D; ++k) {
325
326 normal_p[k] -= DN;
327 normal_n[k] += DN;
328
329 T vp = velocities->getScalarVelocity(localCoordArray, material,
330 normal_p, pointId);
331 T vn = velocities->getScalarVelocity(localCoordArray, material,
332 normal_n, pointId);
333 // central difference
334 velocityDelta[k] = (vn - vp) / (2.0 * DN);
335
336 normal_p[k] += DN;
337 normal_n[k] -= DN;
338 }
339
340 // determine \partial H / \partial phi_l
341 for (int k = 0; k < D; ++k) { // iterate over dimensions
342
343 // Monti term
344 T monti = 0;
345 // Toifl Quell term
346 T toifl = 0;
347
348 VectorType<T, D> gradient = calculateGradient(currentIndex);
349
350 for (int j = 0; j < D - 1; ++j) { // phi_p**2 + phi_q**2
351 int idx = (k + 1 + j) % D;
352 monti += gradient[idx] * gradient[idx];
353 toifl += gradient[idx] * velocityDelta[idx];
354 }
355 // denominator: |grad(phi)|^2
356 T denom = DotProduct(gradient, gradient);
357 monti *= velocityDelta[k] / denom;
358 toifl *= -gradient[k] / denom;
359
360 // Osher (constant V) term
361 T osher = localScalarVelocity * localNormal[k];
362 // Total derivative is sum of terms given above
363 alpha[k] = std::fabs(monti + toifl + osher);
364 }
365
366 incrementIndex(currentIndex);
367 }
368
369 // determine max alphas for every axis
370 VectorType<T, D> gradientDiff = calculateGradientDiff();
371 for (int d = 0; d < D; ++d) {
372 T maxAlpha = 0;
373
374 for (size_t i = 0; i < numStencilPoints; ++i) {
375 maxAlpha = std::max(maxAlpha, alphas[i][d]);
376 }
377
378 finalAlphas[d] = std::min(maxAlpha, static_cast<T>(maxDissipation));
379 dissipation += finalAlphas[d] * gradientDiff[d];
380 }
381
382 return {hamiltonian, dissipation};
383 }
384
385 void reduceTimeStepHamiltonJacobi(double &MaxTimeStep,
386 double gridDelta) const {
387 constexpr double alpha_maxCFL = 1.0;
388 // second time step test, based on alphas
389
390 double timeStep = 0;
391 for (int i = 0; i < D; ++i) {
392 timeStep += finalAlphas[i] / gridDelta;
393 }
394
395 timeStep = alpha_maxCFL / timeStep;
396 MaxTimeStep = std::min(timeStep, MaxTimeStep);
397 }
398};
399
400template <class T, int D, int order,
401 DifferentiationSchemeEnum finiteDifferenceScheme>
402double StencilLocalLaxFriedrichsScalar<T, D, order,
403 finiteDifferenceScheme>::maxDissipation =
404 std::numeric_limits<double>::max();
405
406} // namespace lsInternal
407
408namespace viennals {
409
410using namespace viennacore;
411
423template <class T, int D>
425 std::vector<SmartPointer<Domain<T, D>>> &levelSets,
426 std::vector<bool> isDepo) {
427 if (isDepo.size() < levelSets.size()) {
428 VIENNACORE_LOG_WARNING(
429 "PrepareStencilLocalLaxFriedrichs: isDepo does not have enough "
430 "elements. Assuming all higher layers are not depo layers.");
431 }
432 // always resize, so it has the correct number of elements
433 isDepo.resize(levelSets.size(), false);
434
435 // Begin with the biggest level set (top LS wrapped around all others)
436 auto layerIt = levelSets.rbegin();
437
438 // make empty LS which will contain the final top layer
439 auto finalTop = SmartPointer<Domain<T, D>>::New(levelSets[0]->getGrid());
440
441 bool layerAboveIsDepo = false;
442 auto depoIt = isDepo.rbegin();
443
444 // subtract each first non-depo layer below a depo layer from the latter
445 // then union these results to the final layer
446 for (auto maskIt = levelSets.rbegin(); maskIt != levelSets.rend(); ++maskIt) {
447 if (isDepo[*depoIt]) {
448 // this layer will be deposited on
449 if (!layerAboveIsDepo) {
450 layerIt = maskIt;
451 layerAboveIsDepo = true;
452 }
453 } else {
454 // no depo on this layer
455 if (layerAboveIsDepo) {
456 auto layerAbove = SmartPointer<Domain<T, D>>::New(*layerIt);
457 BooleanOperation<T, D>(layerAbove, *maskIt,
459 .apply();
460
461 BooleanOperation<T, D>(finalTop, layerAbove,
463 .apply();
464 Prune<T, D>(finalTop).apply();
465 }
466 layerAboveIsDepo = false;
467 }
468 ++depoIt;
469 }
470
471 // If the lowest layer is a depo substrate, add it to the final top layer
472 if (layerAboveIsDepo) {
474 .apply();
475 }
476
478 levelSets.back()->deepCopy(finalTop);
479}
480
483template <class T, int D>
485 std::vector<SmartPointer<Domain<T, D>>> &levelSets) {
486 auto layerIt = levelSets.rbegin();
487 auto lastIt = ++levelSets.rbegin();
488
491 .apply();
492}
493
494} // namespace viennals
constexpr int D
Definition Epitaxy.cpp:12
double T
Definition Epitaxy.cpp:13
static constexpr unsigned getNumberOfValues()
Definition lsFiniteDifferences.hpp:40
static T calculateGradient(const T *values, const double &delta)
Definition lsFiniteDifferences.hpp:177
static T calculateGradientDiff(const T *values, const double &delta)
Definition lsFiniteDifferences.hpp:183
static void setMaxDissipation(double maxDiss)
Definition lsStencilLocalLaxFriedrichsScalar.hpp:236
std::pair< T, T > operator()(const viennahrle::Index< D > &indices, int material)
Definition lsStencilLocalLaxFriedrichsScalar.hpp:238
StencilLocalLaxFriedrichsScalar(LevelSetType passedlsDomain, SmartPointer< viennals::VelocityField< T > > vel, double a=1.0)
Definition lsStencilLocalLaxFriedrichsScalar.hpp:229
static void prepareLS(LevelSetType passedlsDomain)
Definition lsStencilLocalLaxFriedrichsScalar.hpp:223
void reduceTimeStepHamiltonJacobi(double &MaxTimeStep, double gridDelta) const
Definition lsStencilLocalLaxFriedrichsScalar.hpp:385
This class is used to perform boolean operations on two level sets and write the resulting level set ...
Definition lsBooleanOperation.hpp:45
void apply()
Perform operation.
Definition lsBooleanOperation.hpp:319
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:28
Expands the levelSet to the specified number of layers. The largest value in the levelset is thus wid...
Definition lsExpand.hpp:17
void apply()
Apply the expansion to the specified width.
Definition lsExpand.hpp:44
Removes all level set points, which do not have at least one oppositely signed neighbour (Meaning the...
Definition lsPrune.hpp:17
void apply()
removes all grid points, which do not have at least one opposite signed neighbour returns the number ...
Definition lsPrune.hpp:74
Abstract class defining the interface for the velocity field used during advection using lsAdvect.
Definition lsVelocityField.hpp:11
Definition lsAdvectIntegrationSchemes.hpp:41
DifferentiationSchemeEnum
Definition lsFiniteDifferences.hpp:10
@ FIRST_ORDER
Definition lsFiniteDifferences.hpp:11
double StencilLocalLaxFriedrichsScalar< T, D, order, finiteDifferenceScheme >::maxDissipation
Definition lsStencilLocalLaxFriedrichsScalar.hpp:403
Definition lsAdvect.hpp:41
void PrepareStencilLocalLaxFriedrichs(std::vector< SmartPointer< Domain< T, D > > > &levelSets, std::vector< bool > isDepo)
This function creates the specialized layer wrapping which produces better results for the SSLF spati...
Definition lsStencilLocalLaxFriedrichsScalar.hpp:424
void FinalizeStencilLocalLaxFriedrichs(std::vector< SmartPointer< Domain< T, D > > > &levelSets)
After advection using the SLLF layer wrapping approach is done, restore the original layer wrapping u...
Definition lsStencilLocalLaxFriedrichsScalar.hpp:484
@ INVERT
Definition lsBooleanOperation.hpp:31
@ RELATIVE_COMPLEMENT
Definition lsBooleanOperation.hpp:30
@ UNION
Definition lsBooleanOperation.hpp:29