TTK
Loading...
Searching...
No Matches
DiscreteVectorField_Template.h
Go to the documentation of this file.
1#pragma once
2
3#include <DiscreteGradient.h>
5
10
11template <typename dataType, typename triangulationType>
13 const std::vector<Cell> &vpath,
14 const triangulationType &triangulation) const {
15 // Function will trace the provided vpath to calculate something similar to
16 // 'persistence' value
17 // Loop over vpath
18 float result = 0;
19 const SimplexId numberOfCellsInPath = vpath.size();
20#ifndef TTK_ENABLE_KAMIKAZE
21 if(numberOfCellsInPath > 0) {
22 // Validating starting saddle assumption
23 if(vpath[0].dim_ != 1 && this->dimensionality_ == 2) {
24 this->printErr(
25 "Persistence calculation must start with a critical saddle(dim=1)");
26 }
27 }
28#endif
29 for(SimplexId i = 0; i < numberOfCellsInPath - 1; i++) {
30 Cell firstCell = vpath[i];
31 vectorValue firstCenter;
32 vectorValue firstVector;
33 Cell secondCell = vpath[i + 1];
34 vectorValue secondCenter;
35 vectorValue secondVector;
36 SimplexId v;
37 vectorValue pointAdd;
38 // Compute average point and vector value depending on dimension of cell
39 switch(firstCell.dim_) {
40 case 0:
41 triangulation.getVertexPoint(
42 firstCell.id_, firstCenter[0], firstCenter[1], firstCenter[2]);
43 firstVector = getVectorValueAt<dataType>(firstCell.id_);
44 break;
45 case 1:
46 triangulation.getEdgeVertex(firstCell.id_, 0, v);
47 triangulation.getVertexPoint(
48 v, firstCenter[0], firstCenter[1], firstCenter[2]);
49 firstVector = getVectorValueAt<dataType>(v);
50 triangulation.getEdgeVertex(firstCell.id_, 1, v);
51 triangulation.getVertexPoint(v, pointAdd[0], pointAdd[1], pointAdd[2]);
52 firstCenter = firstCenter + pointAdd;
53 firstCenter = firstCenter / 2;
54 firstVector = firstVector + getVectorValueAt<dataType>(v);
55 break;
56 case 2:
57 triangulation.getTriangleVertex(firstCell.id_, 0, v);
58 triangulation.getVertexPoint(
59 v, firstCenter[0], firstCenter[1], firstCenter[2]);
60 firstVector = getVectorValueAt<dataType>(v);
61 triangulation.getTriangleVertex(firstCell.id_, 1, v);
62 triangulation.getVertexPoint(v, pointAdd[0], pointAdd[1], pointAdd[2]);
63 firstCenter = firstCenter + pointAdd;
64 firstVector = firstVector + getVectorValueAt<dataType>(v);
65 triangulation.getTriangleVertex(firstCell.id_, 2, v);
66 triangulation.getVertexPoint(v, pointAdd[0], pointAdd[1], pointAdd[2]);
67 firstCenter = firstCenter + pointAdd;
68 firstCenter = firstCenter / 3;
69 firstVector = firstVector + getVectorValueAt<dataType>(v);
70 break;
71 case 3:
72 triangulation.getCellVertex(firstCell.id_, 0, v);
73 triangulation.getVertexPoint(
74 v, firstCenter[0], firstCenter[1], firstCenter[2]);
75 firstVector = getVectorValueAt<dataType>(v);
76 triangulation.getCellVertex(firstCell.id_, 1, v);
77 triangulation.getVertexPoint(v, pointAdd[0], pointAdd[1], pointAdd[2]);
78 firstCenter = firstCenter + pointAdd;
79 firstVector = firstVector + getVectorValueAt<dataType>(v);
80 triangulation.getCellVertex(firstCell.id_, 2, v);
81 triangulation.getVertexPoint(v, pointAdd[0], pointAdd[1], pointAdd[2]);
82 firstCenter = firstCenter + pointAdd;
83 firstVector = firstVector + getVectorValueAt<dataType>(v);
84 triangulation.getCellVertex(firstCell.id_, 3, v);
85 triangulation.getVertexPoint(v, pointAdd[0], pointAdd[1], pointAdd[2]);
86 firstCenter = firstCenter + pointAdd;
87 firstCenter = firstCenter / 4;
88 firstVector = firstVector + getVectorValueAt<dataType>(v);
89 break;
90 }
91 switch(secondCell.dim_) {
92 case 0:
93 triangulation.getVertexPoint(
94 secondCell.id_, secondCenter[0], secondCenter[1], secondCenter[2]);
95 secondVector = getVectorValueAt<dataType>(secondCell.id_);
96 break;
97 case 1:
98 triangulation.getEdgeVertex(secondCell.id_, 0, v);
99 triangulation.getVertexPoint(
100 v, secondCenter[0], secondCenter[1], secondCenter[2]);
101 secondVector = getVectorValueAt<dataType>(v);
102 triangulation.getEdgeVertex(secondCell.id_, 1, v);
103 triangulation.getVertexPoint(v, pointAdd[0], pointAdd[1], pointAdd[2]);
104 secondCenter = secondCenter + pointAdd;
105 secondCenter = secondCenter / 2;
106 secondVector = secondVector + getVectorValueAt<dataType>(v);
107 break;
108 case 2:
109 triangulation.getTriangleVertex(secondCell.id_, 0, v);
110 triangulation.getVertexPoint(
111 v, secondCenter[0], secondCenter[1], secondCenter[2]);
112 secondVector = getVectorValueAt<dataType>(v);
113 triangulation.getTriangleVertex(secondCell.id_, 1, v);
114 triangulation.getVertexPoint(v, pointAdd[0], pointAdd[1], pointAdd[2]);
115 secondCenter = secondCenter + pointAdd;
116 secondVector = secondVector + getVectorValueAt<dataType>(v);
117 triangulation.getTriangleVertex(secondCell.id_, 2, v);
118 triangulation.getVertexPoint(v, pointAdd[0], pointAdd[1], pointAdd[2]);
119 secondCenter = secondCenter + pointAdd;
120 secondCenter = secondCenter / 3;
121 secondVector = secondVector + getVectorValueAt<dataType>(v);
122 break;
123 case 3:
124 triangulation.getCellVertex(secondCell.id_, 0, v);
125 triangulation.getVertexPoint(
126 v, secondCenter[0], secondCenter[1], secondCenter[2]);
127 secondVector = getVectorValueAt<dataType>(v);
128 triangulation.getCellVertex(secondCell.id_, 1, v);
129 triangulation.getVertexPoint(v, pointAdd[0], pointAdd[1], pointAdd[2]);
130 secondCenter = secondCenter + pointAdd;
131 secondVector = secondVector + getVectorValueAt<dataType>(v);
132 triangulation.getCellVertex(secondCell.id_, 2, v);
133 triangulation.getVertexPoint(v, pointAdd[0], pointAdd[1], pointAdd[2]);
134 secondCenter = secondCenter + pointAdd;
135 secondVector = secondVector + getVectorValueAt<dataType>(v);
136 triangulation.getCellVertex(secondCell.id_, 3, v);
137 triangulation.getVertexPoint(v, pointAdd[0], pointAdd[1], pointAdd[2]);
138 secondCenter = secondCenter + pointAdd;
139 secondCenter = secondCenter / 4;
140 secondVector = secondVector + getVectorValueAt<dataType>(v);
141 break;
142 }
143 vectorValue changeDirection;
144 // Handle discrete vector direction(toward higher dim)
145 if(firstCell.dim_ < secondCell.dim_) {
146 changeDirection = secondCenter - firstCenter;
147 } else {
148 changeDirection = firstCenter - secondCenter;
149 }
150 vectorValue avgVector = (firstVector + secondVector) / 2;
151 if(i % 2 == 0) { // Handle alternation in Vpath calculation
152 result -= Geometry::dotProduct(changeDirection.data(), avgVector.data());
153 } else {
154 result += Geometry::dotProduct(changeDirection.data(), avgVector.data());
155 }
156
157 } // End VPath loop
158
159 return result;
160}
161
162template <typename dataType, typename triangulationType>
163int DiscreteVectorField::buildField(const triangulationType &triangulation) {
164
165 // set member variables at each buildField() call
166 this->dimensionality_ = triangulation.getCellVertexNumber(0) - 1;
167 this->numberOfVertices_ = triangulation.getNumberOfVertices();
168
169 this->vectors_ = &this->localVectors_;
170 // allocate field memory
171 this->initMemory(triangulation);
172
173 Timer tm{};
174 // compute discrete pairs
175 this->processOutwardStars<dataType, triangulationType>(triangulation);
176
177 this->printMsg(
178 "Built discrete vectors", 1.0, tm.getElapsedTime(), this->threadNumber_);
179
180 return 0;
181}
182
183template <typename dataType, typename triangulationType>
185 const std::array<std::vector<SimplexId>, 4> &criticalCellsByDim,
186 std::vector<std::array<float, 3>> &points,
187 std::vector<char> &cellDimensions,
188 std::vector<SimplexId> &cellIds,
189 std::vector<char> &isOnBoundary,
190 std::vector<SimplexId> &PLVertexIdentifiers,
191 const triangulationType &triangulation) const {
192
193 std::array<size_t, 5> partSums{};
194 for(size_t i = 0; i < criticalCellsByDim.size(); ++i) {
195 partSums[i + 1] = partSums[i] + criticalCellsByDim[i].size();
196 }
197
198 const auto nCritPoints = partSums.back();
199
200 points.resize(nCritPoints);
201 cellDimensions.resize(nCritPoints);
202 cellIds.resize(nCritPoints);
203 isOnBoundary.resize(nCritPoints);
204 PLVertexIdentifiers.resize(nCritPoints);
205
206 for(size_t i = 0; i < criticalCellsByDim.size(); ++i) {
207#ifdef TTK_ENABLE_OPENMP
208#pragma omp parallel for num_threads(threadNumber_)
209#endif // TTK_ENABLE_OPENMP
210 for(size_t j = 0; j < criticalCellsByDim[i].size(); ++j) {
211 const SimplexId cellId = criticalCellsByDim[i][j];
212 const int cellDim = i;
213 const auto o{partSums[i] + j};
214
215 triangulation.getCellIncenter(cellId, i, points[o].data());
216 cellDimensions[o] = cellDim;
217#ifdef TTK_ENABLE_MPI
218 ttk::SimplexId globalId{-1};
219 triangulation.getDistributedGlobalCellId(cellId, cellDim, globalId);
220 cellIds[o] = globalId;
221#else
222 cellIds[o] = cellId;
223#endif // TTK_ENABLE_MPI
224 const Cell cell{static_cast<int>(i), cellId};
225 isOnBoundary[o]
226 = this->isBoundary<dataType, triangulationType>(cell, triangulation);
227 PLVertexIdentifiers[o]
229 cell, triangulation);
230 }
231 }
232
233 std::vector<std::vector<std::string>> rows(this->dimensionality_ + 1);
234 for(int i = 0; i < this->dimensionality_ + 1; ++i) {
235 rows[i]
236 = std::vector<std::string>{"#" + std::to_string(i) + "-cell(s)",
237 std::to_string(criticalCellsByDim[i].size())};
238 }
239 this->printMsg(rows);
240
241 return 0;
242}
243
244template <typename dataType, typename triangulationType>
246 std::vector<std::array<float, 3>> &points,
247 std::vector<char> &cellDimensions,
248 std::vector<SimplexId> &cellIds,
249 std::vector<char> &isOnBoundary,
250 std::vector<SimplexId> &PLVertexIdentifiers,
251 const triangulationType &triangulation) const {
252
253 std::array<std::vector<SimplexId>, 4> criticalCellsByDim;
254 getCriticalPoints(criticalCellsByDim, triangulation);
256 criticalCellsByDim, points, cellDimensions, cellIds, isOnBoundary,
257 PLVertexIdentifiers, triangulation);
258
259 return 0;
260}
261
262template <typename triangulationType>
264 std::array<std::vector<SimplexId>, 4> &criticalCellsByDim,
265 const triangulationType &triangulation) const {
266
267 const auto dims{this->getNumberOfDimensions()};
268 for(int i = 0; i < dims; ++i) {
269
270 // map: store critical cell per dimension per thread
271 std::vector<std::vector<SimplexId>> critCellsPerThread(this->threadNumber_);
272 const auto numberOfCells{this->getNumberOfCells(i, triangulation)};
273
274 // use static scheduling to ensure that critical cells
275 // are sorted by id
276
277#ifdef TTK_ENABLE_OPENMP
278#pragma omp parallel for num_threads(this->threadNumber_) schedule(static)
279#endif // TTK_ENABLE_OPENMP
280 for(SimplexId j = 0; j < numberOfCells; ++j) {
281#ifdef TTK_ENABLE_OPENMP
282 const auto tid = omp_get_thread_num();
283#else
284 const auto tid = 0;
285#endif // TTK_ENABLE_OPENMP
286 // std::cout << "Testing " << i << "," << j << std::endl;
287 if(this->isCellCritical(i, j)) {
288 critCellsPerThread[tid].emplace_back(j);
289 }
290 }
291 // reduce: aggregate critical cells per thread
292 criticalCellsByDim[i] = std::move(critCellsPerThread[0]);
293 for(size_t j = 1; j < critCellsPerThread.size(); ++j) {
294 const auto &vec{critCellsPerThread[j]};
295 criticalCellsByDim[i].insert(
296 criticalCellsByDim[i].end(), vec.begin(), vec.end());
297 }
298 }
299
300 return 0;
301}
302
303template <typename triangulationType>
305 const int dimension, const triangulationType &triangulation) const {
306
307 if(dimension > this->dimensionality_ || dimension < 0) {
308 return -1;
309 }
310
311 switch(dimension) {
312 case 0:
313 return triangulation.getNumberOfVertices();
314 break;
315
316 case 1:
317 return triangulation.getNumberOfEdges();
318 break;
319
320 case 2:
321 return triangulation.getNumberOfTriangles();
322 break;
323
324 case 3:
325 return triangulation.getNumberOfCells();
326 break;
327 }
328
329 return -1;
330}
331
332template <typename dataType, typename triangulationType>
333inline bool DiscreteVectorField::compare(const triangulationType &triangulation,
334 SimplexId vertexA,
335 SimplexId vertexB,
336 float &weightValue) const {
337 weightValue = 0; // Silences unassignment warnings
338
339#ifndef TTK_ENABLE_KAMIKAZE
340 if(vertexA == -1 || vertexB == -1) {
341 this->printErr("Passed Null value to compare()");
342 return vertexA < vertexB;
343 }
344 SimplexId numberOfVertices = triangulation.getNumberOfVertices();
345 if(vertexA < 0 || vertexB < 0 || vertexA >= numberOfVertices
346 || vertexB >= numberOfVertices) {
347 std::cout << vertexA << "," << vertexB << std::endl;
348 this->printErr("Passed invalid vertex values");
349 return vertexA < vertexB;
350 }
351#endif
352 // Compute the edge direction a->b (c(b)-c(a))
353 vectorValue locationA;
354 triangulation.getVertexPoint(
355 vertexA, locationA[0], locationA[1], locationA[2]);
356
357 vectorValue locationB;
358 triangulation.getVertexPoint(
359 vertexB, locationB[0], locationB[1], locationB[2]);
360 vectorValue edgeDirection = locationB - locationA;
361
362 // Averaged vector computation
363 auto averageVector = (getVectorValueAt<dataType>(vertexA)
365 / 2;
366 weightValue
367 = Geometry::dotProduct(edgeDirection.data(), averageVector.data());
368
369 if(weightValue > 0)
370 return true;
371 if(weightValue < 0)
372 return false;
373 return vertexA < vertexB; // Tiebreaking scheme on id, lower ID 'wins'
374}
375
376template <typename dataType, typename triangulationType>
377inline void DiscreteVectorField::OutwardStar(
378 outwardStarType &os,
379 const SimplexId a,
380 const triangulationType &triangulation) const {
381
382 // make sure that os is cleared
383 for(auto &vec : os) {
384 vec.clear();
385 }
386
387 // a belongs to its outward star
388 os[0].emplace_back(CellOutExt{0, a});
389
390 // store outward edges
391 const auto nedges = triangulation.getVertexEdgeNumber(a);
392 os[1].reserve(nedges);
393 for(SimplexId i = 0; i < nedges; i++) {
394 SimplexId edgeId;
395 triangulation.getVertexEdge(a, i, edgeId);
396 SimplexId vertexId;
397 triangulation.getEdgeVertex(edgeId, 0, vertexId);
398 if(vertexId == a) {
399 triangulation.getEdgeVertex(edgeId, 1, vertexId);
400 }
401 float edgeWeight{0};
403 triangulation, a, vertexId, edgeWeight)) {
404 os[1].emplace_back(CellOutExt{1,
405 edgeId,
406 {vertexId, -1, -1},
407 {-(edgeWeight), -INFINITY, -INFINITY},
408 {}});
409 }
410 }
411
412 if(os[1].size() < 2) {
413 // at least two edges in the outward star for one triangle
414 return;
415 }
416
417 const auto processTriangle
418 = [&](const SimplexId triangleId, const SimplexId v0, const SimplexId v1,
419 const SimplexId v2) {
420 std::array<SimplexId, 3> lowVerts{-1, -1, -1};
421 if(v0 == a) {
422 lowVerts[0] = v1;
423 lowVerts[1] = v2;
424 } else if(v1 == a) {
425 lowVerts[0] = v0;
426 lowVerts[1] = v2;
427 } else if(v2 == a) {
428 lowVerts[0] = v0;
429 lowVerts[1] = v1;
430 }
431
432 float edgeWeight1, edgeWeight2;
434 triangulation, a, lowVerts[0], edgeWeight1)
436 triangulation, a, lowVerts[1],
437 edgeWeight2)) { // triangle in outwardStar
438 uint8_t j{}, k{};
439 // store edges indices(in outward star) of current triangle
440
441 std::array<uint8_t, 3>
442 faces{}; // Might need to find faces differently(simply replace if
443 // condition)
444 for(const auto &e : os[1]) {
445 if(e.lowVerts_[0] == lowVerts[0] || e.lowVerts_[0] == lowVerts[1]) {
446 faces[k++] = j;
447 }
448 j++;
449 }
450
451 std::array<float, 3> lowVertWeights
452 = {-(edgeWeight1), -(edgeWeight2), -INFINITY};
453 if(edgeWeight2
454 < edgeWeight1) { // Flip order if necessary (decreasing negated)
455 lowVertWeights[0] = -(edgeWeight2);
456 lowVertWeights[1] = -(edgeWeight1);
457 }
458 os[2].emplace_back(
459 CellOutExt{2, triangleId, lowVerts, lowVertWeights, faces});
460 }
461 };
462
463 if(dimensionality_ == 2) {
464 // store outward triangles
465
466 // use optimised triangulation methods:
467 // getVertexStar instead of getVertexTriangle
468 // getCellVertex instead of getTriangleVertex
469 const auto ncells = triangulation.getVertexStarNumber(a);
470 os[2].reserve(ncells);
471 for(SimplexId i = 0; i < ncells; ++i) {
472 SimplexId cellId;
473 triangulation.getVertexStar(a, i, cellId);
474 SimplexId v0{}, v1{}, v2{};
475 triangulation.getCellVertex(cellId, 0, v0);
476 triangulation.getCellVertex(cellId, 1, v1);
477 triangulation.getCellVertex(cellId, 2, v2);
478 processTriangle(cellId, v0, v1, v2);
479 }
480 } else if(dimensionality_ == 3) {
481 // store outward triangles
482 const auto ntri = triangulation.getVertexTriangleNumber(a);
483 os[2].reserve(ntri);
484 for(SimplexId i = 0; i < ntri; i++) {
485 SimplexId triangleId;
486 triangulation.getVertexTriangle(a, i, triangleId);
487 SimplexId v0{}, v1{}, v2{};
488 triangulation.getTriangleVertex(triangleId, 0, v0);
489 triangulation.getTriangleVertex(triangleId, 1, v1);
490 triangulation.getTriangleVertex(triangleId, 2, v2);
491 processTriangle(triangleId, v0, v1, v2);
492 }
493
494 // at least three triangles in the outward star for one tetra
495 if(os[2].size() >= 3) {
496 // store outward tetra
497 const auto ncells = triangulation.getVertexStarNumber(a);
498 os[3].reserve(ncells);
499 for(SimplexId i = 0; i < ncells; ++i) {
500 SimplexId cellId;
501 triangulation.getVertexStar(a, i, cellId);
502 std::array<SimplexId, 3> lowVerts{-1, -1, -1};
503 SimplexId v0{}, v1{}, v2{}, v3{};
504 triangulation.getCellVertex(cellId, 0, v0);
505 triangulation.getCellVertex(cellId, 1, v1);
506 triangulation.getCellVertex(cellId, 2, v2);
507 triangulation.getCellVertex(cellId, 3, v3);
508 if(v0 == a) {
509 lowVerts[0] = v1;
510 lowVerts[1] = v2;
511 lowVerts[2] = v3;
512 } else if(v1 == a) {
513 lowVerts[0] = v0;
514 lowVerts[1] = v2;
515 lowVerts[2] = v3;
516 } else if(v2 == a) {
517 lowVerts[0] = v0;
518 lowVerts[1] = v1;
519 lowVerts[2] = v3;
520 } else if(v3 == a) {
521 lowVerts[0] = v0;
522 lowVerts[1] = v1;
523 lowVerts[2] = v2;
524 }
525 // Need to check each of the edges individually with compare function
526 float eWeight0, eWeight1, eWeight2;
528 triangulation, a, lowVerts[0], eWeight0)
530 triangulation, a, lowVerts[1], eWeight1)
532 triangulation, a, lowVerts[2], eWeight2)) { // tetra in outwardStar
533
534 uint8_t j{}, k{};
535 // store triangles indices of current tetra
536 std::array<uint8_t, 3> faces{};
537 for(const auto &t : os[2]) {
538 // lowVerts & t.lowVerts are not ordered, so need to check if
539 // t.lowVerts has both elements in lowVerts
540 bool vert1Found
541 = std::find(lowVerts.begin(), lowVerts.end(), t.lowVerts_[0])
542 != lowVerts.end();
543 bool vert2Found
544 = std::find(lowVerts.begin(), lowVerts.end(), t.lowVerts_[1])
545 != lowVerts.end();
546
547 if(vert1Found && vert2Found) {
548 faces[k++] = j;
549 }
550 j++;
551 }
552 std::array<float, 3> lowVertWeights
553 = {-(eWeight0), -(eWeight1), -(eWeight2)};
554 std::sort(lowVertWeights.rbegin(),
555 lowVertWeights
556 .rend()); // Sort in decreasing order e.g.,(-1, -2, -3)
557 os[3].emplace_back(
558 CellOutExt{3, cellId, lowVerts, lowVertWeights, faces});
559 }
560 }
561 }
562 }
563}
564
565template <typename triangulationType>
566inline void DiscreteVectorField::pairCells(
567 CellOutExt &alpha, CellOutExt &beta, const triangulationType &triangulation) {
568#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
569 char localBId{0}, localAId{0};
570 SimplexId a{}, b{};
571
572 if(beta.dim_ == 1) {
573
574 for(SimplexId i = 0; i < 2; ++i) {
575 triangulation.getEdgeVertex(beta.id_, i, a);
576 if(a == alpha.id_) {
577 localAId = i;
578 break;
579 }
580 }
581 const auto nedges = triangulation.getVertexEdgeNumber(alpha.id_);
582 for(SimplexId i = 0; i < nedges; ++i) {
583 triangulation.getVertexEdge(alpha.id_, i, b);
584 if(b == beta.id_) {
585 localBId = i;
586 break;
587 }
588 }
589 } else if(beta.dim_ == 2) {
590 for(SimplexId i = 0; i < 3; ++i) {
591 triangulation.getTriangleEdge(beta.id_, i, a);
592 if(a == alpha.id_) {
593 localAId = i;
594 break;
595 }
596 }
597 const auto ntri = triangulation.getEdgeTriangleNumber(alpha.id_);
598 for(SimplexId i = 0; i < ntri; ++i) {
599 triangulation.getEdgeTriangle(alpha.id_, i, b);
600 if(b == beta.id_) {
601 localBId = i;
602 break;
603 }
604 }
605 } else {
606 for(SimplexId i = 0; i < 4; ++i) {
607 triangulation.getCellTriangle(beta.id_, i, a);
608 if(a == alpha.id_) {
609 localAId = i;
610 break;
611 }
612 }
613 const auto ntetra = triangulation.getTriangleStarNumber(alpha.id_);
614 for(SimplexId i = 0; i < ntetra; ++i) {
615 triangulation.getTriangleStar(alpha.id_, i, b);
616 if(b == beta.id_) {
617 localBId = i;
618 break;
619 }
620 }
621 }
622 (*vectors_)[2 * alpha.dim_][alpha.id_] = localBId;
623 (*vectors_)[2 * alpha.dim_ + 1][beta.id_] = localAId;
624#else
625 TTK_FORCE_USE(triangulation);
626 (*vectors_)[2 * alpha.dim_][alpha.id_] = beta.id_;
627 (*vectors_)[2 * alpha.dim_ + 1][beta.id_] = alpha.id_;
628#endif // TTK_ENABLE_DCG_OPTIMIZE_MEMORY
629 alpha.paired_ = true;
630 beta.paired_ = true;
631}
632
633template <typename dataType, typename triangulationType>
634int DiscreteVectorField::processOutwardStars(
635 const triangulationType &triangulation) {
636
637 /* Compute discrete vector field */
638
639 auto nverts = triangulation.getNumberOfVertices();
640
641 // Comparison function for Cells inside priority queues
642 const auto orderCells
643 = [&](const CellOutExt &a, const CellOutExt &b) -> bool {
644 return a.lowVertWeights_ > b.lowVertWeights_;
645 };
646
647 // Type alias for priority queues
648 using pqType
649 = std::priority_queue<std::reference_wrapper<CellOutExt>,
650 std::vector<std::reference_wrapper<CellOutExt>>,
651 decltype(orderCells)>;
652
653 // To reduce allocations, priority queues and outwardStar objects are
654 // cleaned & reused between iterations.
655
656 // Priority queues are pushed at the beginning and popped at the
657 // end. To pop the minimum, elements should be sorted in a
658 // decreasing order.
659 pqType pqZero{orderCells}, pqOne{orderCells};
660
661 // store outward star structure
662 outwardStarType Lx;
663
664#ifdef TTK_ENABLE_OPENMP
665#pragma omp parallel for num_threads(threadNumber_) \
666 firstprivate(Lx, pqZero, pqOne)
667#endif // TTK_ENABLE_OPENMP
668 for(SimplexId x = 0; x < nverts; x++) {
669
670 // clear priority queues (they should be empty at the end of the
671 // previous iteration)
672 while(!pqZero.empty()) {
673 pqZero.pop();
674 }
675 while(!pqOne.empty()) {
676 pqOne.pop();
677 }
678
679 // Insert into pqOne cofacets of cell c_alpha such as numUnpairedFaces == 1
680 const auto insertCofacets = [&](const CellOutExt &ca, outwardStarType &ls) {
681 if(ca.dim_ == 1) {
682 for(auto &beta : ls[2]) {
683 if(ls[1][beta.faces_[0]].id_ == ca.id_
684 || ls[1][beta.faces_[1]].id_ == ca.id_) {
685 // edge ca belongs to triangle beta
686 if(numUnpairedFacesTriangle(beta, ls).first == 1) {
687 pqOne.push(beta);
688 }
689 }
690 }
691
692 } else if(ca.dim_ == 2) {
693 for(auto &beta : ls[3]) {
694 if(ls[2][beta.faces_[0]].id_ == ca.id_
695 || ls[2][beta.faces_[1]].id_ == ca.id_
696 || ls[2][beta.faces_[2]].id_ == ca.id_) {
697 // triangle ca belongs to tetra beta
698 if(numUnpairedFacesTetra(beta, ls).first == 1) {
699 pqOne.push(beta);
700 }
701 }
702 }
703 }
704 };
705
706 OutwardStar<dataType, triangulationType>(Lx, x, triangulation);
707 // In case the vertex is a ghost, the gradient of the
708 // simplices of its star is set to GHOST_CONNECTION
709#ifdef TTK_ENABLE_MPI
710 if(ttk::isRunningWithMPI()
711 && triangulation.getVertexRank(x) != ttk::MPIrank_) {
712 int sizeDim = Lx.size();
713 for(int i = 0; i < sizeDim; i++) {
714 int nCells = Lx[i].size();
715 for(int j = 0; j < nCells; j++) {
716 setCellToGhost(Lx[i][j].dim_, Lx[i][j].id_);
717 }
718 }
719 } else
720#endif // TTK_ENABLE_MPI
721
722 {
723 // Lx[1] empty => x is a local minimum
724 if(!Lx[1].empty()) {
725 // get delta: 1-cell (edge) with minimal negated weight value (steeper
726 // 'descent')
727 size_t minId = 0;
728 for(size_t i = 1; i < Lx[1].size(); ++i) {
729 const auto &a = Lx[1][minId].lowVertWeights_[0];
730 const auto &b = Lx[1][i].lowVertWeights_[0];
731 if(a > b) { // Check for the smallest negated weight
732 minId = i;
733 }
734 }
735
736 auto &c_delta = Lx[1][minId];
737
738 // store x (0-cell) -> delta (1-cell) V-path
739 pairCells(Lx[0][0], c_delta, triangulation);
740
741 // push every 1-cell in Lx that is not delta into pqZero
742 for(auto &alpha : Lx[1]) {
743 if(alpha.id_ != c_delta.id_) {
744 pqZero.push(alpha);
745 }
746 }
747
748 // push into pqOne every coface of delta in Lx (2-cells only,
749 // 3-cells have not any facet paired yet) such that
750 // numUnpairedFaces == 1
751 insertCofacets(c_delta, Lx);
752
753 while(!pqOne.empty() || !pqZero.empty()) {
754 while(!pqOne.empty()) {
755 auto &c_alpha = pqOne.top().get();
756 pqOne.pop();
757 auto unpairedFaces = numUnpairedFaces(c_alpha, Lx);
758 if(unpairedFaces.first == 0) {
759 pqZero.push(c_alpha);
760 } else {
761 auto &c_pair_alpha = Lx[c_alpha.dim_ - 1][unpairedFaces.second];
762
763 // store (pair_alpha) -> (alpha) V-path
764 pairCells(c_pair_alpha, c_alpha, triangulation);
765
766 // add cofaces of c_alpha and c_pair_alpha to pqOne
767 insertCofacets(c_alpha, Lx);
768 insertCofacets(c_pair_alpha, Lx);
769 }
770 }
771
772 // skip pair_alpha from pqZero:
773 // cells in pqZero are not critical if already paired
774 while(!pqZero.empty() && pqZero.top().get().paired_) {
775 pqZero.pop();
776 }
777
778 if(!pqZero.empty()) {
779 auto &c_gamma = pqZero.top().get();
780 pqZero.pop();
781
782 // gamma is a critical cell
783 // mark gamma as paired
784 c_gamma.paired_ = true;
785
786 // add cofacets of c_gamma to pqOne
787 insertCofacets(c_gamma, Lx);
788 }
789 }
790 }
791 }
792 }
793
794 return 0;
795}
796
797template <typename dataType, typename triangulationType>
799 const Cell &cell, const triangulationType &triangulation) const {
800
801 if(cell.dim_ > this->dimensionality_ || cell.dim_ < 0 || cell.id_ < 0) {
802 return false;
803 }
804
806 cell, triangulation)};
807 if(vert == -1) {
808 return false;
809 }
810 return triangulation.isVertexOnBoundary(vert);
811}
812
813template <typename triangulationType>
816 const triangulationType &triangulation,
817 bool isReverse) const {
818
819 // ensure that getPairedCell(Cell, boolean) calls are rejected
820 static_assert(
821 std::is_base_of<AbstractTriangulation, triangulationType>(),
822 "triangulationType should be an AbstractTriangulation derivative");
823
824#ifndef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
825 TTK_FORCE_USE(triangulation);
826#endif // !TTK_ENABLE_DCG_OPTIMIZE_MEMORY
827
828 if((cell.dim_ > this->dimensionality_ - 1 && !isReverse)
829 || (cell.dim_ > this->dimensionality_ && isReverse) || cell.dim_ < 0) {
830 return -1;
831 }
832
833 SimplexId id{-1};
834
835 if(cell.dim_ == 0) {
836 if(!isReverse) {
837#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
838 const auto locId{(*vectors_)[0][cell.id_]};
839 if(locId != -1) {
840 triangulation.getVertexEdge(cell.id_, locId, id);
841 }
842#else
843 id = (*vectors_)[0][cell.id_];
844#endif
845 }
846 }
847
848 else if(cell.dim_ == 1) {
849 if(isReverse) {
850#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
851 const auto locId{(*vectors_)[1][cell.id_]};
852 if(locId != -1) {
853 triangulation.getEdgeVertex(cell.id_, locId, id);
854 }
855#else
856 id = (*vectors_)[1][cell.id_];
857#endif
858 } else {
859#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
860 const auto locId{(*vectors_)[2][cell.id_]};
861 if(locId != -1) {
862 triangulation.getEdgeTriangle(cell.id_, locId, id);
863 }
864#else
865 id = (*vectors_)[2][cell.id_];
866#endif
867 }
868 }
869
870 else if(cell.dim_ == 2) {
871 if(isReverse) {
872#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
873 const auto locId{(*vectors_)[3][cell.id_]};
874 if(locId != -1) {
875 triangulation.getTriangleEdge(cell.id_, locId, id);
876 }
877#else
878 id = (*vectors_)[3][cell.id_];
879#endif
880 } else {
881#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
882 const auto locId{(*vectors_)[4][cell.id_]};
883 if(locId != -1) {
884 triangulation.getTriangleStar(cell.id_, locId, id);
885 }
886#else
887 id = (*vectors_)[4][cell.id_];
888#endif
889 }
890 }
891
892 else if(cell.dim_ == 3) {
893 if(isReverse) {
894#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
895 const auto locId{(*vectors_)[5][cell.id_]};
896 if(locId != -1) {
897 triangulation.getCellTriangle(cell.id_, locId, id);
898 }
899#else
900 id = (*vectors_)[5][cell.id_];
901#endif
902 }
903 }
904
905 return id;
906}
907
908template <typename dataType, typename triangulationType>
910 const Cell &cell,
911 std::vector<Cell> &vpath,
912 const triangulationType &triangulation,
913 const bool stopOnCycle) const {
914
915 const SimplexId numberOfVerts = triangulation.getNumberOfVertices();
916 std::vector<char> isCycle;
917 isCycle.resize(numberOfVerts, 0);
918 int numAlternates{0};
919
920 if(cell.dim_ == 0) {
921 // assume that cellId is a vertex
922 SimplexId currentId = cell.id_;
923 SimplexId connectedEdgeId;
924 do {
925 // Check if a cycle occured first
926 if(isCycle[currentId] == 0) {
927 isCycle[currentId] = 1;
928 } else {
929 if(stopOnCycle || dimensionality_ == 3) {
930 break; // Don't trace beyond (not possible in 3D)
931 }
932 // Otherwise simulate the vpath connected with new saddle.
933 if(!reverseFullOrbit) {
934 // If desired, remove vpath around the cycle
935 while(!(vpath.back() == Cell(0, currentId))) {
936 vpath.pop_back();
937 }
938 }
939
940 if(dimensionality_ == 2) {
941 // Can trace the ascending paths (pick and add smallest)
942 if(!reverseFullOrbit) {
943 const Cell vertex(0, currentId);
944 connectedEdgeId = getPairedCell(vertex, triangulation);
945 } else {
946 connectedEdgeId = vpath.back().id_;
947 vpath.pop_back(); // Added back later
948 }
949 float bestWeight{std::numeric_limits<float>::max()};
950 bool firstCellCritical{false};
951 std::vector<Cell> vpathBest;
952 int bestAlternates{0};
953 SimplexId triangleNumber
954 = triangulation.getEdgeTriangleNumber(connectedEdgeId);
955 for(SimplexId i = 0; i < triangleNumber; ++i) {
956 SimplexId triangle;
957 std::vector<Cell> newVpath;
958 newVpath.emplace_back(
959 Cell(1, connectedEdgeId)); // Add saddle to vpath for persistence
960 // calculation
961 std::vector<char> ascIsCycle;
962 const SimplexId numberOfCells = triangulation.getNumberOfCells();
963 ascIsCycle.resize(numberOfCells, 0);
964 triangulation.getEdgeTriangle(connectedEdgeId, i, triangle);
965 Cell firstTriangle = Cell(2, triangle);
966 int alternates
968 firstTriangle, newVpath, triangulation, isCycle, ascIsCycle);
970 newVpath, triangulation);
971 if(i == 0) {
972 bestWeight = weight;
973 vpathBest = newVpath;
974 bestAlternates = alternates;
975 firstCellCritical = isCellCritical(newVpath.back());
976 } else if((!firstCellCritical && isCellCritical(newVpath.back()))
977 || (weight < bestWeight
978 && isCellCritical(newVpath.back()))) {
979 bestWeight = weight;
980 vpathBest = newVpath;
981 bestAlternates = alternates;
982 firstCellCritical = true;
983 }
984 }
985 // Update the vpath with new path
986 for(Cell newCell : vpathBest) {
987 vpath.emplace_back(newCell);
988 }
989 numAlternates = 1 + bestAlternates;
990 break;
991 }
992 }
993 // add a vertex
994 const Cell vertex(0, currentId);
995 vpath.emplace_back(vertex);
996
997 if(isCellCritical(vertex)) {
998 break;
999 }
1000
1001 connectedEdgeId = getPairedCell(vertex, triangulation);
1002 if(connectedEdgeId == -1) {
1003 break;
1004 }
1005
1006 // add an edge
1007 const Cell edge(1, connectedEdgeId);
1008 vpath.emplace_back(edge);
1009
1010 if(isCellCritical(edge)) {
1011 break;
1012 }
1013
1014 for(int i = 0; i < 2; ++i) {
1015 SimplexId vertexId;
1016 triangulation.getEdgeVertex(connectedEdgeId, i, vertexId);
1017
1018 if(vertexId != currentId) {
1019 currentId = vertexId;
1020 break;
1021 }
1022 }
1023
1024 } while(connectedEdgeId != -1);
1025 }
1026
1027 return numAlternates;
1028}
1029
1030template <typename dataType, typename triangulationType>
1032 const Cell &cell,
1033 std::vector<Cell> &vpath,
1034 const triangulationType &triangulation,
1035 std::vector<char> &previousDescPaths,
1036 std::vector<char> &previousAscPaths) const {
1037
1038 const SimplexId numberOfVerts = triangulation.getNumberOfVertices();
1039 std::vector<char> isCycle;
1040 isCycle.resize(numberOfVerts, 0);
1041 int numAlternates{0};
1042
1043 if(cell.dim_ == 0) {
1044 // assume that cellId is a vertex
1045 SimplexId currentId = cell.id_;
1046 SimplexId connectedEdgeId;
1047 do {
1048 // Check if a cycle occured first
1049 if(isCycle[currentId] == 0) {
1050 if(previousDescPaths[currentId] == 1) {
1051 if(vpath.size() == 0) {
1052 // add a vertex
1053 const Cell vertex(0, currentId);
1054 vpath.emplace_back(vertex);
1055 }
1056 break; // Already seen in previous recursive loop
1057 }
1058 isCycle[currentId] = 1;
1059 previousDescPaths[currentId] = 1;
1060 } else {
1061 if(dimensionality_ == 3) {
1062 break; // Don't trace beyond (not possible in 3D)
1063 }
1064 // Otherwise simulate the vpath connected with new saddle
1065 if(!reverseFullOrbit) {
1066 // Remove vpath around the cycle(if desired)
1067 while(!(vpath.back() == Cell(0, currentId))) {
1068 vpath.pop_back();
1069 }
1070 }
1071
1072 if(dimensionality_ == 2) {
1073 // Can trace the ascending paths (pick and add smallest)
1074 if(!reverseFullOrbit) {
1075 const Cell vertex(0, currentId);
1076 connectedEdgeId = getPairedCell(vertex, triangulation);
1077 } else {
1078 connectedEdgeId = vpath.back().id_;
1079 vpath.pop_back(); // Added back later
1080 }
1081 float bestWeight{std::numeric_limits<float>::max()};
1082 bool firstCellCritical{false};
1083 std::vector<Cell> vpathBest;
1084 int bestAlternates{0};
1085 SimplexId triangleNumber
1086 = triangulation.getEdgeTriangleNumber(connectedEdgeId);
1087 for(SimplexId i = 0; i < triangleNumber; ++i) {
1088 SimplexId triangle;
1089 std::vector<Cell> newVpath;
1090 newVpath.emplace_back(Cell(1, connectedEdgeId));
1091 triangulation.getEdgeTriangle(connectedEdgeId, i, triangle);
1092 Cell firstTriangle = Cell(2, triangle);
1093 int alternates
1095 firstTriangle, newVpath, triangulation, previousDescPaths,
1096 previousAscPaths);
1098 newVpath, triangulation);
1099 if(i == 0) {
1100 bestWeight = weight;
1101 vpathBest = newVpath;
1102 bestAlternates = alternates;
1103 firstCellCritical = isCellCritical(newVpath.back());
1104 } else if((!firstCellCritical && isCellCritical(newVpath.back()))
1105 || (weight < bestWeight
1106 && isCellCritical(newVpath.back()))) {
1107 bestWeight = weight;
1108 vpathBest = newVpath;
1109 bestAlternates = alternates;
1110 firstCellCritical = true;
1111 }
1112 }
1113 // Update the vpath with new path
1114 for(Cell newCell : vpathBest) {
1115 vpath.emplace_back(newCell);
1116 }
1117 numAlternates = 1 + bestAlternates;
1118 break;
1119 }
1120 }
1121 // add a vertex
1122 const Cell vertex(0, currentId);
1123 vpath.emplace_back(vertex);
1124
1125 if(isCellCritical(vertex)) {
1126 break;
1127 }
1128
1129 connectedEdgeId = getPairedCell(vertex, triangulation);
1130 if(connectedEdgeId == -1) {
1131 break;
1132 }
1133
1134 // add an edge
1135 const Cell edge(1, connectedEdgeId);
1136 vpath.emplace_back(edge);
1137
1138 if(isCellCritical(edge)) {
1139 break;
1140 }
1141
1142 for(int i = 0; i < 2; ++i) {
1143 SimplexId vertexId;
1144 triangulation.getEdgeVertex(connectedEdgeId, i, vertexId);
1145
1146 if(vertexId != currentId) {
1147 currentId = vertexId;
1148 break;
1149 }
1150 }
1151
1152 } while(connectedEdgeId != -1);
1153 }
1154
1155 return numAlternates;
1156}
1157
1158template <typename triangulationType>
1160 const Cell &saddle2,
1161 const Cell &saddle1,
1162 const std::vector<bool> &isVisited,
1163 std::vector<Cell> *const vpath,
1164 const triangulationType &triangulation,
1165 const bool stopIfMultiConnected,
1166 const bool enableCycleDetector) const {
1167
1168 // debug
1169 const SimplexId numberOfEdges = triangulation.getNumberOfEdges();
1170 std::vector<char> isCycle;
1171 if(enableCycleDetector) {
1172 isCycle.resize(numberOfEdges, 0);
1173 }
1174
1175 if(dimensionality_ == 3) {
1176 // add the 2-saddle to the path
1177 if(vpath != nullptr) {
1178 vpath->emplace_back(saddle2);
1179 }
1180
1181 SimplexId currentId = -1;
1182 {
1183 int nconnections = 0;
1184 for(int i = 0; i < 3; ++i) {
1185 SimplexId edgeId;
1186 triangulation.getTriangleEdge(saddle2.id_, i, edgeId);
1187 if(isVisited[edgeId]) {
1188 // saddle2 can be adjacent to saddle1 on the wall
1189 if(isCellCritical(Cell(1, edgeId))) {
1190 if(vpath != nullptr) {
1191 vpath->emplace_back(Cell(1, edgeId));
1192 }
1193 return false;
1194 }
1195
1196 currentId = edgeId;
1197 ++nconnections;
1198 }
1199 }
1200 if(stopIfMultiConnected && nconnections > 1) {
1201 return true;
1202 }
1203 }
1204
1205 int oldId;
1206 do {
1207
1208 // debug
1209 if(enableCycleDetector) {
1210 if(isCycle[currentId] == 0) {
1211 isCycle[currentId] = 1;
1212 } else {
1213 this->printErr("Cycle detected on the wall of 1-saddle "
1214 + std::to_string(saddle1.id_));
1215 break;
1216 }
1217 }
1218
1219 oldId = currentId;
1220
1221 // add an edge
1222 const Cell edge(1, currentId);
1223 if(vpath != nullptr) {
1224 vpath->emplace_back(edge);
1225 }
1226
1227 if(isCellCritical(edge)) {
1228 break;
1229 }
1230
1231 const SimplexId connectedTriangleId = getPairedCell(edge, triangulation);
1232
1233 // add a triangle
1234 const Cell triangle(2, connectedTriangleId);
1235 if(vpath != nullptr) {
1236 vpath->emplace_back(triangle);
1237 }
1238
1239 if(isCellCritical(triangle)) {
1240 break;
1241 }
1242
1243 int nconnections = 0;
1244 for(int i = 0; i < 3; ++i) {
1245 SimplexId edgeId;
1246 triangulation.getTriangleEdge(connectedTriangleId, i, edgeId);
1247
1248 if(isVisited[edgeId] and edgeId != oldId) {
1249 currentId = edgeId;
1250 ++nconnections;
1251 }
1252 }
1253 if(stopIfMultiConnected && nconnections > 1) {
1254 return true;
1255 }
1256
1257 // stop at convergence caused by boundary effect
1258 } while(currentId != oldId);
1259 }
1260
1261 return false;
1262}
1263
1264template <typename dataType, typename triangulationType>
1266 const Cell &cell,
1267 std::vector<Cell> &vpath,
1268 const triangulationType &triangulation,
1269 const bool stopOnCycle) const {
1270
1271 const SimplexId numberOfCells = triangulation.getNumberOfCells();
1272 std::vector<char> isCycle;
1273 isCycle.resize(numberOfCells, 0);
1274 int numAlternates{0};
1275
1276 if(dimensionality_ == 2) {
1277 if(cell.dim_ == 2) {
1278 // assume that cellId is a triangle
1279 SimplexId currentId = cell.id_;
1280 SimplexId oldId;
1281 do {
1282 oldId = currentId;
1283 if(isCycle[currentId] == 0) {
1284 isCycle[currentId] = 1;
1285 } else {
1286 // Found cycle in path, don't add triangle already seen
1287 if(stopOnCycle) {
1288 break; // Don't trace beyond cycle
1289 }
1290 // Else, simulate finding the next path on the saddle
1291 if(!reverseFullOrbit) {
1292 // First, Remove vpath around the cycle
1293 while(!(vpath.back() == Cell(2, currentId))) {
1294 vpath.pop_back();
1295 }
1296 }
1297
1298 // Can trace the descending paths (pick and add smallest)
1299 SimplexId connectedEdgeId;
1300 if(!reverseFullOrbit) {
1301 const Cell triangle(2, currentId);
1302 connectedEdgeId = getPairedCell(triangle, triangulation, true);
1303 } else {
1304 connectedEdgeId = vpath.back().id_;
1305 vpath.pop_back(); // Added back later
1306 }
1307 // vpath.emplace_back(Cell(1, connectedEdgeId));//Need to add the
1308 // saddle skipping over(handled later)
1309 float bestWeight{std::numeric_limits<float>::max()};
1310 int bestAlternates{0};
1311 bool firstCellCritical{false};
1312 std::vector<Cell> vpathBest;
1313 for(int i = 0; i < 2; ++i) {
1314 SimplexId vertex;
1315 std::vector<Cell> newVpath;
1316 newVpath.emplace_back(Cell(1, connectedEdgeId));
1317 const SimplexId numberOfVerts = triangulation.getNumberOfVertices();
1318 std::vector<char> descIsCycle;
1319 descIsCycle.resize(numberOfVerts, 0);
1320 triangulation.getEdgeVertex(connectedEdgeId, i, vertex);
1321 Cell firstVertex = Cell(0, vertex);
1322 int alternates
1324 firstVertex, newVpath, triangulation, descIsCycle, isCycle);
1326 newVpath, triangulation);
1327 if(i == 0) {
1328 bestWeight = weight;
1329 bestAlternates = alternates;
1330 vpathBest = newVpath;
1331 firstCellCritical = isCellCritical(newVpath.back());
1332 } else if((!firstCellCritical && isCellCritical(newVpath.back()))
1333 || (weight < bestWeight
1334 && isCellCritical(newVpath.back()))) {
1335 bestWeight = weight;
1336 bestAlternates = alternates;
1337 vpathBest = newVpath;
1338 firstCellCritical = true;
1339 }
1340 }
1341 // Update the vpath with new path
1342 for(Cell newCell : vpathBest) {
1343 vpath.emplace_back(newCell);
1344 }
1345 numAlternates = 1 + bestAlternates;
1346 break;
1347 }
1348
1349 // add a triangle
1350 const Cell triangle(2, currentId);
1351 vpath.emplace_back(triangle);
1352
1353 if(isCellCritical(triangle)) {
1354 break;
1355 }
1356
1357 const SimplexId connectedEdgeId
1358 = getPairedCell(triangle, triangulation, true);
1359 if(connectedEdgeId == -1) {
1360 break;
1361 }
1362
1363 // add an edge
1364 const Cell edge(1, connectedEdgeId);
1365 vpath.emplace_back(edge);
1366
1367 if(isCellCritical(edge)) {
1368 break;
1369 }
1370
1371 const SimplexId starNumber
1372 = triangulation.getEdgeStarNumber(connectedEdgeId);
1373 for(SimplexId i = 0; i < starNumber; ++i) {
1374 SimplexId starId;
1375 triangulation.getEdgeStar(connectedEdgeId, i, starId);
1376
1377 if(starId != currentId) {
1378 currentId = starId;
1379 break;
1380 }
1381 }
1382
1383 // stop at convergence caused by boundary effect
1384 } while(currentId != oldId);
1385 }
1386 } else if(dimensionality_ == 3) {
1387 if(cell.dim_ == 3) {
1388 // assume that cellId is a tetra
1389 SimplexId currentId = cell.id_;
1390 SimplexId oldId;
1391 do {
1392
1393 if(isCycle[currentId] == 0) {
1394 isCycle[currentId] = 1;
1395 } else {
1396 break; // Cycle detected
1397 }
1398
1399 oldId = currentId;
1400
1401 // add a tetra
1402 const Cell tetra(3, currentId);
1403 vpath.emplace_back(tetra);
1404
1405 if(isCellCritical(tetra)) {
1406 break;
1407 }
1408
1409 const SimplexId connectedTriangleId
1410 = getPairedCell(tetra, triangulation, true);
1411 if(connectedTriangleId == -1) {
1412 break;
1413 }
1414
1415 // add a triangle
1416 const Cell triangle(2, connectedTriangleId);
1417 vpath.emplace_back(triangle);
1418
1419 if(isCellCritical(triangle)) {
1420 break;
1421 }
1422
1423 const SimplexId starNumber
1424 = triangulation.getTriangleStarNumber(connectedTriangleId);
1425 for(SimplexId i = 0; i < starNumber; ++i) {
1426 SimplexId starId;
1427 triangulation.getTriangleStar(connectedTriangleId, i, starId);
1428
1429 if(starId != currentId) {
1430 currentId = starId;
1431 break;
1432 }
1433 }
1434
1435 // stop at convergence caused by boundary effect
1436 } while(currentId != oldId);
1437 }
1438 }
1439
1440 return numAlternates;
1441}
1442
1443template <typename dataType, typename triangulationType>
1445 const Cell &cell,
1446 std::vector<Cell> &vpath,
1447 const triangulationType &triangulation,
1448 std::vector<char> &previousDescPaths,
1449 std::vector<char> &previousAscPaths) const {
1450
1451 const SimplexId numberOfCells = triangulation.getNumberOfCells();
1452 std::vector<char> isCycle;
1453 isCycle.resize(numberOfCells, 0);
1454 int numAlternates{0};
1455
1456 if(dimensionality_ == 2) {
1457 if(cell.dim_ == 2) {
1458 // assume that cellId is a triangle
1459 SimplexId currentId = cell.id_;
1460 SimplexId oldId;
1461 do {
1462 oldId = currentId;
1463 if(isCycle[currentId] == 0) {
1464 if(previousAscPaths[currentId] == 1) {
1465 if(vpath.size() == 0) {
1466 // add a triangle
1467 const Cell triangle(2, currentId);
1468 vpath.emplace_back(triangle);
1469 }
1470 break; // Already traced path previously
1471 }
1472 isCycle[currentId] = 1;
1473 previousAscPaths[currentId] = 1;
1474 } else {
1475 // Found cycle in path, don't add triangle already seen
1476
1477 // Simulate finding the next path on the saddle
1478 // First, Remove vpath around the cycle(if we don't want to trace it)
1479 if(!reverseFullOrbit) {
1480 while(!(vpath.back() == Cell(2, currentId))) {
1481 vpath.pop_back();
1482 }
1483 }
1484
1485 // Can trace the descending paths (pick and add smallest)
1486 SimplexId connectedEdgeId;
1487 if(!reverseFullOrbit) {
1488 const Cell triangle(2, currentId);
1489 connectedEdgeId = getPairedCell(triangle, triangulation, true);
1490 } else {
1491 connectedEdgeId = vpath.back().id_;
1492 vpath.pop_back();
1493 }
1494 float bestWeight{std::numeric_limits<float>::max()};
1495 int bestAlternates{0};
1496 bool firstCellCritical{false};
1497 std::vector<Cell> vpathBest;
1498 for(int i = 0; i < 2; ++i) {
1499 SimplexId vertex;
1500 std::vector<Cell> newVpath;
1501 newVpath.emplace_back(Cell(1, connectedEdgeId));
1502 triangulation.getEdgeVertex(connectedEdgeId, i, vertex);
1503 Cell firstVertex = Cell(0, vertex);
1504 int alternates
1506 firstVertex, newVpath, triangulation, previousDescPaths,
1507 previousAscPaths);
1509 newVpath, triangulation);
1510 if(i == 0) {
1511 bestWeight = weight;
1512 bestAlternates = alternates;
1513 vpathBest = newVpath;
1514 firstCellCritical = isCellCritical(newVpath.back());
1515 } else if((!firstCellCritical && isCellCritical(newVpath.back()))
1516 || (weight < bestWeight
1517 && isCellCritical(newVpath.back()))) {
1518 bestWeight = weight;
1519 bestAlternates = alternates;
1520 vpathBest = newVpath;
1521 firstCellCritical = true;
1522 }
1523 }
1524 // Update the vpath with new path
1525 for(Cell newCell : vpathBest) {
1526 vpath.emplace_back(newCell);
1527 }
1528 numAlternates = 1 + bestAlternates;
1529 break;
1530 }
1531
1532 // add a triangle
1533 const Cell triangle(2, currentId);
1534 vpath.emplace_back(triangle);
1535
1536 if(isCellCritical(triangle)) {
1537 break;
1538 }
1539
1540 const SimplexId connectedEdgeId
1541 = getPairedCell(triangle, triangulation, true);
1542 if(connectedEdgeId == -1) {
1543 break;
1544 }
1545
1546 // add an edge
1547 const Cell edge(1, connectedEdgeId);
1548 vpath.emplace_back(edge);
1549
1550 if(isCellCritical(edge)) {
1551 break;
1552 }
1553
1554 const SimplexId starNumber
1555 = triangulation.getEdgeStarNumber(connectedEdgeId);
1556 for(SimplexId i = 0; i < starNumber; ++i) {
1557 SimplexId starId;
1558 triangulation.getEdgeStar(connectedEdgeId, i, starId);
1559
1560 if(starId != currentId) {
1561 currentId = starId;
1562 break;
1563 }
1564 }
1565
1566 // stop at convergence caused by boundary effect
1567 } while(currentId != oldId);
1568 }
1569 } else if(dimensionality_ == 3) {
1570 if(cell.dim_ == 3) {
1571 // assume that cellId is a tetra
1572 SimplexId currentId = cell.id_;
1573 SimplexId oldId;
1574 do {
1575
1576 if(isCycle[currentId] == 0) {
1577 isCycle[currentId] = 1;
1578 } else {
1579 break; // Cycle detected
1580 }
1581
1582 oldId = currentId;
1583
1584 // add a tetra
1585 const Cell tetra(3, currentId);
1586 vpath.emplace_back(tetra);
1587
1588 if(isCellCritical(tetra)) {
1589 break;
1590 }
1591
1592 const SimplexId connectedTriangleId
1593 = getPairedCell(tetra, triangulation, true);
1594 if(connectedTriangleId == -1) {
1595 break;
1596 }
1597
1598 // add a triangle
1599 const Cell triangle(2, connectedTriangleId);
1600 vpath.emplace_back(triangle);
1601
1602 if(isCellCritical(triangle)) {
1603 break;
1604 }
1605
1606 const SimplexId starNumber
1607 = triangulation.getTriangleStarNumber(connectedTriangleId);
1608 for(SimplexId i = 0; i < starNumber; ++i) {
1609 SimplexId starId;
1610 triangulation.getTriangleStar(connectedTriangleId, i, starId);
1611
1612 if(starId != currentId) {
1613 currentId = starId;
1614 break;
1615 }
1616 }
1617
1618 // stop at convergence caused by boundary effect
1619 } while(currentId != oldId);
1620 }
1621 }
1622
1623 return numAlternates;
1624}
1625
1626template <typename triangulationType>
1628 const Cell &saddle1,
1629 const Cell &saddle2,
1630 const std::vector<bool> &isVisited,
1631 std::vector<Cell> *const vpath,
1632 const triangulationType &triangulation) const {
1633
1634 const SimplexId numberOfTriangles = triangulation.getNumberOfTriangles();
1635 std::vector<char> isCycle;
1636 isCycle.resize(numberOfTriangles, 0);
1637 std::vector<SimplexId> alternatePathStack; // Keep track of alternate paths
1638
1639 if(dimensionality_ == 3) {
1640 // add the 1-saddle to the path
1641 if(vpath != nullptr) {
1642 vpath->emplace_back(saddle1);
1643 }
1644
1645 SimplexId currentId = -1;
1646 {
1647 int nconnections = 0;
1648 const SimplexId triangleNumber
1649 = triangulation.getEdgeTriangleNumber(saddle1.id_);
1650 for(SimplexId i = 0; i < triangleNumber; ++i) {
1651 SimplexId triangleId;
1652 triangulation.getEdgeTriangle(saddle1.id_, i, triangleId);
1653 if(isVisited[triangleId]) {
1654 // saddle1 can be adjacent to saddle2 on the wall
1655 if(isCellCritical(Cell(2, triangleId))) {
1656 if(vpath != nullptr) {
1657 vpath->emplace_back(Cell(2, triangleId));
1658 }
1659 return;
1660 }
1661 if(nconnections < 1) {
1662 currentId = triangleId;
1663 ++nconnections;
1664 } else {
1665 alternatePathStack.emplace_back(triangleId);
1666 }
1667 }
1668 }
1669 }
1670
1671 if(currentId == -1) { // Shouldn't happen
1672 this->printErr(
1673 "Current ID not updated for getAscendingPathThroughWall()");
1674 return;
1675 }
1676
1677 SimplexId oldId;
1678 do {
1679
1680 if(isCycle[currentId] == 0) {
1681 isCycle[currentId] = 1;
1682 } else {
1683 // Probably can't get here
1684 this->printErr("Cycle detected on the wall of 2-saddle "
1685 + std::to_string(saddle2.id_));
1686 return;
1687 }
1688
1689 oldId = currentId;
1690
1691 // add a triangle
1692 const Cell triangle(2, currentId);
1693 if(vpath != nullptr) {
1694 vpath->emplace_back(triangle);
1695 }
1696
1697 if(isCellCritical(triangle)) {
1698 break;
1699 }
1700
1701 const SimplexId connectedEdgeId
1702 = getPairedCell(triangle, triangulation, true);
1703
1704 // add an edge
1705 const Cell edge(1, connectedEdgeId);
1706 if(vpath != nullptr) {
1707 vpath->emplace_back(edge);
1708 }
1709
1710 if(isCellCritical(edge)) {
1711 break;
1712 }
1713
1714 int nconnections = 0;
1715 const SimplexId triangleNumber
1716 = triangulation.getEdgeTriangleNumber(connectedEdgeId);
1717 for(SimplexId i = 0; i < triangleNumber; ++i) {
1718 SimplexId triangleId;
1719 triangulation.getEdgeTriangle(connectedEdgeId, i, triangleId);
1720
1721 if(isVisited[triangleId] and triangleId != oldId
1722 and isCycle[triangleId] == 0) {
1723 if(nconnections < 1) {
1724 currentId = triangleId;
1725 ++nconnections;
1726 } else {
1727 alternatePathStack.emplace_back(triangleId);
1728 }
1729 }
1730 }
1731 if(nconnections == 0) {
1732 // No path found, trace back using the stack
1733 if(!alternatePathStack.empty()) {
1734 currentId = alternatePathStack.back();
1735 alternatePathStack.pop_back();
1736 } else { // Should be impossible (assuming a direct path exists to
1737 // saddle2)
1738 this->printErr(
1739 "No alternate paths detected for getAscendingPathThroughWall()");
1740 return;
1741 }
1742 }
1743
1744 // stop at convergence caused by boundary effect
1745 } while(currentId != oldId);
1746 }
1747
1748 return;
1749}
1750
1751template <typename triangulationType>
1753 const Cell &cell,
1754 VisitedMask &mask,
1755 const triangulationType &triangulation,
1756 std::vector<Cell> *const wall,
1757 std::vector<SimplexId> *const saddles) const {
1758
1759 if(saddles != nullptr) {
1760 saddles->clear();
1761 }
1762
1763 if(dimensionality_ == 3) {
1764 if(cell.dim_ == 2) {
1765 // assume that cellId is a triangle
1766 const SimplexId originId = cell.id_;
1767
1768 std::queue<SimplexId> bfs;
1769 bfs.push(originId);
1770
1771 // BFS traversal
1772 while(!bfs.empty()) {
1773 const SimplexId triangleId = bfs.front();
1774 bfs.pop();
1775
1776 if(!mask.isVisited_[triangleId]) {
1777 mask.isVisited_[triangleId] = true;
1778 mask.visitedIds_.emplace_back(triangleId);
1779
1780 // add the triangle
1781 if(wall != nullptr) {
1782 wall->emplace_back(Cell(2, triangleId));
1783 }
1784
1785 for(int j = 0; j < 3; ++j) {
1786 SimplexId edgeId;
1787 triangulation.getTriangleEdge(triangleId, j, edgeId);
1788
1789 if((saddles != nullptr) and isCellCritical(Cell(1, edgeId))) {
1790 saddles->emplace_back(edgeId);
1791 }
1792
1793 const SimplexId pairedCellId
1794 = getPairedCell(Cell(1, edgeId), triangulation);
1795
1796 if(pairedCellId != -1 and pairedCellId != triangleId) {
1797 bfs.push(pairedCellId);
1798 }
1799 }
1800 }
1801 }
1802
1803 if(saddles != nullptr && saddles->size() > 1) {
1804 std::sort(saddles->begin(), saddles->end());
1805 const auto last = std::unique(saddles->begin(), saddles->end());
1806 saddles->erase(last, saddles->end());
1807 }
1808 }
1809 }
1810
1811 return 0;
1812}
1813
1814template <typename triangulationType>
1816 const Cell &cell,
1817 VisitedMask &mask,
1818 const triangulationType &triangulation,
1819 std::vector<Cell> *const wall,
1820 std::vector<SimplexId> *const saddles) const {
1821
1822 if(saddles != nullptr) {
1823 saddles->clear();
1824 }
1825
1826 if(dimensionality_ == 3) {
1827 if(cell.dim_ == 1) {
1828 // assume that cellId is an edge
1829 const SimplexId originId = cell.id_;
1830
1831 std::queue<SimplexId> bfs;
1832 bfs.push(originId);
1833
1834 // BFS traversal
1835 while(!bfs.empty()) {
1836 const SimplexId edgeId = bfs.front();
1837 bfs.pop();
1838
1839 if(!mask.isVisited_[edgeId]) {
1840 mask.isVisited_[edgeId] = true;
1841 mask.visitedIds_.emplace_back(edgeId);
1842
1843 // add the edge
1844 if(wall != nullptr) {
1845 wall->emplace_back(Cell(1, edgeId));
1846 }
1847
1848 const SimplexId triangleNumber
1849 = triangulation.getEdgeTriangleNumber(edgeId);
1850 for(SimplexId j = 0; j < triangleNumber; ++j) {
1851 SimplexId triangleId;
1852 triangulation.getEdgeTriangle(edgeId, j, triangleId);
1853
1854 if((saddles != nullptr) and isCellCritical(Cell(2, triangleId))) {
1855 saddles->emplace_back(triangleId);
1856 }
1857
1858 const SimplexId pairedCellId
1859 = getPairedCell(Cell(2, triangleId), triangulation, true);
1860
1861 if(pairedCellId != -1 and pairedCellId != edgeId) {
1862 bfs.push(pairedCellId);
1863 }
1864 }
1865 }
1866 }
1867
1868 if(saddles != nullptr && saddles->size() > 1) {
1869 std::sort(saddles->begin(), saddles->end());
1870 const auto last = std::unique(saddles->begin(), saddles->end());
1871 saddles->erase(last, saddles->end());
1872 }
1873 }
1874 }
1875
1876 return 0;
1877}
1878
1879template <typename triangulationType>
1881 const std::vector<Cell> &vpath,
1882 const triangulationType &triangulation) const {
1883
1884 if(dimensionality_ == 2) {
1885 // assume that the first cell is an edge
1886 const SimplexId numberOfCellsInPath = vpath.size();
1887 for(SimplexId i = 0; i < numberOfCellsInPath; i += 2) {
1888 const SimplexId edgeId = vpath[i].id_;
1889
1890 if((i + 1) == numberOfCellsInPath) {
1891 TTK_FORCE_USE(triangulation);
1892 (*vectors_)[2][edgeId] = NULL_CONNECTION;
1893 break;
1894 } // Handle cycle case
1895 const SimplexId triangleId = vpath[i + 1].id_;
1896
1897#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
1898 for(int k = 0; k < 3; ++k) {
1899 SimplexId tmp;
1900 triangulation.getCellEdge(triangleId, k, tmp);
1901 if(tmp == edgeId) {
1902 (*vectors_)[3][triangleId] = k;
1903 break;
1904 }
1905 }
1906 for(int k = 0; k < triangulation.getEdgeStarNumber(edgeId); ++k) {
1907 SimplexId tmp;
1908 triangulation.getEdgeStar(edgeId, k, tmp);
1909 if(tmp == triangleId) {
1910 (*vectors_)[2][edgeId] = k;
1911 break;
1912 }
1913 }
1914#else
1915 TTK_FORCE_USE(triangulation);
1916 (*vectors_)[3][triangleId] = edgeId;
1917 (*vectors_)[2][edgeId] = triangleId;
1918#endif
1919 }
1920 } else if(dimensionality_ == 3) {
1921 // assume that the first cell is a triangle
1922 const SimplexId numberOfCellsInPath = vpath.size();
1923 for(SimplexId i = 0; i < numberOfCellsInPath; i += 2) {
1924 const SimplexId triangleId = vpath[i].id_;
1925 if((i + 1) == numberOfCellsInPath) {
1926 (*vectors_)[4][triangleId] = NULL_CONNECTION;
1927 break;
1928 } // Handle cycle case
1929 const SimplexId tetraId = vpath[i + 1].id_;
1930
1931#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
1932 for(int k = 0; k < 4; ++k) {
1933 SimplexId tmp;
1934 triangulation.getCellTriangle(tetraId, k, tmp);
1935 if(tmp == triangleId) {
1936 (*vectors_)[5][tetraId] = k;
1937 break;
1938 }
1939 }
1940 for(int k = 0; k < triangulation.getTriangleStarNumber(triangleId); ++k) {
1941 SimplexId tmp;
1942 triangulation.getTriangleStar(triangleId, k, tmp);
1943 if(tmp == tetraId) {
1944 (*vectors_)[4][triangleId] = k;
1945 break;
1946 }
1947 }
1948#else
1949 (*vectors_)[5][tetraId] = triangleId;
1950 (*vectors_)[4][triangleId] = tetraId;
1951#endif
1952 }
1953 }
1954
1955 return 0;
1956}
1957
1958template <typename triangulationType>
1960 const std::vector<Cell> &vpath,
1961 const triangulationType &triangulation) const {
1962
1963 // assume that the first cell is an edge
1964 for(size_t i = 0; i < vpath.size(); i += 2) {
1965 const SimplexId edgeId = vpath[i].id_;
1966 if((i + 1) == vpath.size()) {
1967 TTK_FORCE_USE(triangulation);
1968 (*vectors_)[1][edgeId] = NULL_CONNECTION;
1969 continue;
1970 } // Handle cycle case
1971 const SimplexId vertId = vpath[i + 1].id_;
1972
1973#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
1974 const auto nneighs = triangulation.getVertexEdgeNumber();
1975 for(int k = 0; k < nneighs; ++k) {
1976 SimplexId tmp;
1977 triangulation.getVertexEdge(vertId, k, tmp);
1978 if(tmp == edgeId) {
1979 (*vectors_)[0][vertId] = k;
1980 break;
1981 }
1982 }
1983 const auto nverts = triangulation.getEdgeStarNumber(edgeId);
1984 for(int k = 0; k < nverts; ++k) {
1985 SimplexId tmp;
1986 triangulation.getEdgeVertex(edgeId, k, tmp);
1987 if(tmp == vertId) {
1988 (*vectors_)[1][edgeId] = k;
1989 break;
1990 }
1991 }
1992#else
1993 TTK_FORCE_USE(triangulation);
1994 (*vectors_)[0][vertId] = edgeId;
1995 (*vectors_)[1][edgeId] = vertId;
1996#endif
1997 }
1998
1999 return 0;
2000}
2001
2002template <typename triangulationType>
2004 const std::vector<Cell> &vpath,
2005 const triangulationType &triangulation) const {
2006 if(dimensionality_ == 2) {
2007 const SimplexId numberOfCellsInPath = vpath.size();
2008 std::vector<Cell> currentVpath;
2009 int currentDim{-1};
2010 if(numberOfCellsInPath > 1) {
2011 currentDim = vpath[1].dim_;
2012 } else {
2013 this->printErr("Rotating vpath not large enough to call reverse");
2014 }
2015 // Assume the first cell is always an edge
2016 for(SimplexId i = 0; i < numberOfCellsInPath; i += 2) {
2017 currentVpath.emplace_back(vpath[i]);
2018 if(currentDim == vpath[i + 1].dim_) {
2019 currentVpath.emplace_back(vpath[i + 1]);
2020 } else {
2021 if(currentDim == 0) {
2022 reverseDescendingPath(currentVpath, triangulation);
2023 } else {
2024 reverseAscendingPath(currentVpath, triangulation);
2025 }
2026 currentVpath.clear();
2027 currentVpath.emplace_back(vpath[i]);
2028 currentDim = vpath[i + 1].dim_;
2029 currentVpath.emplace_back(vpath[i + 1]);
2030 }
2031 }
2032 if(currentDim == 0) {
2033 reverseDescendingPath(currentVpath, triangulation);
2034 } else {
2035 reverseAscendingPath(currentVpath, triangulation);
2036 }
2037 }
2038 return 0;
2039}
2040
2041template <typename triangulationType>
2043 const std::vector<Cell> &vpath,
2044 const triangulationType &triangulation) const {
2045
2046 if(dimensionality_ == 3) {
2047 // assume that the first cell is an edge
2048 const SimplexId numberOfCellsInPath = vpath.size();
2049 for(SimplexId i = 0; i < numberOfCellsInPath; i += 2) {
2050 const SimplexId edgeId = vpath[i].id_;
2051 const SimplexId triangleId = vpath[i + 1].id_;
2052
2053#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
2054 for(int k = 0; k < 3; ++k) {
2055 SimplexId tmp;
2056 triangulation.getTriangleEdge(triangleId, k, tmp);
2057 if(tmp == edgeId) {
2058 (*vectors_)[3][triangleId] = k;
2059 break;
2060 }
2061 }
2062 for(int k = 0; k < triangulation.getEdgeTriangleNumber(edgeId); ++k) {
2063 SimplexId tmp;
2064 triangulation.getEdgeTriangle(edgeId, k, tmp);
2065 if(tmp == triangleId) {
2066 (*vectors_)[2][edgeId] = k;
2067 break;
2068 }
2069 }
2070#else
2071 TTK_FORCE_USE(triangulation);
2072 (*vectors_)[3][triangleId] = edgeId;
2073 (*vectors_)[2][edgeId] = triangleId;
2074#endif
2075 }
2076 }
2077
2078 return 0;
2079}
2080
2081template <typename triangulationType>
2083 const std::vector<Cell> &vpath,
2084 const triangulationType &triangulation) const {
2085
2086 if(dimensionality_ == 3) {
2087 // assume that the first cell is a triangle
2088 const SimplexId numberOfCellsInPath = vpath.size();
2089 for(SimplexId i = 0; i < numberOfCellsInPath; i += 2) {
2090 const SimplexId triangleId = vpath[i].id_;
2091 const SimplexId edgeId = vpath[i + 1].id_;
2092
2093#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
2094 for(int k = 0; k < 3; ++k) {
2095 SimplexId tmp;
2096 triangulation.getTriangleEdge(triangleId, k, tmp);
2097 if(tmp == edgeId) {
2098 (*vectors_)[3][triangleId] = k;
2099 break;
2100 }
2101 }
2102 for(int k = 0; k < triangulation.getEdgeTriangleNumber(edgeId); ++k) {
2103 SimplexId tmp;
2104 triangulation.getEdgeTriangle(edgeId, k, tmp);
2105 if(tmp == triangleId) {
2106 (*vectors_)[2][edgeId] = k;
2107 break;
2108 }
2109 }
2110#else
2111 TTK_FORCE_USE(triangulation);
2112 (*vectors_)[2][edgeId] = triangleId;
2113 (*vectors_)[3][triangleId] = edgeId;
2114#endif
2115 }
2116 }
2117
2118 return 0;
2119}
2120
2121template <typename dataType, typename triangulationType>
2123 const Cell c, const triangulationType &triangulation) const {
2124
2125 auto cellDim = c.dim_;
2126 auto cellId = c.id_;
2127 SimplexId vertexId = -1;
2128 if(cellDim == 0) {
2129 vertexId = cellId;
2130 }
2131
2132 else if(cellDim == 1) {
2133 SimplexId v0;
2134 SimplexId v1;
2135 triangulation.getEdgeVertex(cellId, 0, v0);
2136 triangulation.getEdgeVertex(cellId, 1, v1);
2137 float edgeWeight;
2139 triangulation, v0, v1, edgeWeight)) {
2140 vertexId = v0;
2141 } else {
2142 vertexId = v1;
2143 }
2144 }
2145
2146 else if(cellDim == 2) {
2147 SimplexId v0, v1, v2;
2148 triangulation.getTriangleVertex(cellId, 0, v0);
2149 triangulation.getTriangleVertex(cellId, 1, v1);
2150 triangulation.getTriangleVertex(cellId, 2, v2);
2151 float edgeWeight;
2152 bool hasV0For1
2153 = compare<dataType, triangulationType>(triangulation, v0, v1, edgeWeight);
2154 bool hasV0For2
2155 = compare<dataType, triangulationType>(triangulation, v0, v2, edgeWeight);
2156 bool hasV1For2
2157 = compare<dataType, triangulationType>(triangulation, v1, v2, edgeWeight);
2158 if(hasV0For1 && hasV0For2) {
2159 vertexId = v0;
2160 } else if(!hasV0For1 && hasV1For2) {
2161 vertexId = v1;
2162 } else if(!hasV0For2 && !hasV1For2) {
2163 vertexId = v2;
2164 } else {
2165 vertexId = -1; // No clear greater vertex
2166 }
2167 }
2168
2169 else if(cellDim == 3) {
2170 SimplexId v0, v1, v2, v3;
2171 triangulation.getCellVertex(cellId, 0, v0);
2172 triangulation.getCellVertex(cellId, 1, v1);
2173 triangulation.getCellVertex(cellId, 2, v2);
2174 triangulation.getCellVertex(cellId, 3, v3);
2175 float edgeWeight;
2176 bool hasV0For1
2177 = compare<dataType, triangulationType>(triangulation, v0, v1, edgeWeight);
2178 bool hasV0For2
2179 = compare<dataType, triangulationType>(triangulation, v0, v2, edgeWeight);
2180 bool hasV0For3
2181 = compare<dataType, triangulationType>(triangulation, v0, v3, edgeWeight);
2182 bool hasV1For2
2183 = compare<dataType, triangulationType>(triangulation, v1, v2, edgeWeight);
2184 bool hasV1For3
2185 = compare<dataType, triangulationType>(triangulation, v1, v3, edgeWeight);
2186 bool hasV2For3
2187 = compare<dataType, triangulationType>(triangulation, v2, v3, edgeWeight);
2188 if(hasV0For1 && hasV0For2 && hasV0For3) {
2189 vertexId = v0;
2190 } else if(!hasV0For1 && hasV1For2 && hasV1For3) {
2191 vertexId = v1;
2192 } else if(!hasV0For2 && !hasV1For2 && hasV2For3) {
2193 vertexId = v2;
2194 } else if(!hasV0For3 && !hasV1For3 && !hasV2For3) {
2195 vertexId = v3;
2196 } else {
2197 vertexId = -1; // No clear greater vertex
2198 }
2199 }
2200 return vertexId;
2201}
2202
2203template <typename dataType, typename triangulationType>
2205 const Cell c, const triangulationType &triangulation) const {
2206
2207 auto cellDim = c.dim_;
2208 auto cellId = c.id_;
2209
2210 SimplexId vertexId = -1;
2211 if(cellDim == 0) {
2212 vertexId = cellId;
2213 }
2214
2215 else if(cellDim == 1) {
2216 SimplexId v0;
2217 SimplexId v1;
2218 triangulation.getEdgeVertex(cellId, 0, v0);
2219 triangulation.getEdgeVertex(cellId, 1, v1);
2220 float edgeWeight;
2222 triangulation, v1, v0, edgeWeight)) {
2223 vertexId = v0;
2224 } else {
2225 vertexId = v1;
2226 }
2227 }
2228
2229 else if(cellDim == 2) {
2230 SimplexId v0{}, v1{}, v2{};
2231 triangulation.getTriangleVertex(cellId, 0, v0);
2232 triangulation.getTriangleVertex(cellId, 1, v1);
2233 triangulation.getTriangleVertex(cellId, 2, v2);
2234 float edgeWeight;
2235 bool hasV0For1
2236 = compare<dataType, triangulationType>(triangulation, v0, v1, edgeWeight);
2237 bool hasV0For2
2238 = compare<dataType, triangulationType>(triangulation, v0, v2, edgeWeight);
2239 bool hasV1For2
2240 = compare<dataType, triangulationType>(triangulation, v1, v2, edgeWeight);
2241 if(!(hasV0For1) && !(hasV0For2)) {
2242 vertexId = v0;
2243 } else if(hasV0For1 && !hasV1For2) {
2244 vertexId = v1;
2245 } else if(hasV1For2 && hasV0For2) {
2246 vertexId = v2;
2247 } else {
2248 vertexId = -1; // No clear lower vertex
2249 }
2250 }
2251
2252 else if(cellDim == 3) {
2253 SimplexId v0{}, v1{}, v2{}, v3{};
2254 triangulation.getCellVertex(cellId, 0, v0);
2255 triangulation.getCellVertex(cellId, 1, v1);
2256 triangulation.getCellVertex(cellId, 2, v2);
2257 triangulation.getCellVertex(cellId, 3, v3);
2258 float edgeWeight;
2259 bool hasV0For1
2260 = compare<dataType, triangulationType>(triangulation, v0, v1, edgeWeight);
2261 bool hasV0For2
2262 = compare<dataType, triangulationType>(triangulation, v0, v2, edgeWeight);
2263 bool hasV0For3
2264 = compare<dataType, triangulationType>(triangulation, v0, v3, edgeWeight);
2265 bool hasV1For2
2266 = compare<dataType, triangulationType>(triangulation, v1, v2, edgeWeight);
2267 bool hasV1For3
2268 = compare<dataType, triangulationType>(triangulation, v1, v3, edgeWeight);
2269 bool hasV2For3
2270 = compare<dataType, triangulationType>(triangulation, v2, v3, edgeWeight);
2271 if(!hasV0For1 && !hasV0For2 && !hasV0For3) {
2272 vertexId = v0;
2273 } else if(hasV0For1 && !hasV1For2 && !hasV1For3) {
2274 vertexId = v1;
2275 } else if(hasV0For2 && hasV1For2 && !hasV2For3) {
2276 vertexId = v2;
2277 } else if(hasV0For3 && hasV1For3 && hasV2For3) {
2278 vertexId = v3;
2279 } else {
2280 vertexId = -1; // No clear lower vertex
2281 }
2282 }
2283 return vertexId;
2284}
2285
2286template <typename triangulationType>
2288 std::vector<std::array<float, 3>> &points,
2289 std::vector<char> &points_pairOrigins,
2290 std::vector<char> &cells_pairTypes,
2291 std::vector<SimplexId> &cellIds,
2292 std::vector<char> &cellDimensions,
2293 const triangulationType &triangulation) const {
2294
2295 const auto nDims = this->getNumberOfDimensions();
2296
2297 // number of glyphs per dimension
2298 std::vector<size_t> nGlyphsPerDim(nDims);
2299
2300#ifdef TTK_ENABLE_OPENMP
2301#pragma omp parallel for num_threads(threadNumber_)
2302#endif // TTK_ENABLE_OPENMP
2303 for(int i = 0; i < nDims - 1; ++i) {
2304 const auto nCells = this->getNumberOfCells(i, triangulation);
2305 for(SimplexId j = 0; j < nCells; ++j) {
2306 if(this->getPairedCell(Cell{i, j}, triangulation) > -1) {
2307 nGlyphsPerDim[i]++;
2308 }
2309 }
2310 }
2311
2312 // partial sum of number of gradient glyphs
2313 std::vector<size_t> offsets(nDims + 1);
2314 for(SimplexId i = 0; i < nDims; ++i) {
2315 offsets[i + 1] = offsets[i] + nGlyphsPerDim[i];
2316 }
2317
2318 // total number of glyphs
2319 const auto nGlyphs = offsets.back();
2320
2321 // resize arrays accordingly
2322 points.resize(2 * nGlyphs);
2323 points_pairOrigins.resize(2 * nGlyphs);
2324 cells_pairTypes.resize(nGlyphs);
2325 cellIds.resize(2 * nGlyphs);
2326 cellDimensions.resize(2 * nGlyphs);
2327
2328#ifdef TTK_ENABLE_OPENMP
2329#pragma omp parallel for num_threads(threadNumber_)
2330#endif // TTK_ENABLE_OPENMP
2331 for(int i = 0; i < nDims - 1; ++i) {
2332 const SimplexId nCells = getNumberOfCells(i, triangulation);
2333 size_t nProcessedGlyphs{offsets[i]};
2334 for(SimplexId j = 0; j < nCells; ++j) {
2335 const Cell c{i, j};
2336 const auto pcid = this->getPairedCell(c, triangulation);
2337 if(pcid > -1) {
2338 const Cell pc{i + 1, pcid};
2339 triangulation.getCellIncenter(
2340 c.id_, c.dim_, points[2 * nProcessedGlyphs].data());
2341 triangulation.getCellIncenter(
2342 pc.id_, pc.dim_, points[2 * nProcessedGlyphs + 1].data());
2343 points_pairOrigins[2 * nProcessedGlyphs] = 0;
2344 points_pairOrigins[2 * nProcessedGlyphs + 1] = 1;
2345 cells_pairTypes[nProcessedGlyphs] = i;
2346#ifdef TTK_ENABLE_MPI
2347 ttk::SimplexId globalId{-1};
2348 triangulation.getDistributedGlobalCellId(j, i, globalId);
2349 cellIds[2 * nProcessedGlyphs + 0] = globalId;
2350 triangulation.getDistributedGlobalCellId(pcid, i + 1, globalId);
2351 cellIds[2 * nProcessedGlyphs + 1] = globalId;
2352#else
2353 cellIds[2 * nProcessedGlyphs + 0] = j;
2354 cellIds[2 * nProcessedGlyphs + 1] = pcid;
2355#endif // TTK_ENABLE_MPI
2356 cellDimensions[2 * nProcessedGlyphs + 0] = i;
2357 cellDimensions[2 * nProcessedGlyphs + 1] = i + 1;
2358 nProcessedGlyphs++;
2359 }
2360 }
2361 }
2362
2363 return 0;
2364}
#define TTK_FORCE_USE(x)
Force the compiler to use the function/method parameter.
Definition BaseClass.h:57
float getPersistence(const std::vector< Cell > &vpath, const triangulationType &triangulation) const
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:149
TTK discreteVectorField processing package.
int reverseAscendingPath(const std::vector< Cell > &vpath, const triangulationType &triangulation) const
bool isCellCritical(const int cellDim, const SimplexId cellId) const
int setCriticalPoints(const std::array< std::vector< SimplexId >, 4 > &criticalCellsByDim, std::vector< std::array< float, 3 > > &points, std::vector< char > &cellDimensions, std::vector< SimplexId > &cellIds, std::vector< char > &isOnBoundary, std::vector< SimplexId > &PLVertexIdentifiers, const triangulationType &triangulation) const
std::array< std::vector< SimplexId >, 6 > localVectors_
int getAscendingWall(const Cell &cell, VisitedMask &mask, const triangulationType &triangulation, std::vector< Cell > *const wall=nullptr, std::vector< SimplexId > *const saddles=nullptr) const
SimplexId getPairedCell(const Cell &cell, const triangulationType &triangulation, bool isReverse=false) const
vectorValue getVectorValueAt(SimplexId &vertex) const
std::array< std::vector< SimplexId >, 6 > * vectors_
int getDescendingPathRecursive(const Cell &cell, std::vector< Cell > &vpath, const triangulationType &triangulation, std::vector< char > &previousDescPaths, std::vector< char > &previousAscPaths) const
int reverseDescendingPathOnWall(const std::vector< Cell > &vpath, const triangulationType &triangulation) const
float getPersistence(const std::vector< Cell > &vpath, const triangulationType &triangulation) const
int getCriticalPoints(std::array< std::vector< SimplexId >, 4 > &criticalCellsByDim, const triangulationType &triangulation) const
SimplexId getCellLowerVertex(const Cell c, const triangulationType &triangulation) const
int getAscendingPath(const Cell &cell, std::vector< Cell > &vpath, const triangulationType &triangulation, const bool stopOnCycle) const
SimplexId getNumberOfCells(const int dimension, const triangulationType &triangulation) const
int setVectorGlyphs(std::vector< std::array< float, 3 > > &points, std::vector< char > &points_pairOrigins, std::vector< char > &cells_pairTypes, std::vector< SimplexId > &cellsIds, std::vector< char > &cellsDimensions, const triangulationType &triangulation) const
int buildField(const triangulationType &triangulation)
bool compare(const triangulationType &triangulation, SimplexId vertexA, SimplexId vertexB, float &weightValue) const
void getAscendingPathThroughWall(const Cell &saddle1, const Cell &saddle2, const std::vector< bool > &isVisited, std::vector< Cell > *const vpath, const triangulationType &triangulation) const
bool isBoundary(const Cell &cell, const triangulationType &triangulation) const
int reverseAlternatingPath(const std::vector< Cell > &vpath, const triangulationType &triangulation) const
int getDescendingPath(const Cell &cell, std::vector< Cell > &vpath, const triangulationType &triangulation, const bool stopOnCycle) const
int getDescendingWall(const Cell &cell, VisitedMask &mask, const triangulationType &triangulation, std::vector< Cell > *const wall=nullptr, std::vector< SimplexId > *const saddles=nullptr) const
SimplexId getCellGreaterVertex(const Cell c, const triangulationType &triangulation) const
int reverseDescendingPath(const std::vector< Cell > &vpath, const triangulationType &triangulation) const
int reverseAscendingPathOnWall(const std::vector< Cell > &vpath, const triangulationType &triangulation) const
int getAscendingPathRecursive(const Cell &cell, std::vector< Cell > &vpath, const triangulationType &triangulation, std::vector< char > &previousDescPaths, std::vector< char > &previousAscPaths) const
bool getDescendingPathThroughWall(const Cell &saddle2, const Cell &saddle1, const std::vector< bool > &isVisited, std::vector< Cell > *const vpath, const triangulationType &triangulation, const bool stopIfMultiConnected=false, const bool enableCycleDetector=false) const
T dotProduct(const T *vA0, const T *vA1, const T *vB0, const T *vB1)
Definition Geometry.cpp:388
SurfaceGeometrySmoother::Point vectorValue
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
COMMON_EXPORTS int MPIrank_
Definition BaseClass.cpp:9
T end(std::pair< T, T > &p)
Definition ripser.cpp:503
Auto-cleaning re-usable graph propagations data structure.
Definition VisitedMask.h:27
std::vector< bool > & isVisited_
Definition VisitedMask.h:28
std::vector< SimplexId > & visitedIds_
Definition VisitedMask.h:29
Extended Cell structure for processOutwardStars.
const std::array< float, 3 > lowVertWeights_
const std::array< uint8_t, 3 > faces_
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/| (_) |"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)