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 VIENNACORE_LOG_INFO("CompareArea: Expanded target level set to width " +
204 std::to_string(minimumWidth) +
205 " to avoid undefined values.");
206 }
207
208 if (levelSetSample->getLevelSetWidth() < minimumWidth) {
209 workingSample = SmartPointer<Domain<T, D>>::New(levelSetSample);
210 Expand<T, D>(workingSample, minimumWidth).apply();
211 VIENNACORE_LOG_INFO("CompareArea: Expanded sample level set to width " +
212 std::to_string(minimumWidth) +
213 " to avoid undefined values.");
214 }
215
216 // Set up dense cell iterators for both level sets
217 viennahrle::ConstDenseCellIterator<hrleDomainType> itTarget(
218 workingTarget->getDomain(), minIndex);
219 viennahrle::ConstDenseCellIterator<hrleDomainType> itSample(
220 workingSample->getDomain(), minIndex);
221
222 differentCellsCount = 0;
223 customDifferentCellCount = 0;
224
225 // Initialize mesh-related variables if generating a mesh
226 const bool generateMesh = outputMesh != nullptr;
227 if (generateMesh) {
228 // Save the extent of the resulting mesh
229 outputMesh->clear();
230 for (unsigned i = 0; i < D; ++i) {
231 outputMesh->minimumExtent[i] = std::numeric_limits<T>::max();
232 outputMesh->maximumExtent[i] = std::numeric_limits<T>::lowest();
233 }
234 }
235
236 // Vector for cell differences and increment values to be inserted in the
237 // mesh
238 std::vector<T> cellDifference;
239 std::vector<T> incrementValues;
240 size_t currentPointId = 0;
241 std::unordered_map<viennahrle::Index<D>, size_t,
242 typename viennahrle::Index<D>::hash>
243 pointIdMapping;
244
245 // Iterate through the domain defined by the bounding box
246 for (; itTarget.getIndices() < maxIndex; itTarget.next()) {
247 itSample.goToIndicesSequential(itTarget.getIndices());
248
249 // Compare cell values at the current position
250 T centerValueTarget = 0.;
251 T centerValueSample = 0.;
252
253 // Calculate the average value of the corners for both level sets
254 for (int i = 0; i < (1 << D); ++i) {
255 centerValueTarget += itTarget.getCorner(i).getValue();
256 centerValueSample += itSample.getCorner(i).getValue();
257 }
258 centerValueTarget /= (1 << D);
259 centerValueSample /= (1 << D);
260
261 // Determine if cell is inside for each level set
262 bool insideTarget = (centerValueTarget <= 0.);
263 bool insideSample = (centerValueSample <= 0.);
264 bool isDifferent = insideTarget != insideSample;
265
266 // Calculate range checks once per cell
267 bool inXRange = useCustomXIncrement &&
268 (itTarget.getIndices()[0] * gridDelta >= xRangeMin &&
269 itTarget.getIndices()[0] * gridDelta <= xRangeMax);
270 bool inYRange = useCustomYIncrement &&
271 (itTarget.getIndices()[1] * gridDelta >= yRangeMin &&
272 itTarget.getIndices()[1] * gridDelta <= yRangeMax);
273
274 // Calculate increment to add based on ranges
275 unsigned short int incrementToAdd = defaultIncrement;
276 if (inXRange && inYRange) {
277 // Apply both increments
278 incrementToAdd = customXIncrement + customYIncrement;
279 } else if (inXRange) {
280 incrementToAdd = customXIncrement;
281 } else if (inYRange) {
282 incrementToAdd = customYIncrement;
283 }
284
285 // If cells differ, update the counters
286 if (isDifferent) {
287 // Always increment simple cell count by 1
288 differentCellsCount += 1;
289
290 // For custom cell count, apply the calculated increment
291 customDifferentCellCount += incrementToAdd;
292 }
293
294 // For mesh generation, process all cells where at least one level set is
295 // inside
296 if (generateMesh && (insideTarget || insideSample)) {
297 // Material ID: 0 for matching inside, 1 for mismatched
298 int difference = isDifferent ? 1 : 0;
299
300 std::array<unsigned, 1 << D> voxel;
301 bool addVoxel = true;
302 // TODO: I think this check whether Voxel is added or not can be removed
303
304 // Insert all points of voxel into pointList
305 for (unsigned i = 0; i < (1 << D); ++i) {
306 viennahrle::Index<D> index;
307 for (unsigned j = 0; j < D; ++j) {
308 index[j] =
309 itTarget.getIndices(j) + itTarget.getCorner(i).getOffset()[j];
310 if (index[j] > maxIndex[j]) {
311 addVoxel = false;
312 break;
313 }
314 }
315 if (addVoxel) {
316 auto pointIdValue = std::make_pair(index, currentPointId);
317 auto pointIdPair = pointIdMapping.insert(pointIdValue);
318 voxel[i] = pointIdPair.first->second;
319 if (pointIdPair.second) {
320 ++currentPointId;
321 }
322 } else {
323 break;
324 }
325 }
326
327 if (addVoxel) {
328 if constexpr (D == 3) {
329 std::array<unsigned, 8> hexa{voxel[0], voxel[1], voxel[3],
330 voxel[2], voxel[4], voxel[5],
331 voxel[7], voxel[6]};
332 outputMesh->hexas.push_back(hexa);
333 } else if constexpr (D == 2) {
334 std::array<unsigned, 4> quad{voxel[0], voxel[1], voxel[3],
335 voxel[2]};
336 outputMesh->tetras.push_back(quad);
337 }
338
339 cellDifference.push_back(difference);
340
341 // Store increment value (0 if not different)
342 incrementValues.push_back(isDifferent ? static_cast<T>(incrementToAdd)
343 : 0);
344 }
345 }
346 }
347
348 // Finalize mesh if generating one
349 if (generateMesh && !pointIdMapping.empty()) {
350 // Insert points into the mesh
351 outputMesh->nodes.resize(pointIdMapping.size());
352 for (auto it = pointIdMapping.begin(); it != pointIdMapping.end(); ++it) {
353 Vec3D<T> coords;
354 for (unsigned i = 0; i < D; ++i) {
355 coords[i] = gridDelta * it->first[i];
356
357 if (coords[i] < outputMesh->minimumExtent[i]) {
358 outputMesh->minimumExtent[i] = coords[i];
359 } else if (coords[i] > outputMesh->maximumExtent[i]) {
360 outputMesh->maximumExtent[i] = coords[i];
361 }
362 }
363 outputMesh->nodes[it->second] = coords;
364 }
365
366 // Add cell data to the mesh
367 assert(cellDifference.size() == incrementValues.size());
368 assert(incrementValues.size() ==
369 outputMesh->template getElements<1 << D>().size());
370 outputMesh->cellData.insertNextScalarData(cellDifference, "Difference");
371 outputMesh->cellData.insertNextScalarData(incrementValues,
372 "CustomIncrement");
373 }
374 }
375};
376
377// Precompile for common precision and dimensions
379
380} // 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:37