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
34
35template <class T, int D = 2> class CompareCriticalDimensions {
36 using hrleIndexType = viennahrle::IndexType;
37
38 SmartPointer<Domain<T, D>> levelSetTarget = nullptr;
39 SmartPointer<Domain<T, D>> levelSetSample = nullptr;
40
41 // Structure to hold a range specification
42 struct RangeSpec {
43 bool isXRange; // true if X range, false if Y range
44 T rangeMin;
45 T rangeMax;
46 bool findMaximum; // true for maximum, false for minimum
47 };
48
49 std::vector<RangeSpec> rangeSpecs;
50
51 // Structure to hold critical dimension results
52 struct CriticalDimensionResult {
53 bool isXRange;
54 T rangeMin;
55 T rangeMax;
56 bool findMaximum;
57 T positionTarget; // Critical dimension position in target LS
58 T positionSample; // Critical dimension position in sample LS
59 T difference; // Absolute difference
60 bool valid; // Whether both critical dimensions were found
61 };
62
63 std::vector<CriticalDimensionResult> results;
64
65 // Optional mesh output
66 SmartPointer<Mesh<T>> outputMesh = nullptr;
67
68 bool checkInputs() {
69 if (levelSetTarget == nullptr || levelSetSample == nullptr) {
70 VIENNACORE_LOG_WARNING("Missing level set in CompareCriticalDimensions.");
71 return false;
72 }
73
74 // Check if the grids are compatible
75 const auto &gridTarget = levelSetTarget->getGrid();
76 const auto &gridSample = levelSetSample->getGrid();
77
78 if (gridTarget.getGridDelta() != gridSample.getGridDelta()) {
79 VIENNACORE_LOG_WARNING(
80 "Grid delta mismatch in CompareCriticalDimensions. The "
81 "grid deltas of the two level sets must be equal.");
82 return false;
83 }
84
85 if (rangeSpecs.empty()) {
86 VIENNACORE_LOG_WARNING(
87 "No ranges specified in CompareCriticalDimensions.");
88 return false;
89 }
90
91 return true;
92 }
93
94 // Extract surface positions from mesh nodes within the specified range
95 // Returns the position coordinates (Y if isXRange=true, X if isXRange=false)
96 std::vector<T> findSurfaceCrossings(SmartPointer<Mesh<T>> surfaceMesh,
97 bool isXRange, T scanMin, T scanMax,
98 T perpMin, T perpMax) {
99 std::vector<T> crossings;
100
101 // Iterate through all surface mesh nodes
102 for (const auto &node : surfaceMesh->nodes) {
103 T xCoord = node[0];
104 T yCoord = node[1];
105
106 // Check if point is in our scan range
107 bool inRange = false;
108 if (isXRange) {
109 // X range specified - check if X is in scan range
110 inRange = (xCoord >= scanMin && xCoord <= scanMax);
111 } else {
112 // Y range specified - check if Y is in scan range
113 inRange = (yCoord >= scanMin && yCoord <= scanMax);
114 }
115
116 if (inRange) {
117 // Extract perpendicular coordinate
118 T perpCoord = isXRange ? yCoord : xCoord;
119
120 // Check if perpendicular coordinate is in range
121 if (perpCoord >= perpMin && perpCoord <= perpMax) {
122 crossings.push_back(perpCoord);
123 }
124 }
125 }
126
127 return crossings;
128 }
129
130 // Find critical dimension (max or min) from a list of surface crossings
131 std::pair<bool, T> findCriticalDimension(const std::vector<T> &crossings,
132 bool findMaximum) {
133 if (crossings.empty()) {
134 return {false, 0.0};
135 }
136
137 if (findMaximum) {
138 return {true, *std::max_element(crossings.begin(), crossings.end())};
139 } else {
140 return {true, *std::min_element(crossings.begin(), crossings.end())};
141 }
142 }
143
144public:
146 static_assert(D == 2 && "CompareCriticalDimensions is currently only "
147 "implemented for 2D level sets.");
148 }
149
150 CompareCriticalDimensions(SmartPointer<Domain<T, D>> passedLevelSetTarget,
151 SmartPointer<Domain<T, D>> passedLevelSetSample)
152 : levelSetTarget(passedLevelSetTarget),
153 levelSetSample(passedLevelSetSample) {
154 static_assert(D == 2 && "CompareCriticalDimensions is currently only "
155 "implemented for 2D level sets.");
156 }
157
158 void setLevelSetTarget(SmartPointer<Domain<T, D>> passedLevelSet) {
159 levelSetTarget = passedLevelSet;
160 }
161
162 void setLevelSetSample(SmartPointer<Domain<T, D>> passedLevelSet) {
163 levelSetSample = passedLevelSet;
164 }
165
167 void addXRange(T minX, T maxX, bool findMaximum = true) {
168 RangeSpec spec;
169 spec.isXRange = true;
170 spec.rangeMin = minX;
171 spec.rangeMax = maxX;
172 spec.findMaximum = findMaximum;
173 rangeSpecs.push_back(spec);
174 }
175
177 void addYRange(T minY, T maxY, bool findMaximum = true) {
178 RangeSpec spec;
179 spec.isXRange = false;
180 spec.rangeMin = minY;
181 spec.rangeMax = maxY;
182 spec.findMaximum = findMaximum;
183 rangeSpecs.push_back(spec);
184 }
185
187 void clearRanges() { rangeSpecs.clear(); }
188
190 void setOutputMesh(SmartPointer<Mesh<T>> passedMesh) {
191 outputMesh = passedMesh;
192 }
193
195 void apply() {
196 results.clear();
197
198 if (!checkInputs()) {
199 return;
200 }
201
202 const auto &grid = levelSetTarget->getGrid();
203 const T gridDelta = grid.getGridDelta();
204
205 // Convert both level sets to surface meshes once
206 auto surfaceMeshRef = SmartPointer<Mesh<T>>::New();
207 auto surfaceMeshCmp = SmartPointer<Mesh<T>>::New();
208
209 ToSurfaceMesh<T, D>(levelSetTarget, surfaceMeshRef).apply();
210 ToSurfaceMesh<T, D>(levelSetSample, surfaceMeshCmp).apply();
211
212 // Get actual mesh extents instead of grid bounds
213 // This ensures we don't filter out surface points that extend beyond grid
214 // bounds
215 T xMin = std::numeric_limits<T>::max();
216 T xMax = std::numeric_limits<T>::lowest();
217 T yMin = std::numeric_limits<T>::max();
218 T yMax = std::numeric_limits<T>::lowest();
219
220 for (const auto &node : surfaceMeshRef->nodes) {
221 xMin = std::min(xMin, node[0]);
222 xMax = std::max(xMax, node[0]);
223 yMin = std::min(yMin, node[1]);
224 yMax = std::max(yMax, node[1]);
225 }
226 for (const auto &node : surfaceMeshCmp->nodes) {
227 xMin = std::min(xMin, node[0]);
228 xMax = std::max(xMax, node[0]);
229 yMin = std::min(yMin, node[1]);
230 yMax = std::max(yMax, node[1]);
231 }
232
233 // Pre-allocate results vector to avoid race conditions
234 results.resize(rangeSpecs.size());
235
236 // Process each range specification in parallel
237#pragma omp parallel for
238 for (size_t specIdx = 0; specIdx < rangeSpecs.size(); ++specIdx) {
239 const auto &spec = rangeSpecs[specIdx];
240 CriticalDimensionResult result;
241 result.isXRange = spec.isXRange;
242 result.rangeMin = spec.rangeMin;
243 result.rangeMax = spec.rangeMax;
244 result.findMaximum = spec.findMaximum;
245 result.valid = false;
246
247 // Determine scan and perpendicular ranges
248 T scanMin, scanMax, perpMin, perpMax;
249 if (spec.isXRange) {
250 // X range specified - scan along X, find Y positions
251 scanMin = spec.rangeMin;
252 scanMax = spec.rangeMax;
253 perpMin = yMin;
254 perpMax = yMax;
255 } else {
256 // Y range specified - scan along Y, find X positions
257 scanMin = spec.rangeMin;
258 scanMax = spec.rangeMax;
259 perpMin = xMin;
260 perpMax = xMax;
261 }
262
263 // Find all surface crossings from the mesh nodes
264 auto crossingsRef = findSurfaceCrossings(
265 surfaceMeshRef, spec.isXRange, scanMin, scanMax, perpMin, perpMax);
266 auto crossingsCmp = findSurfaceCrossings(
267 surfaceMeshCmp, spec.isXRange, scanMin, scanMax, perpMin, perpMax);
268
269 // Find critical dimensions
270 auto [validRef, cdRef] =
271 findCriticalDimension(crossingsRef, spec.findMaximum);
272 auto [validCmp, cdCmp] =
273 findCriticalDimension(crossingsCmp, spec.findMaximum);
274
275 if (validRef && validCmp) {
276 result.valid = true;
277 result.positionTarget = cdRef;
278 result.positionSample = cdCmp;
279 result.difference = std::abs(cdRef - cdCmp);
280 }
281
282 // Direct assignment instead of push_back to avoid race condition
283 results[specIdx] = result;
284 }
285
286 // Generate mesh if requested
287 if (outputMesh != nullptr) {
288 generateMesh();
289 }
290 }
291
293 size_t getNumCriticalDimensions() const { return results.size(); }
294
296 bool getCriticalDimensionResult(size_t index, T &positionTarget,
297 T &positionSample, T &difference) const {
298 if (index >= results.size() || !results[index].valid) {
299 return false;
300 }
301 positionTarget = results[index].positionTarget;
302 positionSample = results[index].positionSample;
303 difference = results[index].difference;
304 return true;
305 }
306
309 T sum = 0.0;
310 size_t count = 0;
311 for (const auto &result : results) {
312 if (result.valid) {
313 sum += result.difference;
314 count++;
315 }
316 }
317 return count > 0 ? sum / count : std::numeric_limits<T>::quiet_NaN();
318 }
319
322 T maxDiff = 0.0;
323 for (const auto &result : results) {
324 if (result.valid) {
325 maxDiff = std::max(maxDiff, result.difference);
326 }
327 }
328 return maxDiff;
329 }
330
332 T getRMSE() const {
333 T sumSquared = 0.0;
334 size_t count = 0;
335 for (const auto &result : results) {
336 if (result.valid) {
337 sumSquared += result.difference * result.difference;
338 count++;
339 }
340 }
341 return count > 0 ? std::sqrt(sumSquared / count)
342 : std::numeric_limits<T>::quiet_NaN();
343 }
344
346 std::vector<T> getAllDifferences() const {
347 std::vector<T> differences;
348 for (const auto &result : results) {
349 if (result.valid) {
350 differences.push_back(result.difference);
351 }
352 }
353 return differences;
354 }
355
356private:
357 void generateMesh() {
358 outputMesh->clear();
359
360 std::vector<Vec3D<T>> nodeCoordinates;
361 std::vector<std::array<unsigned, 1>> vertexIndices;
362 std::vector<T> differenceValues;
363 std::vector<T> targetValues;
364 std::vector<T> sampleValues;
365
366 for (unsigned i = 0; i < D; ++i) {
367 outputMesh->minimumExtent[i] = std::numeric_limits<T>::max();
368 outputMesh->maximumExtent[i] = std::numeric_limits<T>::lowest();
369 }
370
371 unsigned pointId = 0;
372 for (const auto &result : results) {
373 if (!result.valid)
374 continue;
375
376 // Create points for target and sample positions
377 Vec3D<T> coordTarget, coordSample;
378
379 if (result.isXRange) {
380 // Critical dimension is in Y, position is along X range
381 T xMid = (result.rangeMin + result.rangeMax) / 2.0;
382 coordTarget = {xMid, result.positionTarget, 0.0};
383 coordSample = {xMid, result.positionSample, 0.0};
384 } else {
385 // Critical dimension is in X, position is along Y range
386 T yMid = (result.rangeMin + result.rangeMax) / 2.0;
387 coordTarget = {result.positionTarget, yMid, 0.0};
388 coordSample = {result.positionSample, yMid, 0.0};
389 }
390
391 // Add target point
392 nodeCoordinates.push_back(coordTarget);
393 vertexIndices.push_back({pointId++});
394 differenceValues.push_back(result.difference);
395 targetValues.push_back(result.positionTarget);
396 sampleValues.push_back(result.positionSample);
397
398 // Add sample point
399 nodeCoordinates.push_back(coordSample);
400 vertexIndices.push_back({pointId++});
401 differenceValues.push_back(result.difference);
402 targetValues.push_back(result.positionTarget);
403 sampleValues.push_back(result.positionSample);
404
405 // Update extent
406 for (unsigned i = 0; i < D; ++i) {
407 outputMesh->minimumExtent[i] = std::min(
408 {outputMesh->minimumExtent[i], coordTarget[i], coordSample[i]});
409 outputMesh->maximumExtent[i] = std::max(
410 {outputMesh->maximumExtent[i], coordTarget[i], coordSample[i]});
411 }
412 }
413
414 if (!nodeCoordinates.empty()) {
415 outputMesh->nodes = std::move(nodeCoordinates);
416 outputMesh->vertices = std::move(vertexIndices);
417 outputMesh->pointData.insertNextScalarData(std::move(differenceValues),
418 "Difference");
419 outputMesh->pointData.insertNextScalarData(std::move(targetValues),
420 "TargetPosition");
421 outputMesh->pointData.insertNextScalarData(std::move(sampleValues),
422 "SamplePosition");
423 }
424 }
425};
426
427// Add template specializations for this class
429
430} // 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:35
void apply()
Apply the comparison.
Definition lsCompareCriticalDimensions.hpp:195
std::vector< T > getAllDifferences() const
Get all valid results.
Definition lsCompareCriticalDimensions.hpp:346
void setOutputMesh(SmartPointer< Mesh< T > > passedMesh)
Set the output mesh where critical dimension locations will be stored.
Definition lsCompareCriticalDimensions.hpp:190
bool getCriticalDimensionResult(size_t index, T &positionTarget, T &positionSample, T &difference) const
Get a specific critical dimension result.
Definition lsCompareCriticalDimensions.hpp:296
size_t getNumCriticalDimensions() const
Get the number of critical dimensions compared.
Definition lsCompareCriticalDimensions.hpp:293
CompareCriticalDimensions(SmartPointer< Domain< T, D > > passedLevelSetTarget, SmartPointer< Domain< T, D > > passedLevelSetSample)
Definition lsCompareCriticalDimensions.hpp:150
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:187
void setLevelSetTarget(SmartPointer< Domain< T, D > > passedLevelSet)
Definition lsCompareCriticalDimensions.hpp:158
T getMeanDifference() const
Get mean absolute difference across all valid critical dimensions.
Definition lsCompareCriticalDimensions.hpp:308
T getMaxDifference() const
Get maximum difference across all valid critical dimensions.
Definition lsCompareCriticalDimensions.hpp:321
void addXRange(T minX, T maxX, bool findMaximum=true)
Add an X range to find maximum or minimum Y position.
Definition lsCompareCriticalDimensions.hpp:167
CompareCriticalDimensions()
Definition lsCompareCriticalDimensions.hpp:145
T getRMSE() const
Get RMSE across all valid critical dimensions.
Definition lsCompareCriticalDimensions.hpp:332
void setLevelSetSample(SmartPointer< Domain< T, D > > passedLevelSet)
Definition lsCompareCriticalDimensions.hpp:162
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:27
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:40