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