ViennaLS
Loading...
Searching...
No Matches
lsAdvect.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <functional>
6#include <limits>
7#include <vector>
8
9#include <hrleSparseIterator.hpp>
10#include <hrleSparseStarIterator.hpp>
11
12#include <vcLogger.hpp>
13#include <vcSmartPointer.hpp>
14
16#include <lsDomain.hpp>
17#include <lsMarkVoidPoints.hpp>
18#include <lsReduce.hpp>
19
20// Spatial discretization schemes
21#include <lsEngquistOsher.hpp>
22#include <lsLaxFriedrichs.hpp>
27#include <lsWENO.hpp>
28
29// Include implementation of time integration schemes
31
32// Velocity accessor
33#include <lsVelocityField.hpp>
34
35// #define DEBUG_LS_ADVECT_HPP
36#ifdef DEBUG_LS_ADVECT_HPP
37#include <lsToMesh.hpp>
38#include <lsVTKWriter.hpp>
39#endif
40
41namespace viennals {
42
43using namespace viennacore;
44
54template <class T, int D> class Advect {
55 using ConstSparseIterator =
56 viennahrle::ConstSparseIterator<typename Domain<T, D>::DomainType>;
57 using hrleIndexType = viennahrle::IndexType;
58
59 // Allow the time integration struct to access private members
61
62 std::vector<SmartPointer<Domain<T, D>>> levelSets;
63 SmartPointer<VelocityField<T>> velocities = nullptr;
66 double timeStepRatio = 0.4999;
67 double dissipationAlpha = 1.0;
68 bool calculateNormalVectors = true;
69 bool ignoreVoids = false;
70 double advectionTime = 0.;
71 bool performOnlySingleStep = false;
72 double advectedTime = 0.;
73 unsigned numberOfTimeSteps = 0;
74 bool saveAdvectionVelocities = false;
75 bool updatePointData = true;
76 bool checkDissipation = true;
77 double integrationCutoff = 0.5;
78 bool adaptiveTimeStepping = false;
79 unsigned adaptiveTimeStepSubdivisions = 20;
80 static constexpr double wrappingLayerEpsilon = 1e-4;
81 std::vector<SmartPointer<Domain<T, D>>> initialLevelSets;
82 std::function<bool(SmartPointer<Domain<T, D>>)> velocityUpdateCallback =
83 nullptr;
84
85 // this vector will hold the maximum time step for each point and the
86 // corresponding velocity
87 std::vector<std::vector<std::pair<std::pair<T, T>, T>>> storedRates;
88 double currentTimeStep = -1.;
89
90 VectorType<T, D> findGlobalAlphas() const {
91
92 auto &topDomain = levelSets.back()->getDomain();
93 auto &grid = levelSets.back()->getGrid();
94
95 const T gridDelta = grid.getGridDelta();
96 const T deltaPos = gridDelta;
97 const T deltaNeg = -gridDelta;
98
99 VectorType<T, D> finalAlphas{};
100
101#pragma omp parallel for
102 for (unsigned p = 0; p < levelSets.back()->getNumberOfSegments(); ++p) {
103 VectorType<T, D> localAlphas{};
104
105 viennahrle::Index<D> startVector =
106 (p == 0) ? grid.getMinGridPoint()
107 : topDomain.getSegmentation()[p - 1];
108 viennahrle::Index<D> endVector =
109 (p != static_cast<int>(topDomain.getNumberOfSegments() - 1))
110 ? topDomain.getSegmentation()[p]
111 : grid.incrementIndices(grid.getMaxGridPoint());
112
113 // an iterator for each level set
114 std::vector<ConstSparseIterator> iterators;
115 for (auto const &ls : levelSets) {
116 iterators.emplace_back(ls->getDomain());
117 }
118
119 // neighborIterator for the top level set
120 viennahrle::ConstSparseStarIterator<typename Domain<T, D>::DomainType, 1>
121 neighborIterator(topDomain);
122
123 for (ConstSparseIterator it(topDomain, startVector);
124 it.getStartIndices() < endVector; ++it) {
125
126 if (!it.isDefined() || std::abs(it.getValue()) > integrationCutoff)
127 continue;
128
129 const T value = it.getValue();
130 const auto indices = it.getStartIndices();
131
132 // check if there is any other levelset at the same point:
133 // if yes, take the velocity of the lowest levelset
134 for (unsigned lowerLevelSetId = 0; lowerLevelSetId < levelSets.size();
135 ++lowerLevelSetId) {
136 // put iterator to same position as the top levelset
137 iterators[lowerLevelSetId].goToIndicesSequential(indices);
138
139 // if the lower surface is actually outside, i.e. its LS value
140 // is lower or equal
141 if (iterators[lowerLevelSetId].getValue() <=
142 value + wrappingLayerEpsilon) {
143
144 // move neighborIterator to current position
145 neighborIterator.goToIndicesSequential(indices);
146
147 Vec3D<T> coords{};
148 for (unsigned i = 0; i < D; ++i) {
149 coords[i] = indices[i] * gridDelta;
150 }
151
152 Vec3D<T> normal{};
153 T normalModulus = 0.;
154 for (unsigned i = 0; i < D; ++i) {
155 const T phiPos = neighborIterator.getNeighbor(i).getValue();
156 const T phiNeg = neighborIterator.getNeighbor(i + D).getValue();
157
158 normal[i] = phiPos - phiNeg;
159 normalModulus += normal[i] * normal[i];
160 }
161 normalModulus = 1. / std::sqrt(normalModulus);
162 for (unsigned i = 0; i < D; ++i)
163 normal[i] *= normalModulus;
164
165 T scaVel = velocities->getScalarVelocity(
166 coords, lowerLevelSetId, normal,
167 neighborIterator.getCenter().getPointId());
168 auto vecVel = velocities->getVectorVelocity(
169 coords, lowerLevelSetId, normal,
170 neighborIterator.getCenter().getPointId());
171
172 for (unsigned i = 0; i < D; ++i) {
173 T tempAlpha = std::abs((scaVel + vecVel[i]) * normal[i]);
174 localAlphas[i] = std::max(localAlphas[i], tempAlpha);
175 }
176
177 // exit material loop
178 break;
179 }
180 }
181 }
182
183#pragma omp critical
184 {
185 for (unsigned i = 0; i < D; ++i) {
186 finalAlphas[i] = std::max(finalAlphas[i], localAlphas[i]);
187 }
188 }
189 } // end of parallel section
190
191 return finalAlphas;
192 }
193
194 // Helper function for linear combination:
195 // target = wTarget * target + wSource * source
196 bool combineLevelSets(T wTarget, T wSource) {
197 // Calculate required expansion width based on CFL and RK steps
198 int steps = 1;
199 if (temporalScheme == TemporalSchemeEnum::RUNGE_KUTTA_2ND_ORDER) {
200 steps = 2;
201 } else if (temporalScheme == TemporalSchemeEnum::RUNGE_KUTTA_3RD_ORDER) {
202 steps = 3;
203 }
204
205 // Expand both level sets to ensure sufficient overlap
206 int expansionWidth = std::ceil(2.0 * steps * timeStepRatio + 1);
207 viennals::Expand<T, D>(levelSets.back(), expansionWidth).apply();
208 viennals::Expand<T, D>(initialLevelSets.back(), expansionWidth).apply();
209
210 bool movedDown = false;
211
212 viennals::BooleanOperation<T, D> op(levelSets.back(),
213 initialLevelSets.back(),
216 [wTarget, wSource, &movedDown](const T &a, const T &b) {
219 T res = wSource * a + wTarget * b;
220 if (res > b)
221 movedDown = true;
222 return std::make_pair(res, true);
223 }
225 return std::make_pair(a, false);
226 return std::make_pair(b, false);
227 });
228 op.apply();
229
230 rebuildLS();
231
232 return movedDown;
233 }
234
235 void rebuildLS() {
236 // TODO: this function uses Manhattan distances for renormalisation,
237 // since this is the quickest. For visualisation applications, better
238 // renormalisation is needed, so it might be good to implement
239 // Euler distance renormalisation as an option
240 auto &grid = levelSets.back()->getGrid();
241 auto newlsDomain = SmartPointer<Domain<T, D>>::New(grid);
242 auto &newDomain = newlsDomain->getDomain();
243 auto &domain = levelSets.back()->getDomain();
244
245 // Determine cutoff and width based on discretization scheme to avoid
246 // immediate re-expansion
247 T cutoff = 1.0;
248 int finalWidth = 2;
249 if (spatialScheme ==
251 cutoff = 1.5;
252 finalWidth = 3;
253 }
254
255 newDomain.initialize(domain.getNewSegmentation(),
256 domain.getAllocation() *
257 (2.0 / levelSets.back()->getLevelSetWidth()));
258
259 const bool updateData = updatePointData;
260 // save how data should be transferred to new level set
261 // list of indices into the old pointData vector
262 std::vector<std::vector<unsigned>> newDataSourceIds;
263 if (updateData)
264 newDataSourceIds.resize(newDomain.getNumberOfSegments());
265
266#ifdef DEBUG_LS_ADVECT_HPP
267 {
268 auto mesh = SmartPointer<Mesh<T>>::New();
269 ToMesh<T, D>(levelSets.back(), mesh).apply();
270 VTKWriter<T>(mesh, "Advect_beforeRebuild.vtk").apply();
271 }
272#endif
273
274#pragma omp parallel for
275 for (unsigned p = 0; p < newDomain.getNumberOfSegments(); ++p) {
276 auto &domainSegment = newDomain.getDomainSegment(p);
277
278 viennahrle::Index<D> startVector =
279 (p == 0) ? grid.getMinGridPoint()
280 : newDomain.getSegmentation()[p - 1];
281
282 viennahrle::Index<D> endVector =
283 (p != newDomain.getNumberOfSegments() - 1)
284 ? newDomain.getSegmentation()[p]
285 : grid.incrementIndices(grid.getMaxGridPoint());
286
287 // reserve a bit more to avoid reallocation
288 // would expect number of points to roughly double
289 if (updateData)
290 newDataSourceIds[p].reserve(2.5 * domainSegment.getNumberOfPoints());
291
292 for (viennahrle::ConstSparseStarIterator<
293 typename Domain<T, D>::DomainType, 1>
294 it(domain, startVector);
295 it.getIndices() < endVector; ++it) {
296
297 // if the center is an active grid point
298 // <1.0 since it could have been change by 0.5 max
299 if (std::abs(it.getCenter().getValue()) <= 1.0) {
300
301 int k = 0;
302 for (; k < 2 * D; k++)
303 if (std::signbit(it.getNeighbor(k).getValue() - 1e-7) !=
304 std::signbit(it.getCenter().getValue() + 1e-7))
305 break;
306
307 // if there is at least one neighbor of opposite sign
308 if (k != 2 * D) {
309 if (it.getCenter().getDefinedValue() > 0.5) {
310 int j = 0;
311 for (; j < 2 * D; j++) {
312 if (std::abs(it.getNeighbor(j).getValue()) <= 1.0)
313 if (it.getNeighbor(j).getDefinedValue() < -0.5)
314 break;
315 }
316 if (j == 2 * D) {
317 domainSegment.insertNextDefinedPoint(
318 it.getIndices(), it.getCenter().getDefinedValue());
319 if (updateData)
320 newDataSourceIds[p].push_back(it.getCenter().getPointId());
321 // if there is at least one active grid point, which is < -0.5
322 } else {
323 domainSegment.insertNextDefinedPoint(it.getIndices(), 0.5);
324 if (updateData)
325 newDataSourceIds[p].push_back(it.getNeighbor(j).getPointId());
326 }
327 } else if (it.getCenter().getDefinedValue() < -0.5) {
328 int j = 0;
329 for (; j < 2 * D; j++) {
330 if (std::abs(it.getNeighbor(j).getValue()) <= 1.0)
331 if (it.getNeighbor(j).getDefinedValue() > 0.5)
332 break;
333 }
334
335 if (j == 2 * D) {
336 domainSegment.insertNextDefinedPoint(
337 it.getIndices(), it.getCenter().getDefinedValue());
338 if (updateData)
339 newDataSourceIds[p].push_back(it.getCenter().getPointId());
340 // if there is at least one active grid point, which is > 0.5
341 } else {
342 domainSegment.insertNextDefinedPoint(it.getIndices(), -0.5);
343 if (updateData)
344 newDataSourceIds[p].push_back(it.getNeighbor(j).getPointId());
345 }
346 } else {
347 domainSegment.insertNextDefinedPoint(
348 it.getIndices(), it.getCenter().getDefinedValue());
349 if (updateData)
350 newDataSourceIds[p].push_back(it.getCenter().getPointId());
351 }
352 } else {
353 domainSegment.insertNextUndefinedPoint(
354 it.getIndices(), (it.getCenter().getDefinedValue() < 0)
357 }
358
359 } else { // if the center is not an active grid point
360 if (it.getCenter().getValue() >= 0) {
361 int usedNeighbor = -1;
362 T distance = Domain<T, D>::POS_VALUE;
363 for (int i = 0; i < 2 * D; i++) {
364 T value = it.getNeighbor(i).getValue();
365 if (std::abs(value) <= 1.0 && (value < 0.)) {
366 if (distance > value + 1.0) {
367 distance = value + 1.0;
368 usedNeighbor = i;
369 }
370 }
371 }
372
373 if (distance <= cutoff) {
374 domainSegment.insertNextDefinedPoint(it.getIndices(), distance);
375 if (updateData)
376 newDataSourceIds[p].push_back(
377 it.getNeighbor(usedNeighbor).getPointId());
378 } else {
379 domainSegment.insertNextUndefinedPoint(it.getIndices(),
381 }
382
383 } else {
384 int usedNeighbor = -1;
385 T distance = Domain<T, D>::NEG_VALUE;
386 for (int i = 0; i < 2 * D; i++) {
387 T value = it.getNeighbor(i).getValue();
388 if (std::abs(value) <= 1.0 && (value > 0)) {
389 if (distance < value - 1.0) {
390 // distance = std::max(distance, value - T(1.0));
391 distance = value - 1.0;
392 usedNeighbor = i;
393 }
394 }
395 }
396
397 if (distance >= -cutoff) {
398 domainSegment.insertNextDefinedPoint(it.getIndices(), distance);
399 if (updateData)
400 newDataSourceIds[p].push_back(
401 it.getNeighbor(usedNeighbor).getPointId());
402 } else {
403 domainSegment.insertNextUndefinedPoint(it.getIndices(),
405 }
406 }
407 }
408 }
409 }
410
411 // now copy old data into new level set
412 if (updateData) {
413 auto &pointData = levelSets.back()->getPointData();
414 newlsDomain->getPointData().translateFromMultiData(pointData,
415 newDataSourceIds);
416 }
417
418 newDomain.finalize();
419 newDomain.segment();
420 levelSets.back()->deepCopy(newlsDomain);
421 levelSets.back()->finalize(finalWidth);
422 }
423
428 template <class DiscretizationSchemeType>
429 double integrateTime(DiscretizationSchemeType spatialScheme,
430 double maxTimeStep) {
431
432 auto &topDomain = levelSets.back()->getDomain();
433 auto &grid = levelSets.back()->getGrid();
434
435 typename PointData<T>::ScalarDataType *voidMarkerPointer;
436 if (ignoreVoids) {
437 MarkVoidPoints<T, D>(levelSets.back()).apply();
438 auto &pointData = levelSets.back()->getPointData();
439 voidMarkerPointer =
440 pointData.getScalarData(MarkVoidPoints<T, D>::voidPointLabel, true);
441 if (voidMarkerPointer == nullptr) {
442 VIENNACORE_LOG_WARNING("Advect: Cannot find void point markers. Not "
443 "ignoring void points.");
444 ignoreVoids = false;
445 }
446 }
447 const bool ignoreVoidPoints = ignoreVoids;
448 const bool useAdaptiveTimeStepping = adaptiveTimeStepping;
449 const auto adaptiveFactor = 1.0 / adaptiveTimeStepSubdivisions;
450
451 if (!storedRates.empty()) {
452 VIENNACORE_LOG_WARNING("Advect: Overwriting previously stored rates.");
453 }
454
455 storedRates.resize(topDomain.getNumberOfSegments());
456
457#pragma omp parallel for
458 for (unsigned p = 0; p < topDomain.getNumberOfSegments(); ++p) {
459
460 viennahrle::Index<D> startVector =
461 (p == 0) ? grid.getMinGridPoint()
462 : topDomain.getSegmentation()[p - 1];
463
464 viennahrle::Index<D> endVector =
465 (p != topDomain.getNumberOfSegments() - 1)
466 ? topDomain.getSegmentation()[p]
467 : grid.incrementIndices(grid.getMaxGridPoint());
468
469 double tempMaxTimeStep = maxTimeStep;
470 // store the rates and value of underneath LS for this segment
471 auto &rates = storedRates[p];
472 rates.reserve(
473 topDomain.getNumberOfPoints() /
474 static_cast<double>((levelSets.back())->getNumberOfSegments()) +
475 10);
476
477 // an iterator for each level set
478 std::vector<ConstSparseIterator> iterators;
479 for (auto const &ls : levelSets) {
480 iterators.emplace_back(ls->getDomain());
481 }
482
483 DiscretizationSchemeType scheme(spatialScheme);
484
485 for (ConstSparseIterator it(topDomain, startVector);
486 it.getStartIndices() < endVector; it.next()) {
487
488 if (!it.isDefined() || std::abs(it.getValue()) > integrationCutoff)
489 continue;
490
491 T value = it.getValue();
492 double maxStepTime = 0;
493 double cfl = timeStepRatio;
494
495 for (int currentLevelSetId = levelSets.size() - 1;
496 currentLevelSetId >= 0; --currentLevelSetId) {
497
498 std::pair<T, T> gradNDissipation;
499
500 if (!(ignoreVoidPoints && (*voidMarkerPointer)[it.getPointId()])) {
501 // check if there is any other levelset at the same point:
502 // if yes, take the velocity of the lowest levelset
503 for (unsigned lowerLevelSetId = 0;
504 lowerLevelSetId < levelSets.size(); ++lowerLevelSetId) {
505 // put iterator to same position as the top levelset
506 iterators[lowerLevelSetId].goToIndicesSequential(
507 it.getStartIndices());
508
509 // if the lower surface is actually outside, i.e. its LS value
510 // is lower or equal
511 if (iterators[lowerLevelSetId].getValue() <=
512 value + wrappingLayerEpsilon) {
513 gradNDissipation =
514 scheme(it.getStartIndices(), lowerLevelSetId);
515 break;
516 }
517 }
518 }
519
520 T velocity = gradNDissipation.first - gradNDissipation.second;
521 if (velocity > 0.) {
522 // Case 1: Growth / Deposition (Velocity > 0)
523 // Limit the time step based on the standard CFL condition.
524 maxStepTime += cfl / velocity;
525 rates.emplace_back(gradNDissipation,
526 -std::numeric_limits<T>::max());
527 break;
528 } else if (velocity == 0.) {
529 // Case 2: Static (Velocity == 0)
530 // No time step limit imposed by this point.
531 maxStepTime = std::numeric_limits<T>::max();
532 rates.emplace_back(gradNDissipation, std::numeric_limits<T>::max());
533 break;
534 } else {
535 // Case 3: Etching (Velocity < 0)
536 // Retrieve the interface location of the underlying material.
537 T valueBelow;
538 if (currentLevelSetId > 0) {
539 iterators[currentLevelSetId - 1].goToIndicesSequential(
540 it.getStartIndices());
541 valueBelow = iterators[currentLevelSetId - 1].getValue();
542 } else {
543 valueBelow = std::numeric_limits<T>::max();
544 }
545 // Calculate the top material thickness
546 T difference = std::abs(valueBelow - value);
547
548 if (difference >= cfl) {
549 // Sub-case 3a: Standard Advection
550 // Far from interface: Use full CFL time step.
551 maxStepTime -= cfl / velocity;
552 rates.emplace_back(gradNDissipation,
553 std::numeric_limits<T>::max());
554 break;
555 } else {
556 // Sub-case 3b: Interface Interaction
557 // Use adaptiveFactor as threshold.
558 if (useAdaptiveTimeStepping &&
559 difference > adaptiveFactor * cfl) {
560 // Adaptive Sub-stepping:
561 // Approaching boundary: Force small steps to gather
562 // flux statistics and prevent numerical overshoot ("Soft
563 // Landing").
564 maxStepTime -= adaptiveFactor * cfl / velocity;
565 rates.emplace_back(gradNDissipation,
566 std::numeric_limits<T>::max());
567 break;
568 } else {
569 // Terminal Step:
570 // Within tolerance: Snap to boundary, consume budget, and
571 // switch material.
572 cfl -= difference;
573 value = valueBelow;
574 maxStepTime -= difference / velocity;
575 rates.emplace_back(gradNDissipation, valueBelow);
576 }
577 }
578 }
579 }
580
581 if (maxStepTime < tempMaxTimeStep)
582 tempMaxTimeStep = maxStepTime;
583 }
584
585#pragma omp critical
586 {
587 // If a Lax Friedrichs scheme is selected the time step is
588 // reduced depending on the dissipation coefficients
589 // For Engquist Osher scheme this function is empty.
590 scheme.reduceTimeStepHamiltonJacobi(
591 tempMaxTimeStep, levelSets.back()->getGrid().getGridDelta());
592
593 // set global timestep maximum
594 if (tempMaxTimeStep < maxTimeStep)
595 maxTimeStep = tempMaxTimeStep;
596 }
597 } // end of parallel section
598
599 // maxTimeStep is now the maximum time step possible for all points
600 // and rates are stored in a vector
601 return maxTimeStep;
602 }
603
606 void computeRates(double maxTimeStep = std::numeric_limits<double>::max()) {
607 prepareLS();
608 if (spatialScheme == SpatialSchemeEnum::ENGQUIST_OSHER_1ST_ORDER) {
609 auto is = lsInternal::EngquistOsher<T, D, 1>(levelSets.back(), velocities,
610 calculateNormalVectors);
611 currentTimeStep = integrateTime(is, maxTimeStep);
612 } else if (spatialScheme == SpatialSchemeEnum::ENGQUIST_OSHER_2ND_ORDER) {
613 auto is = lsInternal::EngquistOsher<T, D, 2>(levelSets.back(), velocities,
614 calculateNormalVectors);
615 currentTimeStep = integrateTime(is, maxTimeStep);
616 } else if (spatialScheme == SpatialSchemeEnum::LAX_FRIEDRICHS_1ST_ORDER) {
617 auto alphas = findGlobalAlphas();
618 auto is = lsInternal::LaxFriedrichs<T, D, 1>(levelSets.back(), velocities,
619 dissipationAlpha, alphas,
620 calculateNormalVectors);
621 currentTimeStep = integrateTime(is, maxTimeStep);
622 } else if (spatialScheme == SpatialSchemeEnum::LAX_FRIEDRICHS_2ND_ORDER) {
623 auto alphas = findGlobalAlphas();
624 auto is = lsInternal::LaxFriedrichs<T, D, 2>(levelSets.back(), velocities,
625 dissipationAlpha, alphas,
626 calculateNormalVectors);
627 currentTimeStep = integrateTime(is, maxTimeStep);
628 } else if (spatialScheme ==
631 levelSets.back(), velocities);
632 currentTimeStep = integrateTime(is, maxTimeStep);
633 } else if (spatialScheme ==
636 levelSets.back(), velocities, dissipationAlpha);
637 currentTimeStep = integrateTime(is, maxTimeStep);
638 } else if (spatialScheme ==
641 levelSets.back(), velocities, dissipationAlpha);
642 currentTimeStep = integrateTime(is, maxTimeStep);
643 } else if (spatialScheme ==
646 levelSets.back(), velocities, dissipationAlpha);
647 currentTimeStep = integrateTime(is, maxTimeStep);
648 } else if (spatialScheme ==
651 levelSets.back(), velocities, dissipationAlpha);
652 currentTimeStep = integrateTime(is, maxTimeStep);
653 } else if (spatialScheme ==
656 levelSets.back(), velocities, dissipationAlpha);
657 currentTimeStep = integrateTime(is, maxTimeStep);
658 } else if (spatialScheme == SpatialSchemeEnum::WENO_3RD_ORDER) {
659 // Instantiate WENO with order 3
660 auto is = lsInternal::WENO<T, D, 3>(levelSets.back(), velocities,
661 dissipationAlpha);
662 currentTimeStep = integrateTime(is, maxTimeStep);
663 } else if (spatialScheme == SpatialSchemeEnum::WENO_5TH_ORDER) {
664 // Instantiate WENO with order 5
665 auto is = lsInternal::WENO<T, D, 5>(levelSets.back(), velocities,
666 dissipationAlpha);
667 currentTimeStep = integrateTime(is, maxTimeStep);
668 } else {
669 VIENNACORE_LOG_ERROR("Advect: Discretization scheme not found.");
670 currentTimeStep = -1.;
671 }
672 }
673
674 // Level Sets below are also considered in order to adjust the advection
675 // depth accordingly if there would be a material change.
676 void updateLevelSet(double dt) {
677 if (timeStepRatio >= 0.5) {
678 VIENNACORE_LOG_WARNING(
679 "Integration time step ratio should be smaller than 0.5. "
680 "Advection might fail!");
681 }
682
683 auto &topDomain = levelSets.back()->getDomain();
684
685 assert(dt >= 0. && "No time step set!");
686 assert(storedRates.size() == topDomain.getNumberOfSegments());
687
688 // reduce to one layer thickness and apply new values directly to the
689 // domain segments --> DO NOT CHANGE SEGMENTATION HERE (true parameter)
690 Reduce<T, D>(levelSets.back(), 1, true).apply();
691
692 const bool saveVelocities = saveAdvectionVelocities;
693 std::vector<std::vector<double>> dissipationVectors(
694 levelSets.back()->getNumberOfSegments());
695 std::vector<std::vector<double>> velocityVectors(
696 levelSets.back()->getNumberOfSegments());
697
698 const bool checkDiss = checkDissipation;
699
700#pragma omp parallel for
701 for (unsigned p = 0; p < topDomain.getNumberOfSegments(); ++p) {
702
703 auto itRS = storedRates[p].cbegin();
704 auto &segment = topDomain.getDomainSegment(p);
705 const unsigned maxId = segment.getNumberOfPoints();
706
707 if (saveVelocities) {
708 velocityVectors[p].resize(maxId);
709 dissipationVectors[p].resize(maxId);
710 }
711
712 for (unsigned localId = 0; localId < maxId; ++localId) {
713 T &value = segment.definedValues[localId];
714
715 // Skip points that were not part of computeRates (outer layers)
716 if (std::abs(value) > integrationCutoff)
717 continue;
718
719 double time = dt;
720
721 // if there is a change in materials during one time step, deduct
722 // the time taken to advect up to the end of the top material and
723 // set the LS value to the one below
724 auto const [gradient, dissipation] = itRS->first;
725 T velocity = gradient - dissipation;
726 // check if dissipation is too high and would cause a change in
727 // direction of the velocity
728 if (checkDiss && ((gradient < 0 && velocity > 0) ||
729 (gradient > 0 && velocity < 0))) {
730 velocity = 0;
731 }
732
733 T rate = time * velocity;
734 while (std::abs(itRS->second - value) < std::abs(rate)) {
735 time -= std::abs((itRS->second - value) / velocity);
736 value = itRS->second;
737 ++itRS; // advance the TempStopRates iterator by one
738
739 // recalculate velocity and rate
740 velocity = itRS->first.first - itRS->first.second;
741 if (checkDiss && ((itRS->first.first < 0 && velocity > 0) ||
742 (itRS->first.first > 0 && velocity < 0))) {
743 velocity = 0;
744 }
745 rate = time * velocity;
746 }
747
748 // now deduct the velocity times the time step we take
749 value -= rate;
750
751 if (saveVelocities) {
752 velocityVectors[p][localId] = rate;
753 dissipationVectors[p][localId] = itRS->first.second;
754 }
755
756 // this is run when two materials are close but the velocity is too slow
757 // to actually reach the second material, to get rid of the extra
758 // entry in the TempRatesStop
759 while (std::abs(itRS->second) != std::numeric_limits<T>::max())
760 ++itRS;
761
762 // advance the TempStopRates iterator by one
763 ++itRS;
764 }
765 } // end of parallel section
766
767 if (saveVelocities) {
768 auto &pointData = levelSets.back()->getPointData();
769
770 typename PointData<T>::ScalarDataType vels;
771 typename PointData<T>::ScalarDataType diss;
772
773 for (unsigned i = 0; i < velocityVectors.size(); ++i) {
774 vels.insert(vels.end(),
775 std::make_move_iterator(velocityVectors[i].begin()),
776 std::make_move_iterator(velocityVectors[i].end()));
777 diss.insert(diss.end(),
778 std::make_move_iterator(dissipationVectors[i].begin()),
779 std::make_move_iterator(dissipationVectors[i].end()));
780 }
781 pointData.insertReplaceScalarData(std::move(vels), velocityLabel);
782 pointData.insertReplaceScalarData(std::move(diss), dissipationLabel);
783 }
784
785 // clear the stored rates since surface has changed
786 storedRates.clear();
787 }
788
789 void adjustLowerLayers() {
790 // Adjust all level sets below the advected one
791 if (spatialScheme !=
793 for (unsigned i = 0; i < levelSets.size() - 1; ++i) {
795 levelSets[i], levelSets.back(),
797 .apply();
798 }
799 }
800 }
801
804 double advect(double maxTimeStep) {
805 switch (temporalScheme) {
808 *this, maxTimeStep);
811 *this, maxTimeStep);
813 default:
815 *this, maxTimeStep);
816 }
817 }
818
819public:
820 static constexpr char velocityLabel[] = "AdvectionVelocities";
821 static constexpr char dissipationLabel[] = "Dissipation";
822
823 Advect() = default;
824
825 explicit Advect(SmartPointer<Domain<T, D>> passedlsDomain) {
826 levelSets.push_back(passedlsDomain);
827 }
828
829 Advect(SmartPointer<Domain<T, D>> passedlsDomain,
830 SmartPointer<VelocityField<T>> passedVelocities) {
831 levelSets.push_back(passedlsDomain);
832 velocities = passedVelocities;
833 }
834
835 Advect(std::vector<SmartPointer<Domain<T, D>>> passedlsDomains,
836 SmartPointer<VelocityField<T>> passedVelocities)
837 : levelSets(passedlsDomains) {
838 velocities = passedVelocities;
839 }
840
843 void insertNextLevelSet(SmartPointer<Domain<T, D>> passedlsDomain) {
844 levelSets.push_back(passedlsDomain);
845 }
846
847 void clearLevelSets() { levelSets.clear(); }
848
851 void setVelocityField(SmartPointer<VelocityField<T>> passedVelocities) {
852 velocities = passedVelocities;
853 }
854
860 void setAdvectionTime(double time) { advectionTime = time; }
861
866 void setSingleStep(bool singleStep) { performOnlySingleStep = singleStep; }
867
872 void setTimeStepRatio(const double &cfl) { timeStepRatio = cfl; }
873
878 void setCalculateNormalVectors(bool cnv) { calculateNormalVectors = cnv; }
879
886 void setIgnoreVoids(bool iV) { ignoreVoids = iV; }
887
891 void setAdaptiveTimeStepping(bool aTS = true, unsigned subdivisions = 20) {
892 adaptiveTimeStepping = aTS;
893 if (subdivisions < 1) {
894 VIENNACORE_LOG_WARNING("Advect: Adaptive time stepping subdivisions must "
895 "be at least 1. Setting to 1.");
896 subdivisions = 1;
897 }
898 adaptiveTimeStepSubdivisions = subdivisions;
899 }
900
903 void setSaveAdvectionVelocities(bool sAV) { saveAdvectionVelocities = sAV; }
904
907 double getAdvectedTime() const { return advectedTime; }
908
910 double getCurrentTimeStep() const { return currentTimeStep; }
911
913 unsigned getNumberOfTimeSteps() const { return numberOfTimeSteps; }
914
916 double getTimeStepRatio() const { return timeStepRatio; }
917
919 bool getCalculateNormalVectors() const { return calculateNormalVectors; }
920
923 void setSpatialScheme(SpatialSchemeEnum scheme) { spatialScheme = scheme; }
924
925 // Deprecated and will be removed in future versions:
926 // use setSpatialScheme instead
927 [[deprecated("Use setSpatialScheme instead")]] void
929 VIENNACORE_LOG_WARNING(
930 "Advect::setIntegrationScheme is deprecated and will be removed in "
931 "future versions. Use setSpatialScheme instead.");
932 spatialScheme = scheme;
933 }
934
936 void setTemporalScheme(TemporalSchemeEnum scheme) { temporalScheme = scheme; }
937
942 void setDissipationAlpha(const double &a) { dissipationAlpha = a; }
943
944 // Sets the velocity to 0 if the dissipation is too high
945 void setCheckDissipation(bool check) { checkDissipation = check; }
946
949 void setUpdatePointData(bool update) { updatePointData = update; }
950
954 std::function<bool(SmartPointer<Domain<T, D>>)> callback) {
955 velocityUpdateCallback = callback;
956 }
957
958 // Prepare the levelset for advection, based on the provided spatial
959 // discretization scheme.
960 void prepareLS() {
961 // check whether a level set and velocities have been given
962 if (levelSets.empty()) {
963 VIENNACORE_LOG_ERROR("No level sets passed to Advect.");
964 return;
965 }
966
967 if (spatialScheme == SpatialSchemeEnum::ENGQUIST_OSHER_1ST_ORDER) {
969 } else if (spatialScheme == SpatialSchemeEnum::ENGQUIST_OSHER_2ND_ORDER) {
971 } else if (spatialScheme == SpatialSchemeEnum::LAX_FRIEDRICHS_1ST_ORDER) {
973 } else if (spatialScheme == SpatialSchemeEnum::LAX_FRIEDRICHS_2ND_ORDER) {
975 } else if (spatialScheme ==
978 levelSets.back());
979 } else if (spatialScheme ==
982 } else if (spatialScheme ==
985 } else if (spatialScheme ==
988 } else if (spatialScheme ==
991 } else if (spatialScheme ==
994 levelSets.back());
995 } else if (spatialScheme == SpatialSchemeEnum::WENO_3RD_ORDER) {
996 lsInternal::WENO<T, D, 3>::prepareLS(levelSets.back());
997 } else if (spatialScheme == SpatialSchemeEnum::WENO_5TH_ORDER) {
998 lsInternal::WENO<T, D, 5>::prepareLS(levelSets.back());
999 } else {
1000 VIENNACORE_LOG_ERROR("Advect: Discretization scheme not found.");
1001 }
1002 }
1003
1004 void apply() {
1005 // check whether a level set and velocities have been given
1006 if (levelSets.empty()) {
1007 VIENNACORE_LOG_ERROR("No level sets passed to Advect. Not advecting.");
1008 return;
1009 }
1010 if (velocities == nullptr) {
1011 VIENNACORE_LOG_ERROR(
1012 "No velocity field passed to Advect. Not advecting.");
1013 return;
1014 }
1015
1016 if (advectionTime == 0.) {
1017 advectedTime = advect(std::numeric_limits<double>::max());
1018 numberOfTimeSteps = 1;
1019 } else {
1020 double currentTime = 0.0;
1021 numberOfTimeSteps = 0;
1022 while (currentTime < advectionTime) {
1023 currentTime += advect(advectionTime - currentTime);
1024 ++numberOfTimeSteps;
1025 if (performOnlySingleStep)
1026 break;
1027 }
1028 advectedTime = currentTime;
1029 }
1030 }
1031};
1032
1033// add all template specializations for this class
1035
1036} // namespace viennals
constexpr int D
Definition Epitaxy.cpp:12
double T
Definition Epitaxy.cpp:13
Engquist-Osher spatial discretization scheme based on the upwind spatial discretization scheme....
Definition lsEngquistOsher.hpp:18
static void prepareLS(SmartPointer< viennals::Domain< T, D > > &passedlsDomain)
Definition lsEngquistOsher.hpp:29
Lax Friedrichs spatial discretization scheme with constant alpha value for dissipation....
Definition lsLaxFriedrichs.hpp:19
static void prepareLS(SmartPointer< viennals::Domain< T, D > > passedlsDomain)
Definition lsLaxFriedrichs.hpp:33
Lax Friedrichs spatial discretization scheme, which uses alpha values provided by the user in getDiss...
Definition lsLocalLaxFriedrichsAnalytical.hpp:20
static void prepareLS(SmartPointer< viennals::Domain< T, D > > passedlsDomain)
Definition lsLocalLaxFriedrichsAnalytical.hpp:49
Lax Friedrichs spatial discretization scheme, which uses a first neighbour stencil to calculate the a...
Definition lsLocalLaxFriedrichs.hpp:20
static void prepareLS(SmartPointer< viennals::Domain< T, D > > passedlsDomain)
Definition lsLocalLaxFriedrichs.hpp:51
Lax Friedrichs spatial discretization scheme, which considers only the current point for alpha calcul...
Definition lsLocalLocalLaxFriedrichs.hpp:18
static void prepareLS(SmartPointer< viennals::Domain< T, D > > passedlsDomain)
Definition lsLocalLocalLaxFriedrichs.hpp:30
Stencil Local Lax Friedrichs Discretization Scheme. It uses a stencil of order around active points,...
Definition lsStencilLocalLaxFriedrichsScalar.hpp:33
static void prepareLS(LevelSetType passedlsDomain)
Definition lsStencilLocalLaxFriedrichsScalar.hpp:223
Weighted Essentially Non-Oscillatory (WENO) scheme. This kernel acts as the grid-interface for the ma...
Definition lsWENO.hpp:20
static void prepareLS(SmartPointer< viennals::Domain< T, D > > passedlsDomain)
Definition lsWENO.hpp:42
void setVelocityField(SmartPointer< VelocityField< T > > passedVelocities)
Set the velocity field used for advection. This should be a concrete implementation of lsVelocityFiel...
Definition lsAdvect.hpp:851
static constexpr char velocityLabel[]
Definition lsAdvect.hpp:820
void insertNextLevelSet(SmartPointer< Domain< T, D > > passedlsDomain)
Pushes the passed level set to the back of the list of level sets used for advection.
Definition lsAdvect.hpp:843
void setVelocityUpdateCallback(std::function< bool(SmartPointer< Domain< T, D > >)> callback)
Set a callback function that is called after the level set has been updated during intermediate time ...
Definition lsAdvect.hpp:953
void setUpdatePointData(bool update)
Set whether the point data in the old LS should be translated to the advected LS. Defaults to true.
Definition lsAdvect.hpp:949
void setSpatialScheme(SpatialSchemeEnum scheme)
Set which spatial discretization scheme should be used out of the ones specified in SpatialSchemeEnum...
Definition lsAdvect.hpp:923
bool getCalculateNormalVectors() const
Get whether normal vectors were calculated.
Definition lsAdvect.hpp:919
void setIgnoreVoids(bool iV)
Set whether level set values, which are not part of the "top" geometrically connected part of values,...
Definition lsAdvect.hpp:886
Advect()=default
void clearLevelSets()
Definition lsAdvect.hpp:847
void setCalculateNormalVectors(bool cnv)
Set whether normal vectors should be calculated at each level set point. Defaults to true....
Definition lsAdvect.hpp:878
void apply()
Definition lsAdvect.hpp:1004
void setSingleStep(bool singleStep)
If set to true, only a single advection step will be performed, even if the advection time set with s...
Definition lsAdvect.hpp:866
void prepareLS()
Definition lsAdvect.hpp:960
double getAdvectedTime() const
Get by how much the physical time was advanced during the last apply() call.
Definition lsAdvect.hpp:907
void setTemporalScheme(TemporalSchemeEnum scheme)
Set which time integration scheme should be used.
Definition lsAdvect.hpp:936
void setDissipationAlpha(const double &a)
Set the alpha dissipation coefficient. For lsLaxFriedrichs, this is used as the alpha value....
Definition lsAdvect.hpp:942
void setCheckDissipation(bool check)
Definition lsAdvect.hpp:945
Advect(std::vector< SmartPointer< Domain< T, D > > > passedlsDomains, SmartPointer< VelocityField< T > > passedVelocities)
Definition lsAdvect.hpp:835
void setIntegrationScheme(IntegrationSchemeEnum scheme)
Definition lsAdvect.hpp:928
double getCurrentTimeStep() const
Return the last applied time step.
Definition lsAdvect.hpp:910
void setAdvectionTime(double time)
Set the time until when the level set should be advected. If this takes more than one advection step,...
Definition lsAdvect.hpp:860
unsigned getNumberOfTimeSteps() const
Get how many advection steps were performed during the last apply() call.
Definition lsAdvect.hpp:913
void setAdaptiveTimeStepping(bool aTS=true, unsigned subdivisions=20)
Set whether adaptive time stepping should be used when approaching material boundaries during etching...
Definition lsAdvect.hpp:891
void setTimeStepRatio(const double &cfl)
Set the CFL condition to use during advection. The CFL condition sets the maximum distance a surface ...
Definition lsAdvect.hpp:872
Advect(SmartPointer< Domain< T, D > > passedlsDomain)
Definition lsAdvect.hpp:825
Advect(SmartPointer< Domain< T, D > > passedlsDomain, SmartPointer< VelocityField< T > > passedVelocities)
Definition lsAdvect.hpp:829
double getTimeStepRatio() const
Get the value of the CFL number.
Definition lsAdvect.hpp:916
void setSaveAdvectionVelocities(bool sAV)
Set whether the velocities applied to each point should be saved in the level set for debug purposes.
Definition lsAdvect.hpp:903
static constexpr char dissipationLabel[]
Definition lsAdvect.hpp:821
This class is used to perform boolean operations on two level sets and write the resulting level set ...
Definition lsBooleanOperation.hpp:45
void setBooleanOperationComparator(ComparatorType passedOperationComp)
Set the comparator to be used when the BooleanOperation is set to CUSTOM.
Definition lsBooleanOperation.hpp:307
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:27
viennahrle::Domain< T, D > DomainType
Definition lsDomain.hpp:32
static constexpr T NEG_VALUE
Definition lsDomain.hpp:52
static constexpr T POS_VALUE
Definition lsDomain.hpp:51
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
static constexpr char voidPointLabel[]
Definition lsMarkVoidPoints.hpp:86
Reduce()=default
ToMesh()=default
Abstract class defining the interface for the velocity field used during advection using lsAdvect.
Definition lsVelocityField.hpp:11
#define PRECOMPILE_PRECISION_DIMENSION(className)
Definition lsPreCompileMacros.hpp:24
Definition lsAdvect.hpp:41
SpatialSchemeEnum
Enumeration for the different spatial discretization schemes used by the advection kernel.
Definition lsAdvectIntegrationSchemes.hpp:10
@ LOCAL_LOCAL_LAX_FRIEDRICHS_2ND_ORDER
Definition lsAdvectIntegrationSchemes.hpp:17
@ STENCIL_LOCAL_LAX_FRIEDRICHS_1ST_ORDER
Definition lsAdvectIntegrationSchemes.hpp:20
@ LOCAL_LOCAL_LAX_FRIEDRICHS_1ST_ORDER
Definition lsAdvectIntegrationSchemes.hpp:16
@ LAX_FRIEDRICHS_2ND_ORDER
Definition lsAdvectIntegrationSchemes.hpp:14
@ LOCAL_LAX_FRIEDRICHS_1ST_ORDER
Definition lsAdvectIntegrationSchemes.hpp:18
@ ENGQUIST_OSHER_2ND_ORDER
Definition lsAdvectIntegrationSchemes.hpp:12
@ LAX_FRIEDRICHS_1ST_ORDER
Definition lsAdvectIntegrationSchemes.hpp:13
@ LOCAL_LAX_FRIEDRICHS_2ND_ORDER
Definition lsAdvectIntegrationSchemes.hpp:19
@ WENO_3RD_ORDER
Definition lsAdvectIntegrationSchemes.hpp:21
@ ENGQUIST_OSHER_1ST_ORDER
Definition lsAdvectIntegrationSchemes.hpp:11
@ LOCAL_LAX_FRIEDRICHS_ANALYTICAL_1ST_ORDER
Definition lsAdvectIntegrationSchemes.hpp:15
@ WENO_5TH_ORDER
Definition lsAdvectIntegrationSchemes.hpp:22
SpatialSchemeEnum IntegrationSchemeEnum
TemporalSchemeEnum
Enumeration for the different time integration schemes used to select the advection kernel.
Definition lsAdvectIntegrationSchemes.hpp:31
@ RUNGE_KUTTA_2ND_ORDER
Definition lsAdvectIntegrationSchemes.hpp:33
@ RUNGE_KUTTA_3RD_ORDER
Definition lsAdvectIntegrationSchemes.hpp:34
@ FORWARD_EULER
Definition lsAdvectIntegrationSchemes.hpp:32
@ INTERSECT
Definition lsBooleanOperation.hpp:28
@ CUSTOM
Definition lsBooleanOperation.hpp:32
Definition lsAdvectIntegrationSchemes.hpp:43
static double evolveRungeKutta3(AdvectType &kernel, double maxTimeStep)
Definition lsAdvectIntegrationSchemes.hpp:110
static double evolveForwardEuler(AdvectType &kernel, double maxTimeStep, bool updateLowerLayers=true)
Definition lsAdvectIntegrationSchemes.hpp:46
static double evolveRungeKutta2(AdvectType &kernel, double maxTimeStep)
Definition lsAdvectIntegrationSchemes.hpp:61