ViennaLS
Loading...
Searching...
No Matches
lsCompareVolume.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cmath>
4#include <limits>
5#include <unordered_map>
6
7#include <hrleDenseCellIterator.hpp>
8#include <lsDomain.hpp>
9#include <lsExpand.hpp>
10#include <lsMesh.hpp>
12
13namespace viennals {
14
15using namespace viennacore;
16
25template <class T, int D = 3> class CompareVolume {
26 using hrleDomainType = typename Domain<T, D>::DomainType;
27 using hrleIndexType = viennahrle::IndexType;
28
29 SmartPointer<Domain<T, D>> levelSetTarget = nullptr;
30 SmartPointer<Domain<T, D>> levelSetSample = nullptr;
31 viennahrle::Index<D> minIndex, maxIndex;
32
33 unsigned long int differentCellsCount = 0;
34 unsigned long int customDifferentCellCount = 0;
35
36 hrleIndexType xRangeMin = std::numeric_limits<hrleIndexType>::lowest();
37 hrleIndexType xRangeMax = std::numeric_limits<hrleIndexType>::max();
38 hrleIndexType yRangeMin = std::numeric_limits<hrleIndexType>::lowest();
39 hrleIndexType yRangeMax = std::numeric_limits<hrleIndexType>::max();
40 hrleIndexType zRangeMin = std::numeric_limits<hrleIndexType>::lowest();
41 hrleIndexType zRangeMax = std::numeric_limits<hrleIndexType>::max();
42 bool useCustomXIncrement = false;
43 bool useCustomYIncrement = false;
44 bool useCustomZIncrement = false;
45
46 unsigned short int customXIncrement = 0;
47 unsigned short int customYIncrement = 0;
48 unsigned short int customZIncrement = 0;
49 unsigned short int defaultIncrement = 1;
50
51 double gridDelta = 0.0;
52
53 // Mesh output related members
54 SmartPointer<Mesh<T>> outputMesh = nullptr;
55
56 // Helper to get the class name for logging
57 static const char *getClassName() {
58 if constexpr (D == 2)
59 return "CompareArea";
60 return "CompareVolume";
61 }
62
63 bool checkAndCalculateBounds() {
64 if (levelSetTarget == nullptr || levelSetSample == nullptr) {
65 Logger::getInstance()
66 .addError(std::string("Missing level set in ") + getClassName() + ".")
67 .print();
68 return false;
69 }
70
71 // Check if the grids are compatible
72 const auto &gridTarget = levelSetTarget->getGrid();
73 const auto &gridSample = levelSetSample->getGrid();
74
75 if (gridTarget.getGridDelta() != gridSample.getGridDelta()) {
76 Logger::getInstance()
77 .addError(std::string("Grid delta mismatch in ") + getClassName() +
78 ". The grid deltas of the two level sets must be equal.")
79 .print();
80 return false;
81 } else {
82 gridDelta = gridTarget.getGridDelta();
83 }
84
85 // Initialize min and max indices
86 for (unsigned i = 0; i < D; ++i) {
87 minIndex[i] = std::numeric_limits<hrleIndexType>::max();
88 maxIndex[i] = std::numeric_limits<hrleIndexType>::lowest();
89 }
90
91 // Get the boundaries of the two level sets
92 auto &domainTarget = levelSetTarget->getDomain();
93 auto &domainSample = levelSetSample->getDomain();
94
95 // Calculate actual bounds
96 for (unsigned i = 0; i < D; ++i) {
97 minIndex[i] = std::min({minIndex[i],
98 (gridTarget.isNegBoundaryInfinite(i))
99 ? domainTarget.getMinRunBreak(i)
100 : gridTarget.getMinBounds(i),
101 (gridSample.isNegBoundaryInfinite(i))
102 ? domainSample.getMinRunBreak(i)
103 : gridSample.getMinBounds(i)});
104
105 maxIndex[i] = std::max({maxIndex[i],
106 (gridTarget.isPosBoundaryInfinite(i))
107 ? domainTarget.getMaxRunBreak(i)
108 : gridTarget.getMaxBounds(i),
109 (gridSample.isPosBoundaryInfinite(i))
110 ? domainSample.getMaxRunBreak(i)
111 : gridSample.getMaxBounds(i)});
112 }
113
114 return true;
115 }
116
117public:
119
120 CompareVolume(SmartPointer<Domain<T, D>> passedLevelSetTarget,
121 SmartPointer<Domain<T, D>> passedLevelSetSample)
122 : levelSetTarget(passedLevelSetTarget),
123 levelSetSample(passedLevelSetSample) {}
124
126 void setLevelSetTarget(SmartPointer<Domain<T, D>> passedLevelSet) {
127 levelSetTarget = passedLevelSet;
128 }
129
131 void setLevelSetSample(SmartPointer<Domain<T, D>> passedLevelSet) {
132 levelSetSample = passedLevelSet;
133 }
134
136 void setDefaultIncrement(unsigned short int increment) {
137 defaultIncrement = increment;
138 }
139
141 void setXRangeAndIncrement(hrleIndexType minXRange, hrleIndexType maxXRange,
142 unsigned short int Xincrement) {
143 xRangeMin = minXRange;
144 xRangeMax = maxXRange;
145 customXIncrement = Xincrement;
146 useCustomXIncrement = true;
147 }
148
150 void setYRangeAndIncrement(hrleIndexType minYRange, hrleIndexType maxYRange,
151 unsigned short int Yincrement) {
152 yRangeMin = minYRange;
153 yRangeMax = maxYRange;
154 customYIncrement = Yincrement;
155 useCustomYIncrement = true;
156 }
157
159 void setZRangeAndIncrement(hrleIndexType minZRange, hrleIndexType maxZRange,
160 unsigned short int Zincrement) {
161 zRangeMin = minZRange;
162 zRangeMax = maxZRange;
163 customZIncrement = Zincrement;
164 useCustomZIncrement = true;
165 }
166
171 void setOutputMesh(SmartPointer<Mesh<T>> passedMesh) {
172 outputMesh = passedMesh;
173 }
174
176 double getVolumeMismatch() const {
177 return static_cast<double>(differentCellsCount) * std::pow(gridDelta, D);
178 }
179
181 double getAreaMismatch() const { return getVolumeMismatch(); }
182
184 double getCustomVolumeMismatch() const {
185 return static_cast<double>(customDifferentCellCount) *
186 std::pow(gridDelta, D);
187 }
188
191
193 unsigned long int getCellCount() const { return differentCellsCount; }
194
197 unsigned long int getCustomCellCount() const {
198 return customDifferentCellCount;
199 }
200
202 void apply() {
203 // Calculate the bounds for iteration
204 if (!checkAndCalculateBounds()) {
205 differentCellsCount =
206 std::numeric_limits<unsigned long int>::max(); // Error indicator
207 customDifferentCellCount =
208 std::numeric_limits<unsigned long int>::max(); // Error indicator
209 return;
210 }
211
212 // Ensure both level sets have sufficient width to avoid floating point
213 // arithmetic errors. A new working copy is created if expansion is needed,
214 // leaving the original level set unmodified.
215 constexpr int minimumWidth = 3;
216
217 // Use the original or expanded copies as needed
218 auto workingTarget = levelSetTarget;
219 auto workingSample = levelSetSample;
220
221 if (levelSetTarget->getLevelSetWidth() < minimumWidth) {
222 workingTarget = SmartPointer<Domain<T, D>>::New(levelSetTarget);
223 Expand<T, D>(workingTarget, minimumWidth).apply();
224 VIENNACORE_LOG_INFO(std::string(getClassName()) +
225 ": Expanded target level set to width " +
226 std::to_string(minimumWidth) +
227 " to avoid undefined values.");
228 }
229
230 if (levelSetSample->getLevelSetWidth() < minimumWidth) {
231 workingSample = SmartPointer<Domain<T, D>>::New(levelSetSample);
232 Expand<T, D>(workingSample, minimumWidth).apply();
233 VIENNACORE_LOG_INFO(std::string(getClassName()) +
234 ": Expanded sample level set to width " +
235 std::to_string(minimumWidth) +
236 " to avoid undefined values.");
237 }
238
239 // Set up dense cell iterators for both level sets
240 viennahrle::ConstDenseCellIterator<hrleDomainType> itTarget(
241 workingTarget->getDomain(), minIndex);
242 viennahrle::ConstDenseCellIterator<hrleDomainType> itSample(
243 workingSample->getDomain(), minIndex);
244
245 differentCellsCount = 0;
246 customDifferentCellCount = 0;
247
248 // Initialize mesh-related variables if generating a mesh
249 const bool generateMesh = outputMesh != nullptr;
250 if (generateMesh) {
251 // Save the extent of the resulting mesh
252 outputMesh->clear();
253 for (unsigned i = 0; i < 3; ++i) {
254 outputMesh->minimumExtent[i] =
255 (i < D) ? std::numeric_limits<T>::max() : 0.0;
256 outputMesh->maximumExtent[i] =
257 (i < D) ? std::numeric_limits<T>::lowest() : 0.0;
258 }
259 }
260
261 // Vector for cell differences and increment values to be inserted in the
262 // mesh
263 std::vector<T> cellDifference;
264 std::vector<T> incrementValues;
265 size_t currentPointId = 0;
266 std::unordered_map<viennahrle::Index<D>, size_t,
267 typename viennahrle::Index<D>::hash>
268 pointIdMapping;
269
270 // Iterate through the domain defined by the bounding box
271 for (; itTarget.getIndices() < maxIndex; itTarget.next()) {
272 itSample.goToIndicesSequential(itTarget.getIndices());
273
274 // Compare cell values at the current position
275 T centerValueTarget = 0.;
276 T centerValueSample = 0.;
277
278 // Calculate the average value of the corners for both level sets
279 for (int i = 0; i < (1 << D); ++i) {
280 centerValueTarget += itTarget.getCorner(i).getValue();
281 centerValueSample += itSample.getCorner(i).getValue();
282 }
283 centerValueTarget /= (1 << D);
284 centerValueSample /= (1 << D);
285
286 // Determine if cell is inside for each level set
287 bool insideTarget = (centerValueTarget <= 0.);
288 bool insideSample = (centerValueSample <= 0.);
289 bool isDifferent = insideTarget != insideSample;
290
291 // Calculate range checks once per cell
292 bool inXRange = useCustomXIncrement &&
293 (itTarget.getIndices()[0] * gridDelta >= xRangeMin &&
294 itTarget.getIndices()[0] * gridDelta <= xRangeMax);
295 bool inYRange = useCustomYIncrement &&
296 (itTarget.getIndices()[1] * gridDelta >= yRangeMin &&
297 itTarget.getIndices()[1] * gridDelta <= yRangeMax);
298 bool inZRange =
299 (D < 3) || (useCustomZIncrement &&
300 (itTarget.getIndices()[2] * gridDelta >= zRangeMin &&
301 itTarget.getIndices()[2] * gridDelta <= zRangeMax));
302
303 // Calculate increment to add based on ranges
304 unsigned short int incrementToAdd = defaultIncrement;
305
306 // If any custom range is active, start from 0 and add the custom
307 // increments
308 if (inXRange || inYRange || (D == 3 && inZRange)) {
309 incrementToAdd = 0;
310 if (inXRange)
311 incrementToAdd += customXIncrement;
312 if (inYRange)
313 incrementToAdd += customYIncrement;
314 if (D == 3 && inZRange)
315 incrementToAdd += customZIncrement;
316 }
317
318 // If cells differ, update the counters
319 if (isDifferent) {
320 // Always increment simple cell count by 1
321 differentCellsCount += 1;
322
323 // For custom cell count, apply the calculated increment
324 customDifferentCellCount += incrementToAdd;
325 }
326
327 // For mesh generation, process all cells where at least one level set is
328 // inside
329 if (generateMesh && (insideTarget || insideSample)) {
330 // Material ID: 0 for matching inside, 1 for mismatched
331 int difference = isDifferent ? 1 : 0;
332
333 std::array<unsigned, 1 << D> voxel;
334 bool addVoxel = true;
335 // TODO: I think this check whether Voxel is added or not can be removed
336
337 // Insert all points of voxel into pointList
338 for (unsigned i = 0; i < (1 << D); ++i) {
339 viennahrle::Index<D> index;
340 for (unsigned j = 0; j < D; ++j) {
341 index[j] =
342 itTarget.getIndices(j) + itTarget.getCorner(i).getOffset()[j];
343 if (index[j] > maxIndex[j]) {
344 addVoxel = false;
345 break;
346 }
347 }
348 if (addVoxel) {
349 auto pointIdValue = std::make_pair(index, currentPointId);
350 auto pointIdPair = pointIdMapping.insert(pointIdValue);
351 voxel[i] = pointIdPair.first->second;
352 if (pointIdPair.second) {
353 ++currentPointId;
354 }
355 } else {
356 break;
357 }
358 }
359
360 if (addVoxel) {
361 if constexpr (D == 3) {
362 std::array<unsigned, 8> hexa{voxel[0], voxel[1], voxel[3],
363 voxel[2], voxel[4], voxel[5],
364 voxel[7], voxel[6]};
365 outputMesh->hexas.push_back(hexa);
366 } else if constexpr (D == 2) {
367 std::array<unsigned, 4> quad{voxel[0], voxel[1], voxel[3],
368 voxel[2]};
369 outputMesh->tetras.push_back(quad);
370 }
371
372 cellDifference.push_back(difference);
373
374 // Store increment value (0 if not different)
375 incrementValues.push_back(isDifferent ? static_cast<T>(incrementToAdd)
376 : 0);
377 }
378 }
379 }
380
381 // Finalize mesh if generating one
382 if (generateMesh && !pointIdMapping.empty()) {
383 // Insert points into the mesh
384 outputMesh->nodes.resize(pointIdMapping.size());
385 for (auto it = pointIdMapping.begin(); it != pointIdMapping.end(); ++it) {
386 Vec3D<T> coords = {0.0, 0.0, 0.0};
387 for (unsigned i = 0; i < D; ++i) {
388 coords[i] = gridDelta * it->first[i];
389
390 if (coords[i] < outputMesh->minimumExtent[i]) {
391 outputMesh->minimumExtent[i] = coords[i];
392 } else if (coords[i] > outputMesh->maximumExtent[i]) {
393 outputMesh->maximumExtent[i] = coords[i];
394 }
395 }
396 outputMesh->nodes[it->second] = coords;
397 }
398
399 // Add cell data to the mesh
400 assert(cellDifference.size() == incrementValues.size());
401 assert(incrementValues.size() ==
402 outputMesh->template getElements<1 << D>().size());
403 outputMesh->cellData.insertNextScalarData(cellDifference, "Difference");
404 outputMesh->cellData.insertNextScalarData(incrementValues,
405 "CustomIncrement");
406 }
407 }
408};
409
410// Aliases for backward compatibility and clarity
411template <class T> using CompareArea = CompareVolume<T, 2>;
412
413// Precompile for common precision and dimensions
415
416} // namespace viennals
constexpr int D
Definition Epitaxy.cpp:11
double T
Definition Epitaxy.cpp:12
Computes an estimate of the volume/area where two level sets differ. The volume is calculated by iter...
Definition lsCompareVolume.hpp:25
void setOutputMesh(SmartPointer< Mesh< T > > passedMesh)
Set the output mesh where difference areas will be stored for visualization. Each cell in the mesh wi...
Definition lsCompareVolume.hpp:171
unsigned long int getCellCount() const
Returns the number of cells where the level sets differ.
Definition lsCompareVolume.hpp:193
void setXRangeAndIncrement(hrleIndexType minXRange, hrleIndexType maxXRange, unsigned short int Xincrement)
Sets the x-range and custom increment value.
Definition lsCompareVolume.hpp:141
void setLevelSetTarget(SmartPointer< Domain< T, D > > passedLevelSet)
Sets the target level set.
Definition lsCompareVolume.hpp:126
void apply()
Computes the volume/area difference between the two level sets.
Definition lsCompareVolume.hpp:202
CompareVolume(SmartPointer< Domain< T, D > > passedLevelSetTarget, SmartPointer< Domain< T, D > > passedLevelSetSample)
Definition lsCompareVolume.hpp:120
double getAreaMismatch() const
Alias for getVolumeMismatch for 2D compatibility.
Definition lsCompareVolume.hpp:181
CompareVolume()
Definition lsCompareVolume.hpp:118
void setYRangeAndIncrement(hrleIndexType minYRange, hrleIndexType maxYRange, unsigned short int Yincrement)
Sets the y-range and custom increment value.
Definition lsCompareVolume.hpp:150
void setZRangeAndIncrement(hrleIndexType minZRange, hrleIndexType maxZRange, unsigned short int Zincrement)
Sets the z-range and custom increment value.
Definition lsCompareVolume.hpp:159
void setDefaultIncrement(unsigned short int increment)
Set default increment value.
Definition lsCompareVolume.hpp:136
double getCustomVolumeMismatch() const
Returns the computed volume/area mismatch, with custom increments applied.
Definition lsCompareVolume.hpp:184
unsigned long int getCustomCellCount() const
Returns the number of cells where the level sets differ, with custom increments applied.
Definition lsCompareVolume.hpp:197
double getVolumeMismatch() const
Returns the computed volume/area mismatch.
Definition lsCompareVolume.hpp:176
void setLevelSetSample(SmartPointer< Domain< T, D > > passedLevelSet)
Sets the sample level set.
Definition lsCompareVolume.hpp:131
double getCustomAreaMismatch() const
Alias for getCustomVolumeMismatch for 2D compatibility.
Definition lsCompareVolume.hpp:190
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:28
viennahrle::Domain< T, D > DomainType
Definition lsDomain.hpp:33
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:41
CompareVolume< T, 2 > CompareArea
Definition lsCompareVolume.hpp:411