ViennaLS
Loading...
Searching...
No Matches
lsCalculateNormalVectors.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <algorithm>
6
7#include <hrleSparseStarIterator.hpp>
8
9#include <lsDomain.hpp>
10#include <lsExpand.hpp>
11
12#include <vcLogger.hpp>
13#include <vcSmartPointer.hpp>
14#include <vcVectorType.hpp>
15
16namespace viennals {
17
18using namespace viennacore;
19
24
40template <class T, int D> class CalculateNormalVectors {
41 SmartPointer<Domain<T, D>> levelSet = nullptr;
42 T maxValue = 0.5;
45
46 // Constants for calculation
47 static constexpr T DEFAULT_MAX_VALUE = 0.5;
48 static constexpr T EPSILON = 1e-12;
49 static constexpr T FINITE_DIFF_FACTOR = 0.5;
50
51public:
52 static constexpr char normalVectorsLabel[] = "Normals";
53
55
56 CalculateNormalVectors(SmartPointer<Domain<T, D>> passedLevelSet,
57 T passedMaxValue = DEFAULT_MAX_VALUE,
58 NormalCalculationMethodEnum passedMethod =
60 : levelSet(passedLevelSet), maxValue(passedMaxValue),
61 method(passedMethod) {}
62
63 void setLevelSet(SmartPointer<Domain<T, D>> passedLevelSet) {
64 levelSet = passedLevelSet;
65 }
66
67 void setMaxValue(const T passedMaxValue) {
68 if (passedMaxValue <= 0) {
69 VIENNACORE_LOG_WARNING(
70 "CalculateNormalVectors: maxValue should be positive. "
71 "Using default value " +
72 std::to_string(DEFAULT_MAX_VALUE) + ".");
73 maxValue = DEFAULT_MAX_VALUE;
74 } else {
75 maxValue = passedMaxValue;
76 }
77 }
78
80 method = passedMethod;
81 }
82
83 SmartPointer<Domain<T, D>> getLevelSet() const { return levelSet; }
84
85 T getMaxValue() const { return maxValue; }
86
88 bool hasNormalVectors() const {
89 if (levelSet == nullptr)
90 return false;
91 auto &pointData = levelSet->getPointData();
92 return pointData.getVectorData(normalVectorsLabel) != nullptr;
93 }
94
95 void apply() {
96 if (levelSet == nullptr) {
97 VIENNACORE_LOG_ERROR(
98 "No level set was passed to CalculateNormalVectors.");
99 return;
100 }
101
102 switch (method) {
104 calculateCentralDifferences();
105 break;
107 calculateOneSidedMinMod();
108 break;
109 }
110 }
111
112private:
113 void calculateCentralDifferences() {
114 if (levelSet->getLevelSetWidth() < (maxValue * 4) + 1) {
115 VIENNACORE_LOG_WARNING("CalculateNormalVectors: Level set width must be "
116 "greater than " +
117 std::to_string((maxValue * 4) + 1) +
118 ". Expanding level set to " +
119 std::to_string((maxValue * 4) + 1) + ".");
120 Expand<T, D>(levelSet, (maxValue * 4) + 1).apply();
121 }
122
123 std::vector<std::vector<Vec3D<T>>> normalVectorsVector(
124 levelSet->getNumberOfSegments());
125
126 // Estimate memory requirements per thread to improve cache performance
127 double pointsPerSegment =
128 double(2 * levelSet->getDomain().getNumberOfPoints()) /
129 double(levelSet->getLevelSetWidth());
130
131 auto grid = levelSet->getGrid();
132
133 // Calculate Normalvectors
134#pragma omp parallel num_threads(levelSet->getNumberOfSegments())
135 {
136 int p = 0;
137#ifdef _OPENMP
138 p = omp_get_thread_num();
139#endif
140
141 auto &normalVectors = normalVectorsVector[p];
142 normalVectors.reserve(pointsPerSegment);
143
144 viennahrle::Index<D> const startVector =
145 (p == 0) ? grid.getMinGridPoint()
146 : levelSet->getDomain().getSegmentation()[p - 1];
147
148 viennahrle::Index<D> const endVector =
149 (p != static_cast<int>(levelSet->getNumberOfSegments() - 1))
150 ? levelSet->getDomain().getSegmentation()[p]
151 : grid.incrementIndices(grid.getMaxGridPoint());
152
153 for (viennahrle::ConstSparseStarIterator<
154 typename Domain<T, D>::DomainType, 1>
155 neighborIt(levelSet->getDomain(), startVector);
156 neighborIt.getIndices() < endVector; neighborIt.next()) {
157
158 auto &center = neighborIt.getCenter();
159 if (!center.isDefined()) {
160 continue;
161 } else if (std::abs(center.getValue()) > maxValue) {
162 // push an empty vector to keep ordering correct
163 Vec3D<T> tmp{};
164 normalVectors.push_back(tmp);
165 continue;
166 }
167
168 Vec3D<T> n{};
169
170 T denominator = 0;
171 for (int i = 0; i < D; i++) {
172 viennahrle::Index<D> posIdx(0);
173 posIdx[i] = 1;
174 viennahrle::Index<D> negIdx(0);
175 negIdx[i] = -1;
176 T pos = neighborIt.getNeighbor(posIdx).getValue() - center.getValue();
177 T neg = center.getValue() - neighborIt.getNeighbor(negIdx).getValue();
178 n[i] = (pos + neg) * FINITE_DIFF_FACTOR;
179 denominator += n[i] * n[i];
180 }
181
182 denominator = std::sqrt(denominator);
183 if (std::abs(denominator) < EPSILON) {
184 VIENNACORE_LOG_WARNING(
185 "CalculateNormalVectors: Vector of length 0 at " +
186 neighborIt.getIndices().to_string());
187 for (unsigned i = 0; i < D; ++i)
188 n[i] = 0.;
189 } else {
190 for (unsigned i = 0; i < D; ++i) {
191 n[i] /= denominator;
192 }
193 }
194
195 normalVectors.push_back(n);
196 }
197 }
198 insertIntoPointData(normalVectorsVector);
199 }
200
201 void calculateOneSidedMinMod() {
202 // This method does not require expansion, since it is robust to missing
203 // neighbors.
204 auto &domain = levelSet->getDomain();
205 auto &grid = levelSet->getGrid();
206
207 // Directly write to a single vector indexed by point ID.
208 // This is thread-safe since each thread works on a distinct range of
209 // points.
210 std::vector<Vec3D<T>> normalVectors(levelSet->getNumberOfPoints());
211
212#pragma omp parallel num_threads(levelSet->getNumberOfSegments())
213 {
214 int p = 0;
215#ifdef _OPENMP
216 p = omp_get_thread_num();
217#endif
218
219 viennahrle::Index<D> startVector =
220 (p == 0) ? grid.getMinGridPoint()
221 : levelSet->getDomain().getSegmentation()[p - 1];
222 viennahrle::Index<D> endVector =
223 (p != static_cast<int>(levelSet->getNumberOfSegments() - 1))
224 ? levelSet->getDomain().getSegmentation()[p]
225 : grid.incrementIndices(grid.getMaxGridPoint());
226
227 viennahrle::SparseStarIterator<typename Domain<T, D>::DomainType, 1>
228 neighborIt(domain, startVector);
229
230 for (; neighborIt.getIndices() < endVector; neighborIt.next()) {
231 if (neighborIt.getCenter().isDefined()) {
232 Vec3D<T> grad{};
233 for (int i = 0; i < D; ++i) {
234 viennahrle::Index<D> posIdx(0);
235 posIdx[i] = 1;
236 viennahrle::Index<D> negIdx(0);
237 negIdx[i] = -1;
238 bool negDefined = neighborIt.getNeighbor(negIdx).isDefined();
239 bool posDefined = neighborIt.getNeighbor(posIdx).isDefined();
240
241 if (negDefined && posDefined) {
242 T valNeg = neighborIt.getNeighbor(negIdx).getValue();
243 T valCenter = neighborIt.getCenter().getValue();
244 T valPos = neighborIt.getNeighbor(posIdx).getValue();
245
246 const bool centerSign = valCenter >= 0;
247 const bool negSign = valNeg > 0;
248 const bool posSign = valPos > 0;
249
250 const T d_neg = valCenter - valNeg;
251 const T d_pos = valPos - valCenter;
252
253 if (centerSign != negSign && centerSign != posSign) {
254 // Center is an extremum, use minmod to be safe
255 grad[i] = 0.;
256 // (std::abs(d_pos) < std::abs(d_neg)) ? d_pos : d_neg;
257 } else if (centerSign != negSign) {
258 // Interface is on the negative side, use backward difference
259 grad[i] = d_neg;
260 } else if (centerSign != posSign) {
261 // Interface is on the positive side, use forward difference
262 grad[i] = d_pos;
263 } else {
264 // No sign change, use minmod to handle sharp features smoothly
265 grad[i] = (std::abs(d_pos) < std::abs(d_neg)) ? d_pos : d_neg;
266 }
267 } else if (negDefined) {
268 grad[i] = (neighborIt.getCenter().getValue() -
269 neighborIt.getNeighbor(negIdx).getValue());
270 } else if (posDefined) {
271 grad[i] = (neighborIt.getNeighbor(posIdx).getValue() -
272 neighborIt.getCenter().getValue());
273 } else {
274 grad[i] = 0;
275 }
276 }
277 Normalize(grad);
278 normalVectors[neighborIt.getCenter().getPointId()] = grad;
279 }
280 }
281 }
282
283 // insert into pointData of levelSet
284 auto &pointData = levelSet->getPointData();
285 auto vectorDataPointer = pointData.getVectorData(normalVectorsLabel, true);
286 // if it does not exist, insert new normals vector
287 if (vectorDataPointer == nullptr) {
288 pointData.insertNextVectorData(normalVectors, normalVectorsLabel);
289 } else {
290 // if it does exist, just swap the old with the new values
291 *vectorDataPointer = std::move(normalVectors);
292 }
293 }
294
295 void
296 insertIntoPointData(std::vector<std::vector<Vec3D<T>>> &normalVectorsVector) {
297 // copy all normals
298 unsigned numberOfNormals = 0;
299 for (unsigned i = 0; i < levelSet->getNumberOfSegments(); ++i) {
300 numberOfNormals += normalVectorsVector[i].size();
301 }
302 normalVectorsVector[0].reserve(numberOfNormals);
303
304 for (unsigned i = 1; i < levelSet->getNumberOfSegments(); ++i) {
305 normalVectorsVector[0].insert(normalVectorsVector[0].end(),
306 normalVectorsVector[i].begin(),
307 normalVectorsVector[i].end());
308 }
309
310 // insert into pointData of levelSet
311 auto &pointData = levelSet->getPointData();
312 auto vectorDataPointer = pointData.getVectorData(normalVectorsLabel, true);
313 // if it does not exist, insert new normals vector
314 if (vectorDataPointer == nullptr) {
315 pointData.insertNextVectorData(normalVectorsVector[0],
317 } else {
318 // if it does exist, just swap the old with the new values
319 *vectorDataPointer = std::move(normalVectorsVector[0]);
320 }
321 }
322};
323
324// add all template specialisations for this class
326
327} // namespace viennals
constexpr int D
Definition Epitaxy.cpp:11
double T
Definition Epitaxy.cpp:12
This algorithm is used to compute the normal vectors for all points with level set values <= maxValue...
Definition lsCalculateNormalVectors.hpp:40
static constexpr char normalVectorsLabel[]
Definition lsCalculateNormalVectors.hpp:52
SmartPointer< Domain< T, D > > getLevelSet() const
Definition lsCalculateNormalVectors.hpp:83
void setLevelSet(SmartPointer< Domain< T, D > > passedLevelSet)
Definition lsCalculateNormalVectors.hpp:63
void apply()
Definition lsCalculateNormalVectors.hpp:95
T getMaxValue() const
Definition lsCalculateNormalVectors.hpp:85
void setMethod(NormalCalculationMethodEnum passedMethod)
Definition lsCalculateNormalVectors.hpp:79
void setMaxValue(const T passedMaxValue)
Definition lsCalculateNormalVectors.hpp:67
bool hasNormalVectors() const
Check if normal vectors are already calculated for the level set.
Definition lsCalculateNormalVectors.hpp:88
CalculateNormalVectors(SmartPointer< Domain< T, D > > passedLevelSet, T passedMaxValue=DEFAULT_MAX_VALUE, NormalCalculationMethodEnum passedMethod=NormalCalculationMethodEnum::CENTRAL_DIFFERENCES)
Definition lsCalculateNormalVectors.hpp:56
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:28
viennahrle::Domain< T, D > DomainType
Definition lsDomain.hpp:33
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
#define PRECOMPILE_PRECISION_DIMENSION(className)
Definition lsPreCompileMacros.hpp:24
Definition lsAdvect.hpp:41
NormalCalculationMethodEnum
Definition lsCalculateNormalVectors.hpp:20
@ CENTRAL_DIFFERENCES
Definition lsCalculateNormalVectors.hpp:21
@ ONE_SIDED_MIN_MOD
Definition lsCalculateNormalVectors.hpp:22