Skip to content

Commit

Permalink
Refactor alpha calculation (#113)
Browse files Browse the repository at this point in the history
* Refactor global alpha calculation

* Small improvement in alpha calculation

* Add dissipation output

* Add dissipation check

* Expose wrapping layer epsilon in lsWriteVisualizationMesh

* Discard misformed triangles from surface mesh

* Set default dissipation check on and bump version
  • Loading branch information
tobre1 authored Feb 17, 2025
1 parent a4e0682 commit 36127ae
Show file tree
Hide file tree
Showing 14 changed files with 315 additions and 314 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.20 FATAL_ERROR)
project(
ViennaLS
LANGUAGES CXX
VERSION 4.1.2)
VERSION 4.1.3)

# --------------------------------------------------------------------------------------------------------
# Library options
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ We recommend using [CPM.cmake](https://github.com/cpm-cmake/CPM.cmake) to consum

* Installation with CPM
```cmake
CPMAddPackage("gh:viennatools/[email protected].2")
CPMAddPackage("gh:viennatools/[email protected].3")
```

* With a local installation
Expand Down
197 changes: 162 additions & 35 deletions include/viennals/lsAdvect.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,125 @@ template <class T, int D> class Advect {
unsigned numberOfTimeSteps = 0;
bool saveAdvectionVelocities = false;
bool updatePointData = true;
bool checkDissipation = true;
static constexpr double wrappingLayerEpsilon = 1e-4;

hrleVectorType<T, 3> findGlobalAlphas() const {

auto &topDomain = levelSets.back()->getDomain();
auto &grid = levelSets.back()->getGrid();

const T gridDelta = grid.getGridDelta();
const T deltaPos = gridDelta;
const T deltaNeg = -gridDelta;

hrleVectorType<T, 3> finalAlphas(0., 0., 0.);

#pragma omp parallel num_threads((levelSets.back())->getNumberOfSegments())
{
int p = 0;
#ifdef _OPENMP
p = omp_get_thread_num();
#endif

hrleVectorType<T, 3> localAlphas(0., 0., 0.);

hrleVectorType<hrleIndexType, D> startVector =
(p == 0) ? grid.getMinGridPoint()
: topDomain.getSegmentation()[p - 1];

hrleVectorType<hrleIndexType, D> endVector =
(p != static_cast<int>(topDomain.getNumberOfSegments() - 1))
? topDomain.getSegmentation()[p]
: grid.incrementIndices(grid.getMaxGridPoint());

// an iterator for each level set
std::vector<hrleConstSparseIterator<typename Domain<T, D>::DomainType>>
iterators;
for (auto it = levelSets.begin(); it != levelSets.end(); ++it) {
iterators.push_back(
hrleConstSparseIterator<typename Domain<T, D>::DomainType>(
(*it)->getDomain()));
}

// neighborIterator for the top level set
hrleConstSparseStarIterator<hrleDomain<T, D>, 1> neighborIterator(
topDomain);

for (hrleSparseIterator<typename Domain<T, D>::DomainType> it(
topDomain, startVector);
it.getStartIndices() < endVector; ++it) {

if (!it.isDefined() || std::abs(it.getValue()) > 0.5)
continue;

const T value = it.getValue();
const auto indices = it.getStartIndices();

// check if there is any other levelset at the same point:
// if yes, take the velocity of the lowest levelset
for (unsigned lowerLevelSetId = 0; lowerLevelSetId < levelSets.size();
++lowerLevelSetId) {
// put iterator to same position as the top levelset
iterators[lowerLevelSetId].goToIndicesSequential(indices);

// if the lower surface is actually outside, i.e. its LS value
// is lower or equal
if (iterators[lowerLevelSetId].getValue() <=
value + wrappingLayerEpsilon) {

// move neighborIterator to current position
neighborIterator.goToIndicesSequential(indices);

Vec3D<T> coords;
for (unsigned i = 0; i < D; ++i) {
coords[i] = indices[i] * gridDelta;
}

Vec3D<T> normal = {};
T normalModulus = 0.;
for (unsigned i = 0; i < D; ++i) {
const T phiPos = neighborIterator.getNeighbor(i).getValue();
const T phiNeg = neighborIterator.getNeighbor(i + D).getValue();

T diffPos = (phiPos - value) / deltaPos;
T diffNeg = (phiNeg - value) / deltaNeg;

normal[i] = (diffNeg + diffPos) * 0.5;
normalModulus += normal[i] * normal[i];
}
normalModulus = std::sqrt(normalModulus);
for (unsigned i = 0; i < D; ++i)
normal[i] /= normalModulus;

T scaVel = velocities->getScalarVelocity(
coords, lowerLevelSetId, normal,
neighborIterator.getCenter().getPointId());
auto vecVel = velocities->getVectorVelocity(
coords, lowerLevelSetId, normal,
neighborIterator.getCenter().getPointId());

for (unsigned i = 0; i < D; ++i) {
T tempAlpha = std::abs((scaVel + vecVel[i]) * normal[i]);
localAlphas[i] = std::max(localAlphas[i], tempAlpha);
}

break;
}
}
}

#pragma omp critical
{
for (unsigned i = 0; i < D; ++i) {
finalAlphas[i] = std::max(finalAlphas[i], localAlphas[i]);
}
}
} // end of parallel section

return finalAlphas;
}

void rebuildLS() {
// TODO: this function uses manhatten distances for renormalisation,
// since this is the quickest. For visualisation applications, better
Expand Down Expand Up @@ -293,14 +410,16 @@ template <class T, int D> class Advect {
currentTime = integrateTime(is, maxTimeStep);
} else if (integrationScheme ==
IntegrationSchemeEnum::LAX_FRIEDRICHS_1ST_ORDER) {
auto alphas = findGlobalAlphas();
auto is = lsInternal::LaxFriedrichs<T, D, 1>(levelSets.back(), velocities,
dissipationAlpha,
dissipationAlpha, alphas,
calculateNormalVectors);
currentTime = integrateTime(is, maxTimeStep);
} else if (integrationScheme ==
IntegrationSchemeEnum::LAX_FRIEDRICHS_2ND_ORDER) {
auto alphas = findGlobalAlphas();
auto is = lsInternal::LaxFriedrichs<T, D, 2>(levelSets.back(), velocities,
dissipationAlpha,
dissipationAlpha, alphas,
calculateNormalVectors);
currentTime = integrateTime(is, maxTimeStep);
} else if (integrationScheme ==
Expand Down Expand Up @@ -381,7 +500,7 @@ template <class T, int D> class Advect {
auto &topDomain = levelSets.back()->getDomain();
auto &grid = levelSets.back()->getGrid();

std::vector<std::vector<std::pair<T, T>>> totalTempRates;
std::vector<std::vector<std::pair<std::pair<T, T>, T>>> totalTempRates;
totalTempRates.resize((levelSets.back())->getNumberOfSegments());

typename PointData<T>::ScalarDataType *voidMarkerPointer;
Expand All @@ -399,16 +518,6 @@ template <class T, int D> class Advect {
}
}

// The global alpha values are stored in the integration scheme.
if (integrationScheme == IntegrationSchemeEnum::LAX_FRIEDRICHS_1ST_ORDER) {
lsInternal::advect::findGlobalAlpha<IntegrationSchemeType, T, D, 1>(
IntegrationScheme, levelSets, velocities);
} else if (integrationScheme ==
IntegrationSchemeEnum::LAX_FRIEDRICHS_2ND_ORDER) {
lsInternal::advect::findGlobalAlpha<IntegrationSchemeType, T, D, 2>(
IntegrationScheme, levelSets, velocities);
}

const bool ignoreVoidPoints = ignoreVoids;

#pragma omp parallel num_threads((levelSets.back())->getNumberOfSegments())
Expand Down Expand Up @@ -458,7 +567,7 @@ template <class T, int D> class Advect {
for (int currentLevelSetId = levelSets.size() - 1;
currentLevelSetId >= 0; --currentLevelSetId) {

T velocity = 0;
std::pair<T, T> gradNDissipation;

if (!(ignoreVoidPoints && (*voidMarkerPointer)[it.getPointId()])) {
// check if there is any other levelset at the same point:
Expand All @@ -473,7 +582,8 @@ template <class T, int D> class Advect {
// is lower or equal
if (iterators[lowerLevelSetId].getValue() <=
value + wrappingLayerEpsilon) {
velocity = scheme(it.getStartIndices(), lowerLevelSetId);
gradNDissipation =
scheme(it.getStartIndices(), lowerLevelSetId);
break;
}
}
Expand All @@ -491,16 +601,17 @@ template <class T, int D> class Advect {

// if velocity is positive, set maximum time step possible without
// violating the cfl condition
T velocity = gradNDissipation.first - gradNDissipation.second;
if (velocity > 0.) {
maxStepTime += cfl / velocity;
tempRates.push_back(
std::make_pair(velocity, -std::numeric_limits<T>::max()));
tempRates.push_back(std::make_pair(gradNDissipation,
-std::numeric_limits<T>::max()));
break;
// if velocity is 0, maximum time step is infinite
} else if (velocity == 0.) {
maxStepTime = std::numeric_limits<T>::max();
tempRates.push_back(
std::make_pair(velocity, std::numeric_limits<T>::max()));
tempRates.push_back(std::make_pair(gradNDissipation,
std::numeric_limits<T>::max()));
break;
// if the velocity is negative apply the velocity for as long as
// possible without infringing on material below
Expand All @@ -509,14 +620,14 @@ template <class T, int D> class Advect {

if (difference >= cfl) {
maxStepTime -= cfl / velocity;
tempRates.push_back(
std::make_pair(velocity, std::numeric_limits<T>::max()));
tempRates.push_back(std::make_pair(
gradNDissipation, std::numeric_limits<T>::max()));
break;
} else {
maxStepTime -= difference / velocity;
// the second part of the pair indicates how far we can move
// in this time step until the end of the material is reached
tempRates.push_back(std::make_pair(velocity, valueBelow));
tempRates.push_back(std::make_pair(gradNDissipation, valueBelow));
cfl -= difference;
// use new LS value for next calculations
value = valueBelow;
Expand All @@ -540,30 +651,34 @@ template <class T, int D> class Advect {
if (tempMaxTimeStep < maxTimeStep)
maxTimeStep = tempMaxTimeStep;
}
}
} // end of parallel section

// reduce to one layer thickness and apply new values directly to the
// domain segments --> DO NOT CHANGE SEGMENTATION HERE (true parameter)
Reduce<T, D>(levelSets.back(), 1, true).apply();

const bool saveVelocities = saveAdvectionVelocities;
std::vector<std::vector<double>> dissipationVectors(
levelSets.back()->getNumberOfSegments());
std::vector<std::vector<double>> velocityVectors(
levelSets.back()->getNumberOfSegments());

const bool checkDiss = checkDissipation;

#pragma omp parallel num_threads((levelSets.back())->getNumberOfSegments())
{
int p = 0;
#ifdef _OPENMP
p = omp_get_thread_num();
#endif

typename std::vector<std::pair<T, T>>::const_iterator itRS =
totalTempRates[p].begin();
auto itRS = totalTempRates[p].cbegin();
auto &segment = topDomain.getDomainSegment(p);
unsigned maxId = segment.getNumberOfPoints();

if (saveVelocities) {
velocityVectors[p].resize(maxId);
dissipationVectors[p].resize(maxId);
}

for (unsigned localId = 0; localId < maxId; ++localId) {
Expand All @@ -573,17 +688,24 @@ template <class T, int D> class Advect {
// if there is a change in materials during one time step, deduct
// the time taken to advect up to the end of the top material and
// set the LS value to the one below
while (std::abs(itRS->second - value) < std::abs(time * itRS->first)) {
time -= std::abs((itRS->second - value) / itRS->first);
T velocity = itRS->first.first - itRS->first.second;
if (checkDiss && (itRS->first.first < 0 && velocity > 0) ||
(itRS->first.first > 0 && velocity < 0)) {
velocity = 0;
}
T rate = time * velocity;
while (std::abs(itRS->second - value) < std::abs(rate)) {
time -= std::abs((itRS->second - value) / velocity);
value = itRS->second;
++itRS;
}

// now deduct the velocity times the time step we take
value -= time * itRS->first;
value -= rate;

if (saveVelocities) {
velocityVectors[p][localId] = time * itRS->first;
velocityVectors[p][localId] = rate;
dissipationVectors[p][localId] = itRS->first.second;
}

// this
Expand All @@ -596,30 +718,32 @@ template <class T, int D> class Advect {
// advance the TempStopRates iterator by one
++itRS;
}
}
} // end of parallel section

if (saveVelocities) {
auto &pointData = levelSets.back()->getPointData();
// delete if already exists
if (int i = pointData.getScalarDataIndex(velocityLabel); i != -1) {
pointData.eraseScalarData(i);
}

typename PointData<T>::ScalarDataType vels;
typename PointData<T>::ScalarDataType diss;

for (unsigned i = 0; i < velocityVectors.size(); ++i) {
vels.insert(vels.end(),
std::make_move_iterator(velocityVectors[i].begin()),
std::make_move_iterator(velocityVectors[i].end()));
diss.insert(diss.end(),
std::make_move_iterator(dissipationVectors[i].begin()),
std::make_move_iterator(dissipationVectors[i].end()));
}
pointData.insertNextScalarData(std::move(vels), velocityLabel);
pointData.insertReplaceScalarData(std::move(vels), velocityLabel);
pointData.insertReplaceScalarData(std::move(diss), dissipationLabel);
}

return maxTimeStep;
}

public:
static constexpr char velocityLabel[] = "AdvectionVelocities";
static constexpr char dissipationLabel[] = "Dissipation";

Advect() {}

Expand Down Expand Up @@ -713,6 +837,9 @@ template <class T, int D> class Advect {
/// scaling factor for the calculated alpha values.
void setDissipationAlpha(const double &a) { dissipationAlpha = a; }

// Sets the velocity to 0 if the dissipation is too high
void setCheckDissipation(bool check) { checkDissipation = check; }

/// Set whether the point data in the old LS should
/// be translated to the advected LS. Defaults to true.
void setUpdatePointData(bool update) { updatePointData = update; }
Expand Down
7 changes: 3 additions & 4 deletions include/viennals/lsEnquistOsher.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ template <class T, int D, int order> class EnquistOsher {
levelSet->getDomain())),
calculateNormalVectors(calcNormal) {}

T operator()(const hrleVectorType<hrleIndexType, D> &indices, int material) {
std::pair<T, T> operator()(const hrleVectorType<hrleIndexType, D> &indices,
int material) {
auto &grid = levelSet->getGrid();
double gridDelta = grid.getGridDelta();

Expand Down Expand Up @@ -160,11 +161,9 @@ template <class T, int D, int order> class EnquistOsher {
}
}

return vel_grad;
return {vel_grad, 0.};
}

void setFinalAlphas(const hrleVectorType<T, 3> &) {}

void reduceTimeStepHamiltonJacobi(double &MaxTimeStep,
hrleCoordType gridDelta) {}
};
Expand Down
Loading

0 comments on commit 36127ae

Please sign in to comment.