TTK
Loading...
Searching...
No Matches
DiscreteGradient_Template.h
Go to the documentation of this file.
1
14
15#pragma once
16
17#include <DiscreteGradient.h>
18
19using ttk::SimplexId;
20using ttk::dcg::Cell;
23
24template <typename dataType, typename triangulationType>
26 const Cell &up,
27 const Cell &down,
28 const dataType *const scalars,
29 const triangulationType &triangulation) const {
30
31 return scalars[getCellGreaterVertex(up, triangulation)]
32 - scalars[getCellGreaterVertex(down, triangulation)];
33}
34
35template <typename triangulationType>
36int DiscreteGradient::buildGradient(const triangulationType &triangulation,
37 bool bypassCache) {
38
39 auto &cacheHandler = *triangulation.getGradientCacheHandler();
40 const auto findGradient
41 = [this, &cacheHandler]() -> AbstractTriangulation::gradientType * {
42 if(this->inputScalarField_.first == nullptr) {
43 return {};
44 }
45 return cacheHandler.get(this->inputScalarField_);
46 };
47
48#ifdef TTK_ENABLE_OPENMP
49 if(!bypassCache && omp_in_parallel()) {
50 this->printWrn(
51 "buildGradient() called inside a parallel region, disabling cache...");
52 bypassCache = true;
53 }
54#endif // TTK_ENABLE_OPENMP
55
56 // set member variables at each buildGradient() call
57 this->dimensionality_ = triangulation.getCellVertexNumber(0) - 1;
58 this->numberOfVertices_ = triangulation.getNumberOfVertices();
59
60 this->gradient_ = bypassCache ? &this->localGradient_ : findGradient();
61 if(this->gradient_ == nullptr || bypassCache) {
62
63 if(!bypassCache) {
64 // add new cache entry
65 cacheHandler.insert(this->inputScalarField_, {});
66 this->gradient_ = cacheHandler.get(this->inputScalarField_);
67 }
68
69 // allocate gradient memory
70 this->initMemory(triangulation);
71
72 Timer tm{};
73 // compute gradient pairs
74 this->processLowerStars(this->inputOffsets_, triangulation);
75
76 this->printMsg(
77 "Built discrete gradient", 1.0, tm.getElapsedTime(), this->threadNumber_);
78 } else {
79 this->printMsg("Fetched cached discrete gradient");
80 }
81
82 return 0;
83}
84
85template <typename triangulationType>
87 const std::array<std::vector<SimplexId>, 4> &criticalCellsByDim,
88 std::vector<std::array<float, 3>> &points,
89 std::vector<char> &cellDimensions,
90 std::vector<SimplexId> &cellIds,
91 std::vector<char> &isOnBoundary,
92 std::vector<SimplexId> &PLVertexIdentifiers,
93 const triangulationType &triangulation) const {
94
95 std::array<size_t, 5> partSums{};
96 for(size_t i = 0; i < criticalCellsByDim.size(); ++i) {
97 partSums[i + 1] = partSums[i] + criticalCellsByDim[i].size();
98 }
99
100 const auto nCritPoints = partSums.back();
101
102 points.resize(nCritPoints);
103 cellDimensions.resize(nCritPoints);
104 cellIds.resize(nCritPoints);
105 isOnBoundary.resize(nCritPoints);
106 PLVertexIdentifiers.resize(nCritPoints);
107
108 for(size_t i = 0; i < criticalCellsByDim.size(); ++i) {
109#ifdef TTK_ENABLE_OPENMP
110#pragma omp parallel for num_threads(threadNumber_)
111#endif // TTK_ENABLE_OPENMP
112 for(size_t j = 0; j < criticalCellsByDim[i].size(); ++j) {
113 const SimplexId cellId = criticalCellsByDim[i][j];
114 const int cellDim = i;
115 const auto o{partSums[i] + j};
116
117 triangulation.getCellIncenter(cellId, i, points[o].data());
118 cellDimensions[o] = cellDim;
119#ifdef TTK_ENABLE_MPI
120 ttk::SimplexId globalId{-1};
121 triangulation.getDistributedGlobalCellId(cellId, cellDim, globalId);
122 cellIds[o] = globalId;
123#else
124 cellIds[o] = cellId;
125#endif // TTK_ENABLE_MPI
126 const Cell cell{static_cast<int>(i), cellId};
127 isOnBoundary[o] = this->isBoundary(cell, triangulation);
128 PLVertexIdentifiers[o] = this->getCellGreaterVertex(cell, triangulation);
129 }
130 }
131
132 std::vector<std::vector<std::string>> rows(this->dimensionality_ + 1);
133 for(int i = 0; i < this->dimensionality_ + 1; ++i) {
134 rows[i]
135 = std::vector<std::string>{"#" + std::to_string(i) + "-cell(s)",
136 std::to_string(criticalCellsByDim[i].size())};
137 }
138 this->printMsg(rows);
139
140 return 0;
141}
142
143template <typename triangulationType>
145 std::vector<std::array<float, 3>> &points,
146 std::vector<char> &cellDimensions,
147 std::vector<SimplexId> &cellIds,
148 std::vector<char> &isOnBoundary,
149 std::vector<SimplexId> &PLVertexIdentifiers,
150 const triangulationType &triangulation) const {
151
152 std::array<std::vector<SimplexId>, 4> criticalCellsByDim;
153 getCriticalPoints(criticalCellsByDim, triangulation);
154 setCriticalPoints(criticalCellsByDim, points, cellDimensions, cellIds,
155 isOnBoundary, PLVertexIdentifiers, triangulation);
156
157 return 0;
158}
159
160template <typename triangulationType>
162 std::array<std::vector<SimplexId>, 4> &criticalCellsByDim,
163 const triangulationType &triangulation) const {
164
165 const auto dims{this->getNumberOfDimensions()};
166 for(int i = 0; i < dims; ++i) {
167
168 // map: store critical cell per dimension per thread
169 std::vector<std::vector<SimplexId>> critCellsPerThread(this->threadNumber_);
170 const auto numberOfCells{this->getNumberOfCells(i, triangulation)};
171
172 // use static scheduling to ensure that critical cells
173 // are sorted by id
174
175#ifdef TTK_ENABLE_OPENMP
176#pragma omp parallel for num_threads(this->threadNumber_) schedule(static)
177#endif // TTK_ENABLE_OPENMP
178 for(SimplexId j = 0; j < numberOfCells; ++j) {
179#ifdef TTK_ENABLE_OPENMP
180 const auto tid = omp_get_thread_num();
181#else
182 const auto tid = 0;
183#endif // TTK_ENABLE_OPENMP
184 if(this->isCellCritical(i, j)) {
185 critCellsPerThread[tid].emplace_back(j);
186 }
187 }
188
189 // reduce: aggregate critical cells per thread
190 criticalCellsByDim[i] = std::move(critCellsPerThread[0]);
191 for(size_t j = 1; j < critCellsPerThread.size(); ++j) {
192 const auto &vec{critCellsPerThread[j]};
193 criticalCellsByDim[i].insert(
194 criticalCellsByDim[i].end(), vec.begin(), vec.end());
195 }
196 }
197
198 return 0;
199}
200
201template <typename triangulationType>
203 const int dimension, const triangulationType &triangulation) const {
204
205 if(dimension > this->dimensionality_ || dimension < 0) {
206 return -1;
207 }
208
209 switch(dimension) {
210 case 0:
211 return triangulation.getNumberOfVertices();
212 break;
213
214 case 1:
215 return triangulation.getNumberOfEdges();
216 break;
217
218 case 2:
219 return triangulation.getNumberOfTriangles();
220 break;
221
222 case 3:
223 return triangulation.getNumberOfCells();
224 break;
225 }
226
227 return -1;
228}
229
230template <typename triangulationType>
231inline void
232 DiscreteGradient::lowerStar(lowerStarType &ls,
233 const SimplexId a,
234 const SimplexId *const offsets,
235 const triangulationType &triangulation) const {
236
237 // make sure that ls is cleared
238 for(auto &vec : ls) {
239 vec.clear();
240 }
241
242 // a belongs to its lower star
243 CellExt const localCellExt{0, a};
244 ls[0].emplace_back(localCellExt);
245
246 // store lower edges
247 const auto nedges = triangulation.getVertexEdgeNumber(a);
248 ls[1].reserve(nedges);
249 for(SimplexId i = 0; i < nedges; i++) {
250 SimplexId edgeId;
251 triangulation.getVertexEdge(a, i, edgeId);
252 SimplexId vertexId;
253 triangulation.getEdgeVertex(edgeId, 0, vertexId);
254 if(vertexId == a) {
255 triangulation.getEdgeVertex(edgeId, 1, vertexId);
256 }
257 if(offsets[vertexId] < offsets[a]) {
258 ls[1].emplace_back(CellExt{1, edgeId, {offsets[vertexId], -1, -1}, {}});
259 }
260 }
261
262 if(ls[1].size() < 2) {
263 // at least two edges in the lower star for one triangle
264 return;
265 }
266
267 const auto processTriangle
268 = [&](const SimplexId triangleId, const SimplexId v0, const SimplexId v1,
269 const SimplexId v2) {
270 std::array<SimplexId, 3> lowVerts{-1, -1, -1};
271 if(v0 == a) {
272 lowVerts[0] = offsets[v1];
273 lowVerts[1] = offsets[v2];
274 } else if(v1 == a) {
275 lowVerts[0] = offsets[v0];
276 lowVerts[1] = offsets[v2];
277 } else if(v2 == a) {
278 lowVerts[0] = offsets[v0];
279 lowVerts[1] = offsets[v1];
280 }
281 // higher order vertex first
282 if(lowVerts[0] < lowVerts[1]) {
283 std::swap(lowVerts[0], lowVerts[1]);
284 }
285 if(offsets[a] > lowVerts[0]) { // triangle in lowerStar
286 uint8_t j{}, k{};
287 // store edges indices of current triangle
288 std::array<uint8_t, 3> faces{};
289 for(const auto &e : ls[1]) {
290 if(e.lowVerts_[0] == lowVerts[0] || e.lowVerts_[0] == lowVerts[1]) {
291 faces[k++] = j;
292 }
293 j++;
294 }
295 CellExt const localCellExt2{2, triangleId, lowVerts, faces};
296 ls[2].emplace_back(localCellExt2);
297 }
298 };
299
300 if(dimensionality_ == 2) {
301 // store lower triangles
302
303 // use optimised triangulation methods:
304 // getVertexStar instead of getVertexTriangle
305 // getCellVertex instead of getTriangleVertex
306 const auto ncells = triangulation.getVertexStarNumber(a);
307 ls[2].reserve(ncells);
308 for(SimplexId i = 0; i < ncells; ++i) {
309 SimplexId cellId;
310 triangulation.getVertexStar(a, i, cellId);
311 SimplexId v0{}, v1{}, v2{};
312 triangulation.getCellVertex(cellId, 0, v0);
313 triangulation.getCellVertex(cellId, 1, v1);
314 triangulation.getCellVertex(cellId, 2, v2);
315 processTriangle(cellId, v0, v1, v2);
316 }
317 } else if(dimensionality_ == 3) {
318 // store lower triangles
319 const auto ntri = triangulation.getVertexTriangleNumber(a);
320 ls[2].reserve(ntri);
321 for(SimplexId i = 0; i < ntri; i++) {
322 SimplexId triangleId;
323 triangulation.getVertexTriangle(a, i, triangleId);
324 SimplexId v0{}, v1{}, v2{};
325 triangulation.getTriangleVertex(triangleId, 0, v0);
326 triangulation.getTriangleVertex(triangleId, 1, v1);
327 triangulation.getTriangleVertex(triangleId, 2, v2);
328 processTriangle(triangleId, v0, v1, v2);
329 }
330
331 // at least three triangles in the lower star for one tetra
332 if(ls[2].size() >= 3) {
333 // store lower tetra
334 const auto ncells = triangulation.getVertexStarNumber(a);
335 ls[3].reserve(ncells);
336 for(SimplexId i = 0; i < ncells; ++i) {
337 SimplexId cellId;
338 triangulation.getVertexStar(a, i, cellId);
339 std::array<SimplexId, 3> lowVerts{-1, -1, -1};
340 SimplexId v0{}, v1{}, v2{}, v3{};
341 triangulation.getCellVertex(cellId, 0, v0);
342 triangulation.getCellVertex(cellId, 1, v1);
343 triangulation.getCellVertex(cellId, 2, v2);
344 triangulation.getCellVertex(cellId, 3, v3);
345 if(v0 == a) {
346 lowVerts[0] = offsets[v1];
347 lowVerts[1] = offsets[v2];
348 lowVerts[2] = offsets[v3];
349 } else if(v1 == a) {
350 lowVerts[0] = offsets[v0];
351 lowVerts[1] = offsets[v2];
352 lowVerts[2] = offsets[v3];
353 } else if(v2 == a) {
354 lowVerts[0] = offsets[v0];
355 lowVerts[1] = offsets[v1];
356 lowVerts[2] = offsets[v3];
357 } else if(v3 == a) {
358 lowVerts[0] = offsets[v0];
359 lowVerts[1] = offsets[v1];
360 lowVerts[2] = offsets[v2];
361 }
362 if(offsets[a] > *std::max_element(
363 lowVerts.begin(), lowVerts.end())) { // tetra in lowerStar
364
365 // higher order vertex first
366 std::sort(lowVerts.rbegin(), lowVerts.rend());
367
368 uint8_t j{}, k{};
369 // store triangles indices of current tetra
370 std::array<uint8_t, 3> faces{};
371 for(const auto &t : ls[2]) {
372 // lowVerts & t.lowVerts are ordered, no need to check if
373 // t.lowVerts[0] == lowVerts[2] or t.lowVerts[1] == lowVerts[0]
374 if((t.lowVerts_[0] == lowVerts[0]
375 && (t.lowVerts_[1] == lowVerts[1]
376 || t.lowVerts_[1] == lowVerts[2]))
377 || (t.lowVerts_[0] == lowVerts[1]
378 && t.lowVerts_[1] == lowVerts[2])) {
379 faces[k++] = j;
380 }
381 j++;
382 }
383
384 CellExt const localCellExt3{3, cellId, lowVerts, faces};
385 ls[3].emplace_back(localCellExt3);
386 }
387 }
388 }
389 }
390}
391
392template <typename triangulationType>
393inline void DiscreteGradient::pairCells(
394 CellExt &alpha, CellExt &beta, const triangulationType &triangulation) {
395#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
396 char localBId{0}, localAId{0};
397 SimplexId a{}, b{};
398
399 if(beta.dim_ == 1) {
400
401 for(SimplexId i = 0; i < 2; ++i) {
402 triangulation.getEdgeVertex(beta.id_, i, a);
403 if(a == alpha.id_) {
404 localAId = i;
405 break;
406 }
407 }
408 const auto nedges = triangulation.getVertexEdgeNumber(alpha.id_);
409 for(SimplexId i = 0; i < nedges; ++i) {
410 triangulation.getVertexEdge(alpha.id_, i, b);
411 if(b == beta.id_) {
412 localBId = i;
413 break;
414 }
415 }
416 } else if(beta.dim_ == 2) {
417 for(SimplexId i = 0; i < 3; ++i) {
418 triangulation.getTriangleEdge(beta.id_, i, a);
419 if(a == alpha.id_) {
420 localAId = i;
421 break;
422 }
423 }
424 const auto ntri = triangulation.getEdgeTriangleNumber(alpha.id_);
425 for(SimplexId i = 0; i < ntri; ++i) {
426 triangulation.getEdgeTriangle(alpha.id_, i, b);
427 if(b == beta.id_) {
428 localBId = i;
429 break;
430 }
431 }
432 } else {
433 for(SimplexId i = 0; i < 4; ++i) {
434 triangulation.getCellTriangle(beta.id_, i, a);
435 if(a == alpha.id_) {
436 localAId = i;
437 break;
438 }
439 }
440 const auto ntetra = triangulation.getTriangleStarNumber(alpha.id_);
441 for(SimplexId i = 0; i < ntetra; ++i) {
442 triangulation.getTriangleStar(alpha.id_, i, b);
443 if(b == beta.id_) {
444 localBId = i;
445 break;
446 }
447 }
448 }
449 (*gradient_)[2 * alpha.dim_][alpha.id_] = localBId;
450 (*gradient_)[2 * alpha.dim_ + 1][beta.id_] = localAId;
451#else
452 TTK_FORCE_USE(triangulation);
453 (*gradient_)[2 * alpha.dim_][alpha.id_] = beta.id_;
454 (*gradient_)[2 * alpha.dim_ + 1][beta.id_] = alpha.id_;
455#endif // TTK_ENABLE_DCG_OPTIMIZE_MEMORY
456 alpha.paired_ = true;
457 beta.paired_ = true;
458}
459
460template <typename triangulationType>
461int DiscreteGradient::processLowerStars(
462 const SimplexId *const offsets, const triangulationType &triangulation) {
463
464 /* Compute gradient */
465
466 auto nverts = triangulation.getNumberOfVertices();
467
468 // Comparison function for Cells inside priority queues
469 const auto orderCells = [&](const CellExt &a, const CellExt &b) -> bool {
470 return a.lowVerts_ > b.lowVerts_;
471 };
472
473 // Type alias for priority queues
474 using pqType
475 = std::priority_queue<std::reference_wrapper<CellExt>,
476 std::vector<std::reference_wrapper<CellExt>>,
477 decltype(orderCells)>;
478
479 // To reduce allocations, priority queues and lowerStar objects are
480 // cleaned & reused between iterations.
481
482 // Priority queues are pushed at the beginning and popped at the
483 // end. To pop the minimum, elements should be sorted in a
484 // decreasing order.
485 pqType pqZero{orderCells}, pqOne{orderCells};
486
487 // store lower star structure
488 lowerStarType Lx;
489
490#ifdef TTK_ENABLE_OPENMP
491#pragma omp parallel for num_threads(threadNumber_) \
492 firstprivate(Lx, pqZero, pqOne)
493#endif // TTK_ENABLE_OPENMP
494 for(SimplexId x = 0; x < nverts; x++) {
495
496 // clear priority queues (they should be empty at the end of the
497 // previous iteration)
498 while(!pqZero.empty()) {
499 pqZero.pop();
500 }
501 while(!pqOne.empty()) {
502 pqOne.pop();
503 }
504
505 // Insert into pqOne cofacets of cell c_alpha such as numUnpairedFaces == 1
506 const auto insertCofacets = [&](const CellExt &ca, lowerStarType &ls) {
507 if(ca.dim_ == 1) {
508 for(auto &beta : ls[2]) {
509 if(ls[1][beta.faces_[0]].id_ == ca.id_
510 || ls[1][beta.faces_[1]].id_ == ca.id_) {
511 // edge ca belongs to triangle beta
512 if(numUnpairedFacesTriangle(beta, ls).first == 1) {
513 pqOne.push(beta);
514 }
515 }
516 }
517
518 } else if(ca.dim_ == 2) {
519 for(auto &beta : ls[3]) {
520 if(ls[2][beta.faces_[0]].id_ == ca.id_
521 || ls[2][beta.faces_[1]].id_ == ca.id_
522 || ls[2][beta.faces_[2]].id_ == ca.id_) {
523 // triangle ca belongs to tetra beta
524 if(numUnpairedFacesTetra(beta, ls).first == 1) {
525 pqOne.push(beta);
526 }
527 }
528 }
529 }
530 };
531
532 lowerStar(Lx, x, offsets, triangulation);
533 // In case the vertex is a ghost, the gradient of the
534 // simplices of its star is set to GHOST_GRADIENT
535#ifdef TTK_ENABLE_MPI
536 if(ttk::isRunningWithMPI()
537 && triangulation.getVertexRank(x) != ttk::MPIrank_) {
538 int sizeDim = Lx.size();
539 for(int i = 0; i < sizeDim; i++) {
540 int nCells = Lx[i].size();
541 for(int j = 0; j < nCells; j++) {
542 setCellToGhost(Lx[i][j].dim_, Lx[i][j].id_);
543 }
544 }
545 } else
546#endif // TTK_ENABLE_MPI
547
548 {
549 // Lx[1] empty => x is a local minimum
550 if(!Lx[1].empty()) {
551 // get delta: 1-cell (edge) with minimal G value (steeper gradient)
552 size_t minId = 0;
553 for(size_t i = 1; i < Lx[1].size(); ++i) {
554 const auto &a = Lx[1][minId].lowVerts_[0];
555 const auto &b = Lx[1][i].lowVerts_[0];
556 if(a > b) {
557 // edge[i] < edge[0]
558 minId = i;
559 }
560 }
561
562 auto &c_delta = Lx[1][minId];
563
564 // store x (0-cell) -> delta (1-cell) V-path
565 pairCells(Lx[0][0], c_delta, triangulation);
566
567 // push every 1-cell in Lx that is not delta into pqZero
568 for(auto &alpha : Lx[1]) {
569 if(alpha.id_ != c_delta.id_) {
570 pqZero.push(alpha);
571 }
572 }
573
574 // push into pqOne every coface of delta in Lx (2-cells only,
575 // 3-cells have not any facet paired yet) such that
576 // numUnpairedFaces == 1
577 insertCofacets(c_delta, Lx);
578
579 while(!pqOne.empty() || !pqZero.empty()) {
580 while(!pqOne.empty()) {
581 auto &c_alpha = pqOne.top().get();
582 pqOne.pop();
583 auto unpairedFaces = numUnpairedFaces(c_alpha, Lx);
584 if(unpairedFaces.first == 0) {
585 pqZero.push(c_alpha);
586 } else {
587 auto &c_pair_alpha = Lx[c_alpha.dim_ - 1][unpairedFaces.second];
588
589 // store (pair_alpha) -> (alpha) V-path
590 pairCells(c_pair_alpha, c_alpha, triangulation);
591
592 // add cofaces of c_alpha and c_pair_alpha to pqOne
593 insertCofacets(c_alpha, Lx);
594 insertCofacets(c_pair_alpha, Lx);
595 }
596 }
597
598 // skip pair_alpha from pqZero:
599 // cells in pqZero are not critical if already paired
600 while(!pqZero.empty() && pqZero.top().get().paired_) {
601 pqZero.pop();
602 }
603
604 if(!pqZero.empty()) {
605 auto &c_gamma = pqZero.top().get();
606 pqZero.pop();
607
608 // gamma is a critical cell
609 // mark gamma as paired
610 c_gamma.paired_ = true;
611
612 // add cofacets of c_gamma to pqOne
613 insertCofacets(c_gamma, Lx);
614 }
615 }
616 }
617 }
618 }
619
620 return 0;
621}
622
623template <typename triangulationType>
625 const Cell &cell, const triangulationType &triangulation) const {
626
627 if(cell.dim_ > this->dimensionality_ || cell.dim_ < 0) {
628 return false;
629 }
630
631 const auto vert{this->getCellGreaterVertex(cell, triangulation)};
632 return triangulation.isVertexOnBoundary(vert);
633}
634
635template <typename triangulationType>
638 const triangulationType &triangulation,
639 bool isReverse) const {
640
641 // ensure that getPairedCell(Cell, boolean) calls are rejected
642 static_assert(
643 std::is_base_of<AbstractTriangulation, triangulationType>(),
644 "triangulationType should be an AbstractTriangulation derivative");
645
646#ifndef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
647 TTK_FORCE_USE(triangulation);
648#endif // !TTK_ENABLE_DCG_OPTIMIZE_MEMORY
649
650 if((cell.dim_ > this->dimensionality_ - 1 && !isReverse)
651 || (cell.dim_ > this->dimensionality_ && isReverse) || cell.dim_ < 0) {
652 return -1;
653 }
654
655 SimplexId id{-1};
656
657 if(cell.dim_ == 0) {
658 if(!isReverse) {
659#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
660 const auto locId{(*gradient_)[0][cell.id_]};
661 if(locId != -1) {
662 triangulation.getVertexEdge(cell.id_, locId, id);
663 }
664#else
665 id = (*gradient_)[0][cell.id_];
666#endif
667 }
668 }
669
670 else if(cell.dim_ == 1) {
671 if(isReverse) {
672#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
673 const auto locId{(*gradient_)[1][cell.id_]};
674 if(locId != -1) {
675 triangulation.getEdgeVertex(cell.id_, locId, id);
676 }
677#else
678 id = (*gradient_)[1][cell.id_];
679#endif
680 } else {
681#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
682 const auto locId{(*gradient_)[2][cell.id_]};
683 if(locId != -1) {
684 triangulation.getEdgeTriangle(cell.id_, locId, id);
685 }
686#else
687 id = (*gradient_)[2][cell.id_];
688#endif
689 }
690 }
691
692 else if(cell.dim_ == 2) {
693 if(isReverse) {
694#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
695 const auto locId{(*gradient_)[3][cell.id_]};
696 if(locId != -1) {
697 triangulation.getTriangleEdge(cell.id_, locId, id);
698 }
699#else
700 id = (*gradient_)[3][cell.id_];
701#endif
702 } else {
703#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
704 const auto locId{(*gradient_)[4][cell.id_]};
705 if(locId != -1) {
706 triangulation.getTriangleStar(cell.id_, locId, id);
707 }
708#else
709 id = (*gradient_)[4][cell.id_];
710#endif
711 }
712 }
713
714 else if(cell.dim_ == 3) {
715 if(isReverse) {
716#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
717 const auto locId{(*gradient_)[5][cell.id_]};
718 if(locId != -1) {
719 triangulation.getCellTriangle(cell.id_, locId, id);
720 }
721#else
722 id = (*gradient_)[5][cell.id_];
723#endif
724 }
725 }
726
727 return id;
728}
729
730template <typename triangulationType>
732 const Cell &cell,
733 std::vector<Cell> &vpath,
734 const triangulationType &triangulation) const {
735
736 if(cell.dim_ == 0) {
737 // assume that cellId is a vertex
738 SimplexId currentId = cell.id_;
739 SimplexId connectedEdgeId;
740 do {
741 // add a vertex
742 const Cell vertex(0, currentId);
743 vpath.push_back(vertex);
744
745 if(isCellCritical(vertex)) {
746 break;
747 }
748
749 connectedEdgeId = getPairedCell(vertex, triangulation);
750 if(connectedEdgeId == -1) {
751 break;
752 }
753
754 // add an edge
755 const Cell edge(1, connectedEdgeId);
756 vpath.push_back(edge);
757
758 if(isCellCritical(edge)) {
759 break;
760 }
761
762 for(int i = 0; i < 2; ++i) {
763 SimplexId vertexId;
764 triangulation.getEdgeVertex(connectedEdgeId, i, vertexId);
765
766 if(vertexId != currentId) {
767 currentId = vertexId;
768 break;
769 }
770 }
771
772 } while(connectedEdgeId != -1);
773 }
774
775 return 0;
776}
777
778template <typename triangulationType>
780 const Cell &saddle2,
781 const Cell &saddle1,
782 const std::vector<bool> &isVisited,
783 std::vector<Cell> *const vpath,
784 const triangulationType &triangulation,
785 const bool stopIfMultiConnected,
786 const bool enableCycleDetector) const {
787
788 // debug
789 const SimplexId numberOfEdges = triangulation.getNumberOfEdges();
790 std::vector<bool> isCycle;
791 if(enableCycleDetector) {
792 isCycle.resize(numberOfEdges, false);
793 }
794
795 if(dimensionality_ == 3) {
796 // add the 2-saddle to the path
797 if(vpath != nullptr) {
798 vpath->push_back(saddle2);
799 }
800
801 SimplexId currentId = -1;
802 {
803 int nconnections = 0;
804 for(int i = 0; i < 3; ++i) {
805 SimplexId edgeId;
806 triangulation.getTriangleEdge(saddle2.id_, i, edgeId);
807 if(isVisited[edgeId]) {
808 // saddle2 can be adjacent to saddle1 on the wall
809 if(isSaddle1(Cell(1, edgeId))) {
810 if(vpath != nullptr) {
811 vpath->push_back(Cell(1, edgeId));
812 }
813 return false;
814 }
815
816 currentId = edgeId;
817 ++nconnections;
818 }
819 }
820 if(stopIfMultiConnected && nconnections > 1) {
821 return true;
822 }
823 }
824
825 int oldId;
826 do {
827
828 // debug
829 if(enableCycleDetector) {
830 if(not isCycle[currentId]) {
831 isCycle[currentId] = true;
832 } else {
833 this->printErr("Cycle detected on the wall of 1-saddle "
834 + std::to_string(saddle1.id_));
835 break;
836 }
837 }
838
839 oldId = currentId;
840
841 // add an edge
842 const Cell edge(1, currentId);
843 if(vpath != nullptr) {
844 vpath->push_back(edge);
845 }
846
847 if(isCellCritical(edge)) {
848 break;
849 }
850
851 const SimplexId connectedTriangleId = getPairedCell(edge, triangulation);
852
853 // add a triangle
854 const Cell triangle(2, connectedTriangleId);
855 if(vpath != nullptr) {
856 vpath->push_back(triangle);
857 }
858
859 if(isCellCritical(triangle)) {
860 break;
861 }
862
863 int nconnections = 0;
864 for(int i = 0; i < 3; ++i) {
865 SimplexId edgeId;
866 triangulation.getTriangleEdge(connectedTriangleId, i, edgeId);
867
868 if(isVisited[edgeId] and edgeId != oldId) {
869 currentId = edgeId;
870 ++nconnections;
871 }
872 }
873 if(stopIfMultiConnected && nconnections > 1) {
874 return true;
875 }
876
877 // stop at convergence caused by boundary effect
878 } while(currentId != oldId);
879 }
880
881 return false;
882}
883
884template <typename triangulationType>
886 std::vector<Cell> &vpath,
887 const triangulationType &triangulation,
888 const bool enableCycleDetector) const {
889
890 const SimplexId numberOfCells = triangulation.getNumberOfCells();
891 std::vector<bool> isCycle;
892 if(enableCycleDetector) {
893 isCycle.resize(numberOfCells, false);
894 }
895
896 if(dimensionality_ == 2) {
897 if(cell.dim_ == 2) {
898 // assume that cellId is a triangle
899 SimplexId currentId = cell.id_;
900 SimplexId oldId;
901 do {
902 oldId = currentId;
903
904 // add a triangle
905 const Cell triangle(2, currentId);
906 vpath.push_back(triangle);
907
908 if(isCellCritical(triangle)) {
909 break;
910 }
911
912 const SimplexId connectedEdgeId
913 = getPairedCell(triangle, triangulation, true);
914 if(connectedEdgeId == -1) {
915 break;
916 }
917
918 // add an edge
919 const Cell edge(1, connectedEdgeId);
920 vpath.push_back(edge);
921
922 if(isCellCritical(edge)) {
923 break;
924 }
925
926 const SimplexId starNumber
927 = triangulation.getEdgeStarNumber(connectedEdgeId);
928 for(SimplexId i = 0; i < starNumber; ++i) {
929 SimplexId starId;
930 triangulation.getEdgeStar(connectedEdgeId, i, starId);
931
932 if(starId != currentId) {
933 currentId = starId;
934 break;
935 }
936 }
937
938 // stop at convergence caused by boundary effect
939 } while(currentId != oldId);
940 }
941 } else if(dimensionality_ == 3) {
942 if(cell.dim_ == 3) {
943 // assume that cellId is a tetra
944 SimplexId currentId = cell.id_;
945 SimplexId oldId;
946 do {
947
948 // debug
949 if(enableCycleDetector) {
950 if(not isCycle[currentId]) {
951 isCycle[currentId] = true;
952 } else {
953 this->printErr("cycle detected in the path from tetra "
954 + std::to_string(cell.id_));
955 break;
956 }
957 }
958
959 oldId = currentId;
960
961 // add a tetra
962 const Cell tetra(3, currentId);
963 vpath.push_back(tetra);
964
965 if(isCellCritical(tetra)) {
966 break;
967 }
968
969 const SimplexId connectedTriangleId
970 = getPairedCell(tetra, triangulation, true);
971 if(connectedTriangleId == -1) {
972 break;
973 }
974
975 // add a triangle
976 const Cell triangle(2, connectedTriangleId);
977 vpath.push_back(triangle);
978
979 if(isCellCritical(triangle)) {
980 break;
981 }
982
983 const SimplexId starNumber
984 = triangulation.getTriangleStarNumber(connectedTriangleId);
985 for(SimplexId i = 0; i < starNumber; ++i) {
986 SimplexId starId;
987 triangulation.getTriangleStar(connectedTriangleId, i, starId);
988
989 if(starId != currentId) {
990 currentId = starId;
991 break;
992 }
993 }
994
995 // stop at convergence caused by boundary effect
996 } while(currentId != oldId);
997 }
998 }
999
1000 return 0;
1001}
1002
1003template <typename triangulationType>
1005 const Cell &saddle1,
1006 const Cell &saddle2,
1007 const std::vector<bool> &isVisited,
1008 std::vector<Cell> *const vpath,
1009 const triangulationType &triangulation,
1010 const bool stopIfMultiConnected,
1011 const bool enableCycleDetector,
1012 bool *const cycleFound) const {
1013
1014 // debug
1015 const SimplexId numberOfTriangles = triangulation.getNumberOfTriangles();
1016 std::vector<bool> isCycle;
1017 if(enableCycleDetector) {
1018 isCycle.resize(numberOfTriangles, false);
1019 }
1020
1021 if(dimensionality_ == 3) {
1022 // add the 1-saddle to the path
1023 if(vpath != nullptr) {
1024 vpath->push_back(saddle1);
1025 }
1026
1027 SimplexId currentId = -1;
1028 {
1029 int nconnections = 0;
1030 const SimplexId triangleNumber
1031 = triangulation.getEdgeTriangleNumber(saddle1.id_);
1032 for(SimplexId i = 0; i < triangleNumber; ++i) {
1033 SimplexId triangleId;
1034 triangulation.getEdgeTriangle(saddle1.id_, i, triangleId);
1035 if(isVisited[triangleId]) {
1036 // saddle1 can be adjacent to saddle2 on the wall
1037 if(isSaddle2(Cell(2, triangleId))) {
1038 if(vpath != nullptr) {
1039 vpath->push_back(Cell(2, triangleId));
1040 }
1041 return false;
1042 }
1043
1044 currentId = triangleId;
1045 ++nconnections;
1046 }
1047 }
1048 if(stopIfMultiConnected && nconnections > 1) {
1049 return true;
1050 }
1051 }
1052
1053 if(currentId == -1) {
1054 return true;
1055 }
1056
1057 SimplexId oldId;
1058 do {
1059
1060 // debug
1061 if(enableCycleDetector) {
1062 if(not isCycle[currentId]) {
1063 isCycle[currentId] = true;
1064 } else {
1065 if(cycleFound)
1066 *cycleFound = true;
1067 else
1068 this->printErr("Cycle detected on the wall of 2-saddle "
1069 + std::to_string(saddle2.id_));
1070 break;
1071 }
1072 }
1073
1074 oldId = currentId;
1075
1076 // add a triangle
1077 const Cell triangle(2, currentId);
1078 if(vpath != nullptr) {
1079 vpath->push_back(triangle);
1080 }
1081
1082 if(isCellCritical(triangle)) {
1083 break;
1084 }
1085
1086 const SimplexId connectedEdgeId
1087 = getPairedCell(triangle, triangulation, true);
1088
1089 // add an edge
1090 const Cell edge(1, connectedEdgeId);
1091 if(vpath != nullptr) {
1092 vpath->push_back(edge);
1093 }
1094
1095 if(isCellCritical(edge)) {
1096 break;
1097 }
1098
1099 int nconnections = 0;
1100 const SimplexId triangleNumber
1101 = triangulation.getEdgeTriangleNumber(connectedEdgeId);
1102 for(SimplexId i = 0; i < triangleNumber; ++i) {
1103 SimplexId triangleId;
1104 triangulation.getEdgeTriangle(connectedEdgeId, i, triangleId);
1105
1106 if(isVisited[triangleId] and triangleId != oldId) {
1107 currentId = triangleId;
1108 ++nconnections;
1109 }
1110 }
1111 if(stopIfMultiConnected && nconnections > 1) {
1112 return true;
1113 }
1114
1115 // stop at convergence caused by boundary effect
1116 } while(currentId != oldId);
1117 }
1118
1119 return false;
1120}
1121
1122template <typename triangulationType>
1124 const Cell &cell, const triangulationType &triangulation) const {
1125 if(dimensionality_ == 3) {
1126 if(cell.dim_ == 1) {
1127 const SimplexId originId = getPairedCell(cell, triangulation);
1128 if(originId == -1)
1129 return false;
1130
1131 std::queue<SimplexId> bfs;
1132 bfs.push(originId);
1133 std::vector<bool> isVisited(triangulation.getNumberOfTriangles(), false);
1134
1135 // BFS traversal
1136 while(!bfs.empty()) {
1137 const SimplexId triangleId = bfs.front();
1138 bfs.pop();
1139
1140 isVisited[triangleId] = true;
1141
1142 for(int j = 0; j < 3; ++j) {
1143 SimplexId edgeId;
1144 triangulation.getTriangleEdge(triangleId, j, edgeId);
1145
1146 const SimplexId pairedCellId
1147 = getPairedCell(Cell(1, edgeId), triangulation);
1148
1149 if(triangleId == pairedCellId or pairedCellId == -1)
1150 continue;
1151
1152 if(isVisited[pairedCellId])
1153 return true;
1154 else
1155 bfs.push(pairedCellId);
1156 }
1157 }
1158 }
1159 }
1160 return false;
1161}
1162
1163template <typename triangulationType>
1165 const Cell &cell,
1166 VisitedMask &mask,
1167 const triangulationType &triangulation,
1168 std::vector<Cell> *const wall,
1169 std::vector<SimplexId> *const saddles) const {
1170
1171 if(saddles != nullptr) {
1172 saddles->clear();
1173 }
1174
1175 if(dimensionality_ == 3) {
1176 if(cell.dim_ == 2) {
1177 // assume that cellId is a triangle
1178 const SimplexId originId = cell.id_;
1179
1180 std::queue<SimplexId> bfs;
1181 bfs.push(originId);
1182
1183 // BFS traversal
1184 while(!bfs.empty()) {
1185 const SimplexId triangleId = bfs.front();
1186 bfs.pop();
1187
1188 if(!mask.isVisited_[triangleId]) {
1189 mask.isVisited_[triangleId] = true;
1190 mask.visitedIds_.emplace_back(triangleId);
1191
1192 // add the triangle
1193 if(wall != nullptr) {
1194 wall->push_back(Cell(2, triangleId));
1195 }
1196
1197 for(int j = 0; j < 3; ++j) {
1198 SimplexId edgeId;
1199 triangulation.getTriangleEdge(triangleId, j, edgeId);
1200
1201 if((saddles != nullptr) and isSaddle1(Cell(1, edgeId))) {
1202 saddles->emplace_back(edgeId);
1203 }
1204
1205 const SimplexId pairedCellId
1206 = getPairedCell(Cell(1, edgeId), triangulation);
1207
1208 if(pairedCellId != -1 and pairedCellId != triangleId) {
1209 bfs.push(pairedCellId);
1210 }
1211 }
1212 }
1213 }
1214
1215 if(saddles != nullptr && saddles->size() > 1) {
1216 std::sort(saddles->begin(), saddles->end());
1217 const auto last = std::unique(saddles->begin(), saddles->end());
1218 saddles->erase(last, saddles->end());
1219 }
1220 }
1221 }
1222
1223 return 0;
1224}
1225
1226template <typename triangulationType>
1228 const Cell &cell,
1229 VisitedMask &mask,
1230 const triangulationType &triangulation,
1231 std::vector<Cell> *const wall,
1232 std::vector<SimplexId> *const saddles) const {
1233
1234 if(saddles != nullptr) {
1235 saddles->clear();
1236 }
1237
1238 if(dimensionality_ == 3) {
1239 if(cell.dim_ == 1) {
1240 // assume that cellId is an edge
1241 const SimplexId originId = cell.id_;
1242
1243 std::queue<SimplexId> bfs;
1244 bfs.push(originId);
1245
1246 // BFS traversal
1247 while(!bfs.empty()) {
1248 const SimplexId edgeId = bfs.front();
1249 bfs.pop();
1250
1251 if(!mask.isVisited_[edgeId]) {
1252 mask.isVisited_[edgeId] = true;
1253 mask.visitedIds_.emplace_back(edgeId);
1254
1255 // add the edge
1256 if(wall != nullptr) {
1257 wall->push_back(Cell(1, edgeId));
1258 }
1259
1260 const SimplexId triangleNumber
1261 = triangulation.getEdgeTriangleNumber(edgeId);
1262 for(SimplexId j = 0; j < triangleNumber; ++j) {
1263 SimplexId triangleId;
1264 triangulation.getEdgeTriangle(edgeId, j, triangleId);
1265
1266 if((saddles != nullptr) and isSaddle2(Cell(2, triangleId))) {
1267 saddles->emplace_back(triangleId);
1268 }
1269
1270 const SimplexId pairedCellId
1271 = getPairedCell(Cell(2, triangleId), triangulation, true);
1272
1273 if(pairedCellId != -1 and pairedCellId != edgeId) {
1274 bfs.push(pairedCellId);
1275 }
1276 }
1277 }
1278 }
1279
1280 if(saddles != nullptr && saddles->size() > 1) {
1281 std::sort(saddles->begin(), saddles->end());
1282 const auto last = std::unique(saddles->begin(), saddles->end());
1283 saddles->erase(last, saddles->end());
1284 }
1285 }
1286 }
1287
1288 return 0;
1289}
1290
1291template <typename triangulationType>
1293 const std::vector<Cell> &vpath,
1294 const triangulationType &triangulation) const {
1295
1296 if(dimensionality_ == 2) {
1297 // assume that the first cell is an edge
1298 const SimplexId numberOfCellsInPath = vpath.size();
1299 for(SimplexId i = 0; i < numberOfCellsInPath; i += 2) {
1300 const SimplexId edgeId = vpath[i].id_;
1301 const SimplexId triangleId = vpath[i + 1].id_;
1302
1303#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
1304 for(int k = 0; k < 3; ++k) {
1305 SimplexId tmp;
1306 triangulation.getCellEdge(triangleId, k, tmp);
1307 if(tmp == edgeId) {
1308 (*gradient_)[3][triangleId] = k;
1309 break;
1310 }
1311 }
1312 for(int k = 0; k < triangulation.getEdgeStarNumber(edgeId); ++k) {
1313 SimplexId tmp;
1314 triangulation.getEdgeStar(edgeId, k, tmp);
1315 if(tmp == triangleId) {
1316 (*gradient_)[2][edgeId] = k;
1317 break;
1318 }
1319 }
1320#else
1321 TTK_FORCE_USE(triangulation);
1322 (*gradient_)[3][triangleId] = edgeId;
1323 (*gradient_)[2][edgeId] = triangleId;
1324#endif
1325 }
1326 } else if(dimensionality_ == 3) {
1327 // assume that the first cell is a triangle
1328 const SimplexId numberOfCellsInPath = vpath.size();
1329 for(SimplexId i = 0; i < numberOfCellsInPath; i += 2) {
1330 const SimplexId triangleId = vpath[i].id_;
1331 const SimplexId tetraId = vpath[i + 1].id_;
1332
1333#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
1334 for(int k = 0; k < 4; ++k) {
1335 SimplexId tmp;
1336 triangulation.getCellTriangle(tetraId, k, tmp);
1337 if(tmp == triangleId) {
1338 (*gradient_)[5][tetraId] = k;
1339 break;
1340 }
1341 }
1342 for(int k = 0; k < triangulation.getTriangleStarNumber(triangleId); ++k) {
1343 SimplexId tmp;
1344 triangulation.getTriangleStar(triangleId, k, tmp);
1345 if(tmp == tetraId) {
1346 (*gradient_)[4][triangleId] = k;
1347 break;
1348 }
1349 }
1350#else
1351 (*gradient_)[5][tetraId] = triangleId;
1352 (*gradient_)[4][triangleId] = tetraId;
1353#endif
1354 }
1355 }
1356
1357 return 0;
1358}
1359
1360template <typename triangulationType>
1362 const std::vector<Cell> &vpath,
1363 const triangulationType &triangulation) const {
1364
1365 // assume that the first cell is an edge
1366 for(size_t i = 0; i < vpath.size(); i += 2) {
1367 const SimplexId edgeId = vpath[i].id_;
1368 const SimplexId vertId = vpath[i + 1].id_;
1369
1370#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
1371 const auto nneighs = triangulation.getVertexEdgeNumber();
1372 for(int k = 0; k < nneighs; ++k) {
1373 SimplexId tmp;
1374 triangulation.getVertexEdge(vertId, k, tmp);
1375 if(tmp == edgeId) {
1376 (*gradient_)[0][vertId] = k;
1377 break;
1378 }
1379 }
1380 const auto nverts = triangulation.getEdgeStarNumber(edgeId);
1381 for(int k = 0; k < nverts; ++k) {
1382 SimplexId tmp;
1383 triangulation.getEdgeVertex(edgeId, k, tmp);
1384 if(tmp == vertId) {
1385 (*gradient_)[1][edgeId] = k;
1386 break;
1387 }
1388 }
1389#else
1390 TTK_FORCE_USE(triangulation);
1391 (*gradient_)[0][vertId] = edgeId;
1392 (*gradient_)[1][edgeId] = vertId;
1393#endif
1394 }
1395
1396 return 0;
1397}
1398
1399template <typename triangulationType>
1401 const std::vector<Cell> &vpath,
1402 const triangulationType &triangulation,
1403 bool cancelReversal) const {
1404
1405 if(dimensionality_ == 3) {
1406 // assume that the first cell is an edge
1407 if(cancelReversal) {
1408 if(vpath.empty())
1409 return 0;
1410 (*gradient_)[2][vpath[0].id_] = NULL_GRADIENT;
1411 // assume that the last cell is a triangle
1412 if(vpath.size() <= 1)
1413 return 0;
1414 (*gradient_)[3][vpath[vpath.size() - 1].id_] = NULL_GRADIENT;
1415 }
1416 const SimplexId numberOfCellsInPath = vpath.size();
1417 const SimplexId startIndex = (cancelReversal ? 2 : 0);
1418 for(SimplexId i = startIndex; i < numberOfCellsInPath; i += 2) {
1419 const SimplexId vpathEdgeIndex = i;
1420 const SimplexId vpathTriangleIndex = (cancelReversal ? i - 1 : i + 1);
1421 const SimplexId edgeId = vpath[vpathEdgeIndex].id_;
1422 const SimplexId triangleId = vpath[vpathTriangleIndex].id_;
1423
1424#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
1425 for(int k = 0; k < 3; ++k) {
1426 SimplexId tmp;
1427 triangulation.getTriangleEdge(triangleId, k, tmp);
1428 if(tmp == edgeId) {
1429 (*gradient_)[3][triangleId] = k;
1430 break;
1431 }
1432 }
1433 for(int k = 0; k < triangulation.getEdgeTriangleNumber(edgeId); ++k) {
1434 SimplexId tmp;
1435 triangulation.getEdgeTriangle(edgeId, k, tmp);
1436 if(tmp == triangleId) {
1437 (*gradient_)[2][edgeId] = k;
1438 break;
1439 }
1440 }
1441#else
1442 TTK_FORCE_USE(triangulation);
1443 (*gradient_)[3][triangleId] = edgeId;
1444 (*gradient_)[2][edgeId] = triangleId;
1445#endif
1446 }
1447 }
1448
1449 return 0;
1450}
1451
1452template <typename triangulationType>
1454 const std::vector<Cell> &vpath,
1455 const triangulationType &triangulation) const {
1456
1457 if(dimensionality_ == 3) {
1458 // assume that the first cell is a triangle
1459 const SimplexId numberOfCellsInPath = vpath.size();
1460 for(SimplexId i = 0; i < numberOfCellsInPath; i += 2) {
1461 const SimplexId triangleId = vpath[i].id_;
1462 const SimplexId edgeId = vpath[i + 1].id_;
1463
1464#ifdef TTK_ENABLE_DCG_OPTIMIZE_MEMORY
1465 for(int k = 0; k < 3; ++k) {
1466 SimplexId tmp;
1467 triangulation.getTriangleEdge(triangleId, k, tmp);
1468 if(tmp == edgeId) {
1469 (*gradient_)[3][triangleId] = k;
1470 break;
1471 }
1472 }
1473 for(int k = 0; k < triangulation.getEdgeTriangleNumber(edgeId); ++k) {
1474 SimplexId tmp;
1475 triangulation.getEdgeTriangle(edgeId, k, tmp);
1476 if(tmp == triangleId) {
1477 (*gradient_)[2][edgeId] = k;
1478 break;
1479 }
1480 }
1481#else
1482 TTK_FORCE_USE(triangulation);
1483 (*gradient_)[2][edgeId] = triangleId;
1484 (*gradient_)[3][triangleId] = edgeId;
1485#endif
1486 }
1487 }
1488
1489 return 0;
1490}
1491
1492template <typename triangulationType>
1494 const Cell c, const triangulationType &triangulation) const {
1495
1496 const auto offsets = this->inputOffsets_;
1497
1498 auto cellDim = c.dim_;
1499 auto cellId = c.id_;
1500
1501 SimplexId vertexId = -1;
1502 if(cellDim == 0) {
1503 vertexId = cellId;
1504 }
1505
1506 else if(cellDim == 1) {
1507 SimplexId v0;
1508 SimplexId v1;
1509 triangulation.getEdgeVertex(cellId, 0, v0);
1510 triangulation.getEdgeVertex(cellId, 1, v1);
1511
1512 if(offsets[v0] > offsets[v1]) {
1513 vertexId = v0;
1514 } else {
1515 vertexId = v1;
1516 }
1517 }
1518
1519 else if(cellDim == 2) {
1520 SimplexId v0{}, v1{}, v2{};
1521 triangulation.getTriangleVertex(cellId, 0, v0);
1522 triangulation.getTriangleVertex(cellId, 1, v1);
1523 triangulation.getTriangleVertex(cellId, 2, v2);
1524 if(offsets[v0] > offsets[v1] && offsets[v0] > offsets[v2]) {
1525 vertexId = v0;
1526 } else if(offsets[v1] > offsets[v0] && offsets[v1] > offsets[v2]) {
1527 vertexId = v1;
1528 } else {
1529 vertexId = v2;
1530 }
1531 }
1532
1533 else if(cellDim == 3) {
1534 SimplexId v0{}, v1{}, v2{}, v3{};
1535 triangulation.getCellVertex(cellId, 0, v0);
1536 triangulation.getCellVertex(cellId, 1, v1);
1537 triangulation.getCellVertex(cellId, 2, v2);
1538 triangulation.getCellVertex(cellId, 3, v3);
1539 if(offsets[v0] > offsets[v1] && offsets[v0] > offsets[v2]
1540 && offsets[v0] > offsets[v3]) {
1541 vertexId = v0;
1542 } else if(offsets[v1] > offsets[v0] && offsets[v1] > offsets[v2]
1543 && offsets[v1] > offsets[v3]) {
1544 vertexId = v1;
1545 } else if(offsets[v2] > offsets[v0] && offsets[v2] > offsets[v1]
1546 && offsets[v2] > offsets[v3]) {
1547 vertexId = v2;
1548 } else {
1549 vertexId = v3;
1550 }
1551 }
1552 return vertexId;
1553}
1554
1555template <typename triangulationType>
1557 const Cell c, const triangulationType &triangulation) const {
1558
1559 const auto offsets = this->inputOffsets_;
1560
1561 auto cellDim = c.dim_;
1562 auto cellId = c.id_;
1563
1564 SimplexId vertexId = -1;
1565 if(cellDim == 0) {
1566 vertexId = cellId;
1567 }
1568
1569 else if(cellDim == 1) {
1570 SimplexId v0;
1571 SimplexId v1;
1572 triangulation.getEdgeVertex(cellId, 0, v0);
1573 triangulation.getEdgeVertex(cellId, 1, v1);
1574
1575 if(offsets[v0] < offsets[v1]) {
1576 vertexId = v0;
1577 } else {
1578 vertexId = v1;
1579 }
1580 }
1581
1582 else if(cellDim == 2) {
1583 SimplexId v0{}, v1{}, v2{};
1584 triangulation.getTriangleVertex(cellId, 0, v0);
1585 triangulation.getTriangleVertex(cellId, 1, v1);
1586 triangulation.getTriangleVertex(cellId, 2, v2);
1587 if(offsets[v0] < offsets[v1] && offsets[v0] < offsets[v2]) {
1588 vertexId = v0;
1589 } else if(offsets[v1] < offsets[v0] && offsets[v1] < offsets[v2]) {
1590 vertexId = v1;
1591 } else {
1592 vertexId = v2;
1593 }
1594 }
1595
1596 else if(cellDim == 3) {
1597 SimplexId v0{}, v1{}, v2{}, v3{};
1598 triangulation.getCellVertex(cellId, 0, v0);
1599 triangulation.getCellVertex(cellId, 1, v1);
1600 triangulation.getCellVertex(cellId, 2, v2);
1601 triangulation.getCellVertex(cellId, 3, v3);
1602 if(offsets[v0] < offsets[v1] && offsets[v0] < offsets[v2]
1603 && offsets[v0] < offsets[v3]) {
1604 vertexId = v0;
1605 } else if(offsets[v1] < offsets[v0] && offsets[v1] < offsets[v2]
1606 && offsets[v1] < offsets[v3]) {
1607 vertexId = v1;
1608 } else if(offsets[v2] < offsets[v0] && offsets[v2] < offsets[v1]
1609 && offsets[v2] < offsets[v3]) {
1610 vertexId = v2;
1611 } else {
1612 vertexId = v3;
1613 }
1614 }
1615 return vertexId;
1616}
1617
1618template <typename triangulationType>
1620 std::vector<std::array<float, 3>> &points,
1621 std::vector<char> &points_pairOrigins,
1622 std::vector<char> &cells_pairTypes,
1623 std::vector<SimplexId> &cellIds,
1624 std::vector<char> &cellDimensions,
1625 const triangulationType &triangulation) const {
1626
1627 const auto nDims = this->getNumberOfDimensions();
1628
1629 // number of glyphs per dimension
1630 std::vector<size_t> nGlyphsPerDim(nDims);
1631
1632#ifdef TTK_ENABLE_OPENMP
1633#pragma omp parallel for num_threads(threadNumber_)
1634#endif // TTK_ENABLE_OPENMP
1635 for(int i = 0; i < nDims - 1; ++i) {
1636 const auto nCells = this->getNumberOfCells(i, triangulation);
1637 for(SimplexId j = 0; j < nCells; ++j) {
1638 if(this->getPairedCell(Cell{i, j}, triangulation) > -1) {
1639 nGlyphsPerDim[i]++;
1640 }
1641 }
1642 }
1643
1644 // partial sum of number of gradient glyphs
1645 std::vector<size_t> offsets(nDims + 1);
1646 for(SimplexId i = 0; i < nDims; ++i) {
1647 offsets[i + 1] = offsets[i] + nGlyphsPerDim[i];
1648 }
1649
1650 // total number of glyphs
1651 const auto nGlyphs = offsets.back();
1652
1653 // resize arrays accordingly
1654 points.resize(2 * nGlyphs);
1655 points_pairOrigins.resize(2 * nGlyphs);
1656 cells_pairTypes.resize(nGlyphs);
1657 cellIds.resize(2 * nGlyphs);
1658 cellDimensions.resize(2 * nGlyphs);
1659
1660#ifdef TTK_ENABLE_OPENMP
1661#pragma omp parallel for num_threads(threadNumber_)
1662#endif // TTK_ENABLE_OPENMP
1663 for(int i = 0; i < nDims - 1; ++i) {
1664 const SimplexId nCells = getNumberOfCells(i, triangulation);
1665 size_t nProcessedGlyphs{offsets[i]};
1666 for(SimplexId j = 0; j < nCells; ++j) {
1667 const Cell c{i, j};
1668 const auto pcid = this->getPairedCell(c, triangulation);
1669 if(pcid > -1) {
1670 const Cell pc{i + 1, pcid};
1671 triangulation.getCellIncenter(
1672 c.id_, c.dim_, points[2 * nProcessedGlyphs].data());
1673 triangulation.getCellIncenter(
1674 pc.id_, pc.dim_, points[2 * nProcessedGlyphs + 1].data());
1675 points_pairOrigins[2 * nProcessedGlyphs] = 0;
1676 points_pairOrigins[2 * nProcessedGlyphs + 1] = 1;
1677 cells_pairTypes[nProcessedGlyphs] = i;
1678#ifdef TTK_ENABLE_MPI
1679 ttk::SimplexId globalId{-1};
1680 triangulation.getDistributedGlobalCellId(j, i, globalId);
1681 cellIds[2 * nProcessedGlyphs + 0] = globalId;
1682 triangulation.getDistributedGlobalCellId(pcid, i + 1, globalId);
1683 cellIds[2 * nProcessedGlyphs + 1] = globalId;
1684#else
1685 cellIds[2 * nProcessedGlyphs + 0] = j;
1686 cellIds[2 * nProcessedGlyphs + 1] = pcid;
1687#endif // TTK_ENABLE_MPI
1688 cellDimensions[2 * nProcessedGlyphs + 0] = i;
1689 cellDimensions[2 * nProcessedGlyphs + 1] = i + 1;
1690 nProcessedGlyphs++;
1691 }
1692 }
1693 }
1694
1695 return 0;
1696}
#define TTK_FORCE_USE(x)
Force the compiler to use the function/method parameter.
Definition BaseClass.h:57
std::array< std::vector< gradIdType >, 6 > gradientType
Discrete gradient internal struct.
int printWrn(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:159
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:149
TTK discreteGradient processing package.
SimplexId getCellLowerVertex(const Cell c, const triangulationType &triangulation) const
int reverseAscendingPath(const std::vector< Cell > &vpath, const triangulationType &triangulation) const
int reverseDescendingPath(const std::vector< Cell > &vpath, const triangulationType &triangulation) const
int setGradientGlyphs(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 reverseAscendingPathOnWall(const std::vector< Cell > &vpath, const triangulationType &triangulation, bool cancelReversal=false) 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
bool isSaddle1(const Cell &cell) const
bool isSaddle2(const Cell &cell) const
AbstractTriangulation::gradientKeyType inputScalarField_
SimplexId getNumberOfCells(const int dimension, const triangulationType &triangulation) const
bool isBoundary(const Cell &cell, const triangulationType &triangulation) const
bool isCellCritical(const int cellDim, const SimplexId cellId) const
SimplexId getPairedCell(const Cell &cell, const triangulationType &triangulation, bool isReverse=false) const
int getAscendingWall(const Cell &cell, VisitedMask &mask, const triangulationType &triangulation, std::vector< Cell > *const wall=nullptr, std::vector< SimplexId > *const saddles=nullptr) const
int reverseDescendingPathOnWall(const std::vector< Cell > &vpath, const triangulationType &triangulation) const
SimplexId getCellGreaterVertex(const Cell c, const triangulationType &triangulation) const
int getCriticalPoints(std::array< std::vector< SimplexId >, 4 > &criticalCellsByDim, const triangulationType &triangulation) const
bool getAscendingPathThroughWall(const Cell &saddle1, const Cell &saddle2, const std::vector< bool > &isVisited, std::vector< Cell > *const vpath, const triangulationType &triangulation, const bool stopIfMultiConnected=false, const bool enableCycleDetector=false, bool *const cycleFound=nullptr) const
dataType getPersistence(const Cell &up, const Cell &down, const dataType *const scalars, const triangulationType &triangulation) const
int buildGradient(const triangulationType &triangulation, bool bypassCache=false)
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
int getDescendingPath(const Cell &cell, std::vector< Cell > &vpath, const triangulationType &triangulation) const
bool detectGradientCycle(const Cell &cell, const triangulationType &triangulation) const
int getAscendingPath(const Cell &cell, std::vector< Cell > &vpath, const triangulationType &triangulation, const bool enableCycleDetector=false) const
AbstractTriangulation::gradientType localGradient_
int getDescendingWall(const Cell &cell, VisitedMask &mask, const triangulationType &triangulation, std::vector< Cell > *const wall=nullptr, std::vector< SimplexId > *const saddles=nullptr) const
AbstractTriangulation::gradientType * gradient_
const SimplexId * inputOffsets_
std::string to_string(__int128)
Definition ripserpy.cpp:99
COMMON_EXPORTS int MPIrank_
Definition BaseClass.cpp:9
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
T end(std::pair< T, T > &p)
Definition ripserpy.cpp:472
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 processLowerStars.
const std::array< SimplexId, 3 > lowVerts_
const std::array< uint8_t, 3 > faces_
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/|__ _|"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)