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