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