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 for
135 for (unsigned p = 0; p < levelSet->getNumberOfSegments(); ++p) {
136
137 auto &normalVectors = normalVectorsVector[p];
138 normalVectors.reserve(pointsPerSegment);
139
140 viennahrle::Index<D> const startVector =
141 (p == 0) ? grid.getMinGridPoint()
142 : levelSet->getDomain().getSegmentation()[p - 1];
143
144 viennahrle::Index<D> const endVector =
145 (p != static_cast<int>(levelSet->getNumberOfSegments() - 1))
146 ? levelSet->getDomain().getSegmentation()[p]
147 : grid.incrementIndices(grid.getMaxGridPoint());
148
149 for (viennahrle::ConstSparseStarIterator<
150 typename Domain<T, D>::DomainType, 1>
151 neighborIt(levelSet->getDomain(), startVector);
152 neighborIt.getIndices() < endVector; neighborIt.next()) {
153
154 auto &center = neighborIt.getCenter();
155 if (!center.isDefined()) {
156 continue;
157 } else if (std::abs(center.getValue()) > maxValue) {
158 // push an empty vector to keep ordering correct
159 Vec3D<T> tmp{};
160 normalVectors.push_back(tmp);
161 continue;
162 }
163
164 Vec3D<T> n{};
165
166 T denominator = 0;
167 for (int i = 0; i < D; i++) {
168 viennahrle::Index<D> posIdx(0);
169 posIdx[i] = 1;
170 viennahrle::Index<D> negIdx(0);
171 negIdx[i] = -1;
172 T pos = neighborIt.getNeighbor(posIdx).getValue() - center.getValue();
173 T neg = center.getValue() - neighborIt.getNeighbor(negIdx).getValue();
174 n[i] = (pos + neg) * FINITE_DIFF_FACTOR;
175 denominator += n[i] * n[i];
176 }
177
178 denominator = std::sqrt(denominator);
179 if (std::abs(denominator) < EPSILON) {
180 VIENNACORE_LOG_WARNING(
181 "CalculateNormalVectors: Vector of length 0 at " +
182 neighborIt.getIndices().to_string());
183 for (unsigned i = 0; i < D; ++i)
184 n[i] = 0.;
185 } else {
186 for (unsigned i = 0; i < D; ++i) {
187 n[i] /= denominator;
188 }
189 }
190
191 normalVectors.push_back(n);
192 }
193 }
194 insertIntoPointData(normalVectorsVector);
195 }
196
197 void calculateOneSidedMinMod() {
198 // This method does not require expansion, since it is robust to missing
199 // neighbors.
200 auto &domain = levelSet->getDomain();
201 auto &grid = levelSet->getGrid();
202
203 // Directly write to a single vector indexed by point ID.
204 // This is thread-safe since each thread works on a distinct range of
205 // points.
206 std::vector<Vec3D<T>> normalVectors(levelSet->getNumberOfPoints());
207
208#pragma omp parallel for
209 for (unsigned p = 0; p < levelSet->getNumberOfSegments(); ++p) {
210
211 viennahrle::Index<D> startVector =
212 (p == 0) ? grid.getMinGridPoint()
213 : levelSet->getDomain().getSegmentation()[p - 1];
214 viennahrle::Index<D> endVector =
215 (p != static_cast<int>(levelSet->getNumberOfSegments() - 1))
216 ? levelSet->getDomain().getSegmentation()[p]
217 : grid.incrementIndices(grid.getMaxGridPoint());
218
219 viennahrle::SparseStarIterator<typename Domain<T, D>::DomainType, 1>
220 neighborIt(domain, startVector);
221
222 for (; neighborIt.getIndices() < endVector; neighborIt.next()) {
223 if (neighborIt.getCenter().isDefined()) {
224 Vec3D<T> grad{};
225 for (int i = 0; i < D; ++i) {
226 viennahrle::Index<D> posIdx(0);
227 posIdx[i] = 1;
228 viennahrle::Index<D> negIdx(0);
229 negIdx[i] = -1;
230 bool negDefined = neighborIt.getNeighbor(negIdx).isDefined();
231 bool posDefined = neighborIt.getNeighbor(posIdx).isDefined();
232
233 if (negDefined && posDefined) {
234 T valNeg = neighborIt.getNeighbor(negIdx).getValue();
235 T valCenter = neighborIt.getCenter().getValue();
236 T valPos = neighborIt.getNeighbor(posIdx).getValue();
237
238 const bool centerSign = valCenter >= 0;
239 const bool negSign = valNeg > 0;
240 const bool posSign = valPos > 0;
241
242 const T d_neg = valCenter - valNeg;
243 const T d_pos = valPos - valCenter;
244
245 if (centerSign != negSign && centerSign != posSign) {
246 // Center is an extremum, use minmod to be safe
247 grad[i] = 0.;
248 // (std::abs(d_pos) < std::abs(d_neg)) ? d_pos : d_neg;
249 } else if (centerSign != negSign) {
250 // Interface is on the negative side, use backward difference
251 grad[i] = d_neg;
252 } else if (centerSign != posSign) {
253 // Interface is on the positive side, use forward difference
254 grad[i] = d_pos;
255 } else {
256 // No sign change, use minmod to handle sharp features smoothly
257 grad[i] = (std::abs(d_pos) < std::abs(d_neg)) ? d_pos : d_neg;
258 }
259 } else if (negDefined) {
260 grad[i] = (neighborIt.getCenter().getValue() -
261 neighborIt.getNeighbor(negIdx).getValue());
262 } else if (posDefined) {
263 grad[i] = (neighborIt.getNeighbor(posIdx).getValue() -
264 neighborIt.getCenter().getValue());
265 } else {
266 grad[i] = 0;
267 }
268 }
269 Normalize(grad);
270 normalVectors[neighborIt.getCenter().getPointId()] = grad;
271 }
272 }
273 }
274
275 // insert into pointData of levelSet
276 auto &pointData = levelSet->getPointData();
277 auto vectorDataPointer = pointData.getVectorData(normalVectorsLabel, true);
278 // if it does not exist, insert new normals vector
279 if (vectorDataPointer == nullptr) {
280 pointData.insertNextVectorData(normalVectors, normalVectorsLabel);
281 } else {
282 // if it does exist, just swap the old with the new values
283 *vectorDataPointer = std::move(normalVectors);
284 }
285 }
286
287 void
288 insertIntoPointData(std::vector<std::vector<Vec3D<T>>> &normalVectorsVector) {
289 // copy all normals
290 unsigned numberOfNormals = 0;
291 for (unsigned i = 0; i < levelSet->getNumberOfSegments(); ++i) {
292 numberOfNormals += normalVectorsVector[i].size();
293 }
294 normalVectorsVector[0].reserve(numberOfNormals);
295
296 for (unsigned i = 1; i < levelSet->getNumberOfSegments(); ++i) {
297 normalVectorsVector[0].insert(normalVectorsVector[0].end(),
298 normalVectorsVector[i].begin(),
299 normalVectorsVector[i].end());
300 }
301
302 // insert into pointData of levelSet
303 auto &pointData = levelSet->getPointData();
304 auto vectorDataPointer = pointData.getVectorData(normalVectorsLabel, true);
305 // if it does not exist, insert new normals vector
306 if (vectorDataPointer == nullptr) {
307 pointData.insertNextVectorData(normalVectorsVector[0],
309 } else {
310 // if it does exist, just swap the old with the new values
311 *vectorDataPointer = std::move(normalVectorsVector[0]);
312 }
313 }
314};
315
316// add all template specialisations for this class
318
319} // namespace viennals
constexpr int D
Definition Epitaxy.cpp:12
double T
Definition Epitaxy.cpp:13
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:27
viennahrle::Domain< T, D > DomainType
Definition lsDomain.hpp:32
Expand()=default
#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