ViennaLS
Loading...
Searching...
No Matches
lsCompareSparseField.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <hrleSparseIterator.hpp>
4#include <lsDomain.hpp>
5#include <lsExpand.hpp>
6#include <lsMesh.hpp>
8#include <lsReduce.hpp>
9
10#include <cmath>
11#include <limits>
12
13namespace viennals {
14
15using namespace viennacore;
16
33
34template <class T, int D = 2> class CompareSparseField {
35 using hrleIndexType = viennahrle::IndexType;
36
37 SmartPointer<Domain<T, D>> levelSetExpanded = nullptr;
38 SmartPointer<Domain<T, D>> levelSetIterated = nullptr;
39
40 // Variables for x and y range restrictions
41 T xRangeMin = std::numeric_limits<T>::lowest();
42 T xRangeMax = std::numeric_limits<T>::max();
43 T yRangeMin = std::numeric_limits<T>::lowest();
44 T yRangeMax = std::numeric_limits<T>::max();
45 bool useXRange = false;
46 bool useYRange = false;
47
48 // Fields to store the calculation results
49 T sumSquaredDifferences = 0.0;
50 T sumDifferences = 0.0;
51 unsigned numPoints = 0;
52 unsigned numSkippedPoints = 0;
53
54 // Add mesh output capability
55 SmartPointer<Mesh<T>> outputMesh = nullptr;
56
57 // Add bool for filling the iterated level set with distances
58 bool fillIteratedWithDistances = false;
59
60 bool checkAndCalculateBounds() {
61 if (levelSetExpanded == nullptr || levelSetIterated == nullptr) {
62 Logger::getInstance()
63 .addWarning("Missing level set in CompareSparseField.")
64 .print();
65 return false;
66 }
67
68 // Check if the grids are compatible
69 const auto &gridExpanded = levelSetExpanded->getGrid();
70 const auto &gridIterated = levelSetIterated->getGrid();
71
72 if (gridExpanded.getGridDelta() != gridIterated.getGridDelta()) {
73 Logger::getInstance()
74 .addWarning("Grid delta mismatch in CompareSparseField. The grid "
75 "deltas of the two level sets must be equal.")
76 .print();
77 return false;
78 }
79
80 // // Check if the x extents of both level sets are equal
81 // const auto &domainExpanded = levelSetExpanded->getDomain();
82 // const auto &domainIterated = levelSetIterated->getDomain();
83
84 // hrleIndexType expandedMinX = gridExpanded.isNegBoundaryInfinite(0)
85 // ? domainExpanded.getMinRunBreak(0)
86 // : gridExpanded.getMinIndex(0);
87 // hrleIndexType expandedMaxX = gridExpanded.isPosBoundaryInfinite(0)
88 // ? domainExpanded.getMaxRunBreak(0)
89 // : gridExpanded.getMaxIndex(0);
90 // hrleIndexType iteratedMinX = gridIterated.isNegBoundaryInfinite(0)
91 // ? domainIterated.getMinRunBreak(0)
92 // : gridIterated.getMinIndex(0);
93 // hrleIndexType iteratedMaxX = gridIterated.isPosBoundaryInfinite(0)
94 // ? domainIterated.getMaxRunBreak(0)
95 // : gridIterated.getMaxIndex(0);
96
97 // if (expandedMinX != iteratedMinX || expandedMaxX != iteratedMaxX) {
98 // Logger::getInstance()
99 // .addWarning("X extent mismatch in CompareSparseField. The x extents
100 // "
101 // "of both level sets must be equal.")
102 // .print();
103 // return false;
104 // }
105
106 // Check if expanded level set width is sufficient
107 if (levelSetExpanded->getLevelSetWidth() < 50) {
108 Logger::getInstance()
109 .addWarning(
110 "Expanded level set width is insufficient. It must have a width "
111 "of "
112 "at least 50. \n"
113 " CORRECTION: The expansion was performed. \n"
114 "ALTERNATIVE: Alternatively, please expand the expanded yourself "
115 "using lsExpand before passing it to this function. \n")
116 .print();
117 Expand<T, D>(levelSetExpanded, 50).apply();
118 }
119
120 // Reduce the iterated level set to a sparse field if necessary
121 if (levelSetIterated->getLevelSetWidth() > 1) {
122 Logger::getInstance()
123 .addWarning(
124 "Iterated level set width is too large. It must be reduced to a "
125 "sparse field. \n"
126 " CORRECTION: The reduction was performed. \n"
127 "ALTERNATIVE: Alternatively, please reduce the iterated yourself "
128 "using lsReduce before passing it to this function. \n")
129 .print();
130 Reduce<T, D>(levelSetIterated, 1).apply();
131 }
132
133 return true;
134 }
135
136public:
138 static_assert(
139 D == 2 &&
140 "CompareSparseField is currently only implemented for 2D level sets.");
141 }
142
143 CompareSparseField(SmartPointer<Domain<T, D>> passedLevelSetExpanded,
144 SmartPointer<Domain<T, D>> passedLevelSetIterated)
145 : levelSetExpanded(passedLevelSetExpanded),
146 levelSetIterated(passedLevelSetIterated) {
147 static_assert(
148 D == 2 &&
149 "CompareSparseField is currently only implemented for 2D level sets.");
150 }
151
152 void setLevelSetExpanded(SmartPointer<Domain<T, D>> passedLevelSet) {
153 levelSetExpanded = passedLevelSet;
154 }
155
156 void setLevelSetIterated(SmartPointer<Domain<T, D>> passedLevelSet) {
157 levelSetIterated = passedLevelSet;
158 }
159
161 void setXRange(T minXRange, T maxXRange) {
162 xRangeMin = minXRange;
163 xRangeMax = maxXRange;
164 useXRange = true;
165 }
166
168 void setYRange(T minYRange, T maxYRange) {
169 yRangeMin = minYRange;
170 yRangeMax = maxYRange;
171 useYRange = true;
172 }
173
175 void clearXRange() {
176 useXRange = false;
177 xRangeMin = std::numeric_limits<T>::lowest();
178 xRangeMax = std::numeric_limits<T>::max();
179 }
180
182 void clearYRange() {
183 useYRange = false;
184 yRangeMin = std::numeric_limits<T>::lowest();
185 yRangeMax = std::numeric_limits<T>::max();
186 }
187
189 void setOutputMesh(SmartPointer<Mesh<T>> passedMesh) {
190 outputMesh = passedMesh;
191 }
192
195 fillIteratedWithDistances = fill;
196 }
197
199 void apply() {
200 // Perform compatibility checks
201 if (!checkAndCalculateBounds()) {
202 // If checks fail, return NaN
203 sumSquaredDifferences = std::numeric_limits<T>::quiet_NaN();
204 sumDifferences = std::numeric_limits<T>::quiet_NaN();
205 numPoints = 0;
206 numSkippedPoints = 0;
207 return;
208 }
209
210 const auto &gridExpanded = levelSetExpanded->getGrid();
211 const auto gridDelta = gridExpanded.getGridDelta();
212
213 sumSquaredDifferences = 0.0;
214 sumDifferences = 0.0;
215 numPoints = 0;
216 numSkippedPoints = 0;
217
218 // Direct storage for points and differences
219 std::vector<Vec3D<T>> nodeCoordinates; // 3D necessary for lsMesh
220 std::vector<std::array<unsigned, 1>> vertexIndices;
221 std::vector<T> differenceValues;
222 std::vector<T> squaredDifferenceValues;
223
224 // Prepare mesh output if needed
225 const bool generateMesh = outputMesh != nullptr;
226 if (generateMesh) {
227 outputMesh->clear();
228
229 // Initialize mesh extent
230 for (unsigned i = 0; i < D; ++i) {
231 outputMesh->minimumExtent[i] = std::numeric_limits<T>::max();
232 outputMesh->maximumExtent[i] = std::numeric_limits<T>::lowest();
233 }
234
235 // Reserve space for mesh data
236 nodeCoordinates.reserve(levelSetIterated->getNumberOfPoints());
237 vertexIndices.reserve(levelSetIterated->getNumberOfPoints());
238 differenceValues.reserve(levelSetIterated->getNumberOfPoints());
239 squaredDifferenceValues.reserve(levelSetIterated->getNumberOfPoints());
240 }
241
242 // Prepare for point data filling if needed
243 auto &iteratedPointData = levelSetIterated->getPointData();
244 std::vector<T> pointDataDistances;
245 if (fillIteratedWithDistances) {
246 pointDataDistances.reserve(levelSetIterated->getNumberOfPoints());
247 }
248
249 // Create sparse iterators for the level sets
250 viennahrle::ConstSparseIterator<typename Domain<T, D>::DomainType>
251 itIterated(levelSetIterated->getDomain());
252 viennahrle::ConstSparseIterator<typename Domain<T, D>::DomainType>
253 itExpanded(levelSetExpanded->getDomain());
254
255 // Iterate over all defined points in the iterated level set
256 while (!itIterated.isFinished()) {
257 if (!itIterated.isDefined()) {
258 // this block is necessary to skip undefined points, I tested it:
259 // std::cout << "Skipping undefined point" << std::endl;
260 itIterated.next();
261 continue;
262 }
263
264 auto indices = itIterated.getStartIndices();
265
266 // Calculate coordinates
267 T xCoord = indices[0] * gridDelta;
268 T yCoord = indices[1] * gridDelta;
269 T zCoord = 0.0; // Always use 0 for z-coordinate in 2D
270
271 // Skip if outside the specified x-range
272 if (useXRange && (xCoord < xRangeMin || xCoord > xRangeMax)) {
273 itIterated.next();
274 continue;
275 }
276
277 // Skip if outside the specified y-range
278 if (useYRange && (yCoord < yRangeMin || yCoord > yRangeMax)) {
279 itIterated.next();
280 continue;
281 }
282
283 // Get iterated value
284 T valueIterated = itIterated.getValue();
285
286 itExpanded.goToIndicesSequential(indices);
287 T valueExpanded = itExpanded.getValue();
288
289 // Check for infinite or extreme values that might cause numerical
290 // issues
291 if (!itExpanded.isDefined() || std::isinf(valueExpanded) ||
292 std::isinf(valueIterated)) {
293 numSkippedPoints++;
294 itIterated.next();
295 continue;
296 }
297
298 // Calculate difference and add to sum
299 T diff = std::abs(valueExpanded - valueIterated) * gridDelta;
300 T diffSquared = diff * diff;
301 sumDifferences += diff;
302 sumSquaredDifferences += diffSquared;
303 numPoints++;
304
305 // Store difference in mesh if required
306 if (generateMesh) {
307 // Create a new point with the coordinates of the iterated level set
308 // point
309 Vec3D<T> coords{xCoord, yCoord, zCoord}; // lsMesh needs 3D
310
311 // Store the coordinates
312 nodeCoordinates.push_back(coords);
313
314 // Store the difference value (squared and absolute)
315 differenceValues.push_back(diff);
316 squaredDifferenceValues.push_back(diffSquared);
317
318 // Create a vertex for this point
319 std::array<unsigned, 1> vertex = {
320 static_cast<unsigned>(nodeCoordinates.size() - 1)};
321 vertexIndices.push_back(vertex);
322
323 // Update the mesh extent
324 for (unsigned i = 0; i < D; ++i) {
325 outputMesh->minimumExtent[i] =
326 std::min(outputMesh->minimumExtent[i], coords[i]);
327 outputMesh->maximumExtent[i] =
328 std::max(outputMesh->maximumExtent[i], coords[i]);
329 }
330 }
331
332 if (fillIteratedWithDistances) {
333 // Store the distance value in the point data
334 pointDataDistances.push_back(diff);
335 }
336 // Move to next point
337 itIterated.next();
338 }
339
340 // Finalize mesh output
341 if (generateMesh && !nodeCoordinates.empty()) {
342 // Store the node coordinates directly
343 outputMesh->nodes = std::move(nodeCoordinates);
344
345 // Store the vertices for point visualization
346 outputMesh->vertices = std::move(vertexIndices);
347
348 // Store the difference values as point data
349 outputMesh->pointData.insertNextScalarData(std::move(differenceValues),
350 "Absolute differences");
351 outputMesh->pointData.insertNextScalarData(
352 std::move(squaredDifferenceValues), "Squared differences");
353 }
354
355 if (fillIteratedWithDistances) {
356 iteratedPointData.insertNextScalarData(std::move(pointDataDistances),
357 "DistanceToExpanded");
358 }
359 }
360
362 T getSumSquaredDifferences() const { return sumSquaredDifferences; }
363
365 T getSumDifferences() const { return sumDifferences; }
366
368 unsigned getNumPoints() const { return numPoints; }
369
371 unsigned getNumSkippedPoints() const { return numSkippedPoints; }
372
374 T getRMSE() const {
375 return (numPoints > 0) ? std::sqrt(sumSquaredDifferences / numPoints)
376 : std::numeric_limits<T>::infinity();
377 }
378};
379
380// Add template specializations for this class
381PRECOMPILE_PRECISION_DIMENSION(CompareSparseField)
382
383} // namespace viennals
unsigned getNumPoints() const
Return the number of points used in the comparison.
Definition lsCompareSparseField.hpp:368
T getRMSE() const
Calculate the root mean square error from previously computed values.
Definition lsCompareSparseField.hpp:374
CompareSparseField()
Definition lsCompareSparseField.hpp:137
CompareSparseField(SmartPointer< Domain< T, D > > passedLevelSetExpanded, SmartPointer< Domain< T, D > > passedLevelSetIterated)
Definition lsCompareSparseField.hpp:143
void setXRange(T minXRange, T maxXRange)
Set the x-coordinate range to restrict the comparison area.
Definition lsCompareSparseField.hpp:161
void setOutputMesh(SmartPointer< Mesh< T > > passedMesh)
Set the output mesh where difference values will be stored.
Definition lsCompareSparseField.hpp:189
void setLevelSetIterated(SmartPointer< Domain< T, D > > passedLevelSet)
Definition lsCompareSparseField.hpp:156
unsigned getNumSkippedPoints() const
Return the number of skipped points during the comparison.
Definition lsCompareSparseField.hpp:371
void setYRange(T minYRange, T maxYRange)
Set the y-coordinate range to restrict the comparison area.
Definition lsCompareSparseField.hpp:168
void setLevelSetExpanded(SmartPointer< Domain< T, D > > passedLevelSet)
Definition lsCompareSparseField.hpp:152
void setFillIteratedWithDistances(bool fill)
Set whether to fill the iterated level set with distances.
Definition lsCompareSparseField.hpp:194
T getSumSquaredDifferences() const
Return the sum of squared differences calculated by apply().
Definition lsCompareSparseField.hpp:362
T getSumDifferences() const
Return the sum of differences calculated by apply().
Definition lsCompareSparseField.hpp:365
void clearYRange()
Clear the y-range restriction.
Definition lsCompareSparseField.hpp:182
void apply()
Apply the comparison and calculate the sum of squared differences.
Definition lsCompareSparseField.hpp:199
void clearXRange()
Clear the x-range restriction.
Definition lsCompareSparseField.hpp:175
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:21
Reduce the level set size to the specified width. This means all level set points with value <= 0....
Definition lsReduce.hpp:14
void apply()
Reduces the leveleSet to the specified number of layers. The largest value in the levelset is thus wi...
Definition lsReduce.hpp:55
#define PRECOMPILE_PRECISION_DIMENSION(className)
Definition lsPreCompileMacros.hpp:24
Definition lsAdvect.hpp:36
constexpr int D
Definition pyWrap.cpp:71
double T
Definition pyWrap.cpp:69