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