54template <
class T,
int D>
class Advect {
55 using ConstSparseIterator =
56 viennahrle::ConstSparseIterator<typename Domain<T, D>::DomainType>;
57 using hrleIndexType = viennahrle::IndexType;
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 SmartPointer<Domain<T, D>> originalLevelSet =
nullptr;
82 std::function<bool(SmartPointer<
Domain<T, D>>)> velocityUpdateCallback =
87 std::vector<std::vector<std::pair<std::pair<T, T>,
T>>> storedRates;
88 double currentTimeStep = -1.;
90 VectorType<T, 3> findGlobalAlphas()
const {
92 auto &topDomain = levelSets.back()->getDomain();
93 auto &grid = levelSets.back()->getGrid();
95 const T gridDelta = grid.getGridDelta();
96 const T deltaPos = gridDelta;
97 const T deltaNeg = -gridDelta;
99 VectorType<T, 3> finalAlphas = {0., 0., 0.};
101#pragma omp parallel num_threads((levelSets.back())->getNumberOfSegments())
103 VectorType<T, 3> localAlphas = {0., 0., 0.};
106 p = omp_get_thread_num();
108 viennahrle::Index<D> startVector =
109 (p == 0) ? grid.getMinGridPoint()
110 : topDomain.getSegmentation()[p - 1];
111 viennahrle::Index<D> endVector =
112 (p !=
static_cast<int>(topDomain.getNumberOfSegments() - 1))
113 ? topDomain.getSegmentation()[p]
114 : grid.incrementIndices(grid.getMaxGridPoint());
117 std::vector<ConstSparseIterator> iterators;
118 for (
auto const &
ls : levelSets) {
119 iterators.emplace_back(
ls->getDomain());
123 viennahrle::ConstSparseStarIterator<typename Domain<T, D>::DomainType, 1>
124 neighborIterator(topDomain);
126 for (ConstSparseIterator it(topDomain, startVector);
127 it.getStartIndices() < endVector; ++it) {
129 if (!it.isDefined() || std::abs(it.getValue()) > integrationCutoff)
132 const T value = it.getValue();
133 const auto indices = it.getStartIndices();
137 for (
unsigned lowerLevelSetId = 0; lowerLevelSetId < levelSets.size();
140 iterators[lowerLevelSetId].goToIndicesSequential(indices);
144 if (iterators[lowerLevelSetId].getValue() <=
145 value + wrappingLayerEpsilon) {
148 neighborIterator.goToIndicesSequential(indices);
151 for (
unsigned i = 0; i <
D; ++i) {
152 coords[i] = indices[i] * gridDelta;
156 T normalModulus = 0.;
157 for (
unsigned i = 0; i <
D; ++i) {
158 const T phiPos = neighborIterator.getNeighbor(i).getValue();
159 const T phiNeg = neighborIterator.getNeighbor(i +
D).getValue();
161 T diffPos = (phiPos - value) / deltaPos;
162 T diffNeg = (phiNeg - value) / deltaNeg;
164 normal[i] = (diffNeg + diffPos) * 0.5;
165 normalModulus += normal[i] * normal[i];
167 normalModulus = std::sqrt(normalModulus);
168 for (
unsigned i = 0; i <
D; ++i)
169 normal[i] /= normalModulus;
171 T scaVel = velocities->getScalarVelocity(
172 coords, lowerLevelSetId, normal,
173 neighborIterator.getCenter().getPointId());
174 auto vecVel = velocities->getVectorVelocity(
175 coords, lowerLevelSetId, normal,
176 neighborIterator.getCenter().getPointId());
178 for (
unsigned i = 0; i <
D; ++i) {
179 T tempAlpha = std::abs((scaVel + vecVel[i]) * normal[i]);
180 localAlphas[i] = std::max(localAlphas[i], tempAlpha);
191 for (
unsigned i = 0; i <
D; ++i) {
192 finalAlphas[i] = std::max(finalAlphas[i], localAlphas[i]);
202 void combineLevelSets(
double wTarget,
double wSource) {
204 auto &domainDest = levelSets.back()->getDomain();
205 auto &grid = levelSets.back()->getGrid();
207#pragma omp parallel num_threads(domainDest.getNumberOfSegments())
211 p = omp_get_thread_num();
213 auto &segDest = domainDest.getDomainSegment(p);
215 viennahrle::Index<D> start = (p == 0)
216 ? grid.getMinGridPoint()
217 : domainDest.getSegmentation()[p - 1];
218 viennahrle::Index<D> end =
219 (p !=
static_cast<int>(domainDest.getNumberOfSegments()) - 1)
220 ? domainDest.getSegmentation()[p]
221 : grid.incrementIndices(grid.getMaxGridPoint());
223 ConstSparseIterator itDest(domainDest, start);
224 ConstSparseIterator itTarget(originalLevelSet->getDomain(), start);
226 unsigned definedValueIndex = 0;
227 for (; itDest.getStartIndices() < end; ++itDest) {
228 if (itDest.isDefined()) {
229 itTarget.goToIndicesSequential(itDest.getStartIndices());
230 T valSource = itDest.getValue();
231 T valTarget = itTarget.getValue();
232 segDest.definedValues[definedValueIndex++] =
233 wTarget * valTarget + wSource * valSource;
244 auto &grid = levelSets.back()->getGrid();
245 auto newlsDomain = SmartPointer<Domain<T, D>>::New(grid);
246 auto &newDomain = newlsDomain->getDomain();
247 auto &domain = levelSets.back()->getDomain();
259 newDomain.initialize(domain.getNewSegmentation(),
260 domain.getAllocation() *
261 (2.0 / levelSets.back()->getLevelSetWidth()));
263 const bool updateData = updatePointData;
266 std::vector<std::vector<unsigned>> newDataSourceIds;
268 newDataSourceIds.resize(newDomain.getNumberOfSegments());
270#ifdef DEBUG_LS_ADVECT_HPP
272 auto mesh = SmartPointer<Mesh<T>>::New();
278#pragma omp parallel num_threads(newDomain.getNumberOfSegments())
282 p = omp_get_thread_num();
284 auto &domainSegment = newDomain.getDomainSegment(p);
286 viennahrle::Index<D> startVector =
287 (p == 0) ? grid.getMinGridPoint()
288 : newDomain.getSegmentation()[p - 1];
290 viennahrle::Index<D> endVector =
291 (p !=
static_cast<int>(newDomain.getNumberOfSegments() - 1))
292 ? newDomain.getSegmentation()[p]
293 : grid.incrementIndices(grid.getMaxGridPoint());
298 newDataSourceIds[p].reserve(2.5 * domainSegment.getNumberOfPoints());
301 it(domain, startVector);
302 it.getIndices() < endVector; ++it) {
306 if (std::abs(it.getCenter().getValue()) <= 1.0) {
309 for (; k < 2 *
D; k++)
310 if (std::signbit(it.getNeighbor(k).getValue() - 1e-7) !=
311 std::signbit(it.getCenter().getValue() + 1e-7))
316 if (it.getCenter().getDefinedValue() > 0.5) {
318 for (; j < 2 *
D; j++) {
319 if (std::abs(it.getNeighbor(j).getValue()) <= 1.0)
320 if (it.getNeighbor(j).getDefinedValue() < -0.5)
324 domainSegment.insertNextDefinedPoint(
325 it.getIndices(), it.getCenter().getDefinedValue());
327 newDataSourceIds[p].push_back(it.getCenter().getPointId());
330 domainSegment.insertNextDefinedPoint(it.getIndices(), 0.5);
332 newDataSourceIds[p].push_back(it.getNeighbor(j).getPointId());
334 }
else if (it.getCenter().getDefinedValue() < -0.5) {
336 for (; j < 2 *
D; j++) {
337 if (std::abs(it.getNeighbor(j).getValue()) <= 1.0)
338 if (it.getNeighbor(j).getDefinedValue() > 0.5)
343 domainSegment.insertNextDefinedPoint(
344 it.getIndices(), it.getCenter().getDefinedValue());
346 newDataSourceIds[p].push_back(it.getCenter().getPointId());
349 domainSegment.insertNextDefinedPoint(it.getIndices(), -0.5);
351 newDataSourceIds[p].push_back(it.getNeighbor(j).getPointId());
354 domainSegment.insertNextDefinedPoint(
355 it.getIndices(), it.getCenter().getDefinedValue());
357 newDataSourceIds[p].push_back(it.getCenter().getPointId());
360 domainSegment.insertNextUndefinedPoint(
361 it.getIndices(), (it.getCenter().getDefinedValue() < 0)
367 if (it.getCenter().getValue() >= 0) {
368 int usedNeighbor = -1;
370 for (
int i = 0; i < 2 *
D; i++) {
371 T value = it.getNeighbor(i).getValue();
372 if (std::abs(value) <= 1.0 && (value < 0.)) {
373 if (distance > value + 1.0) {
374 distance = value + 1.0;
380 if (distance <= cutoff) {
381 domainSegment.insertNextDefinedPoint(it.getIndices(), distance);
383 newDataSourceIds[p].push_back(
384 it.getNeighbor(usedNeighbor).getPointId());
386 domainSegment.insertNextUndefinedPoint(it.getIndices(),
391 int usedNeighbor = -1;
393 for (
int i = 0; i < 2 *
D; i++) {
394 T value = it.getNeighbor(i).getValue();
395 if (std::abs(value) <= 1.0 && (value > 0)) {
396 if (distance < value - 1.0) {
398 distance = value - 1.0;
404 if (distance >= -cutoff) {
405 domainSegment.insertNextDefinedPoint(it.getIndices(), distance);
407 newDataSourceIds[p].push_back(
408 it.getNeighbor(usedNeighbor).getPointId());
410 domainSegment.insertNextUndefinedPoint(it.getIndices(),
420 auto &pointData = levelSets.back()->getPointData();
421 newlsDomain->getPointData().translateFromMultiData(pointData,
425 newDomain.finalize();
427 levelSets.back()->deepCopy(newlsDomain);
428 levelSets.back()->finalize(finalWidth);
435 template <
class DiscretizationSchemeType>
436 double integrateTime(DiscretizationSchemeType spatialScheme,
437 double maxTimeStep) {
439 auto &topDomain = levelSets.back()->getDomain();
440 auto &grid = levelSets.back()->getGrid();
445 auto &pointData = levelSets.back()->getPointData();
448 if (voidMarkerPointer ==
nullptr) {
449 VIENNACORE_LOG_WARNING(
"Advect: Cannot find void point markers. Not "
450 "ignoring void points.");
454 const bool ignoreVoidPoints = ignoreVoids;
455 const bool useAdaptiveTimeStepping = adaptiveTimeStepping;
457 if (!storedRates.empty()) {
458 VIENNACORE_LOG_WARNING(
"Advect: Overwriting previously stored rates.");
461 storedRates.resize(topDomain.getNumberOfSegments());
463#pragma omp parallel num_threads(topDomain.getNumberOfSegments())
467 p = omp_get_thread_num();
469 viennahrle::Index<D> startVector =
470 (p == 0) ? grid.getMinGridPoint()
471 : topDomain.getSegmentation()[p - 1];
473 viennahrle::Index<D> endVector =
474 (p !=
static_cast<int>(topDomain.getNumberOfSegments() - 1))
475 ? topDomain.getSegmentation()[p]
476 : grid.incrementIndices(grid.getMaxGridPoint());
478 double tempMaxTimeStep = maxTimeStep;
479 auto &tempRates = storedRates[p];
481 topDomain.getNumberOfPoints() /
482 static_cast<double>((levelSets.back())->getNumberOfSegments()) +
486 std::vector<ConstSparseIterator> iterators;
487 for (
auto const &
ls : levelSets) {
488 iterators.emplace_back(
ls->getDomain());
491 DiscretizationSchemeType scheme(spatialScheme);
493 for (ConstSparseIterator it(topDomain, startVector);
494 it.getStartIndices() < endVector; ++it) {
496 if (!it.isDefined() || std::abs(it.getValue()) > integrationCutoff)
499 T value = it.getValue();
500 double maxStepTime = 0;
501 double cfl = timeStepRatio;
503 for (
int currentLevelSetId = levelSets.size() - 1;
504 currentLevelSetId >= 0; --currentLevelSetId) {
506 std::pair<T, T> gradNDissipation;
508 if (!(ignoreVoidPoints && (*voidMarkerPointer)[it.getPointId()])) {
511 for (
unsigned lowerLevelSetId = 0;
512 lowerLevelSetId < levelSets.size(); ++lowerLevelSetId) {
514 iterators[lowerLevelSetId].goToIndicesSequential(
515 it.getStartIndices());
519 if (iterators[lowerLevelSetId].getValue() <=
520 value + wrappingLayerEpsilon) {
522 scheme(it.getStartIndices(), lowerLevelSetId);
528 T velocity = gradNDissipation.first - gradNDissipation.second;
532 maxStepTime += cfl / velocity;
533 tempRates.push_back(std::make_pair(gradNDissipation,
534 -std::numeric_limits<T>::max()));
536 }
else if (velocity == 0.) {
539 maxStepTime = std::numeric_limits<T>::max();
540 tempRates.push_back(std::make_pair(gradNDissipation,
541 std::numeric_limits<T>::max()));
547 if (currentLevelSetId > 0) {
548 iterators[currentLevelSetId - 1].goToIndicesSequential(
549 it.getStartIndices());
550 valueBelow = iterators[currentLevelSetId - 1].getValue();
552 valueBelow = std::numeric_limits<T>::max();
555 T difference = std::abs(valueBelow - value);
557 if (difference >= cfl) {
560 maxStepTime -= cfl / velocity;
561 tempRates.push_back(std::make_pair(
562 gradNDissipation, std::numeric_limits<T>::max()));
567 auto adaptiveFactor = 1.0 / adaptiveTimeStepSubdivisions;
568 if (useAdaptiveTimeStepping && difference > 0.2 * cfl) {
573 maxStepTime -= adaptiveFactor * cfl / velocity;
574 tempRates.push_back(std::make_pair(
575 gradNDissipation, std::numeric_limits<T>::min()));
581 std::make_pair(gradNDissipation, valueBelow));
584 maxStepTime -= difference / velocity;
590 if (maxStepTime < tempMaxTimeStep)
591 tempMaxTimeStep = maxStepTime;
599 scheme.reduceTimeStepHamiltonJacobi(
600 tempMaxTimeStep, levelSets.back()->getGrid().getGridDelta());
603 if (tempMaxTimeStep < maxTimeStep)
604 maxTimeStep = tempMaxTimeStep;
615 void computeRates(
double maxTimeStep = std::numeric_limits<double>::max()) {
619 calculateNormalVectors);
620 currentTimeStep = integrateTime(is, maxTimeStep);
623 calculateNormalVectors);
624 currentTimeStep = integrateTime(is, maxTimeStep);
626 auto alphas = findGlobalAlphas();
628 dissipationAlpha, alphas,
629 calculateNormalVectors);
630 currentTimeStep = integrateTime(is, maxTimeStep);
632 auto alphas = findGlobalAlphas();
634 dissipationAlpha, alphas,
635 calculateNormalVectors);
636 currentTimeStep = integrateTime(is, maxTimeStep);
637 }
else if (spatialScheme ==
640 levelSets.back(), velocities);
641 currentTimeStep = integrateTime(is, maxTimeStep);
642 }
else if (spatialScheme ==
645 levelSets.back(), velocities, dissipationAlpha);
646 currentTimeStep = integrateTime(is, maxTimeStep);
647 }
else if (spatialScheme ==
650 levelSets.back(), velocities, dissipationAlpha);
651 currentTimeStep = integrateTime(is, maxTimeStep);
652 }
else if (spatialScheme ==
655 levelSets.back(), velocities, dissipationAlpha);
656 currentTimeStep = integrateTime(is, maxTimeStep);
657 }
else if (spatialScheme ==
660 levelSets.back(), velocities, dissipationAlpha);
661 currentTimeStep = integrateTime(is, maxTimeStep);
662 }
else if (spatialScheme ==
665 levelSets.back(), velocities, dissipationAlpha);
666 currentTimeStep = integrateTime(is, maxTimeStep);
671 currentTimeStep = integrateTime(is, maxTimeStep);
676 currentTimeStep = integrateTime(is, maxTimeStep);
678 VIENNACORE_LOG_ERROR(
"Advect: Discretization scheme not found.");
679 currentTimeStep = -1.;
685 void updateLevelSet(
double dt) {
686 if (timeStepRatio >= 0.5) {
687 VIENNACORE_LOG_WARNING(
688 "Integration time step ratio should be smaller than 0.5. "
689 "Advection might fail!");
692 auto &topDomain = levelSets.back()->getDomain();
694 assert(dt >= 0. &&
"No time step set!");
695 assert(storedRates.size() == topDomain.getNumberOfSegments());
701 const bool saveVelocities = saveAdvectionVelocities;
702 std::vector<std::vector<double>> dissipationVectors(
703 levelSets.back()->getNumberOfSegments());
704 std::vector<std::vector<double>> velocityVectors(
705 levelSets.back()->getNumberOfSegments());
707 const bool checkDiss = checkDissipation;
709#pragma omp parallel num_threads(topDomain.getNumberOfSegments())
713 p = omp_get_thread_num();
715 auto itRS = storedRates[p].cbegin();
716 auto &segment = topDomain.getDomainSegment(p);
717 const unsigned maxId = segment.getNumberOfPoints();
719 if (saveVelocities) {
720 velocityVectors[p].resize(maxId);
721 dissipationVectors[p].resize(maxId);
724 for (
unsigned localId = 0; localId < maxId; ++localId) {
725 T &value = segment.definedValues[localId];
728 if (std::abs(value) > integrationCutoff)
736 auto const [gradient, dissipation] = itRS->first;
737 T velocity = gradient - dissipation;
739 if (checkDiss && (gradient < 0 && velocity > 0) ||
740 (gradient > 0 && velocity < 0)) {
744 T rate = time * velocity;
745 while (std::abs(itRS->second - value) < std::abs(rate)) {
746 time -= std::abs((itRS->second - value) / velocity);
747 value = itRS->second;
751 velocity = itRS->first.first - itRS->first.second;
752 if (checkDiss && (itRS->first.first < 0 && velocity > 0) ||
753 (itRS->first.first > 0 && velocity < 0)) {
756 rate = time * velocity;
762 if (saveVelocities) {
763 velocityVectors[p][localId] = rate;
764 dissipationVectors[p][localId] = itRS->first.second;
770 while (std::abs(itRS->second) != std::numeric_limits<T>::max())
778 if (saveVelocities) {
779 auto &pointData = levelSets.back()->getPointData();
784 for (
unsigned i = 0; i < velocityVectors.size(); ++i) {
785 vels.insert(vels.end(),
786 std::make_move_iterator(velocityVectors[i].begin()),
787 std::make_move_iterator(velocityVectors[i].end()));
788 diss.insert(diss.end(),
789 std::make_move_iterator(dissipationVectors[i].begin()),
790 std::make_move_iterator(dissipationVectors[i].end()));
792 pointData.insertReplaceScalarData(std::move(vels),
velocityLabel);
800 void adjustLowerLayers() {
804 for (
unsigned i = 0; i < levelSets.size() - 1; ++i) {
806 levelSets[i], levelSets.back(),
815 double advect(
double maxTimeStep) {
816 switch (temporalScheme) {
837 levelSets.push_back(passedlsDomain);
842 levelSets.push_back(passedlsDomain);
843 velocities = passedVelocities;
848 : levelSets(passedlsDomains) {
849 velocities = passedVelocities;
855 levelSets.push_back(passedlsDomain);
863 velocities = passedVelocities;
903 adaptiveTimeStepping = aTS;
904 if (subdivisions < 1) {
905 VIENNACORE_LOG_WARNING(
"Advect: Adaptive time stepping subdivisions must "
906 "be at least 1. Setting to 1.");
909 adaptiveTimeStepSubdivisions = subdivisions;
938 [[deprecated(
"Use setSpatialScheme instead")]]
void
940 VIENNACORE_LOG_WARNING(
941 "Advect::setIntegrationScheme is deprecated and will be removed in "
942 "future versions. Use setSpatialScheme instead.");
943 spatialScheme = scheme;
965 std::function<
bool(SmartPointer<
Domain<T, D>>)> callback) {
966 velocityUpdateCallback = callback;
973 if (levelSets.empty()) {
974 VIENNACORE_LOG_ERROR(
"No level sets passed to Advect.");
986 }
else if (spatialScheme ==
990 }
else if (spatialScheme ==
993 }
else if (spatialScheme ==
996 }
else if (spatialScheme ==
999 }
else if (spatialScheme ==
1002 }
else if (spatialScheme ==
1011 VIENNACORE_LOG_ERROR(
"Advect: Discretization scheme not found.");
1017 if (levelSets.empty()) {
1018 VIENNACORE_LOG_ERROR(
"No level sets passed to Advect. Not advecting.");
1021 if (velocities ==
nullptr) {
1022 VIENNACORE_LOG_ERROR(
1023 "No velocity field passed to Advect. Not advecting.");
1027 if (advectionTime == 0.) {
1028 advectedTime = advect(std::numeric_limits<double>::max());
1029 numberOfTimeSteps = 1;
1031 double currentTime = 0.0;
1032 numberOfTimeSteps = 0;
1033 while (currentTime < advectionTime) {
1034 currentTime += advect(advectionTime - currentTime);
1035 ++numberOfTimeSteps;
1036 if (performOnlySingleStep)
1039 advectedTime = currentTime;