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