ViennaLS
Loading...
Searching...
No Matches
lsGeometricAdvect.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <unordered_map>
4
5#include <hrleDenseIterator.hpp>
6#include <hrleSparseIterator.hpp>
7#include <hrleSparseStarIterator.hpp>
8#include <hrleVectorType.hpp>
9
11#include <lsConcepts.hpp>
12#include <lsDomain.hpp>
13#include <lsExpand.hpp>
14#include <lsFromMesh.hpp>
17#include <lsToDiskMesh.hpp>
18
19#include <vcLogger.hpp>
20#include <vcSmartPointer.hpp>
21
22#ifndef NDEBUG // if in debug build
23#include <lsCheck.hpp>
24#include <lsToMesh.hpp>
25#include <lsVTKWriter.hpp>
26#endif
27
28namespace viennals {
29
30using namespace viennacore;
31
39template <class T, int D> class GeometricAdvect {
40 SmartPointer<Domain<T, D>> levelSet = nullptr;
41 SmartPointer<Domain<T, D>> maskLevelSet = nullptr;
42 SmartPointer<const GeometricAdvectDistribution<hrleCoordType, D>> dist =
43 nullptr;
44 static constexpr T cutoffValue =
45 T(1.) + std::numeric_limits<T>::epsilon() * T(100);
46
47 static void incrementIndices(hrleVectorType<hrleIndexType, D> &indices,
48 const hrleVectorType<hrleIndexType, D> &min,
49 const hrleVectorType<hrleIndexType, D> &max) {
50 int dim = 0;
51 for (; dim < D - 1; ++dim) {
52 if (indices[dim] < max[dim])
53 break;
54 indices[dim] = min[dim];
55 }
56 ++indices[dim];
57 }
58
59 template <class K, class V, template <class...> class MapType, class... Ts>
60 MapType<V, K> inverseTranslator(MapType<K, V, Ts...> &map) {
61 MapType<V, K> inv;
62 std::for_each(map.begin(), map.end(), [&inv](const std::pair<K, V> &p) {
63 inv.insert(std::make_pair(p.second, p.first));
64 });
65 return inv;
66 }
67
68public:
70
71 template <class DistType,
73 DistType> = lsConcepts::assignable>
74 GeometricAdvect(SmartPointer<Domain<T, D>> passedLevelSet,
75 SmartPointer<DistType> passedDist,
76 SmartPointer<Domain<T, D>> passedMaskLevelSet = nullptr)
77 : levelSet(passedLevelSet), maskLevelSet(passedMaskLevelSet) {
78 dist = std::dynamic_pointer_cast<
80 }
81
83 void setLevelSet(SmartPointer<Domain<T, D>> passedLevelSet) {
84 levelSet = passedLevelSet;
85 }
86
89 template <class DistType,
91 DistType> = lsConcepts::assignable>
92 void setAdvectionDistribution(SmartPointer<DistType> passedDist) {
93 dist = std::dynamic_pointer_cast<
95 }
96
100 void setMaskLevelSet(SmartPointer<Domain<T, D>> passedMaskLevelSet) {
101 maskLevelSet = passedMaskLevelSet;
102 }
103
105 void apply() {
106 if (levelSet == nullptr) {
107 Logger::getInstance()
108 .addWarning("No level set passed to GeometricAdvect. Not Advecting.")
109 .print();
110 return;
111 }
112 if (dist == nullptr) {
113 Logger::getInstance()
114 .addWarning("No GeometricAdvectDistribution passed to "
115 "GeometricAdvect. Not "
116 "Advecting.")
117 .print();
118 return;
119 }
120
121 // levelSet must have at least a width of 3
122 Expand<T, D>(levelSet, 3).apply();
123
124 if (maskLevelSet != nullptr) {
125 Expand<T, D>(maskLevelSet, 3).apply();
126 }
127
128 typedef typename Domain<T, D>::DomainType DomainType;
129
130 auto &domain = levelSet->getDomain();
131
132 auto &grid = levelSet->getGrid();
133 auto gridDelta = grid.getGridDelta();
134
135 // Extract the original surface as a point cloud of grid
136 // points shifted to the surface (disk mesh)
137 auto surfaceMesh = SmartPointer<Mesh<hrleCoordType>>::New();
138 auto pointIdTranslator =
139 SmartPointer<typename ToDiskMesh<T, D>::TranslatorType>::New();
140 ToDiskMesh<T, D, hrleCoordType>(levelSet, surfaceMesh, pointIdTranslator)
141 .apply();
142 *pointIdTranslator = inverseTranslator(*pointIdTranslator);
143
144 // find bounds of distribution
145 auto distBounds = dist->getBounds();
146
147 // TODO: need to add support for periodic boundary conditions!
148 hrleVectorType<hrleIndexType, D> distMin, distMax;
149
150 bool minPointNegative = domain.getDomainSegment(0).definedValues[0] < 0.;
151 bool maxPointNegative =
152 domain.getDomainSegment(domain.getNumberOfSegments() - 1)
153 .definedValues.back() < 0.;
154 bool distIsPositive = true;
155
156 // find bounding box of old domain
157 hrleIndexType bounds[6];
158 domain.getDomainBounds(bounds);
159 hrleVectorType<hrleIndexType, D> min, max;
160 for (unsigned i = 0; i < D; ++i) {
161 // translate from coords to indices
162 distMin[i] =
163 distBounds[2 * i] / gridDelta + ((distBounds[2 * i] < 0) ? -2 : 2);
164 distMax[i] = distBounds[2 * i + 1] / gridDelta +
165 ((distBounds[2 * i + 1] < 0) ? -2 : 2);
166 if (distBounds[2 * i] >= 0) {
167 distIsPositive = false;
168 }
169
170 // use the extent of the diskMesh to identify bounding box of new
171 // level set
172 // TODO: respect periodic boundary condition
173 min[i] = surfaceMesh->minimumExtent[i] / gridDelta;
174 // TODO also do the same thing for positive point and etching
175 if (grid.isNegBoundaryInfinite(i) && minPointNegative && distMin[i] < 0) {
176 min[i] -= 2;
177 } else {
178 if (distIsPositive) {
179 min[i] += distMin[i];
180 } else {
181 min[i] -= distMin[i];
182 }
183 }
184 // if calculated index is out of bounds, set the extent
185 // TODO: need to add periodic BNC handling here
186 if (min[i] < grid.getMinGridPoint(i)) {
187 min[i] = grid.getMinGridPoint(i);
188 }
189
190 max[i] = surfaceMesh->maximumExtent[i] / gridDelta;
191 if (grid.isPosBoundaryInfinite(i) && maxPointNegative && distMax[i] > 0) {
192 max[i] += 2;
193 } else {
194 if (distIsPositive) {
195 max[i] += distMax[i];
196 } else {
197 max[i] -= distMax[i];
198 }
199 }
200 if (max[i] > grid.getMaxGridPoint(i)) {
201 max[i] = grid.getMaxGridPoint(i);
202 }
203 }
204
205 // Remove contribute points if they are part of the mask
206 // If a mask is supplied, remove all contribute points which
207 // lie on (or inside) the mask
208 if (maskLevelSet != nullptr) {
209 // Go over all contribute points and see if they are on the mask surface
210 auto &maskDomain = maskLevelSet->getDomain();
211 auto values = surfaceMesh->cellData.getScalarData("LSValues");
212 auto valueIt = values->begin();
213
214 auto newSurfaceMesh = SmartPointer<Mesh<hrleCoordType>>::New();
216 hrleConstSparseIterator<DomainType> maskIt(maskDomain);
217 for (auto &node : surfaceMesh->getNodes()) {
218 hrleVectorType<hrleIndexType, D> index;
219 for (unsigned i = 0; i < D; ++i) {
220 index[i] = std::round(node[i] / gridDelta);
221 }
222 // can do sequential, because surfaceNodes are lexicographically sorted
223 // from lsToDiskMesh
224 maskIt.goToIndicesSequential(index);
225 // if it is a mask point, mark it to maybe use it in new level set
226 if (!maskIt.isDefined() || !(maskIt.getValue() < *valueIt + 1e-5)) {
227 newSurfaceMesh->insertNextNode(node);
228 newValues.push_back(*valueIt);
229 // insert vertex
230 std::array<unsigned, 1> vertex;
231 vertex[0] = newSurfaceMesh->nodes.size();
232 newSurfaceMesh->insertNextVertex(vertex);
233 }
234 ++valueIt;
235 }
236 newSurfaceMesh->cellData.insertNextScalarData(newValues, "LSValues");
237 // use new mesh as surfaceMesh
238 newSurfaceMesh->minimumExtent = surfaceMesh->minimumExtent;
239 newSurfaceMesh->maximumExtent = surfaceMesh->maximumExtent;
240 surfaceMesh = newSurfaceMesh;
241 }
242
243#ifndef NDEBUG // if in debug build
244 {
245 Logger::getInstance()
246 .addDebug("GeomAdvect: Writing debug meshes")
247 .print();
249 "DEBUG_lsGeomAdvectMesh_contributewoMask.vtp")
250 .apply();
251 auto mesh = SmartPointer<Mesh<T>>::New();
252 if (maskLevelSet != nullptr) {
253 ToMesh<T, D>(maskLevelSet, mesh).apply();
255 "DEBUG_lsGeomAdvectMesh_mask.vtp")
256 .apply();
257 }
258 ToMesh<T, D>(levelSet, mesh).apply();
260 "DEBUG_lsGeomAdvectMesh_initial.vtp")
261 .apply();
262 }
263
264#endif
265
266 typedef std::vector<std::array<hrleCoordType, 3>> SurfaceNodesType;
267 const SurfaceNodesType &surfaceNodes = surfaceMesh->getNodes();
268
269 // initialize with segmentation for whole range
270 typename hrleDomain<T, D>::hrleIndexPoints segmentation;
271
272 {
273 unsigned long long numPoints = 1;
274 unsigned long long pointsPerDimension[D];
275 for (unsigned i = 0; i < D; ++i) {
276 pointsPerDimension[i] = numPoints;
277 numPoints *= max[i] - min[i];
278 }
279 unsigned long numberOfSegments = domain.getNumberOfSegments();
280 unsigned long long pointsPerSegment = numPoints / numberOfSegments;
281 unsigned long long pointId = 0;
282 for (unsigned i = 0; i < numberOfSegments - 1; ++i) {
283 pointId = pointsPerSegment * (i + 1);
284 hrleVectorType<hrleIndexType, D> segmentPoint;
285 for (int j = D - 1; j >= 0; --j) {
286 segmentPoint[j] = pointId / (pointsPerDimension[j]) + min[j];
287 pointId %= pointsPerDimension[j];
288 }
289 segmentation.push_back(segmentPoint);
290 }
291 }
292
293 typedef std::vector<std::pair<hrleVectorType<hrleIndexType, D>, T>>
294 PointValueVector;
295 std::vector<PointValueVector> newPoints;
296 newPoints.resize(domain.getNumberOfSegments());
297
298 const T initialDistance = (distIsPositive)
299 ? std::numeric_limits<double>::max()
300 : std::numeric_limits<double>::lowest();
301
302#ifndef NDEBUG
303 {
304 std::ostringstream oss;
305 oss << "GeomAdvect: Min: " << min << ", Max: " << max << std::endl;
306 Logger::getInstance().addDebug(oss.str()).print();
307 }
308#endif
309// set up multithreading
310#pragma omp parallel num_threads(domain.getNumberOfSegments())
311 {
312 int p = 0;
313#ifdef _OPENMP
314 p = omp_get_thread_num();
315#endif
316
317 hrleVectorType<hrleIndexType, D> startVector;
318 if (p == 0) {
319 startVector = min;
320 } else {
321 startVector = segmentation[p - 1];
322 incrementIndices(startVector, min, max);
323 }
324
325 hrleVectorType<hrleIndexType, D> endVector =
326 (p != static_cast<int>(domain.getNumberOfSegments() - 1))
327 ? segmentation[p]
328 : grid.incrementIndices(max);
329
330 hrleConstSparseIterator<DomainType> checkIt(levelSet->getDomain(),
331 startVector);
332
333 // Mask iterator for checking whether inside mask or not
334 SmartPointer<hrleConstSparseIterator<DomainType>> maskIt = nullptr;
335 if (maskLevelSet != nullptr) {
336 maskIt = SmartPointer<hrleConstSparseIterator<DomainType>>::New(
337 maskLevelSet->getDomain(), startVector);
338 }
339
340 // Iterate through the bounds of new lsDomain lexicographically
341 for (hrleVectorType<hrleIndexType, D> currentIndex = startVector;
342 currentIndex <= endVector;
343 incrementIndices(currentIndex, min, max)) {
344 // if point is already full in old level set, skip it
345 checkIt.goToIndicesSequential(currentIndex);
346 T oldValue = checkIt.getValue();
347 // if run is already negative undefined, just ignore the point
348 if (distIsPositive) {
349 if (oldValue < -cutoffValue) {
350 continue;
351 }
352 } else if (oldValue > cutoffValue) {
353 continue;
354 }
355
356 std::array<hrleCoordType, 3> currentCoords{};
357 std::array<hrleCoordType, 3> currentDistMin{};
358 std::array<hrleCoordType, 3> currentDistMax{};
359
360 for (unsigned i = 0; i < D; ++i) {
361 currentCoords[i] = currentIndex[i] * gridDelta;
362
363 currentDistMin[i] = currentIndex[i] - std::abs(distMin[i]);
364 if (currentDistMin[i] < grid.getMinGridPoint(i)) {
365 currentDistMin[i] = grid.getMinGridPoint(i);
366 }
367 currentDistMin[i] *= gridDelta;
368
369 currentDistMax[i] = currentIndex[i] + std::abs(distMax[i]);
370 if (currentDistMin[i] > grid.getMaxGridPoint(i)) {
371 currentDistMin[i] = grid.getMaxGridPoint(i);
372 }
373 currentDistMax[i] *= gridDelta;
374 }
375
376 T distance = initialDistance;
377
378 unsigned long currentPointId = 0;
379 // now check which surface points contribute to currentIndex
380 for (typename SurfaceNodesType::const_iterator surfIt =
381 surfaceNodes.begin();
382 surfIt != surfaceNodes.end(); ++surfIt, ++currentPointId) {
383
384 auto &currentNode = *surfIt;
385
386 // if we are outside min/max go to next index inside
387 {
388 bool outside = false;
389 for (unsigned i = 0; i < D; ++i) {
390 if ((currentNode[i] < currentDistMin[i]) ||
391 (currentNode[i] > currentDistMax[i])) {
392 outside = true;
393 break;
394 }
395 }
396 if (outside) {
397 continue;
398 }
399 }
400
401 // TODO: does this really save time? Try without it.
402 if (!dist->isInside(currentNode, currentCoords, 2 * gridDelta)) {
403 continue;
404 }
405
406 // get filling fraction from distance to dist surface
407 T tmpDistance = dist->getSignedDistance(
408 currentNode, currentCoords,
409 pointIdTranslator->find(currentPointId)->second) /
410 gridDelta;
411
412 // if cell is far within a distribution, set it filled
413 if (distIsPositive) {
414 if (tmpDistance <= -cutoffValue) {
415 distance = std::numeric_limits<T>::lowest();
416 break;
417 }
418
419 if (tmpDistance < distance) {
420 distance = tmpDistance;
421 }
422 } else {
423 if (tmpDistance >= cutoffValue) {
424 distance = std::numeric_limits<T>::max();
425 break;
426 }
427
428 if (tmpDistance > distance) {
429 distance = tmpDistance;
430 }
431 }
432 }
433
434 // TODO: There are still issues with positive box distributions
435 // if there is a mask used!
436 // if point is part of the mask, keep smaller value
437 if (maskLevelSet != nullptr) {
438 maskIt->goToIndicesSequential(currentIndex);
439
440 // if dist is positive, flip logic of comparison
441 if (distIsPositive ^
442 (std::abs(oldValue - maskIt->getValue()) < 1e-6)) {
443 if (!distIsPositive && std::abs(oldValue) <= cutoffValue) {
444 newPoints[p].push_back(std::make_pair(currentIndex, oldValue));
445 continue;
446 }
447 } else {
448 if (distance != initialDistance) {
449 distance = std::min(maskIt->getValue(), distance);
450 } else if (distIsPositive || oldValue >= 0.) {
451 newPoints[p].push_back(std::make_pair(currentIndex, oldValue));
452 continue;
453 }
454 }
455 }
456
457 if (std::abs(distance) <= cutoffValue) {
458 // avoid using distribution in wrong direction
459 if (distIsPositive && oldValue >= 0.) {
460 newPoints[p].push_back(std::make_pair(currentIndex, distance));
461 } else if (!distIsPositive && oldValue <= 0.) {
462 // if we are etching, need to make sure, we are not inside mask
463 if (maskIt == nullptr || maskIt->getValue() > -cutoffValue) {
464 newPoints[p].push_back(std::make_pair(currentIndex, distance));
465 }
466 } else {
467 // this only happens if distribution is very small, < 2 * gridDelta
468 newPoints[p].push_back(std::make_pair(currentIndex, oldValue));
469 }
470 }
471 } // domainBounds for
472 } // parallel region
473
474 // copy all points into the first vector
475 {
476 unsigned long long numberOfPoints = newPoints[0].size();
477 for (unsigned i = 1; i < domain.getNumberOfSegments(); ++i) {
478 numberOfPoints += newPoints[i].size();
479 }
480 newPoints[0].reserve(numberOfPoints);
481 for (unsigned i = 1; i < domain.getNumberOfSegments(); ++i) {
482 std::move(std::begin(newPoints[i]), std::end(newPoints[i]),
483 std::back_inserter(newPoints[0]));
484 }
485 }
486
487 auto mesh = SmartPointer<Mesh<T>>::New();
488 // output all points directly to mesh
489 {
490 std::vector<T> scalarData;
491 for (auto it = newPoints[0].begin(); it != newPoints[0].end(); ++it) {
492 std::array<T, 3> node = {};
493 for (unsigned i = 0; i < D; ++i) {
494 node[i] = T((it->first)[i]) * gridDelta;
495 }
496
497 mesh->insertNextNode(node);
498 std::array<unsigned, 1> vertex;
499 vertex[0] = mesh->vertices.size();
500 mesh->insertNextVertex(vertex);
501 scalarData.push_back(it->second);
502 }
503 mesh->cellData.insertNextScalarData(scalarData, "LSValues");
504 }
505
506#ifndef NDEBUG // if in debug build
507 Logger::getInstance().addDebug("GeomAdvect: Writing final mesh...").print();
508 VTKWriter<T>(mesh, FileFormatEnum::VTP, "DEBUG_lsGeomAdvectMesh_final.vtp")
509 .apply();
510#endif
511
512 FromMesh<T, D>(levelSet, mesh).apply();
513
514#ifndef NDEBUG // if in debug build
515 Logger::getInstance().addDebug("GeomAdvect: Writing final LS...").print();
516 ToMesh<T, D>(levelSet, mesh).apply();
517 VTKWriter<T>(mesh, FileFormatEnum::VTP, "DEBUG_lsGeomAdvectLS_final.vtp")
518 .apply();
519#endif
520
521 Prune<T, D>(levelSet).apply();
522
523 levelSet->getDomain().segment();
524 levelSet->finalize(1);
525
526 Expand<T, D>(levelSet, 2).apply();
527 }
528};
529
530// add all template specialisations for this class
531PRECOMPILE_PRECISION_DIMENSION(GeometricAdvect)
532
533} // namespace viennals
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:28
hrleDomain< T, D > DomainType
Definition lsDomain.hpp:33
Expands the leveleSet to the specified number of layers. The largest value in the levelset is thus wi...
Definition lsExpand.hpp:16
void apply()
Apply the expansion to the specified width.
Definition lsExpand.hpp:44
Import the regular grid, on which the level set values are defined, from an explicit Mesh<>....
Definition lsFromMesh.hpp:16
void apply()
Definition lsFromMesh.hpp:40
Base class for distributions used by lsGeometricAdvect. All functions are pure virtual and must be im...
Definition lsGeometricAdvectDistributions.hpp:15
This class advects the level set according to a given distribution. This distribution is overlayed at...
Definition lsGeometricAdvect.hpp:39
GeometricAdvect()
Definition lsGeometricAdvect.hpp:69
void setLevelSet(SmartPointer< Domain< T, D > > passedLevelSet)
Set the levelset which should be advected.
Definition lsGeometricAdvect.hpp:83
void setMaskLevelSet(SmartPointer< Domain< T, D > > passedMaskLevelSet)
Set the levelset, which should be used as a mask. This level set has to be wrapped by the levelset se...
Definition lsGeometricAdvect.hpp:100
void setAdvectionDistribution(SmartPointer< DistType > passedDist)
Set which advection distribution to use. Must be derived from GeometricAdvectDistribution.
Definition lsGeometricAdvect.hpp:92
GeometricAdvect(SmartPointer< Domain< T, D > > passedLevelSet, SmartPointer< DistType > passedDist, SmartPointer< Domain< T, D > > passedMaskLevelSet=nullptr)
Definition lsGeometricAdvect.hpp:74
void apply()
Perform geometrical advection.
Definition lsGeometricAdvect.hpp:105
void insertNextScalarData(const ScalarDataType &scalars, std::string label="Scalars")
insert new scalar data array
Definition lsPointData.hpp:57
std::vector< T > ScalarDataType
Definition lsPointData.hpp:22
Removes all level set points, which do not have at least one oppositely signed neighbour (Meaning the...
Definition lsPrune.hpp:19
void apply()
removes all grid points, which do not have at least one opposite signed neighbour returns the number ...
Definition lsPrune.hpp:76
This class creates a mesh from the level set with all grid points with a level set value <= 0....
Definition lsToDiskMesh.hpp:24
void apply()
Definition lsToDiskMesh.hpp:83
Extract the regular grid, on which the level set values are defined, to an explicit Mesh<>....
Definition lsToMesh.hpp:19
void apply()
Definition lsToMesh.hpp:49
Class handling the output of an Mesh<> to VTK file types.
Definition lsVTKWriter.hpp:31
void apply()
Definition lsVTKWriter.hpp:88
#define PRECOMPILE_PRECISION_DIMENSION(className)
Definition lsPreCompileMacros.hpp:24
constexpr AssignType assignable
Definition lsConcepts.hpp:10
std::enable_if_t< std::is_base_of< Base, Derived >::value, AssignType > IsBaseOf
Definition lsConcepts.hpp:13
Definition lsAdvect.hpp:46
constexpr int D
Definition pyWrap.cpp:65
double T
Definition pyWrap.cpp:63