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