ViennaLS
Loading...
Searching...
No Matches
lsCompareCriticalDimensions.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <hrleSparseIterator.hpp>
4#include <lsDomain.hpp>
5#include <lsMesh.hpp>
7#include <lsToSurfaceMesh.hpp>
8
9#include <cmath>
10#include <limits>
11#include <vector>
12
13namespace viennals {
14
15using namespace viennacore;
16
32
33template <class T, int D = 2> class CompareCriticalDimensions {
34 using hrleIndexType = viennahrle::IndexType;
35
36 SmartPointer<Domain<T, D>> levelSetTarget = nullptr;
37 SmartPointer<Domain<T, D>> levelSetSample = nullptr;
38
39 // Structure to hold a range specification
40 struct RangeSpec {
41 int measureDimension;
42 std::array<T, D> minBounds;
43 std::array<T, D> maxBounds;
44 bool findMaximum; // true for maximum, false for minimum
45 };
46
47 std::vector<RangeSpec> rangeSpecs;
48
49 // Structure to hold critical dimension results
50 struct CriticalDimensionResult {
51 int measureDimension;
52 std::array<T, D> minBounds;
53 std::array<T, D> maxBounds;
54 bool findMaximum;
55 T positionTarget; // Critical dimension position in target LS
56 T positionSample; // Critical dimension position in sample LS
57 T difference; // Absolute difference
58 bool valid; // Whether both critical dimensions were found
59 };
60
61 std::vector<CriticalDimensionResult> results;
62
63 // Optional mesh output
64 SmartPointer<Mesh<T>> outputMesh = nullptr;
65
66 bool checkInputs() {
67 if (levelSetTarget == nullptr || levelSetSample == nullptr) {
68 VIENNACORE_LOG_WARNING("Missing level set in CompareCriticalDimensions.");
69 return false;
70 }
71
72 // Check if the grids are compatible
73 const auto &gridTarget = levelSetTarget->getGrid();
74 const auto &gridSample = levelSetSample->getGrid();
75
76 if (gridTarget.getGridDelta() != gridSample.getGridDelta()) {
77 VIENNACORE_LOG_WARNING(
78 "Grid delta mismatch in CompareCriticalDimensions. The "
79 "grid deltas of the two level sets must be equal.");
80 return false;
81 }
82
83 if (rangeSpecs.empty()) {
84 VIENNACORE_LOG_WARNING(
85 "No ranges specified in CompareCriticalDimensions.");
86 return false;
87 }
88
89 return true;
90 }
91
92 // Extract surface positions from mesh nodes within the specified range
93 // Returns the position coordinates (Y if isXRange=true, X if isXRange=false)
94 std::vector<T> findSurfaceCrossings(SmartPointer<Mesh<T>> surfaceMesh,
95 const RangeSpec &spec) {
96 std::vector<T> crossings;
97
98 // Iterate through all surface mesh nodes
99 for (const auto &node : surfaceMesh->nodes) {
100 // Check if point is in our scan range
101 bool inRange = true;
102 for (int i = 0; i < D; ++i) {
103 if (i == spec.measureDimension)
104 continue;
105 if (node[i] < spec.minBounds[i] || node[i] > spec.maxBounds[i]) {
106 inRange = false;
107 break;
108 }
109 }
110
111 if (inRange) {
112 T val = node[spec.measureDimension];
113 if (val >= spec.minBounds[spec.measureDimension] &&
114 val <= spec.maxBounds[spec.measureDimension]) {
115 crossings.push_back(val);
116 }
117 }
118 }
119
120 return crossings;
121 }
122
123 // Find critical dimension (max or min) from a list of surface crossings
124 std::pair<bool, T> findCriticalDimension(const std::vector<T> &crossings,
125 bool findMaximum) {
126 if (crossings.empty()) {
127 return {false, 0.0};
128 }
129
130 if (findMaximum) {
131 return {true, *std::max_element(crossings.begin(), crossings.end())};
132 } else {
133 return {true, *std::min_element(crossings.begin(), crossings.end())};
134 }
135 }
136
137public:
139
140 CompareCriticalDimensions(SmartPointer<Domain<T, D>> passedLevelSetTarget,
141 SmartPointer<Domain<T, D>> passedLevelSetSample)
142 : levelSetTarget(passedLevelSetTarget),
143 levelSetSample(passedLevelSetSample) {}
144
145 void setLevelSetTarget(SmartPointer<Domain<T, D>> passedLevelSet) {
146 levelSetTarget = passedLevelSet;
147 }
148
149 void setLevelSetSample(SmartPointer<Domain<T, D>> passedLevelSet) {
150 levelSetSample = passedLevelSet;
151 }
152
154 void addRange(int measureDimension, const std::array<T, D> &minBounds,
155 const std::array<T, D> &maxBounds, bool findMaximum = true) {
156 RangeSpec spec;
157 spec.measureDimension = measureDimension;
158 spec.minBounds = minBounds;
159 spec.maxBounds = maxBounds;
160 spec.findMaximum = findMaximum;
161 rangeSpecs.push_back(spec);
162 }
163
165 void addXRange(T minX, T maxX, bool findMaximum = true) {
166 if constexpr (D == 2) {
167 std::array<T, D> minBounds = {minX, std::numeric_limits<T>::lowest()};
168 std::array<T, D> maxBounds = {maxX, std::numeric_limits<T>::max()};
169 addRange(1, minBounds, maxBounds, findMaximum);
170 } else {
171 VIENNACORE_LOG_WARNING(
172 "addXRange is only supported for 2D. Use addRange for 3D.");
173 }
174 }
175
177 void addYRange(T minY, T maxY, bool findMaximum = true) {
178 if constexpr (D == 2) {
179 std::array<T, D> minBounds = {std::numeric_limits<T>::lowest(), minY};
180 std::array<T, D> maxBounds = {std::numeric_limits<T>::max(), maxY};
181 addRange(0, minBounds, maxBounds, findMaximum);
182 } else {
183 VIENNACORE_LOG_WARNING(
184 "addYRange is only supported for 2D. Use addRange for 3D.");
185 }
186 }
187
189 void clearRanges() { rangeSpecs.clear(); }
190
192 void setOutputMesh(SmartPointer<Mesh<T>> passedMesh) {
193 outputMesh = passedMesh;
194 }
195
197 void apply() {
198 results.clear();
199
200 if (!checkInputs()) {
201 return;
202 }
203
204 const auto &grid = levelSetTarget->getGrid();
205 const T gridDelta = grid.getGridDelta();
206
207 // Convert both level sets to surface meshes once
208 auto surfaceMeshRef = SmartPointer<Mesh<T>>::New();
209 auto surfaceMeshCmp = SmartPointer<Mesh<T>>::New();
210
211 ToSurfaceMesh<T, D>(levelSetTarget, surfaceMeshRef).apply();
212 ToSurfaceMesh<T, D>(levelSetSample, surfaceMeshCmp).apply();
213
214 // Pre-allocate results vector to avoid race conditions
215 results.resize(rangeSpecs.size());
216
217 // Process each range specification in parallel
218#pragma omp parallel for
219 for (size_t specIdx = 0; specIdx < rangeSpecs.size(); ++specIdx) {
220 const auto &spec = rangeSpecs[specIdx];
221 CriticalDimensionResult result;
222 result.measureDimension = spec.measureDimension;
223 result.minBounds = spec.minBounds;
224 result.maxBounds = spec.maxBounds;
225 result.findMaximum = spec.findMaximum;
226 result.valid = false;
227
228 // Find all surface crossings from the mesh nodes
229 auto crossingsRef = findSurfaceCrossings(surfaceMeshRef, spec);
230 auto crossingsCmp = findSurfaceCrossings(surfaceMeshCmp, spec);
231
232 // Find critical dimensions
233 auto [validRef, cdRef] =
234 findCriticalDimension(crossingsRef, spec.findMaximum);
235 auto [validCmp, cdCmp] =
236 findCriticalDimension(crossingsCmp, spec.findMaximum);
237
238 if (validRef && validCmp) {
239 result.valid = true;
240 result.positionTarget = cdRef;
241 result.positionSample = cdCmp;
242 result.difference = std::abs(cdRef - cdCmp);
243 }
244
245 // Direct assignment instead of push_back to avoid race condition
246 results[specIdx] = result;
247 }
248
249 // Generate mesh if requested
250 if (outputMesh != nullptr) {
251 generateMesh();
252 }
253 }
254
256 size_t getNumCriticalDimensions() const { return results.size(); }
257
259 bool getCriticalDimensionResult(size_t index, T &positionTarget,
260 T &positionSample, T &difference) const {
261 if (index >= results.size() || !results[index].valid) {
262 return false;
263 }
264 positionTarget = results[index].positionTarget;
265 positionSample = results[index].positionSample;
266 difference = results[index].difference;
267 return true;
268 }
269
272 T sum = 0.0;
273 size_t count = 0;
274 for (const auto &result : results) {
275 if (result.valid) {
276 sum += result.difference;
277 count++;
278 }
279 }
280 return count > 0 ? sum / count : std::numeric_limits<T>::quiet_NaN();
281 }
282
285 T maxDiff = 0.0;
286 for (const auto &result : results) {
287 if (result.valid) {
288 maxDiff = std::max(maxDiff, result.difference);
289 }
290 }
291 return maxDiff;
292 }
293
295 T getRMSE() const {
296 T sumSquared = 0.0;
297 size_t count = 0;
298 for (const auto &result : results) {
299 if (result.valid) {
300 sumSquared += result.difference * result.difference;
301 count++;
302 }
303 }
304 return count > 0 ? std::sqrt(sumSquared / count)
305 : std::numeric_limits<T>::quiet_NaN();
306 }
307
309 std::vector<T> getAllDifferences() const {
310 std::vector<T> differences;
311 for (const auto &result : results) {
312 if (result.valid) {
313 differences.push_back(result.difference);
314 }
315 }
316 return differences;
317 }
318
319private:
320 void generateMesh() {
321 outputMesh->clear();
322
323 std::vector<Vec3D<T>> nodeCoordinates;
324 std::vector<std::array<unsigned, 1>> vertexIndices;
325 std::vector<T> differenceValues;
326 std::vector<T> targetValues;
327 std::vector<T> sampleValues;
328
329 for (unsigned i = 0; i < 3; ++i) {
330 outputMesh->minimumExtent[i] =
331 (i < D) ? std::numeric_limits<T>::max() : 0.0;
332 outputMesh->maximumExtent[i] =
333 (i < D) ? std::numeric_limits<T>::lowest() : 0.0;
334 }
335
336 unsigned pointId = 0;
337 for (const auto &result : results) {
338 if (!result.valid)
339 continue;
340
341 // Create points for target and sample positions
342 Vec3D<T> coordTarget{};
343 Vec3D<T> coordSample{};
344
345 for (int i = 0; i < D; ++i) {
346 if (i == result.measureDimension) {
347 coordTarget[i] = result.positionTarget;
348 coordSample[i] = result.positionSample;
349 } else {
350 // Use midpoint of range for other dimensions
351 T mid = (result.minBounds[i] + result.maxBounds[i]) / 2.0;
352 // If bounds are infinite, use 0
353 if (std::abs(mid) > std::numeric_limits<T>::max() / 2.0)
354 mid = 0.0;
355 coordTarget[i] = mid;
356 coordSample[i] = mid;
357 }
358 }
359
360 // Add target point
361 nodeCoordinates.push_back(coordTarget);
362 vertexIndices.push_back({pointId++});
363 differenceValues.push_back(result.difference);
364 targetValues.push_back(result.positionTarget);
365 sampleValues.push_back(result.positionSample);
366
367 // Add sample point
368 nodeCoordinates.push_back(coordSample);
369 vertexIndices.push_back({pointId++});
370 differenceValues.push_back(result.difference);
371 targetValues.push_back(result.positionTarget);
372 sampleValues.push_back(result.positionSample);
373
374 // Update extent
375 for (unsigned i = 0; i < D; ++i) {
376 outputMesh->minimumExtent[i] = std::min(
377 {outputMesh->minimumExtent[i], coordTarget[i], coordSample[i]});
378 outputMesh->maximumExtent[i] = std::max(
379 {outputMesh->maximumExtent[i], coordTarget[i], coordSample[i]});
380 }
381 }
382
383 if (!nodeCoordinates.empty()) {
384 outputMesh->nodes = std::move(nodeCoordinates);
385 outputMesh->vertices = std::move(vertexIndices);
386 outputMesh->pointData.insertNextScalarData(std::move(differenceValues),
387 "Difference");
388 outputMesh->pointData.insertNextScalarData(std::move(targetValues),
389 "TargetPosition");
390 outputMesh->pointData.insertNextScalarData(std::move(sampleValues),
391 "SamplePosition");
392 }
393 }
394};
395
396// Add template specializations for this class
398
399} // namespace viennals
constexpr int D
Definition Epitaxy.cpp:11
double T
Definition Epitaxy.cpp:12
Compares critical dimensions (surface positions) between two level sets. Critical dimensions are defi...
Definition lsCompareCriticalDimensions.hpp:33
void apply()
Apply the comparison.
Definition lsCompareCriticalDimensions.hpp:197
std::vector< T > getAllDifferences() const
Get all valid results.
Definition lsCompareCriticalDimensions.hpp:309
void setOutputMesh(SmartPointer< Mesh< T > > passedMesh)
Set the output mesh where critical dimension locations will be stored.
Definition lsCompareCriticalDimensions.hpp:192
bool getCriticalDimensionResult(size_t index, T &positionTarget, T &positionSample, T &difference) const
Get a specific critical dimension result.
Definition lsCompareCriticalDimensions.hpp:259
size_t getNumCriticalDimensions() const
Get the number of critical dimensions compared.
Definition lsCompareCriticalDimensions.hpp:256
CompareCriticalDimensions(SmartPointer< Domain< T, D > > passedLevelSetTarget, SmartPointer< Domain< T, D > > passedLevelSetSample)
Definition lsCompareCriticalDimensions.hpp:140
void addRange(int measureDimension, const std::array< T, D > &minBounds, const std::array< T, D > &maxBounds, bool findMaximum=true)
Add a generic range specification.
Definition lsCompareCriticalDimensions.hpp:154
void addYRange(T minY, T maxY, bool findMaximum=true)
Add a Y range to find maximum or minimum X position.
Definition lsCompareCriticalDimensions.hpp:177
void clearRanges()
Clear all range specifications.
Definition lsCompareCriticalDimensions.hpp:189
void setLevelSetTarget(SmartPointer< Domain< T, D > > passedLevelSet)
Definition lsCompareCriticalDimensions.hpp:145
T getMeanDifference() const
Get mean absolute difference across all valid critical dimensions.
Definition lsCompareCriticalDimensions.hpp:271
T getMaxDifference() const
Get maximum difference across all valid critical dimensions.
Definition lsCompareCriticalDimensions.hpp:284
void addXRange(T minX, T maxX, bool findMaximum=true)
Add an X range to find maximum or minimum Y position.
Definition lsCompareCriticalDimensions.hpp:165
CompareCriticalDimensions()
Definition lsCompareCriticalDimensions.hpp:138
T getRMSE() const
Get RMSE across all valid critical dimensions.
Definition lsCompareCriticalDimensions.hpp:295
void setLevelSetSample(SmartPointer< Domain< T, D > > passedLevelSet)
Definition lsCompareCriticalDimensions.hpp:149
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:28
This class holds an explicit mesh, which is always given in 3 dimensions. If it describes a 2D mesh,...
Definition lsMesh.hpp:22
Extract an explicit Mesh<> instance from an lsDomain. The interface is then described by explicit sur...
Definition lsToSurfaceMesh.hpp:21
void apply()
Definition lsToSurfaceMesh.hpp:45
#define PRECOMPILE_PRECISION_DIMENSION(className)
Definition lsPreCompileMacros.hpp:24
Definition lsAdvect.hpp:41