ViennaLS
Loading...
Searching...
No Matches
lsCompareNarrowBand.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <hrleDenseCellIterator.hpp>
4#include <lsDomain.hpp>
5#include <lsExpand.hpp>
6#include <lsMesh.hpp>
8
9#include <unordered_map>
10
11namespace viennals {
12
13using namespace viennacore;
14
19template <class T, int D = 2> class CompareNarrowBand {
20 using hrleIndexType = viennahrle::IndexType;
21 SmartPointer<Domain<T, D>> levelSetTarget = nullptr;
22 SmartPointer<Domain<T, D>> levelSetSample = nullptr;
23 viennahrle::Index<D> minIndex, maxIndex;
24
25 // Variables for x and y range restrictions
26 T xRangeMin = std::numeric_limits<T>::lowest();
27 T xRangeMax = std::numeric_limits<T>::max();
28 T yRangeMin = std::numeric_limits<T>::lowest();
29 T yRangeMax = std::numeric_limits<T>::max();
30 T zRangeMin = std::numeric_limits<T>::lowest();
31 T zRangeMax = std::numeric_limits<T>::max();
32 bool useXRange = false;
33 bool useYRange = false;
34 bool useZRange = false;
35
36 // Fields to store the calculation results
37 T sumSquaredDifferences = 0.0;
38 T sumDifferences = 0.0;
39 unsigned numPoints = 0;
40
41 // Add mesh output capability
42 SmartPointer<Mesh<T>> outputMesh = nullptr;
43 bool outputMeshSquaredDifferences = true;
44
45 bool checkAndCalculateBounds() {
46 if (levelSetTarget == nullptr || levelSetSample == nullptr) {
47 Logger::getInstance()
48 .addError("Missing level set in CompareNarrowBand.")
49 .print();
50 return false;
51 }
52
53 // Check if the grids are compatible
54 const auto &gridTarget = levelSetTarget->getGrid();
55 const auto &gridSample = levelSetSample->getGrid();
56
57 if (gridTarget.getGridDelta() != gridSample.getGridDelta()) {
58 Logger::getInstance()
59 .addError("Grid delta mismatch in CompareNarrowBand. The grid "
60 "deltas of the two level sets must be equal.")
61 .print();
62 return false;
63 }
64
65 // Check if the x extents of both level sets are equal
66 const auto &domainTarget = levelSetTarget->getDomain();
67 const auto &domainSample = levelSetSample->getDomain();
68
69 // hrleIndexType targetMinX = gridTarget.isNegBoundaryInfinite(0)
70 // ? domainTarget.getMinRunBreak(0)
71 // : gridTarget.getMinIndex(0);
72 // hrleIndexType targetMaxX = gridTarget.isPosBoundaryInfinite(0)
73 // ? domainTarget.getMaxRunBreak(0)
74 // : gridTarget.getMaxIndex(0);
75 // hrleIndexType sampleMinX = gridSample.isNegBoundaryInfinite(0)
76 // ? domainSample.getMinRunBreak(0)
77 // : gridSample.getMinIndex(0);
78 // hrleIndexType sampleMaxX = gridSample.isPosBoundaryInfinite(0)
79 // ? domainSample.getMaxRunBreak(0)
80 // : gridSample.getMaxIndex(0);
81
82 // if (targetMinX != sampleMinX || targetMaxX != sampleMaxX) {
83 // Logger::getInstance()
84 // .addWarning("X extent mismatch in CompareNarrowBand. The x extents
85 // "
86 // "of both level sets must be equal.")
87 // .print();
88 // return false;
89 // }
90
91 // Expand the sample level set using lsExpand to a default width of 5
92 if (levelSetSample->getLevelSetWidth() < 5) {
93 VIENNACORE_LOG_WARNING(
94 "Sample level set width is insufficient. Expanding it to "
95 "a width of 5.");
96 Expand<T, D>(levelSetSample, 5).apply();
97 }
98
99 // Check if target level set width is sufficient
100 if (levelSetTarget->getLevelSetWidth() <
101 levelSetSample->getLevelSetWidth() + 50) {
102 VIENNACORE_LOG_WARNING(
103 "Target level set width is insufficient. It must exceed sample "
104 "width by least 50. \n"
105 " CORRECTION: The expansion was performed. \n"
106 "ALTERNATIVE: Alternatively, please expand the target yourself "
107 "using lsExpand before passing it to this function. \n");
108 Expand<T, D>(levelSetTarget, levelSetSample->getLevelSetWidth() + 50)
109 .apply();
110 }
111
112 // Initialize min and max indices
113 for (unsigned i = 0; i < D; ++i) {
114 minIndex[i] = std::numeric_limits<hrleIndexType>::max();
115 maxIndex[i] = std::numeric_limits<hrleIndexType>::lowest();
116 }
117
118 // Calculate actual bounds
119 for (unsigned i = 0; i < D; ++i) {
120 minIndex[i] = std::min({minIndex[i],
121 (gridTarget.isNegBoundaryInfinite(i))
122 ? domainTarget.getMinRunBreak(i)
123 : gridTarget.getMinIndex(i),
124 (gridSample.isNegBoundaryInfinite(i))
125 ? domainSample.getMinRunBreak(i)
126 : gridSample.getMinIndex(i)});
127
128 maxIndex[i] = std::max({maxIndex[i],
129 (gridTarget.isPosBoundaryInfinite(i))
130 ? domainTarget.getMaxRunBreak(i)
131 : gridTarget.getMaxIndex(i),
132 (gridSample.isPosBoundaryInfinite(i))
133 ? domainSample.getMaxRunBreak(i)
134 : gridSample.getMaxIndex(i)});
135 }
136
137 return true;
138 }
139
140public:
142
143 CompareNarrowBand(SmartPointer<Domain<T, D>> passedLevelSetTarget,
144 SmartPointer<Domain<T, D>> passedlevelSetSample)
145 : levelSetTarget(passedLevelSetTarget),
146 levelSetSample(passedlevelSetSample) {}
147
148 void setLevelSetTarget(SmartPointer<Domain<T, D>> passedLevelSet) {
149 levelSetTarget = passedLevelSet;
150 }
151
152 void setLevelSetSample(SmartPointer<Domain<T, D>> passedLevelSet) {
153 levelSetSample = passedLevelSet;
154 }
155
157 void setXRange(T minXRange, T maxXRange) {
158 xRangeMin = minXRange;
159 xRangeMax = maxXRange;
160 useXRange = true;
161 }
162
164 void setYRange(T minYRange, T maxYRange) {
165 yRangeMin = minYRange;
166 yRangeMax = maxYRange;
167 useYRange = true;
168 }
169
171 void clearXRange() {
172 useXRange = false;
173 xRangeMin = std::numeric_limits<T>::lowest();
174 xRangeMax = std::numeric_limits<T>::max();
175 }
176
178 void clearYRange() {
179 useYRange = false;
180 yRangeMin = std::numeric_limits<T>::lowest();
181 yRangeMax = std::numeric_limits<T>::max();
182 }
183
185 void setZRange(T minZRange, T maxZRange) {
186 zRangeMin = minZRange;
187 zRangeMax = maxZRange;
188 useZRange = true;
189 }
190
192 void clearZRange() {
193 useZRange = false;
194 zRangeMin = std::numeric_limits<T>::lowest();
195 zRangeMax = std::numeric_limits<T>::max();
196 }
197
199 void setOutputMesh(SmartPointer<Mesh<T>> passedMesh,
200 bool outputMeshSquaredDiffs = true) {
201 outputMesh = passedMesh;
202 outputMeshSquaredDifferences = outputMeshSquaredDiffs;
203 }
204
208 outputMeshSquaredDifferences = value;
209 }
210
212 void apply() {
213 // Perform compatibility checks and calculate bounds
214 if (!checkAndCalculateBounds()) {
215 // If checks fail, return NaN
216 sumSquaredDifferences = std::numeric_limits<T>::quiet_NaN();
217 numPoints = 0;
218 return;
219 }
220
221 const auto &gridTarget = levelSetTarget->getGrid();
222 double gridDelta = gridTarget.getGridDelta();
223
224 // Set up iterators for both level sets
225 viennahrle::ConstDenseCellIterator<typename Domain<T, D>::DomainType>
226 itSample(levelSetSample->getDomain(), minIndex);
227 viennahrle::ConstDenseCellIterator<typename Domain<T, D>::DomainType>
228 itTarget(levelSetTarget->getDomain(), minIndex);
229
230 sumSquaredDifferences = 0.0;
231 numPoints = 0;
232
233 // Prepare mesh output if needed
234 std::unordered_map<viennahrle::Index<D>, size_t,
235 typename viennahrle::Index<D>::hash>
236 pointIdMapping;
237 std::vector<T> differenceValues;
238 size_t currentPointId = 0;
239
240 const bool generateMesh = outputMesh != nullptr;
241 if (generateMesh) {
242 outputMesh->clear();
243
244 // Initialize mesh extent
245 for (unsigned i = 0; i < 3; ++i) {
246 outputMesh->minimumExtent[i] =
247 (i < D) ? std::numeric_limits<T>::max() : 0.0;
248 outputMesh->maximumExtent[i] =
249 (i < D) ? std::numeric_limits<T>::lowest() : 0.0;
250 }
251 }
252
253 // Iterate through the domain defined by the bounding box
254 for (; itSample.getIndices() < maxIndex; itSample.next()) {
255 // Check if current point is within specified x and y ranges
256 T xCoord = itSample.getIndices()[0] * gridDelta;
257 T yCoord = (D > 1) ? itSample.getIndices()[1] * gridDelta : 0;
258 T zCoord = (D > 2) ? itSample.getIndices()[2] * gridDelta : 0;
259
260 // Skip if outside the specified x-range
261 if (useXRange && (xCoord < xRangeMin || xCoord > xRangeMax)) {
262 continue;
263 }
264
265 // Skip if outside the specified y-range (only check in 2D and 3D)
266 if (D > 1 && useYRange && (yCoord < yRangeMin || yCoord > yRangeMax)) {
267 continue;
268 }
269
270 // Skip if outside the specified z-range (only check in 3D)
271 if (D > 2 && useZRange && (zCoord < zRangeMin || zCoord > zRangeMax)) {
272 continue;
273 }
274
275 // Move the second iterator to the same position
276 itTarget.goToIndicesSequential(itSample.getIndices());
277
278 // Get values at current position
279 T valueTarget = 0.0;
280 T valueSample = 0.0;
281
282 // Calculate average value at cell center
283 for (int i = 0; i < (1 << D); ++i) {
284 valueSample += itSample.getCorner(i).getValue();
285 valueTarget += itTarget.getCorner(i).getValue();
286 }
287 valueTarget /= (1 << D);
288 valueSample /= (1 << D);
289
290 // Check for infinite or extreme values that might cause numerical issues
291 if (std::isinf(valueTarget) || std::isinf(valueSample) ||
292 std::abs(valueTarget) > 1000 || std::abs(valueSample) > 1000) {
293 continue;
294 }
295
296 // Calculate difference and add to sum
297 T diff = std::abs(valueTarget - valueSample) * gridDelta;
298 T diffSquared = diff * diff;
299 sumSquaredDifferences += diffSquared;
300 sumDifferences += diff;
301 numPoints++;
302
303 // Store difference in mesh if required
304 if (generateMesh) {
305 std::array<unsigned, 1 << D> voxel;
306 bool addVoxel = true;
307 // TODO: possibly remove this addVoxel check
308 // Insert all points of voxel into pointList
309 for (unsigned i = 0; i < (1 << D); ++i) {
310 viennahrle::Index<D> index;
311 for (unsigned j = 0; j < D; ++j) {
312 index[j] =
313 itSample.getIndices(j) + itSample.getCorner(i).getOffset()[j];
314 if (index[j] > maxIndex[j]) {
315 addVoxel = false;
316 break;
317 }
318 }
319 if (addVoxel) {
320 auto pointIdValue = std::make_pair(index, currentPointId);
321 auto pointIdPair = pointIdMapping.insert(pointIdValue);
322 voxel[i] = pointIdPair.first->second;
323 if (pointIdPair.second) {
324 ++currentPointId;
325 }
326 } else {
327 break;
328 }
329 }
330
331 if (addVoxel) {
332 if constexpr (D == 3) {
333 std::array<unsigned, 8> hexa{voxel[0], voxel[1], voxel[3],
334 voxel[2], voxel[4], voxel[5],
335 voxel[7], voxel[6]};
336 outputMesh->hexas.push_back(hexa);
337 } else if constexpr (D == 2) {
338 std::array<unsigned, 4> quad{voxel[0], voxel[1], voxel[3],
339 voxel[2]};
340 outputMesh->tetras.push_back(quad);
341 }
342
343 // Add difference value to cell data depending on whether squared
344 // differences are requested
345 differenceValues.push_back(outputMeshSquaredDifferences ? diffSquared
346 : diff);
347 }
348 }
349 }
350
351 // Finalize mesh output
352 if (generateMesh && !pointIdMapping.empty()) {
353 outputMesh->nodes.resize(pointIdMapping.size());
354 for (auto it = pointIdMapping.begin(); it != pointIdMapping.end(); ++it) {
355 std::array<T, 3> coords{};
356 for (unsigned i = 0; i < D; ++i) {
357 coords[i] = gridDelta * it->first[i];
358
359 if (coords[i] < outputMesh->minimumExtent[i]) {
360 outputMesh->minimumExtent[i] = coords[i];
361 } else if (coords[i] > outputMesh->maximumExtent[i]) {
362 outputMesh->maximumExtent[i] = coords[i];
363 }
364 }
365 outputMesh->nodes[it->second] = coords;
366 }
367
368 assert(differenceValues.size() ==
369 outputMesh->template getElements<1 << D>().size());
370 outputMesh->cellData.insertNextScalarData(std::move(differenceValues),
371 outputMeshSquaredDifferences
372 ? "Squared differences"
373 : "Absolute differences");
374 }
375 }
376
378 T getSumSquaredDifferences() const { return sumSquaredDifferences; }
379
380 // Return the sum of differences calculated by apply().
381 T getSumDifferences() const { return sumDifferences; }
382
384 unsigned getNumPoints() const { return numPoints; }
385
387 T getRMSE() const {
388 return (numPoints > 0) ? std::sqrt(sumSquaredDifferences / numPoints)
389 : std::numeric_limits<T>::infinity();
390 }
391};
392
393// Add template specializations for this class
394PRECOMPILE_PRECISION_DIMENSION(CompareNarrowBand)
395
396} // namespace viennals
constexpr int D
Definition Epitaxy.cpp:11
double T
Definition Epitaxy.cpp:12
void setYRange(T minYRange, T maxYRange)
Set the y-coordinate range to restrict the comparison area.
Definition lsCompareNarrowBand.hpp:164
void setOutputMeshSquaredDifferences(bool value)
Set whether to output squared differences (true) or absolute differences (false).
Definition lsCompareNarrowBand.hpp:207
void setXRange(T minXRange, T maxXRange)
Set the x-coordinate range to restrict the comparison area.
Definition lsCompareNarrowBand.hpp:157
void setOutputMesh(SmartPointer< Mesh< T > > passedMesh, bool outputMeshSquaredDiffs=true)
Set the output mesh where difference values will be stored.
Definition lsCompareNarrowBand.hpp:199
void clearZRange()
Clear the z-range restriction.
Definition lsCompareNarrowBand.hpp:192
void setLevelSetTarget(SmartPointer< Domain< T, D > > passedLevelSet)
Definition lsCompareNarrowBand.hpp:148
T getRMSE() const
Calculate the root mean square error from previously computed values.
Definition lsCompareNarrowBand.hpp:387
void setLevelSetSample(SmartPointer< Domain< T, D > > passedLevelSet)
Definition lsCompareNarrowBand.hpp:152
unsigned getNumPoints() const
Return the number of points used in the comparison.
Definition lsCompareNarrowBand.hpp:384
void clearYRange()
Clear the y-range restriction.
Definition lsCompareNarrowBand.hpp:178
T getSumSquaredDifferences() const
Return the sum of squared differences calculated by apply().
Definition lsCompareNarrowBand.hpp:378
CompareNarrowBand(SmartPointer< Domain< T, D > > passedLevelSetTarget, SmartPointer< Domain< T, D > > passedlevelSetSample)
Definition lsCompareNarrowBand.hpp:143
void apply()
Apply the comparison and calculate the sum of squared differences.
Definition lsCompareNarrowBand.hpp:212
void clearXRange()
Clear the x-range restriction.
Definition lsCompareNarrowBand.hpp:171
T getSumDifferences() const
Definition lsCompareNarrowBand.hpp:381
void setZRange(T minZRange, T maxZRange)
Set the z-coordinate range to restrict the comparison area.
Definition lsCompareNarrowBand.hpp:185
CompareNarrowBand()
Definition lsCompareNarrowBand.hpp:141
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:28
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
This class holds an explicit mesh, which is always given in 3 dimensions. If it describes a 2D mesh,...
Definition lsMesh.hpp:22
#define PRECOMPILE_PRECISION_DIMENSION(className)
Definition lsPreCompileMacros.hpp:24
Definition lsAdvect.hpp:41