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
23template <class T, int D = 2> class CompareSparseField {
24 using hrleIndexType = viennahrle::IndexType;
25
26 SmartPointer<Domain<T, D>> levelSetTarget = nullptr;
27 SmartPointer<Domain<T, D>> levelSetSample = nullptr;
28
29 // Variables for x and y range restrictions
30 T xRangeMin = std::numeric_limits<T>::lowest();
31 T xRangeMax = std::numeric_limits<T>::max();
32 T yRangeMin = std::numeric_limits<T>::lowest();
33 T yRangeMax = std::numeric_limits<T>::max();
34 bool useXRange = false;
35 bool useYRange = false;
36
37 // Fields to store the calculation results
38 T sumSquaredDifferences = 0.0;
39 T sumDifferences = 0.0;
40 unsigned numPoints = 0;
41
42 // Add mesh output capability
43 SmartPointer<Mesh<T>> outputMesh = nullptr;
44
45 bool checkAndCalculateBounds() {
46 if (levelSetTarget == nullptr || levelSetSample == nullptr) {
47 Logger::getInstance()
48 .addWarning("Missing level set in CompareSparseField.")
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 .addWarning("Grid delta mismatch in CompareSparseField. 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 "of both level sets must be equal.")
86 .print();
87 return false;
88 }
89
90 // Check if target level set width is sufficient
91 if (levelSetTarget->getLevelSetWidth() < 50) {
92 Logger::getInstance()
93 .addWarning(
94 "Target level set width is insufficient. It must have a width of "
95 "at least 50. \n"
96 " CORRECTION: The expansion was performed. \n"
97 "ALTERNATIVE: Alternatively, please expand the target yourself "
98 "using lsExpand before passing it to this function. \n")
99 .print();
100 Expand<T, D>(levelSetTarget, 50).apply();
101 }
102
103 // Reduce the sample level set to a sparse field if necessary
104 if (levelSetSample->getLevelSetWidth() > 1) {
105 Logger::getInstance()
106 .addWarning(
107 "Sample level set width is too large. It must be reduced to a "
108 "sparse field. \n"
109 " CORRECTION: The reduction was performed. \n"
110 "ALTERNATIVE: Alternatively, please reduce the sample yourself "
111 "using lsReduce before passing it to this function. \n")
112 .print();
113 Reduce<T, D>(levelSetSample, 1).apply();
114 }
115
116 return true;
117 }
118
119public:
121 static_assert(
122 D == 2 &&
123 "CompareSparseField is currently only implemented for 2D level sets.");
124 }
125
126 CompareSparseField(SmartPointer<Domain<T, D>> passedLevelSetTarget,
127 SmartPointer<Domain<T, D>> passedLevelSetSample)
128 : levelSetTarget(passedLevelSetTarget),
129 levelSetSample(passedLevelSetSample) {
130 static_assert(
131 D == 2 &&
132 "CompareSparseField is currently only implemented for 2D level sets.");
133 }
134
135 void setLevelSetTarget(SmartPointer<Domain<T, D>> passedLevelSet) {
136 levelSetTarget = passedLevelSet;
137 }
138
139 void setLevelSetSample(SmartPointer<Domain<T, D>> passedLevelSet) {
140 levelSetSample = passedLevelSet;
141 }
142
144 void setXRange(T minXRange, T maxXRange) {
145 xRangeMin = minXRange;
146 xRangeMax = maxXRange;
147 useXRange = true;
148 }
149
151 void setYRange(T minYRange, T maxYRange) {
152 yRangeMin = minYRange;
153 yRangeMax = maxYRange;
154 useYRange = true;
155 }
156
158 void clearXRange() {
159 useXRange = false;
160 xRangeMin = std::numeric_limits<T>::lowest();
161 xRangeMax = std::numeric_limits<T>::max();
162 }
163
165 void clearYRange() {
166 useYRange = false;
167 yRangeMin = std::numeric_limits<T>::lowest();
168 yRangeMax = std::numeric_limits<T>::max();
169 }
170
172 void setOutputMesh(SmartPointer<Mesh<T>> passedMesh) {
173 outputMesh = passedMesh;
174 }
175
177 void apply() {
178 // Perform compatibility checks
179 if (!checkAndCalculateBounds()) {
180 // If checks fail, return NaN
181 sumSquaredDifferences = std::numeric_limits<T>::quiet_NaN();
182 sumDifferences = std::numeric_limits<T>::quiet_NaN();
183 numPoints = 0;
184 return;
185 }
186
187 const auto &gridTarget = levelSetTarget->getGrid();
188 const auto gridDelta = gridTarget.getGridDelta();
189
190 sumSquaredDifferences = 0.0;
191 sumDifferences = 0.0;
192 numPoints = 0;
193
194 // Direct storage for points and differences
195 std::vector<Vec3D<T>> nodeCoordinates; // 3D necessary for lsMesh
196 std::vector<std::array<unsigned, 1>> vertexIndices;
197 std::vector<T> differenceValues;
198 std::vector<T> squaredDifferenceValues;
199
200 // Prepare mesh output if needed
201 const bool generateMesh = outputMesh != nullptr;
202 if (generateMesh) {
203 outputMesh->clear();
204
205 // Initialize mesh extent
206 for (unsigned i = 0; i < D; ++i) {
207 outputMesh->minimumExtent[i] = std::numeric_limits<T>::max();
208 outputMesh->maximumExtent[i] = std::numeric_limits<T>::lowest();
209 }
210
211 // Reserve space for mesh data
212 nodeCoordinates.reserve(levelSetSample->getNumberOfPoints());
213 vertexIndices.reserve(levelSetSample->getNumberOfPoints());
214 differenceValues.reserve(levelSetSample->getNumberOfPoints());
215 squaredDifferenceValues.reserve(levelSetSample->getNumberOfPoints());
216 }
217
218 // Create sparse iterators for the level sets
219 viennahrle::ConstSparseIterator<typename Domain<T, D>::DomainType> itSample(
220 levelSetSample->getDomain());
221 viennahrle::ConstSparseIterator<typename Domain<T, D>::DomainType> itTarget(
222 levelSetTarget->getDomain());
223
224 // Iterate over all defined points in the sample level set
225 while (!itSample.isFinished()) {
226 if (!itSample.isDefined()) {
227 // this block is necessary to skip undefined points, I tested it:
228 // std::cout << "Skipping undefined point" << std::endl;
229 itSample.next();
230 continue;
231 }
232
233 auto indices = itSample.getStartIndices();
234
235 // Calculate coordinates
236 T xCoord = indices[0] * gridDelta;
237 T yCoord = indices[1] * gridDelta;
238 T zCoord = 0.0; // Always use 0 for z-coordinate in 2D
239
240 // Skip if outside the specified x-range
241 if (useXRange && (xCoord < xRangeMin || xCoord > xRangeMax)) {
242 itSample.next();
243 continue;
244 }
245
246 // Skip if outside the specified y-range
247 if (useYRange && (yCoord < yRangeMin || yCoord > yRangeMax)) {
248 itSample.next();
249 continue;
250 }
251
252 // Get sample value
253 T valueSample = itSample.getValue();
254
255 itTarget.goToIndicesSequential(indices);
256 T valueTarget = itTarget.getValue();
257
258 // Check for infinite or extreme values that might cause numerical issues
259 if (!itTarget.isDefined() || std::isinf(valueTarget) ||
260 std::isinf(valueSample)) {
261 itSample.next();
262 continue;
263 }
264
265 // Calculate difference and add to sum
266 T diff = std::abs(valueTarget - valueSample);
267 T diffSquared = diff * diff;
268 sumDifferences += diff;
269 sumSquaredDifferences += diffSquared;
270 numPoints++;
271
272 // Store difference in mesh if required
273 if (generateMesh) {
274 // Create a new point with the coordinates of the sample level set point
275 Vec3D<T> coords{xCoord, yCoord, zCoord}; // lsMesh needs 3D
276
277 // Store the coordinates
278 nodeCoordinates.push_back(coords);
279
280 // Store the difference value (squared and absolute)
281 differenceValues.push_back(diff);
282 squaredDifferenceValues.push_back(diffSquared);
283
284 // Create a vertex for this point
285 std::array<unsigned, 1> vertex = {
286 static_cast<unsigned>(nodeCoordinates.size() - 1)};
287 vertexIndices.push_back(vertex);
288
289 // Update the mesh extent
290 for (unsigned i = 0; i < D; ++i) {
291 outputMesh->minimumExtent[i] =
292 std::min(outputMesh->minimumExtent[i], coords[i]);
293 outputMesh->maximumExtent[i] =
294 std::max(outputMesh->maximumExtent[i], coords[i]);
295 }
296 }
297
298 // Move to next point
299 itSample.next();
300 }
301
302 // Finalize mesh output
303 if (generateMesh && !nodeCoordinates.empty()) {
304 // Store the node coordinates directly
305 outputMesh->nodes = std::move(nodeCoordinates);
306
307 // Store the vertices for point visualization
308 outputMesh->vertices = std::move(vertexIndices);
309
310 // Store the difference values as point data
311 outputMesh->pointData.insertNextScalarData(std::move(differenceValues),
312 "Absolute differences");
313 outputMesh->pointData.insertNextScalarData(
314 std::move(squaredDifferenceValues), "Squared differences");
315 }
316 }
317
319 T getSumSquaredDifferences() const { return sumSquaredDifferences; }
320
322 T getSumDifferences() const { return sumDifferences; }
323
325 unsigned getNumPoints() const { return numPoints; }
326
328 T getRMSE() const {
329 return (numPoints > 0) ? std::sqrt(sumSquaredDifferences / numPoints)
330 : std::numeric_limits<T>::infinity();
331 }
332};
333
334// Add template specializations for this class
335PRECOMPILE_PRECISION_DIMENSION(CompareSparseField)
336
337} // namespace viennals
unsigned getNumPoints() const
Return the number of points used in the comparison.
Definition lsCompareSparseField.hpp:325
T getRMSE() const
Calculate the root mean square error from previously computed values.
Definition lsCompareSparseField.hpp:328
CompareSparseField()
Definition lsCompareSparseField.hpp:120
void setLevelSetSample(SmartPointer< Domain< T, D > > passedLevelSet)
Definition lsCompareSparseField.hpp:139
void setXRange(T minXRange, T maxXRange)
Set the x-coordinate range to restrict the comparison area.
Definition lsCompareSparseField.hpp:144
void setOutputMesh(SmartPointer< Mesh< T > > passedMesh)
Set the output mesh where difference values will be stored.
Definition lsCompareSparseField.hpp:172
void setYRange(T minYRange, T maxYRange)
Set the y-coordinate range to restrict the comparison area.
Definition lsCompareSparseField.hpp:151
CompareSparseField(SmartPointer< Domain< T, D > > passedLevelSetTarget, SmartPointer< Domain< T, D > > passedLevelSetSample)
Definition lsCompareSparseField.hpp:126
void setLevelSetTarget(SmartPointer< Domain< T, D > > passedLevelSet)
Definition lsCompareSparseField.hpp:135
T getSumSquaredDifferences() const
Return the sum of squared differences calculated by apply().
Definition lsCompareSparseField.hpp:319
T getSumDifferences() const
Return the sum of differences calculated by apply().
Definition lsCompareSparseField.hpp:322
void clearYRange()
Clear the y-range restriction.
Definition lsCompareSparseField.hpp:165
void apply()
Apply the comparison and calculate the sum of squared differences.
Definition lsCompareSparseField.hpp:177
void clearXRange()
Clear the x-range restriction.
Definition lsCompareSparseField.hpp:158
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:70
double T
Definition pyWrap.cpp:68