TTK
Loading...
Searching...
No Matches
ScalarFieldCriticalPoints.h
Go to the documentation of this file.
1
46
47#pragma once
48
49#include <map>
50
51// base code includes
52#include <ProgressiveTopology.h>
53#include <Triangulation.h>
54#include <UnionFind.h>
55
56namespace ttk {
57
58 class ScalarFieldCriticalPoints : public virtual Debug {
59
60 public:
62
63 enum class BACKEND { GENERIC = 0, PROGRESSIVE_TOPOLOGY = 1 };
64
78 template <class triangulationType = AbstractTriangulation>
79 int execute(const SimplexId *const offsets,
80 const triangulationType *triangulation);
81
82 template <class triangulationType = AbstractTriangulation>
83 int executeLegacy(const SimplexId *const offsets,
84 const triangulationType *triangulation);
85
86 template <class triangulationType = AbstractTriangulation>
87 int executeProgressive(const SimplexId *const offsets,
88 const triangulationType *triangulation);
89
90 template <class triangulationType>
91 void checkProgressivityRequirement(const triangulationType *triangulation);
92
93 template <class triangulationType = AbstractTriangulation>
95 const SimplexId vertexId,
96 const SimplexId *const offsets,
97 const triangulationType *triangulation,
98 bool &isLowerOnBoundary,
99 bool &isUpperOnBoundary,
100 std::vector<std::vector<ttk::SimplexId>> *upperComponents,
101 std::vector<std::vector<ttk::SimplexId>> *lowerComponents) const;
102
103 template <class triangulationType = AbstractTriangulation>
104 char
105 getCriticalType(const SimplexId &vertexId,
106 const SimplexId *const offsets,
107 const triangulationType *triangulation,
108 std::vector<std::vector<ttk::SimplexId>> *upperComponents
109 = nullptr,
110 std::vector<std::vector<ttk::SimplexId>> *lowerComponents
111 = nullptr) const;
112
113 char getCriticalType(const SimplexId &vertexId,
114 const SimplexId *const offsets,
115 const std::vector<std::pair<SimplexId, SimplexId>>
116 &vertexLinkEdgeList) const;
117
118 inline void setDomainDimension(const int &dimension) {
119 dimension_ = dimension;
120 }
121
122 inline void
123 setOutput(std::vector<std::pair<SimplexId, char>> *criticalPoints) {
124 criticalPoints_ = criticalPoints;
125 }
126
127 inline void
129 // pre-condition functions
130 if(triangulation) {
131 triangulation->preconditionVertexNeighbors();
132 triangulation->preconditionVertexStars();
133 triangulation->preconditionBoundaryVertices();
134 }
135
136 setDomainDimension(triangulation->getDimensionality());
137 setVertexNumber(triangulation->getNumberOfVertices());
138 }
139
141 const std::vector<std::vector<std::pair<SimplexId, SimplexId>>>
142 *edgeList) {
143 vertexLinkEdgeLists_ = edgeList;
144 }
145
149 int setVertexNumber(const SimplexId &vertexNumber) {
150 vertexNumber_ = vertexNumber;
151 return 0;
152 }
153
154 void setNonManifold(const bool b) {
156 }
157
158 void displayStats();
159
160 protected:
163 const std::vector<std::vector<std::pair<SimplexId, SimplexId>>>
165 std::vector<std::pair<SimplexId, char>> *criticalPoints_{};
166
168
169 // progressive
174 bool IsResumable{false};
175 double TimeLimit{};
176 };
177} // namespace ttk
178
179// template functions
180template <class triangulationType>
182 const SimplexId *const offsets, const triangulationType *triangulation) {
183
184 checkProgressivityRequirement(triangulation);
185
186 switch(BackEnd) {
187
189 this->executeProgressive(offsets, triangulation);
190 break;
191
192 case BACKEND::GENERIC:
193 this->executeLegacy(offsets, triangulation);
194 break;
195
196 default:
197 printErr("No method was selected");
198 }
199
201 return 0;
202}
203
204template <class triangulationType>
206 const SimplexId *const offsets, const triangulationType *triangulation) {
207
208 progT_.setDebugLevel(debugLevel_);
209 progT_.setThreadNumber(threadNumber_);
210 progT_.setupTriangulation(const_cast<ttk::ImplicitTriangulation *>(
211 (const ImplicitTriangulation *)triangulation));
212 progT_.setStartingResolutionLevel(StartingResolutionLevel);
213 progT_.setStoppingResolutionLevel(StoppingResolutionLevel);
214 progT_.setTimeLimit(TimeLimit);
215 progT_.setIsResumable(IsResumable);
216 progT_.setPreallocateMemory(true);
217
218 progT_.computeProgressiveCP(criticalPoints_, offsets);
219
220 displayStats();
221 return 0;
222}
223
224template <class triangulationType>
226 const SimplexId *const offsets, const triangulationType *triangulation) {
227
228 // check the consistency of the variables -- to adapt
229#ifndef TTK_ENABLE_KAMIKAZE
230 if((!dimension_) && ((!triangulation) || (triangulation->isEmpty())))
231 return -1;
232 if((!vertexNumber_) && ((!triangulation) || (triangulation->isEmpty())))
233 return -2;
234 if((!vertexLinkEdgeLists_) && (!triangulation))
235 return -4;
236 if(!criticalPoints_)
237 return -5;
238#endif
239
240 if(triangulation) {
241 vertexNumber_ = triangulation->getNumberOfVertices();
242 dimension_ = triangulation->getCellVertexNumber(0) - 1;
243 }
244
245 printMsg("Extracting critical points...");
246
247 Timer t;
248
249 std::vector<char> vertexTypes(vertexNumber_, (char)(CriticalType::Regular));
250
251#ifdef TTK_ENABLE_OPENMP
252 int const chunkSize
253 = std::max(1000, (int)vertexNumber_ / (threadNumber_ * 100));
254#endif
255
256 if(triangulation) {
257
258#ifdef TTK_ENABLE_OPENMP
259#pragma omp parallel for schedule(dynamic, chunkSize) num_threads(threadNumber_)
260#endif
261 for(SimplexId i = 0; i < (SimplexId)vertexNumber_; i++) {
262#ifdef TTK_ENABLE_MPI
263 if(!isRunningWithMPI()
264 || (isRunningWithMPI()
265 && (triangulation->getVertexRank(i) == ttk::MPIrank_)))
266#endif // TTK_ENABLE_MPI
267 {
268 vertexTypes[i] = getCriticalType(i, offsets, triangulation);
269 }
270 }
271 } else if(vertexLinkEdgeLists_) {
272 // legacy implementation
273#ifdef TTK_ENABLE_OPENMP
274#pragma omp parallel for schedule(dynamic, chunkSize) num_threads(threadNumber_)
275#endif
276 for(SimplexId i = 0; i < (SimplexId)vertexNumber_; i++) {
277 // can't use MPI there since triangulation (~> rankArray) is nullptr
278 vertexTypes[i] = getCriticalType(i, offsets, (*vertexLinkEdgeLists_)[i]);
279 }
280 }
281
282 SimplexId minimumNumber = 0, maximumNumber = 0, saddleNumber = 0,
283 oneSaddleNumber = 0, twoSaddleNumber = 0, monkeySaddleNumber = 0;
284
285 // debug msg
286 if(debugLevel_ >= (int)debug::Priority::INFO) {
287 if(dimension_ == 3) {
288 for(SimplexId i = 0; i < vertexNumber_; i++) {
289 switch(vertexTypes[i]) {
290
291 case(char)(CriticalType::Local_minimum):
292 minimumNumber++;
293 break;
294
295 case(char)(CriticalType::Saddle1):
296 oneSaddleNumber++;
297 break;
298
299 case(char)(CriticalType::Saddle2):
300 twoSaddleNumber++;
301 break;
302
303 case(char)(CriticalType::Local_maximum):
304 maximumNumber++;
305 break;
306
307 case(char)(CriticalType::Degenerate):
308 monkeySaddleNumber++;
309 break;
310 }
311 }
312 } else if(dimension_ == 2) {
313 for(SimplexId i = 0; i < vertexNumber_; i++) {
314 switch(vertexTypes[i]) {
315
316 case(char)(CriticalType::Local_minimum):
317 minimumNumber++;
318 break;
319
320 case(char)(CriticalType::Saddle1):
321 saddleNumber++;
322 break;
323
324 case(char)(CriticalType::Local_maximum):
325 maximumNumber++;
326 break;
327
328 case(char)(CriticalType::Degenerate):
329 monkeySaddleNumber++;
330 break;
331 }
332 }
333 }
334
335 {
336 std::vector<std::vector<std::string>> stats;
337 stats.push_back({" #Minima", std::to_string(minimumNumber)});
338 if(dimension_ == 3) {
339 stats.push_back({" #1-saddles", std::to_string(oneSaddleNumber)});
340 stats.push_back({" #2-saddles", std::to_string(twoSaddleNumber)});
341 }
342 if(dimension_ == 2) {
343 stats.push_back({" #Saddles", std::to_string(saddleNumber)});
344 }
345 stats.push_back({" #Multi-saddles", std::to_string(monkeySaddleNumber)});
346 stats.push_back({" #Maxima", std::to_string(maximumNumber)});
347
348 printMsg(stats);
349 }
350 }
351
352 // prepare the output
353 criticalPoints_->clear();
354 criticalPoints_->reserve(vertexNumber_);
355 for(SimplexId i = 0; i < vertexNumber_; i++) {
356 if(vertexTypes[i] != (char)(CriticalType::Regular)) {
357 criticalPoints_->emplace_back(i, vertexTypes[i]);
358 }
359 }
360
361 printMsg("Processed " + std::to_string(vertexNumber_) + " vertices", 1,
362 t.getElapsedTime(), threadNumber_);
363
364 return 0;
365}
366
367template <class triangulationType>
369 const SimplexId vertexId,
370 const SimplexId *const offsets,
371 const triangulationType *triangulation,
372 bool &isLowerOnBoundary,
373 bool &isUpperOnBoundary,
374 std::vector<std::vector<ttk::SimplexId>> *upperComponents,
375 std::vector<std::vector<ttk::SimplexId>> *lowerComponents) const {
376
377 SimplexId const neighborNumber
378 = triangulation->getVertexNeighborNumber(vertexId);
379 std::vector<SimplexId> lowerNeighbors, upperNeighbors;
380
381 for(SimplexId i = 0; i < neighborNumber; i++) {
382 SimplexId neighborId = 0;
383 triangulation->getVertexNeighbor(vertexId, i, neighborId);
384
385 if(offsets[neighborId] < offsets[vertexId]) {
386 lowerNeighbors.push_back(neighborId);
387 if(dimension_ == 3) {
388 if(triangulation->isVertexOnBoundary(neighborId))
389 isLowerOnBoundary = true;
390 }
391 }
392
393 // upper link
394 if(offsets[neighborId] > offsets[vertexId]) {
395 upperNeighbors.push_back(neighborId);
396 if(dimension_ == 3) {
397 if(triangulation->isVertexOnBoundary(neighborId))
398 isUpperOnBoundary = true;
399 }
400 }
401 }
402
403 // now do the actual work
404 std::vector<UnionFind> lowerSeeds(lowerNeighbors.size());
405 std::vector<UnionFind *> lowerList(lowerNeighbors.size());
406 std::vector<UnionFind> upperSeeds(upperNeighbors.size());
407 std::vector<UnionFind *> upperList(upperNeighbors.size());
408
409 for(SimplexId i = 0; i < (SimplexId)lowerSeeds.size(); i++) {
410 lowerList[i] = &(lowerSeeds[i]);
411 }
412 for(SimplexId i = 0; i < (SimplexId)upperSeeds.size(); i++) {
413 upperList[i] = &(upperSeeds[i]);
414 }
415
416 SimplexId const vertexStarSize = triangulation->getVertexStarNumber(vertexId);
417
418 for(SimplexId i = 0; i < vertexStarSize; i++) {
419 SimplexId cellId = 0;
420 triangulation->getVertexStar(vertexId, i, cellId);
421
422 SimplexId const cellSize = triangulation->getCellVertexNumber(cellId);
423 for(SimplexId j = 0; j < cellSize; j++) {
424 SimplexId neighborId0 = -1;
425 triangulation->getCellVertex(cellId, j, neighborId0);
426
427 if(neighborId0 != vertexId) {
428 // we are on the link
429
430 bool const lower0 = offsets[neighborId0] < offsets[vertexId];
431
432 // connect it to everybody except himself and vertexId
433 for(SimplexId k = j + 1; k < cellSize; k++) {
434
435 SimplexId neighborId1 = -1;
436 triangulation->getCellVertex(cellId, k, neighborId1);
437
438 if((neighborId1 != neighborId0) && (neighborId1 != vertexId)) {
439
440 bool const lower1 = offsets[neighborId1] < offsets[vertexId];
441
442 std::vector<SimplexId> *neighbors = &lowerNeighbors;
443 std::vector<UnionFind *> *seeds = &lowerList;
444
445 if(!lower0) {
446 neighbors = &upperNeighbors;
447 seeds = &upperList;
448 }
449
450 if(lower0 == lower1) {
451 // connect their union-find sets!
452 SimplexId lowerId0 = -1, lowerId1 = -1;
453 for(SimplexId l = 0; l < (SimplexId)neighbors->size(); l++) {
454 if((*neighbors)[l] == neighborId0) {
455 lowerId0 = l;
456 }
457 if((*neighbors)[l] == neighborId1) {
458 lowerId1 = l;
459 }
460 }
461 if((lowerId0 != -1) && (lowerId1 != -1)) {
462 (*seeds)[lowerId0] = UnionFind::makeUnion(
463 (*seeds)[lowerId0], (*seeds)[lowerId1]);
464 (*seeds)[lowerId1] = (*seeds)[lowerId0];
465 }
466 }
467 }
468 }
469 }
470 }
471 }
472
473 // let's remove duplicates now
474
475 // update the UF if necessary
476 for(SimplexId i = 0; i < (SimplexId)lowerList.size(); i++)
477 lowerList[i] = lowerList[i]->find();
478 for(SimplexId i = 0; i < (SimplexId)upperList.size(); i++)
479 upperList[i] = upperList[i]->find();
480
481 std::unordered_map<UnionFind *, std::vector<ttk::SimplexId>>::iterator it;
482 std::unordered_map<UnionFind *, std::vector<ttk::SimplexId>>
483 upperComponentId{};
484 std::unordered_map<UnionFind *, std::vector<ttk::SimplexId>>
485 lowerComponentId{};
486
487 // We retrieve the lower and upper components if we want them
488 for(ttk::SimplexId i = 0; i < (SimplexId)upperNeighbors.size(); i++) {
489 it = upperComponentId.find(upperList[i]);
490 if(it != upperComponentId.end()) {
491 upperComponentId[upperList[i]].push_back(upperNeighbors[i]);
492 } else {
493 upperComponentId[upperList[i]]
494 = std::vector<ttk::SimplexId>(1, upperNeighbors[i]);
495 }
496 }
497 for(auto elt : upperComponentId) {
498 upperComponents->push_back(std::vector<ttk::SimplexId>());
499 for(ttk::SimplexId i = 0; i < (ttk::SimplexId)elt.second.size(); i++) {
500 upperComponents->back().push_back(elt.second.at(i));
501 }
502 }
503
504 for(ttk::SimplexId i = 0; i < (SimplexId)lowerNeighbors.size(); i++) {
505 it = lowerComponentId.find(lowerList[i]);
506 if(it != lowerComponentId.end()) {
507 lowerComponentId[lowerList[i]].push_back(lowerNeighbors[i]);
508 } else {
509 lowerComponentId[lowerList[i]]
510 = std::vector<ttk::SimplexId>(1, lowerNeighbors[i]);
511 }
512 }
513 for(auto elt : lowerComponentId) {
514 lowerComponents->push_back(std::vector<ttk::SimplexId>());
515 for(ttk::SimplexId i = 0; i < (ttk::SimplexId)elt.second.size(); i++) {
516 lowerComponents->back().push_back(elt.second.at(i));
517 }
518 }
519
520 if(debugLevel_ >= (int)(debug::Priority::VERBOSE)) {
521 printMsg("Vertex #" + std::to_string(vertexId)
522 + ": lowerLink-#CC=" + std::to_string(lowerComponentId.size())
523 + " upperLink-#CC=" + std::to_string(upperComponentId.size()),
525 }
526
527 return 0;
528}
529
530template <class triangulationType>
532 const SimplexId &vertexId,
533 const SimplexId *const offsets,
534 const triangulationType *triangulation,
535 std::vector<std::vector<ttk::SimplexId>> *upperComponents,
536 std::vector<std::vector<ttk::SimplexId>> *lowerComponents) const {
537
538 bool isLowerOnBoundary = false, isUpperOnBoundary = false;
539 std::vector<std::vector<ttk::SimplexId>> localUpperComponents;
540 std::vector<std::vector<ttk::SimplexId>> localLowerComponents;
541 if(upperComponents == nullptr) {
542 upperComponents = &localUpperComponents;
543 }
544 if(lowerComponents == nullptr) {
545 lowerComponents = &localLowerComponents;
546 }
547 getLowerUpperComponents(vertexId, offsets, triangulation, isLowerOnBoundary,
548 isUpperOnBoundary, upperComponents, lowerComponents);
549 ttk::SimplexId const lowerComponentNumber = lowerComponents->size();
550 ttk::SimplexId const upperComponentNumber = upperComponents->size();
551
552 if(dimension_ == 1) {
553 if(lowerComponentNumber == 0 && upperComponentNumber != 0) {
554 return (char)(CriticalType::Local_minimum);
555 } else if(lowerComponentNumber != 0 && upperComponentNumber == 0) {
556 return (char)(CriticalType::Local_maximum);
557 } else if(lowerComponentNumber == 1 && upperComponentNumber == 1) {
558 return (char)(CriticalType::Regular);
559 }
560 return (char)(CriticalType::Saddle1);
561 }
562
563 if(lowerComponentNumber == 0 && upperComponentNumber == 1) {
564 return (char)(CriticalType::Local_minimum);
565 } else if(lowerComponentNumber == 1 && upperComponentNumber == 0) {
566 return (char)(CriticalType::Local_maximum);
567 } else if(lowerComponentNumber == 1 && upperComponentNumber == 1) {
568
569 if((dimension_ == 3) && (triangulation->isVertexOnBoundary(vertexId))) {
570 // special case of boundary saddles
571 if((isUpperOnBoundary) && (!isLowerOnBoundary))
572 return (char)(CriticalType::Saddle1);
573 if((!isUpperOnBoundary) && (isLowerOnBoundary))
574 return (char)(CriticalType::Saddle2);
575 }
576
577 // regular point
578 return (char)(CriticalType::Regular);
579 } else {
580 // saddles
581 if(dimension_ == 2 || dimension_ == 1) {
582 if((lowerComponentNumber == 2 && upperComponentNumber == 1)
583 || (lowerComponentNumber == 1 && upperComponentNumber == 2)
584 || (lowerComponentNumber == 2 && upperComponentNumber == 2)) {
585 // regular saddle
586 return (char)(CriticalType::Saddle1);
587 } else {
588 // monkey saddle, saddle + extremum
589 return (char)(CriticalType::Degenerate);
590 // NOTE: you may have multi-saddles on the boundary in that
591 // configuration
592 // to make this computation 100% correct, one would need to
593 // disambiguate boundary from interior vertices
594 }
595 } else if(dimension_ == 3) {
596 if(lowerComponentNumber == 2 && upperComponentNumber == 1) {
597 return (char)(CriticalType::Saddle1);
598 } else if(lowerComponentNumber == 1 && upperComponentNumber == 2) {
599 return (char)(CriticalType::Saddle2);
600 } else {
601 // monkey saddle, saddle + extremum
602 return (char)(CriticalType::Degenerate);
603 // NOTE: we may have a similar effect in 3D (TODO)
604 }
605 }
606 }
607
608 // -2: regular points
609 return (char)(CriticalType::Regular);
610}
611
612template <class triangulationType>
614 const triangulationType *ttkNotUsed(triangulation)) {
615 if(BackEnd == BACKEND::PROGRESSIVE_TOPOLOGY
616 && !std::is_same<ttk::ImplicitWithPreconditions, triangulationType>::value
617 && !std::is_same<ttk::ImplicitNoPreconditions, triangulationType>::value) {
618
620 printWrn("Explicit, Compact or Periodic");
621 printWrn("triangulation detected.");
622 printWrn("Defaulting to the generic backend.");
624
625 BackEnd = BACKEND::GENERIC;
626 }
627
628#ifdef TTK_ENABLE_MPI
629 if((ttk::hasInitializedMPI()) && (ttk::isRunningWithMPI())) {
631 printWrn("MPI run detected.");
632 printWrn("Defaulting to the generic backend.");
634
635 BackEnd = BACKEND::GENERIC;
636 }
637#endif // TTK_ENABLE_MPI
638}
#define ttkNotUsed(x)
Mark function/method parameters that are not used in the function body at all.
Definition BaseClass.h:47
AbstractTriangulation is an interface class that defines an interface for efficient traversal methods...
virtual SimplexId getNumberOfVertices() const
virtual int getDimensionality() const
Minimalist debugging class.
Definition Debug.h:88
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:149
ImplicitTriangulation is a class that provides time and memory efficient traversal methods on triangu...
TTK processing package for progressive Topological Data Analysis.
TTK processing package for the computation of critical points in PL scalar fields defined on PL manif...
void checkProgressivityRequirement(const triangulationType *triangulation)
int executeProgressive(const SimplexId *const offsets, const triangulationType *triangulation)
void preconditionTriangulation(AbstractTriangulation *triangulation)
void setVertexLinkEdgeLists(const std::vector< std::vector< std::pair< SimplexId, SimplexId > > > *edgeList)
int setVertexNumber(const SimplexId &vertexNumber)
const std::vector< std::vector< std::pair< SimplexId, SimplexId > > > * vertexLinkEdgeLists_
void setOutput(std::vector< std::pair< SimplexId, char > > *criticalPoints)
int execute(const SimplexId *const offsets, const triangulationType *triangulation)
int executeLegacy(const SimplexId *const offsets, const triangulationType *triangulation)
char getCriticalType(const SimplexId &vertexId, const SimplexId *const offsets, const triangulationType *triangulation, std::vector< std::vector< ttk::SimplexId > > *upperComponents=nullptr, std::vector< std::vector< ttk::SimplexId > > *lowerComponents=nullptr) const
void setDomainDimension(const int &dimension)
int getLowerUpperComponents(const SimplexId vertexId, const SimplexId *const offsets, const triangulationType *triangulation, bool &isLowerOnBoundary, bool &isUpperOnBoundary, std::vector< std::vector< ttk::SimplexId > > *upperComponents, std::vector< std::vector< ttk::SimplexId > > *lowerComponents) const
std::vector< std::pair< SimplexId, char > > * criticalPoints_
double getElapsedTime()
Definition Timer.h:15
static UnionFind * makeUnion(UnionFind *uf0, UnionFind *uf1)
Definition UnionFind.h:40
The Topology ToolKit.
COMMON_EXPORTS int MPIrank_
Definition BaseClass.cpp:9
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/|__ _|"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)