TTK
Loading...
Searching...
No Matches
DiscreteMorseSandwichMPI.h
Go to the documentation of this file.
1
13// of Persistence Diagrams for Massive Scalar Data" \n
20
21#pragma once
22#if defined(TTK_ENABLE_MPI) && defined(TTK_ENABLE_OPENMP)
23#include <DiscreteGradient.h>
24
25#include <algorithm>
26#include <array>
27#include <numeric>
28#include <random>
29#include <string>
30#include <unordered_map>
31
32namespace ttk {
33 class DiscreteMorseSandwichMPI : virtual public Debug {
34 public:
36
37 int setThreadNumber(const int threadNumber) {
38 threadNumber_ = threadNumber;
39 // needs at least 2 threads (communication thread)
40 if(threadNumber_ < 2) {
41 if(!ttk::MPIrank_) {
42 printWrn("The distributed persistence computation");
43 printWrn("needs at least 2 threads.");
44 printWrn("(dedicated communication thread)");
45 printWrn("Defaulting to 2 threads.");
46 }
47 threadNumber_ = 2;
48 }
49
50 return 0;
51 }
52
56 struct PersistencePair {
58 SimplexId birth;
60 SimplexId death;
62 int type;
63
64 PersistencePair(SimplexId b, SimplexId d, int t)
65 : birth{b}, death{d}, type{t} {
66 }
67 };
72 struct Rep {
73 ttk::SimplexId extremaId_{0};
74 ttk::SimplexId saddleId_{-1};
75 };
76
77 template <typename triangulationType>
78 void fillEdgeOrder(const ttk::SimplexId id,
79 const SimplexId *const offsets,
80 const triangulationType &triangulation,
81 ttk::SimplexId *vertsOrder) const {
82 triangulation.getEdgeVertex(id, 0, vertsOrder[0]);
83 triangulation.getEdgeVertex(id, 1, vertsOrder[1]);
84 vertsOrder[0] = offsets[vertsOrder[0]];
85 vertsOrder[1] = offsets[vertsOrder[1]];
86 std::sort(vertsOrder, vertsOrder + 2, std::greater<ttk::SimplexId>());
87 };
88
89 template <typename triangulationType>
90 void fillTriangleOrder(const ttk::SimplexId id,
91 const SimplexId *const offsets,
92 const triangulationType &triangulation,
93 ttk::SimplexId *vertsOrder) const {
94 triangulation.getTriangleVertex(id, 0, vertsOrder[0]);
95 triangulation.getTriangleVertex(id, 1, vertsOrder[1]);
96 triangulation.getTriangleVertex(id, 2, vertsOrder[2]);
97 vertsOrder[0] = offsets[vertsOrder[0]];
98 vertsOrder[1] = offsets[vertsOrder[1]];
99 vertsOrder[2] = offsets[vertsOrder[2]];
100 // sort vertices in decreasing order
101 std::sort(vertsOrder, vertsOrder + 3, std::greater<ttk::SimplexId>());
102 };
103
104 template <typename triangulationType>
105 void fillTetraOrder(const ttk::SimplexId id,
106 const SimplexId *const offsets,
107 const triangulationType &triangulation,
108 ttk::SimplexId *vertsOrder) const {
109 triangulation.getCellVertex(id, 0, vertsOrder[0]);
110 triangulation.getCellVertex(id, 1, vertsOrder[1]);
111 triangulation.getCellVertex(id, 2, vertsOrder[2]);
112 triangulation.getCellVertex(id, 3, vertsOrder[3]);
113 vertsOrder[0] = offsets[vertsOrder[0]];
114 vertsOrder[1] = offsets[vertsOrder[1]];
115 vertsOrder[2] = offsets[vertsOrder[2]];
116 vertsOrder[3] = offsets[vertsOrder[3]];
117 // sort vertices in decreasing order
118 std::sort(vertsOrder, vertsOrder + 4, std::greater<ttk::SimplexId>());
119 };
124 struct vpathToSend {
125 ttk::SimplexId saddleId_;
126 ttk::SimplexId extremaId_;
127 char saddleRank_;
128 };
129
136 template <int sizeExtr>
137 struct vpathFinished {
138 ttk::SimplexId saddleId_;
139 ttk::SimplexId extremaId_;
140 ttk::SimplexId vOrder_[sizeExtr];
141 ttk::SimplexId ghostPresenceSize_{0};
142 char extremaRank_;
143
144 vpathFinished(ttk::SimplexId saddleId,
145 ttk::SimplexId extremaId,
146 char rank)
147 : saddleId_{saddleId}, extremaId_{extremaId}, extremaRank_{rank} {
148 for(int i = 0; i < sizeExtr; i++) {
149 vOrder_[i] = 0;
150 }
151 }
152 vpathFinished(ttk::SimplexId saddleId,
153 ttk::SimplexId extremaId,
154 char rank,
155 ttk::SimplexId vOrder)
156 : saddleId_{saddleId}, extremaId_{extremaId}, extremaRank_{rank} {
157 vOrder_[0] = vOrder;
158 }
159 vpathFinished() : saddleId_{-1}, extremaId_{-1}, extremaRank_{0} {
160 for(int i = 0; i < sizeExtr; i++) {
161 vOrder_[i] = 0;
162 }
163 }
164
165 bool operator==(const vpathFinished<sizeExtr> &vp) {
166 return this->saddleId_ == vp.saddleId_
167 && this->extremaId_ == vp.extremaId_;
168 }
169 };
170
171 void createVpathMPIType(MPI_Datatype &MPI_MessageType) const {
172 ttk::SimplexId id = 0;
173 MPI_Datatype MPI_SimplexId = getMPIType(id);
174 MPI_Datatype types[] = {MPI_SimplexId, MPI_SimplexId, MPI_CHAR};
175 int lengths[] = {1, 1, 1};
176 const long int mpi_offsets[]
177 = {offsetof(vpathToSend, saddleId_), offsetof(vpathToSend, extremaId_),
178 offsetof(vpathToSend, saddleRank_)};
179 MPI_Type_create_struct(3, lengths, mpi_offsets, types, &MPI_MessageType);
180 MPI_Type_commit(&MPI_MessageType);
181 };
182
183 template <int sizeExtr>
184 void createFinishedVpathMPIType(MPI_Datatype &MPI_MessageType) const {
185 ttk::SimplexId id = 0;
186 MPI_Datatype MPI_SimplexId = getMPIType(id);
187 MPI_Datatype types[] = {
188 MPI_SimplexId, MPI_SimplexId, MPI_SimplexId, MPI_SimplexId, MPI_CHAR};
189 int lengths[] = {1, 1, sizeExtr, 1, 1};
190 const long int mpi_offsets[]
191 = {offsetof(vpathFinished<sizeExtr>, saddleId_),
192 offsetof(vpathFinished<sizeExtr>, extremaId_),
193 offsetof(vpathFinished<sizeExtr>, vOrder_),
194 offsetof(vpathFinished<sizeExtr>, ghostPresenceSize_),
195 offsetof(vpathFinished<sizeExtr>, extremaRank_)};
196 MPI_Type_create_struct(5, lengths, mpi_offsets, types, &MPI_MessageType);
197 MPI_Type_commit(&MPI_MessageType);
198 };
209 template <int sizeExtr, int sizeSad>
210 struct messageType {
211 ttk::SimplexId sOrder_[sizeSad];
212 ttk::SimplexId s1Order_[sizeSad];
213 ttk::SimplexId s2Order_[sizeSad];
214 ttk::SimplexId t1Order_[sizeExtr];
215 ttk::SimplexId t2Order_[sizeExtr];
216 ttk::SimplexId t1_;
217 ttk::SimplexId t2_;
219 ttk::SimplexId s1_;
220 ttk::SimplexId s2_;
221 char t1Rank_;
222 char t2Rank_;
223 char sRank_;
224 char s1Rank_;
225 char s2Rank_;
226 char hasBeenModified_{0};
227
228 messageType(ttk::SimplexId t1,
229 ttk::SimplexId *t1Order,
231 ttk::SimplexId *t2Order,
233 ttk::SimplexId *sOrder,
235 ttk::SimplexId *s1Order,
237 ttk::SimplexId *s2Order,
238 char t1Rank,
239 char t2Rank,
240 char sRank,
241 char s1Rank,
242 char s2Rank,
243 char mod) {
244 this->t1_ = t1;
245 this->t2_ = t2;
246 this->s_ = s;
247 this->s1_ = s1;
248 this->s2_ = s2;
249 this->t1Rank_ = t1Rank;
250 this->t2Rank_ = t2Rank;
251 this->sRank_ = sRank;
252 this->s1Rank_ = s1Rank;
253 this->s2Rank_ = s2Rank;
254 this->hasBeenModified_ = mod;
255 for(int i = 0; i < sizeExtr; i++) {
256 t1Order_[i] = t1Order[i];
257 t2Order_[i] = t2Order[i];
258 }
259 for(int i = 0; i < sizeSad; i++) {
260 sOrder_[i] = sOrder[i];
261 s1Order_[i] = s1Order[i];
262 s2Order_[i] = s2Order[i];
263 }
264 }
265
266 messageType() {
267 this->t1_ = -1;
268 this->t2_ = -1;
269 this->s_ = -1;
270 this->s1_ = -1;
271 this->s2_ = -1;
272 this->t1Rank_ = static_cast<char>(ttk::MPIrank_);
273 this->t2Rank_ = static_cast<char>(ttk::MPIrank_);
274 this->sRank_ = static_cast<char>(ttk::MPIrank_);
275 this->s1Rank_ = static_cast<char>(ttk::MPIrank_);
276 this->s2Rank_ = static_cast<char>(ttk::MPIrank_);
277 this->hasBeenModified_ = 0;
278 for(int i = 0; i < sizeExtr; i++) {
279 t1Order_[i] = -1;
280 t2Order_[i] = -1;
281 }
282 for(int i = 0; i < sizeSad; i++) {
283 sOrder_[i] = -1;
284 s1Order_[i] = -1;
285 s2Order_[i] = -1;
286 }
287 }
288
289 messageType(ttk::SimplexId s, ttk::SimplexId *sOrder, char sRank) {
290 this->t1_ = -1;
291 this->t2_ = -1;
292 this->s_ = s;
293 this->s1_ = -1;
294 this->s2_ = -1;
295 this->t1Rank_ = static_cast<char>(ttk::MPIrank_);
296 this->t2Rank_ = static_cast<char>(ttk::MPIrank_);
297 this->sRank_ = sRank;
298 this->s1Rank_ = static_cast<char>(ttk::MPIrank_);
299 this->s2Rank_ = static_cast<char>(ttk::MPIrank_);
300 this->hasBeenModified_ = 0;
301 for(int i = 0; i < sizeExtr; i++) {
302 t1Order_[i] = -1;
303 t2Order_[i] = -1;
304 }
305 for(int i = 0; i < sizeSad; i++) {
306 sOrder_[i] = sOrder[i];
307 s1Order_[i] = -1;
308 s2Order_[i] = -1;
309 }
310 }
311 messageType(ttk::SimplexId t1,
312 ttk::SimplexId *t1Order,
314 ttk::SimplexId *sOrder,
316 ttk::SimplexId *s1Order,
317 char t1Rank,
318 char sRank,
319 char s1Rank,
320 char mod) {
321 this->t1_ = t1;
322 this->t2_ = -1;
323 this->s2_ = -1;
324 this->s_ = s;
325 this->s1_ = s1;
326 this->t1Rank_ = t1Rank;
327 this->t2Rank_ = static_cast<char>(ttk::MPIrank_);
328 this->sRank_ = sRank;
329 this->s1Rank_ = s1Rank;
330 this->s2Rank_ = static_cast<char>(ttk::MPIrank_);
331 this->hasBeenModified_ = mod;
332 for(int i = 0; i < sizeExtr; i++) {
333 t1Order_[i] = t1Order[i];
334 t2Order_[i] = -1;
335 }
336 for(int i = 0; i < sizeSad; i++) {
337 sOrder_[i] = sOrder[i];
338 s1Order_[i] = s1Order[i];
339 }
340 }
341 ~messageType() = default;
342 };
343
344 bool compareArray(const ttk::SimplexId *arr1,
345 const ttk::SimplexId *arr2,
346 const int size) const {
347 for(int i = 0; i < size; i++) {
348 if(arr1[i] != arr2[i]) {
349 return arr1[i] < arr2[i];
350 }
351 }
352 return false;
353 };
354
355 template <int extrSize, int sadSize>
356 void createMPIMessageType(MPI_Datatype &MPI_MessageType) const {
357 ttk::SimplexId id = 0;
358 MPI_Datatype MPI_SimplexId = getMPIType(id);
359 MPI_Datatype types[]
360 = {MPI_SimplexId, MPI_SimplexId, MPI_SimplexId, MPI_SimplexId,
361 MPI_SimplexId, MPI_SimplexId, MPI_SimplexId, MPI_SimplexId,
362 MPI_SimplexId, MPI_SimplexId, MPI_CHAR, MPI_CHAR,
363 MPI_CHAR, MPI_CHAR, MPI_CHAR, MPI_CHAR};
364 int lengths[] = {sadSize, sadSize, sadSize, extrSize, extrSize, 1, 1, 1,
365 1, 1, 1, 1, 1, 1, 1, 1};
366 using simplexMessageType = messageType<extrSize, sadSize>;
367 const long int mpi_offsets[]
368 = {offsetof(simplexMessageType, sOrder_),
369 offsetof(simplexMessageType, s1Order_),
370 offsetof(simplexMessageType, s2Order_),
371 offsetof(simplexMessageType, t1Order_),
372 offsetof(simplexMessageType, t2Order_),
373 offsetof(simplexMessageType, t1_),
374 offsetof(simplexMessageType, t2_),
375 offsetof(simplexMessageType, s_),
376 offsetof(simplexMessageType, s1_),
377 offsetof(simplexMessageType, s2_),
378 offsetof(simplexMessageType, t1Rank_),
379 offsetof(simplexMessageType, t2Rank_),
380 offsetof(simplexMessageType, sRank_),
381 offsetof(simplexMessageType, s1Rank_),
382 offsetof(simplexMessageType, s2Rank_),
383 offsetof(simplexMessageType, hasBeenModified_)};
384 MPI_Type_create_struct(16, lengths, mpi_offsets, types, &MPI_MessageType);
385 MPI_Type_commit(&MPI_MessageType);
386 };
387
395 template <int size>
396 struct extremaNode {
397 ttk::SimplexId gid_{-1};
398 ttk::SimplexId lid_{-1};
399 ttk::SimplexId order_{-1};
400 Rep rep_;
401 char rank_{static_cast<char>(ttk::MPIrank_)};
402 ttk::SimplexId vOrder_[size];
403
404 extremaNode() {
405 rep_ = Rep{-1, -1};
406 };
407
408 extremaNode(ttk::SimplexId gid) : gid_{gid} {
409 rep_ = Rep{-1, -1};
410 };
411
412 extremaNode(ttk::SimplexId gid,
413 ttk::SimplexId lid,
414 ttk::SimplexId order,
415 Rep rep,
416 char rank,
417 ttk::SimplexId *vOrder)
418 : gid_{gid}, lid_{lid}, order_{order}, rank_{rank} {
419 for(ttk::SimplexId i = 0; i < size; i++) {
420 vOrder_[i] = vOrder[i];
421 }
422 rep_ = rep;
423 };
424
425 extremaNode(ttk::SimplexId gid,
426 ttk::SimplexId lid,
427 ttk::SimplexId order,
428 Rep rep,
429 char rank)
430 : gid_{gid}, lid_{lid}, order_{order}, rank_{rank} {
431 for(ttk::SimplexId i = 0; i < size; i++) {
432 vOrder_[i] = 0;
433 }
434 rep_ = rep;
435 };
436
437 bool operator==(const extremaNode<size> &t1) const {
438 return this->gid_ == t1.gid_;
439 }
440
441 bool operator!=(const extremaNode<size> &t1) const {
442 return this->gid_ != t1.gid_;
443 }
444 bool operator<(const extremaNode<size> &t1) const {
445 if(this->gid_ == t1.gid_) {
446 return false;
447 }
448 if(this->order_ != -1 && t1.order_ != -1) {
449 return this->order_ < t1.order_;
450 }
451 for(size_t i = 0; i < size; i++) {
452 if(this->vOrder_[i] != t1.vOrder_[i]) {
453 return this->vOrder_[i] < t1.vOrder_[i];
454 }
455 }
456 return this->gid_ < t1.gid_;
457 }
458 };
459
460 struct saddleIdPerProcess {
461 std::vector<ttk::SimplexId> saddleIds_;
462 char rank_;
463 };
473 template <int size>
474 struct saddleEdge {
475 ttk::SimplexId gid_{-1};
476 ttk::SimplexId lid_{-1};
477 ttk::SimplexId order_{-1};
478 ttk::SimplexId vOrder_[size];
479 std::array<ttk::SimplexId, 2> t_{-1, -1};
480 char rank_{static_cast<char>(ttk::MPIrank_)};
481
482 saddleEdge() {
483 for(ttk::SimplexId i = 0; i < size; i++) {
484 vOrder_[i] = -1;
485 }
486 };
487
488 saddleEdge(ttk::SimplexId gid,
489 ttk::SimplexId order,
490 ttk::SimplexId *vOrder,
491 char rank)
492
493 : gid_{gid}, order_{order}, rank_{rank} {
494 for(ttk::SimplexId i = 0; i < size; i++) {
495 vOrder_[i] = vOrder[i];
496 }
497 };
498
499 saddleEdge(ttk::SimplexId gid, ttk::SimplexId *vOrder, char rank)
500
501 : gid_{gid}, rank_{rank} {
502 for(ttk::SimplexId i = 0; i < size; i++) {
503 vOrder_[i] = vOrder[i];
504 }
505 };
506
507 bool operator==(const saddleEdge<size> &s1) const {
508 return this->gid_ == s1.gid_;
509 }
510
511 bool operator<(const saddleEdge<size> &s1) const {
512 if(this->gid_ == s1.gid_) {
513 return false;
514 }
515 if(this->order_ != -1 && s1.order_ != -1) {
516 return this->order_ < s1.order_;
517 }
518 for(int i = 0; i < size; i++) {
519 if(this->vOrder_[i] != s1.vOrder_[i]) {
520 return this->vOrder_[i] < s1.vOrder_[i];
521 }
522 }
523 return this->gid_ < s1.gid_;
524 }
525 };
532 template <int size>
533 struct saddle {
534 ttk::SimplexId gid_{-1};
535 ttk::SimplexId lidBlock_{-1};
536 ttk::SimplexId lidElement_{-1};
537 ttk::SimplexId order_{-1};
538 ttk::SimplexId vOrder_[size];
539
540 saddle() {
541 for(ttk::SimplexId i = 0; i < size; i++) {
542 vOrder_[i] = -1;
543 }
544 };
545
546 bool operator==(const saddle<size> &s1) {
547 return this->gid_ == s1.gid_;
548 }
549
550 bool operator<(const saddle<size> s1) const {
551 if(this->gid_ == s1.gid_) {
552 return false;
553 }
554 if(this->order_ != -1 && s1.order_ != -1) {
555 return this->order_ < s1.order_;
556 }
557 for(int i = 0; i < size; i++) {
558 if(this->vOrder_[i] != s1.vOrder_[i]) {
559 return this->vOrder_[i] < s1.vOrder_[i];
560 }
561 }
562 return this->gid_ < s1.gid_;
563 };
564
565 bool operator>(const saddle<size> s1) const {
566 if(this->gid_ == s1.gid_) {
567 return false;
568 }
569 if(this->order_ != -1 && s1.order_ != -1) {
570 return this->order_ > s1.order_;
571 }
572 for(int i = 0; i < size; i++) {
573 if(this->vOrder_[i] != s1.vOrder_[i]) {
574 return this->vOrder_[i] > s1.vOrder_[i];
575 }
576 }
577 return this->gid_ > s1.gid_;
578 };
579 };
585 struct maxPerProcess {
586 ttk::SimplexId proc_;
587 ttk::SimplexId max_[2];
588
589 maxPerProcess(ttk::SimplexId rank) : proc_{rank} {
590 for(int i = 0; i < 2; i++) {
591 max_[i] = -1;
592 }
593 };
594
595 maxPerProcess(ttk::SimplexId rank, ttk::SimplexId *max) : proc_{rank} {
596 for(int i = 0; i < 2; i++) {
597 max_[i] = max[i];
598 }
599 };
600
601 bool operator==(const maxPerProcess m1) const {
602 return this->proc_ == m1.proc_;
603 }
604
605 bool operator<(const maxPerProcess b) const {
606 if(this->proc_ == b.proc_) {
607 return false;
608 }
609 for(int i = 0; i < 2; i++) {
610 if(this->max_[i] != b.max_[i]) {
611 return this->max_[i] > b.max_[i];
612 }
613 }
614 return false;
615 }
616 };
617
624 template <typename GlobalBoundary>
625 void updateMax(maxPerProcess m, GlobalBoundary &maxBoundary) const;
633 template <typename GlobalBoundary>
634 void getMaxOfProc(ttk::SimplexId rank,
635 ttk::SimplexId *currentMax,
636 GlobalBoundary &maxBoundary) const;
645 template <typename GlobalBoundary, typename LocalBoundary>
646 bool isEmpty(LocalBoundary &localBoundary,
647 GlobalBoundary &globalBoundary) const;
657 template <typename LocalBoundary>
658 bool addBoundary(const SimplexId e,
659 bool isOnBoundary,
660 LocalBoundary &localBoundary) const;
670 template <typename GlobalBoundary>
671 void updateMaxBoundary(const SimplexId s2,
672 const ttk::SimplexId *tauOrder,
673 GlobalBoundary &boundary,
674 ttk::SimplexId rank,
675 ttk::SimplexId computeProc = -1) const;
687 template <typename GlobalBoundary>
688 void updateLocalBoundary(const saddle<3> &s2,
689 const SimplexId egid1,
690 const SimplexId egid2,
691 const ttk::SimplexId *tauOrder,
692 GlobalBoundary &globalBoundary,
693 ttk::SimplexId rank) const;
708 template <typename LocalBoundary, typename GlobalBoundary>
709 bool mergeGlobalBoundaries(std::vector<bool> &onBoundary,
710 LocalBoundary &s2LocalBoundary,
711 GlobalBoundary &s2GlobalBoundary,
712 LocalBoundary &pTauLocalBoundary,
713 GlobalBoundary &pTauGlobalBoundary) const;
722 template <typename GlobalBoundary>
723 void updateMergedBoundary(const saddle<3> &s2,
724 const SimplexId pTau,
725 const ttk::SimplexId *tauOrder,
726 GlobalBoundary &globalBoundary) const;
738 template <typename triangulationType,
739 typename GlobalBoundary,
740 typename LocalBoundary,
741 typename compareEdges>
742 void receiveBoundaryUpdate(
743 std::vector<ttk::SimplexId> &recvBoundaryBuffer,
744 std::vector<std::vector<int>> &s2Locks,
745 std::vector<std::vector<GlobalBoundary>> &globalBoundaries,
746 std::vector<std::vector<LocalBoundary>> &localBoundaries,
747 std::vector<std::vector<saddle<3>>> &saddles2,
748 triangulationType &triangulation,
749 compareEdges &cmpEdges) const;
750
767 template <typename triangulationType,
768 typename GlobalBoundary,
769 typename LocalBoundary>
770 void addEdgeToBoundary(
771 const ttk::SimplexId pTau,
772 const ttk::SimplexId edgeId,
773 std::vector<bool> &onBoundary,
774 GlobalBoundary &globalBoundaryIds,
775 LocalBoundary &localBoundaryIds,
776 const triangulationType &triangulation,
777 const SimplexId *const offsets,
778 std::vector<std::pair<ttk::SimplexId, ttk::SimplexId>> &ghostEdges,
779 std::vector<bool> &hasChangedMax) const;
794 template <typename triangulationType, typename GlobalBoundary>
795 void packageLocalBoundaryUpdate(
796 const saddle<3> &s2,
797 ttk::SimplexId *tauOrder,
798 GlobalBoundary &globalBoundaryIds,
799 const triangulationType &triangulation,
800 std::vector<std::pair<ttk::SimplexId, ttk::SimplexId>> &ghostEdges,
801 std::vector<bool> &hasChangedMax) const;
802
803 inline void preconditionTriangulation(AbstractTriangulation *const data) {
804 this->dg_.preconditionTriangulation(data);
805 }
806
807 inline void setInputOffsets(const SimplexId *const offsets) {
808 this->dg_.setInputOffsets(offsets);
809 }
810
811 inline void setComputeMinSad(const bool data) {
812 this->ComputeMinSad = data;
813 }
814 inline void setComputeSadSad(const bool data) {
815 this->ComputeSadSad = data;
816 }
817 inline void setComputeSadMax(const bool data) {
818 this->ComputeSadMax = data;
819 }
820
821 template <typename triangulationType>
822 inline int buildGradient(const void *const scalars,
823 const size_t scalarsMTime,
824 const SimplexId *const offsets,
825 const triangulationType &triangulation,
826 const std::vector<bool> *updateMask = nullptr) {
827 this->dg_.setDebugLevel(this->debugLevel_);
828 this->dg_.setThreadNumber(this->threadNumber_);
829 this->dg_.setInputOffsets(offsets);
830 this->dg_.setInputScalarField(scalars, scalarsMTime);
831 return this->dg_.buildGradient(triangulation, false, updateMask);
832 }
833
845 inline void setGradient(ttk::dcg::DiscreteGradient &&dg) {
846 this->dg_ = std::move(dg);
847 // reset gradient pointer to local storage
848 this->dg_.setLocalGradient();
849 }
850 inline ttk::dcg::DiscreteGradient &&getGradient() {
851 return std::move(this->dg_);
852 }
853
854 void setUseTasks(bool useTasks) {
855 this->UseTasks = useTasks;
856 }
857
858 template <typename triangulationType>
859 inline SimplexId
860 getCellGreaterVertex(const dcg::Cell &c,
861 const triangulationType &triangulation) {
862 return this->dg_.getCellGreaterVertex(c, triangulation);
863 }
864
865 inline const std::vector<std::vector<SimplexId>> &
866 get2SaddlesChildren() const {
867 return this->s2Children_;
868 }
869
884 template <typename triangulationType>
885 int computePersistencePairs(std::vector<PersistencePair> &pairs,
886 const SimplexId *const offsets,
887 const triangulationType &triangulation,
888 const bool ignoreBoundary,
889 const bool compute2SaddlesChildren = false);
890
897 struct GeneratorType {
899 std::vector<SimplexId> boundary;
901 SimplexId critTriangleId;
904 std::array<SimplexId, 2> critVertsIds;
905 };
906
907 protected:
917 template <typename triangulationType>
918 int getSaddle1ToMinima(
919 const std::vector<SimplexId> &criticalEdges,
920 const std::unordered_map<ttk::SimplexId, ttk::SimplexId>
921 &localTriangToLocalVectExtrema,
922 const triangulationType &triangulation,
923 const SimplexId *const offsets,
924 std::vector<std::array<extremaNode<1>, 2>> &res,
925 std::vector<std::vector<char>> &ghostPresence,
926 std::unordered_map<ttk::SimplexId, std::vector<char>>
927 &localGhostPresenceMap,
928 std::vector<std::vector<saddleIdPerProcess>> &ghostPresenceVector,
929 MPI_Comm &MPIcomm,
930 int localThreadNumber) const;
931
940 inline void extractGhost(
941 std::vector<std::vector<char>> &ghostPresence,
942 std::vector<std::vector<saddleIdPerProcess>> &ghostPresenceVector,
943 int localThreadNumber) const;
957 template <int sizeExtr>
958 void mergeThreadVectors(
959 std::vector<std::vector<vpathFinished<sizeExtr>>> &finishedVPathToSend,
960 std::vector<std::vector<std::vector<vpathFinished<sizeExtr>>>>
961 &finishedVPathToSendThread,
962 std::vector<std::vector<char>> &ghostPresenceToSend,
963 std::vector<std::vector<std::vector<char>>> &ghostPresenceToSendThread,
964 std::vector<std::vector<ttk::SimplexId>> &ghostCounterThread,
965 int localThreadNumber) const;
977 template <int sizeExtr>
978 void exchangeFinalVPathAndGhosts(
979 std::vector<std::vector<char>> &ghostPresenceToSend,
980 std::vector<std::vector<vpathFinished<sizeExtr>>> &finishedVPathToSend,
981 std::vector<std::vector<char>> &recvGhostPresence,
982 std::vector<std::vector<vpathFinished<sizeExtr>>> &recvVPathFinished,
983 MPI_Datatype &MPI_SimplexId,
984 MPI_Comm &MPIcomm) const;
1008 template <int sizeExtr, int sizeRes, typename GLI, typename GSR>
1009 void unpackGhostPresence(
1010 std::vector<std::vector<char>> &recvGhostPresence,
1011 std::vector<Lock> &extremaLocks,
1012 std::vector<std::vector<char>> &ghostPresence,
1013 std::unordered_map<ttk::SimplexId, std::vector<char>>
1014 &localGhostPresenceMap,
1015 const std::unordered_map<ttk::SimplexId, ttk::SimplexId>
1016 &localTriangToLocalVectExtrema,
1017 std::vector<std::vector<vpathFinished<sizeExtr>>> &recvVPathFinished,
1018 std::vector<char> &saddleAtomic,
1019 std::vector<std::array<extremaNode<sizeExtr>, sizeRes>> &res,
1020 const GLI getSimplexLocalId,
1021 const GSR getSimplexRank,
1022 int localThreadNumber) const;
1039 template <typename GlobalBoundary, typename LocalBoundary>
1040 void mergeDistributedBoundary(
1041 std::vector<ttk::SimplexId> &recvBoundaryBuffer,
1042 std::vector<std::vector<int>> &s2Locks,
1043 std::vector<std::vector<GlobalBoundary>> &globalBoundaries,
1044 std::vector<std::vector<LocalBoundary>> &localBoundaries,
1045 std::vector<std::vector<saddle<3>>> &saddles2,
1047 ttk::SimplexId lidBlock,
1048 ttk::SimplexId lidElement,
1049 ttk::SimplexId pTauLidBlock,
1050 ttk::SimplexId pTauLidElement) const;
1068 template <typename GlobalBoundary, typename LocalBoundary>
1069 void addDistributedEdgeToLocalBoundary(
1070 std::vector<ttk::SimplexId> &recvBoundaryBuffer,
1071 std::vector<std::vector<int>> &s2Locks,
1072 std::vector<std::vector<GlobalBoundary>> &globalBoundaries,
1073 std::vector<std::vector<LocalBoundary>> &localBoundaries,
1074 std::vector<std::vector<saddle<3>>> &saddles2,
1076 ttk::SimplexId lidBlock,
1077 ttk::SimplexId lidElement,
1078 ttk::SimplexId leid1,
1079 ttk::SimplexId leid2) const;
1097 template <int sizeExtr, typename GLI>
1098 void packageGhost(
1099 std::vector<std::vector<std::vector<vpathFinished<sizeExtr>>>>
1100 &finishedVPathToSendThread,
1101 std::vector<std::vector<std::vector<char>>> &ghostPresenceToSendThread,
1102 std::vector<std::vector<ttk::SimplexId>> &ghostCounterThread,
1103 std::vector<std::vector<std::vector<vpathFinished<sizeExtr>>>>
1104 &sendFinishedVPathBufferThread,
1105 const std::unordered_map<ttk::SimplexId, ttk::SimplexId>
1106 &localTriangToLocalVectExtrema,
1107 const GLI getSimplexLocalId,
1108 std::vector<std::vector<char>> &ghostPresence,
1109 int localThreadNumber) const;
1110
1119 inline void getLid(ttk::SimplexId lid,
1120 ttk::SimplexId &lidBlock,
1121 ttk::SimplexId &lidElement) const;
1122
1149 template <int sizeExtr,
1150 int sizeSad,
1151 typename triangulationType,
1152 typename GFS,
1153 typename GFSN,
1154 typename OB,
1155 typename FEO>
1156 void getSaddle2ToMaxima(
1157 const std::vector<SimplexId> &criticalSaddles,
1158 const GFS &getFaceStar,
1159 const GFSN &getFaceStarNumber,
1160 const OB &isOnBoundary,
1161 const FEO &fillExtremaOrder,
1162 const triangulationType &triangulation,
1163 std::vector<std::array<extremaNode<sizeExtr>, sizeSad + 1>> &res,
1164 const std::unordered_map<ttk::SimplexId, ttk::SimplexId>
1165 &localTriangToLocalVectExtrema,
1166 std::vector<std::vector<char>> &ghostPresence,
1167 std::unordered_map<ttk::SimplexId, std::vector<char>>
1168 &localGhostPresenceMap,
1169 std::vector<std::vector<saddleIdPerProcess>> &ghostPresenceVector,
1170 const std::vector<SimplexId> &critMaxsOrder,
1171 MPI_Comm &MPIcomm,
1172 int localThreadNumber) const;
1173
1187 template <typename triangulationType>
1188
1189 void getMinSaddlePairs(std::vector<PersistencePair> &pairs,
1190 const std::vector<ttk::SimplexId> &criticalEdges,
1191 const std::vector<ttk::SimplexId> &critEdgesOrder,
1192 const std::vector<ttk::SimplexId> &criticalExtremas,
1193 const SimplexId *const offsets,
1194 size_t &nConnComp,
1195 const triangulationType &triangulation,
1196 MPI_Comm &MPIcomm,
1197 int localThreadNumber) const;
1198
1215 template <typename triangulationType>
1216 void getMaxSaddlePairs(std::vector<PersistencePair> &pairs,
1217 const std::vector<SimplexId> &criticalSaddles,
1218 const std::vector<SimplexId> &critSaddlesOrder,
1219 const std::vector<ttk::SimplexId> &criticalExtremas,
1220 const std::vector<SimplexId> &critMaxsOrder,
1221 const triangulationType &triangulation,
1222 const bool ignoreBoundary,
1223 const SimplexId *const offsets,
1224 MPI_Comm &MPIcomm,
1225 int localThreadNumber);
1226
1227 template <int sizeExtr,
1228 int sizeSad,
1229 typename triangulationType,
1230 typename GFS,
1231 typename GFSN,
1232 typename OB,
1233 typename FEO,
1234 typename GSGID,
1235 typename FSO>
1262 void computeMaxSaddlePairs(
1263 std::vector<PersistencePair> &pairs,
1264 const std::vector<SimplexId> &criticalSaddles,
1265 const std::vector<SimplexId> &critSaddlesOrder,
1266 const std::vector<ttk::SimplexId> &criticalExtremas,
1267 const std::vector<SimplexId> &critMaxsOrder,
1268 const triangulationType &triangulation,
1269 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalSaddle,
1270 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalExtrema,
1271 const GFS &getFaceStar,
1272 const GFSN &getFaceStarNumber,
1273 const OB &isOnBoundary,
1274 const FEO &fillExtremaOrder,
1275 const GSGID &getSaddleGlobalId,
1276 const FSO &fillSaddleOrder,
1277 MPI_Comm &MPIcomm,
1278 int localThreadNumber);
1279
1291 template <typename triangulationType>
1292 void getSaddleSaddlePairs(std::vector<PersistencePair> &pairs,
1293 const std::vector<SimplexId> &critical1Saddles,
1294 const std::vector<SimplexId> &critical2Saddles,
1295 const std::vector<SimplexId> &crit1SaddlesOrder,
1296 const std::vector<SimplexId> &crit2SaddlesOrder,
1297 const triangulationType &triangulation,
1298 const SimplexId *const offsets) const;
1299
1309 template <typename triangulationType>
1310 void extractCriticalCells(
1311 std::array<std::vector<SimplexId>, 4> &criticalCellsByDim,
1312 std::array<std::vector<SimplexId>, 4> &critCellsOrder,
1313 const SimplexId *const offsets,
1314 const triangulationType &triangulation,
1315 const bool sortEdges) const;
1316
1328 void displayStats(
1329 const std::vector<PersistencePair> &pairs,
1330 const std::array<std::vector<SimplexId>, 4> &criticalCellsByDim,
1331 const std::vector<bool> &pairedMinima,
1332 const std::vector<bool> &paired1Saddles,
1333 const std::vector<bool> &paired2Saddles,
1334 const std::vector<bool> &pairedMaxima) const;
1335
1343 using tripletType = std::array<ttk::SimplexId, 3>;
1344
1369 template <int sizeExtr, int sizeSad>
1370 int processTriplet(
1371 saddleEdge<sizeSad> sv,
1372 std::vector<ttk::SimplexId> &saddleToPairedExtrema,
1373 std::vector<ttk::SimplexId> &extremaToPairedSaddle,
1374 std::vector<saddleEdge<sizeSad>> &saddles,
1375 std::vector<extremaNode<sizeExtr>> &extremas,
1376 bool increasing,
1377 std::vector<std::vector<char>> &ghostPresence,
1378 std::vector<std::vector<messageType<sizeExtr, sizeSad>>> &sendBuffer,
1379 std::set<messageType<sizeExtr, sizeSad>,
1380 std::function<bool(const messageType<sizeExtr, sizeSad> &,
1381 const messageType<sizeExtr, sizeSad> &)>>
1382 &recomputations,
1383 const std::function<bool(const messageType<sizeExtr, sizeSad> &,
1384 const messageType<sizeExtr, sizeSad> &)>
1385 &cmpMessages,
1386 std::vector<messageType<sizeExtr, sizeSad>> &recvBuffer,
1387 ttk::SimplexId beginVect) const;
1405 template <int sizeExtr, int sizeSad>
1406 void storeMessageToSend(
1407 std::vector<std::vector<char>> &ghostPresence,
1408 std::vector<std::vector<messageType<sizeExtr, sizeSad>>> &sendBuffer,
1409 saddleEdge<sizeSad> &sv,
1410 saddleEdge<sizeSad> &s1,
1411 saddleEdge<sizeSad> &s2,
1412 extremaNode<sizeExtr> &rep1,
1413 extremaNode<sizeExtr> &rep2,
1414 char sender = static_cast<char>(ttk::MPIrank_),
1415 char hasBeenModifed = 0) const;
1431 template <int sizeExtr, int sizeSad>
1432 void storeMessageToSend(
1433 std::vector<std::vector<char>> &ghostPresence,
1434 std::vector<std::vector<messageType<sizeExtr, sizeSad>>> &sendBuffer,
1435 saddleEdge<sizeSad> &sv,
1436 saddleEdge<sizeSad> &s1,
1437 extremaNode<sizeExtr> &rep1,
1438 char sender = static_cast<char>(ttk::MPIrank_),
1439 char hasBeenModifed = 0) const;
1452 template <int sizeExtr, int sizeSad>
1453 void storeMessageToSendToRepOwner(
1454 std::vector<std::vector<messageType<sizeExtr, sizeSad>>> &sendBuffer,
1455 saddleEdge<sizeSad> &sv,
1456 std::vector<saddleEdge<sizeSad>> &saddles,
1457 extremaNode<sizeExtr> &rep1,
1458 extremaNode<sizeExtr> &rep2) const;
1470 template <int sizeExtr, int sizeSad>
1471 void storeMessageToSendToRepOwner(
1472 std::vector<std::vector<messageType<sizeExtr, sizeSad>>> &sendBuffer,
1473 saddleEdge<sizeSad> &sv,
1474 std::vector<saddleEdge<sizeSad>> &saddles,
1475 extremaNode<sizeExtr> &rep1) const;
1485 template <int sizeExtr, int sizeSad>
1486 void storeRerunToSend(
1487 std::vector<std::vector<messageType<sizeExtr, sizeSad>>> &sendBuffer,
1488 saddleEdge<sizeSad> &sv) const;
1501 template <int sizeExtr, int sizeSad>
1502 void addPair(const saddleEdge<sizeSad> &sad,
1503 const extremaNode<sizeExtr> &extr,
1504 std::vector<ttk::SimplexId> &saddleToPairedExtrema,
1505 std::vector<ttk::SimplexId> &extremaToPairedSaddle) const;
1521 template <int sizeExtr, int sizeSad>
1522 void addToRecvBuffer(
1523 saddleEdge<sizeSad> &sad,
1524 std::set<messageType<sizeExtr, sizeSad>,
1525 std::function<bool(const messageType<sizeExtr, sizeSad> &,
1526 const messageType<sizeExtr, sizeSad> &)>>
1527 &recomputations,
1528 const std::function<bool(const messageType<sizeExtr, sizeSad> &,
1529 const messageType<sizeExtr, sizeSad> &)>
1530 &cmpMessages,
1531 std::vector<messageType<sizeExtr, sizeSad>> &recvBuffer,
1532 ttk::SimplexId beginVect) const;
1545 template <int sizeExtr, int sizeSad>
1546 void removePair(const saddleEdge<sizeSad> &sad,
1547 const extremaNode<sizeExtr> &extr,
1548 std::vector<ttk::SimplexId> &saddleToPairedExtrema,
1549 std::vector<ttk::SimplexId> &extremaToPairedSaddle) const;
1564 template <int sizeExtr, int sizeSad>
1565 ttk::SimplexId getRep(extremaNode<sizeExtr> extr,
1566 saddleEdge<sizeSad> sv,
1567 bool increasing,
1568 std::vector<extremaNode<sizeExtr>> &extremas,
1569 std::vector<saddleEdge<sizeSad>> &saddles) const;
1570
1582 template <int sizeSad>
1583 struct saddleEdge<sizeSad> addSaddle(
1584 saddleEdge<sizeSad> s,
1585 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalSaddle,
1586 std::vector<saddleEdge<sizeSad>> &saddles,
1587 std::vector<ttk::SimplexId> &saddleToPairedExtrema) const;
1588
1605 template <int sizeExtr, int sizeSad>
1606 ttk::SimplexId getUpdatedT1(
1607 const ttk::SimplexId extremaGid,
1608 messageType<sizeExtr, sizeSad> &elt,
1609 saddleEdge<sizeSad> s,
1610 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalExtrema,
1611 std::vector<extremaNode<sizeExtr>> &extremas,
1612 std::vector<saddleEdge<sizeSad>> &saddles,
1613 bool increasing) const;
1614
1631 template <int sizeExtr, int sizeSad>
1632 ttk::SimplexId getUpdatedT2(
1633 const ttk::SimplexId extremaGid,
1634 messageType<sizeExtr, sizeSad> &elt,
1635 saddleEdge<sizeSad> s,
1636 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalExtrema,
1637 std::vector<extremaNode<sizeExtr>> &extremas,
1638 std::vector<saddleEdge<sizeSad>> &saddles,
1639 bool increasing) const;
1653 template <int sizeExtr, int sizeSad>
1654 void swapT1T2(messageType<sizeExtr, sizeSad> &elt,
1655 ttk::SimplexId &t1Lid,
1656 ttk::SimplexId &t2Lid,
1657 bool increasing) const;
1671
1672 template <int sizeExtr>
1673 void addLocalExtrema(
1674 ttk::SimplexId &lid,
1675 const ttk::SimplexId gid,
1676 char rank,
1677 ttk::SimplexId *vOrder,
1678 std::vector<extremaNode<sizeExtr>> &extremas,
1679 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalExtrema,
1680 std::vector<ttk::SimplexId> &extremaToPairedSaddle) const;
1681
1682 template <int sizeExtr, int sizeSad>
1707 void receiveElement(
1708 messageType<sizeExtr, sizeSad> element,
1709 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalSaddle,
1710 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalExtrema,
1711 std::vector<saddleEdge<sizeSad>> &saddles,
1712 std::vector<extremaNode<sizeExtr>> &extremas,
1713 std::vector<ttk::SimplexId> &extremaToPairedSaddle,
1714 std::vector<ttk::SimplexId> &saddleToPairedExtrema,
1715 std::vector<std::vector<messageType<sizeExtr, sizeSad>>> &sendBuffer,
1716 std::vector<std::vector<char>> &ghostPresence,
1717 char sender,
1718 bool increasing,
1719 std::set<messageType<sizeExtr, sizeSad>,
1720 std::function<bool(const messageType<sizeExtr, sizeSad> &,
1721 const messageType<sizeExtr, sizeSad> &)>>
1722 &recomputations,
1723 const std::function<bool(const messageType<sizeExtr, sizeSad> &,
1724 const messageType<sizeExtr, sizeSad> &)>
1725 &cmpMessages,
1726 std::vector<messageType<sizeExtr, sizeSad>> &recvBuffer,
1727 ttk::SimplexId beginVect) const;
1728
1749 template <int sizeExtr, int sizeSad>
1750 void tripletsToPersistencePairs(
1751 const SimplexId pairDim,
1752 std::vector<extremaNode<sizeExtr>> &extremas,
1753 std::vector<saddleEdge<sizeSad>> &saddles,
1754 std::vector<ttk::SimplexId> &saddleIds,
1755 std::vector<ttk::SimplexId> &saddleToPairedExtrema,
1756 std::vector<ttk::SimplexId> &extremaToPairedSaddle,
1757 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalSaddle,
1758 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalExtrema,
1759 std::vector<std::vector<char>> ghostPresence,
1760 MPI_Datatype &MPI_MessageType,
1761 bool isFirstTime,
1762 MPI_Comm &MPIcomm,
1763 int localThreadNumber) const;
1781 template <int sizeExtr, int sizeSad>
1782 void extractPairs(std::vector<PersistencePair> &pairs,
1783 std::vector<extremaNode<sizeExtr>> &extremas,
1784 std::vector<saddleEdge<sizeSad>> &saddles,
1785 std::vector<ttk::SimplexId> &saddleToPairedExtrema,
1786 bool increasing,
1787 const int pairDim,
1788 int localThreadNumber) const;
1800 template <int sizeExtr, int sizeSad>
1802 computePairNumbers(std::vector<saddleEdge<sizeSad>> &saddles,
1803 std::vector<ttk::SimplexId> &saddleToPairedExtrema,
1804 int localThreadNumber) const;
1822 template <typename triangulationType,
1823 typename GlobalBoundary,
1824 typename LocalBoundary>
1825 SimplexId eliminateBoundariesSandwich(
1826 const saddle<3> &s2,
1827 std::vector<std::vector<bool>> &onBoundaryThread,
1828 std::vector<std::vector<GlobalBoundary>> &s2GlobalBoundaries,
1829 std::vector<std::vector<LocalBoundary>> &s2LocalBoundaries,
1830 std::vector<SimplexId> &partners,
1831 std::vector<int> &s1Locks,
1832 std::vector<std::vector<int>> &s2Locks,
1833 const std::vector<std::vector<saddle<3>>> &saddles2,
1834 const std::vector<ttk::SimplexId> &localEdgeToSaddle1,
1835 const triangulationType &triangulation,
1836 const SimplexId *const offsets) const;
1837
1843 template <int n>
1844 struct Simplex {
1846 SimplexId id_{};
1849 ttk::SimplexId vertsOrder_[n];
1852 friend bool operator<(const Simplex<n> &lhs, const Simplex<n> &rhs) {
1853 for(int i = 0; i < n; i++) {
1854 if(lhs.vertsOrder_[i] != rhs.vertsOrder_[i]) {
1855 return lhs.vertsOrder_[i] < rhs.vertsOrder_[i];
1856 }
1857 }
1858 return false;
1859 }
1860 };
1861
1865 struct EdgeSimplex : Simplex<2> {
1866 template <typename triangulationType>
1867 void fillEdge(const SimplexId id,
1868 const SimplexId *const offsets,
1869 const triangulationType &triangulation) {
1870 this->id_ = id;
1871 triangulation.getEdgeVertex(id, 0, this->vertsOrder_[0]);
1872 triangulation.getEdgeVertex(id, 1, this->vertsOrder_[1]);
1873 this->vertsOrder_[0] = offsets[this->vertsOrder_[0]];
1874 this->vertsOrder_[1] = offsets[this->vertsOrder_[1]];
1875 // sort vertices in decreasing order
1876 std::sort(this->vertsOrder_, this->vertsOrder_ + 2,
1877 std::greater<ttk::SimplexId>());
1878 }
1879 };
1880
1884 struct TriangleSimplex : Simplex<3> {
1885 template <typename triangulationType>
1886 void fillTriangle(const SimplexId id,
1887 const SimplexId *const offsets,
1888 const triangulationType &triangulation) {
1889 this->id_ = id;
1890 triangulation.getTriangleVertex(id, 0, this->vertsOrder_[0]);
1891 triangulation.getTriangleVertex(id, 1, this->vertsOrder_[1]);
1892 triangulation.getTriangleVertex(id, 2, this->vertsOrder_[2]);
1893 this->vertsOrder_[0] = offsets[this->vertsOrder_[0]];
1894 this->vertsOrder_[1] = offsets[this->vertsOrder_[1]];
1895 this->vertsOrder_[2] = offsets[this->vertsOrder_[2]];
1896 // sort vertices in decreasing order
1897 std::sort(this->vertsOrder_, this->vertsOrder_ + 3,
1898 std::greater<ttk::SimplexId>());
1899 }
1900 };
1901
1905 struct TetraSimplex : Simplex<4> {
1906 template <typename triangulationType>
1907 void fillTetra(const SimplexId id,
1908 const SimplexId *const offsets,
1909 const triangulationType &triangulation) {
1910 this->id_ = id;
1911 triangulation.getCellVertex(id, 0, this->vertsOrder_[0]);
1912 triangulation.getCellVertex(id, 1, this->vertsOrder_[1]);
1913 triangulation.getCellVertex(id, 2, this->vertsOrder_[2]);
1914 triangulation.getCellVertex(id, 3, this->vertsOrder_[3]);
1915 this->vertsOrder_[0] = offsets[this->vertsOrder_[0]];
1916 this->vertsOrder_[1] = offsets[this->vertsOrder_[1]];
1917 this->vertsOrder_[2] = offsets[this->vertsOrder_[2]];
1918 this->vertsOrder_[3] = offsets[this->vertsOrder_[3]];
1919 // sort vertices in decreasing order
1920 std::sort(this->vertsOrder_, this->vertsOrder_ + 4,
1921 std::greater<ttk::SimplexId>());
1922 }
1923 };
1924
1925 void clear() {
1926#ifdef TTK_ENABLE_MPI_TIME
1927 ttk::Timer t_mpi;
1928 ttk::startMPITimer(t_mpi, ttk::MPIrank_, ttk::MPIsize_);
1929#endif
1930 this->critCellsOrder_ = {};
1931#ifdef TTK_ENABLE_MPI_TIME
1932 double elapsedTime
1933 = ttk::endMPITimer(t_mpi, ttk::MPIrank_, ttk::MPIsize_);
1934 if(ttk::MPIrank_ == 0) {
1935 printMsg("Memory cleanup performed using "
1936 + std::to_string(ttk::MPIsize_)
1937 + " MPI processes lasted: " + std::to_string(elapsedTime));
1938 }
1939#endif
1940 }
1941
1942 void minMaxClear() const {
1943 this->saddleToPairedMin_ = {};
1944 this->saddleToPairedMax_ = {};
1945 this->minToPairedSaddle_ = {};
1946 this->maxToPairedSaddle_ = {};
1947 }
1948
1949 dcg::DiscreteGradient dg_{};
1950
1951 // factor memory allocations outside computation loops
1952 mutable std::vector<ttk::SimplexId> edgeTrianglePartner_{};
1953 mutable std::vector<ttk::SimplexId> saddleToPairedMin_{},
1954 saddleToPairedMax_{}, minToPairedSaddle_{}, maxToPairedSaddle_{};
1955 mutable std::unordered_map<ttk::SimplexId, ttk::SimplexId>
1956 globalToLocalSaddle1_{}, globalToLocalSaddle2_{};
1957 mutable std::vector<std::vector<bool>> onBoundary_{};
1958 mutable std::vector<ttk::SimplexId> localEdgeToSaddle1_{};
1959 mutable std::array<std::vector<SimplexId>, 4> critCellsOrder_{};
1960 mutable std::vector<std::vector<SimplexId>> s2Children_{};
1961 mutable ttk::SimplexId sadSadLimit_{0};
1962 mutable ttk::SimplexId messageSize_{0};
1963 mutable ttk::SimplexId messageCounter_{0};
1964 mutable ttk::SimplexId globalSaddle2Counter_{0};
1965 mutable ttk::SimplexId taskCounter_{0};
1966 mutable std::array<std::vector<std::vector<ttk::SimplexId>>, 2>
1967 sendBoundaryBuffer_;
1968 mutable std::vector<Lock> sendBoundaryBufferLock_;
1969 mutable std::array<std::vector<std::vector<ttk::SimplexId>>, 2>
1970 sendComputeBuffer_;
1971 mutable std::vector<Lock> sendComputeBufferLock_;
1972 mutable int currentBuffer_{0};
1973 mutable ttk::SimplexId finishedPropagationCounter_{0};
1974 mutable ttk::SimplexId blockSize_{0};
1975 mutable ttk::SimplexId firstBlockSize_{0};
1976 mutable ttk::SimplexId currentLastElement_{0};
1977 mutable ttk::SimplexId currentLastBlock_{1};
1978 const ttk::SimplexId overflow_{10000000};
1979 bool ComputeMinSad{true};
1980 bool ComputeSadSad{true};
1981 bool ComputeSadMax{true};
1982 bool Compute2SaddlesChildren{false};
1983 bool UseTasks{true};
1984 };
1985} // namespace ttk
1986
1987inline void ttk::DiscreteMorseSandwichMPI::extractGhost(
1988 std::vector<std::vector<char>> &ghostPresence,
1989 std::vector<std::vector<saddleIdPerProcess>> &ghostPresenceVector,
1990 int localThreadNumber) const {
1991#pragma omp parallel for shared(ghostPresence) num_threads(localThreadNumber)
1992 for(size_t i = 0; i < ghostPresence.size(); i++) {
1993 auto &ghost{ghostPresenceVector[i]};
1994 auto &ranks{ghostPresence[i]};
1995 // Check if some saddleId appear twice for the same rank
1996 for(size_t j = 0; j < ghost.size(); j++) {
1997 ttk::SimplexId ghostSize = ghost[j].saddleIds_.size();
1998 // Only occurs when the number of saddles is even
1999 if(ghostSize > 0) {
2000 if(ghostSize % 2 == 0) {
2001 std::sort(ghost[j].saddleIds_.begin(), ghost[j].saddleIds_.end());
2002 const auto last = std::unique(
2003 ghost[j].saddleIds_.begin(), ghost[j].saddleIds_.end());
2004 ttk::SimplexId newSize
2005 = std::distance(ghost[j].saddleIds_.begin(), last);
2006 if(!(ghostSize % newSize == 0 && ghostSize / newSize == 2)) {
2007 ranks.emplace_back(ghost[j].rank_);
2008 }
2009 } else {
2010 ranks.emplace_back(ghost[j].rank_);
2011 }
2012 }
2013 }
2014 ghost.clear();
2015 }
2016}
2017
2018template <int sizeExtr>
2019void ttk::DiscreteMorseSandwichMPI::mergeThreadVectors(
2020 std::vector<std::vector<vpathFinished<sizeExtr>>> &finishedVPathToSend,
2021 std::vector<std::vector<std::vector<vpathFinished<sizeExtr>>>>
2022 &finishedVPathToSendThread,
2023 std::vector<std::vector<char>> &ghostPresenceToSend,
2024 std::vector<std::vector<std::vector<char>>> &ghostPresenceToSendThread,
2025 std::vector<std::vector<ttk::SimplexId>> &ghostCounterThread,
2026 int localThreadNumber) const {
2027#pragma omp parallel for schedule(static, 1) num_threads(localThreadNumber)
2028 for(int j = 0; j < ttk::MPIsize_; j++) {
2029 ttk::SimplexId ghostCounter{0};
2030 for(int i = 0; i < this->threadNumber_; i++) {
2031 std::transform(finishedVPathToSendThread[i][j].begin(),
2032 finishedVPathToSendThread[i][j].end(),
2033 finishedVPathToSendThread[i][j].begin(),
2034 [&ghostCounter](vpathFinished<sizeExtr> &vp) {
2035 vp.ghostPresenceSize_ += ghostCounter;
2036 return vp;
2037 });
2038 ghostPresenceToSend[j].insert(ghostPresenceToSend[j].end(),
2039 ghostPresenceToSendThread[i][j].begin(),
2040 ghostPresenceToSendThread[i][j].end());
2041 finishedVPathToSend[j].insert(finishedVPathToSend[j].end(),
2042 finishedVPathToSendThread[i][j].begin(),
2043 finishedVPathToSendThread[i][j].end());
2044 ghostCounter += ghostCounterThread[i][j];
2045 ghostPresenceToSendThread[i][j].clear();
2046 finishedVPathToSendThread[i][j].clear();
2047 }
2048 }
2049}
2050
2051template <int sizeExtr>
2052void ttk::DiscreteMorseSandwichMPI::exchangeFinalVPathAndGhosts(
2053 std::vector<std::vector<char>> &ghostPresenceToSend,
2054 std::vector<std::vector<vpathFinished<sizeExtr>>> &finishedVPathToSend,
2055 std::vector<std::vector<char>> &recvGhostPresence,
2056 std::vector<std::vector<vpathFinished<sizeExtr>>> &recvVPathFinished,
2057 MPI_Datatype &MPI_SimplexId,
2058 MPI_Comm &MPIcomm) const {
2059
2060 MPI_Datatype MPI_FinishedVPathMPIType;
2061 createFinishedVpathMPIType<sizeExtr>(MPI_FinishedVPathMPIType);
2062 std::vector<ttk::SimplexId> recvMessageSize(2 * ttk::MPIsize_, 0);
2063 std::vector<ttk::SimplexId> sendMessageSize(2 * ttk::MPIsize_, 0);
2064 std::vector<MPI_Request> requests(4 * ttk::MPIsize_, MPI_REQUEST_NULL);
2065 for(int i = 0; i < ttk::MPIsize_; i++) {
2066 if(i != ttk::MPIrank_) {
2067 sendMessageSize[2 * i] = finishedVPathToSend[i].size();
2068 sendMessageSize[2 * i + 1] = ghostPresenceToSend[i].size();
2069 }
2070 }
2071 MPI_Alltoall(sendMessageSize.data(), 2, MPI_SimplexId, recvMessageSize.data(),
2072 2, MPI_SimplexId, MPIcomm);
2073 int count{0};
2074 // Exchange of the data
2075 for(int i = 0; i < ttk::MPIsize_; i++) {
2076 recvVPathFinished[i].resize(recvMessageSize[2 * i]);
2077 recvGhostPresence[i].resize(recvMessageSize[2 * i + 1]);
2078 if(recvMessageSize[2 * i] > 0) {
2079 MPI_Irecv(recvVPathFinished[i].data(), recvMessageSize[2 * i],
2080 MPI_FinishedVPathMPIType, i, 1, MPIcomm, &requests[count]);
2081 count++;
2082 }
2083 if(sendMessageSize[2 * i] > 0) {
2084 MPI_Isend(finishedVPathToSend[i].data(), sendMessageSize[2 * i],
2085 MPI_FinishedVPathMPIType, i, 1, MPIcomm, &requests[count]);
2086 count++;
2087 }
2088 if(recvMessageSize[2 * i + 1] > 0) {
2089 MPI_Irecv(recvGhostPresence[i].data(), recvMessageSize[2 * i + 1],
2090 MPI_CHAR, i, 2, MPIcomm, &requests[count]);
2091 count++;
2092 }
2093 if(sendMessageSize[2 * i + 1] > 0) {
2094 MPI_Isend(ghostPresenceToSend[i].data(), sendMessageSize[2 * i + 1],
2095 MPI_CHAR, i, 2, MPIcomm, &requests[count]);
2096 count++;
2097 }
2098 }
2099 MPI_Waitall(count, requests.data(), MPI_STATUSES_IGNORE);
2100}
2101template <typename triangulationType>
2102int ttk::DiscreteMorseSandwichMPI::getSaddle1ToMinima(
2103 const std::vector<SimplexId> &criticalEdges,
2104 const std::unordered_map<ttk::SimplexId, ttk::SimplexId>
2105 &localTriangToLocalVectExtrema,
2106 const triangulationType &triangulation,
2107 const SimplexId *const offsets,
2108 std::vector<std::array<extremaNode<1>, 2>> &res,
2109 std::vector<std::vector<char>> &ghostPresence,
2110 std::unordered_map<ttk::SimplexId, std::vector<char>> &localGhostPresenceMap,
2111 std::vector<std::vector<saddleIdPerProcess>> &ghostPresenceVector,
2112 MPI_Comm &MPIcomm,
2113 int localThreadNumber) const {
2114
2115 Timer tm{};
2116 ttk::SimplexId criticalExtremasNumber = localTriangToLocalVectExtrema.size();
2117 const std::vector<int> neighbors = triangulation.getNeighborRanks();
2118 const std::map<int, int> neighborsToId = triangulation.getNeighborsToId();
2119 int neighborNumber = neighbors.size();
2120 std::vector<std::vector<std::vector<vpathToSend>>> sendBufferThread(
2121 threadNumber_);
2122 std::vector<std::vector<std::vector<vpathFinished<1>>>>
2123 sendFinishedVPathBufferThread(threadNumber_);
2124 std::vector<char> saddleAtomic(criticalEdges.size(), 0);
2125 std::vector<Lock> extremaLocks(criticalExtremasNumber);
2126 MPI_Datatype MPI_SimplexId = getMPIType(static_cast<ttk::SimplexId>(0));
2127 for(int i = 0; i < this->threadNumber_; i++) {
2128 sendBufferThread[i].resize(neighborNumber);
2129 sendFinishedVPathBufferThread[i].resize(ttk::MPIsize_);
2130 }
2131 ttk::SimplexId localElementNumber{0};
2132 ttk::SimplexId totalFinishedElement{
2133 static_cast<ttk::SimplexId>(2 * criticalEdges.size())};
2134 ttk::SimplexId totalElement{0};
2135 MPI_Allreduce(
2136 MPI_IN_PLACE, &totalFinishedElement, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
2137 localElementNumber = 0;
2138 const auto followVPath = [this, &triangulation, &neighborsToId, &extremaLocks,
2139 &res, &saddleAtomic, &ghostPresenceVector,
2140 &sendBufferThread, &sendFinishedVPathBufferThread,
2141 offsets, &localTriangToLocalVectExtrema](
2142 const SimplexId v, ttk::SimplexId saddleId,
2143 char saddleRank, int threadNumber,
2144 ttk::SimplexId &elementNumber) {
2145 std::vector<Cell> vpath{};
2146 this->dg_.getDescendingPath(Cell{0, v}, vpath, triangulation);
2147 const Cell &lastCell = vpath.back();
2148 if(lastCell.dim_ == 0) {
2149 ttk::SimplexId extremaId = triangulation.getVertexGlobalId(lastCell.id_);
2150 int rank = triangulation.getVertexRank(lastCell.id_);
2151 if(rank != ttk::MPIrank_) {
2152 sendBufferThread[threadNumber][neighborsToId.find(rank)->second]
2153 .emplace_back(vpathToSend{.saddleId_ = saddleId,
2154 .extremaId_ = extremaId,
2155 .saddleRank_ = saddleRank});
2156 } else {
2157 elementNumber++;
2158 if(this->dg_.isCellCritical(lastCell)) {
2160 = localTriangToLocalVectExtrema.find(lastCell.id_)->second;
2161 int r{0};
2162 extremaLocks[id].lock();
2163 auto &ghost{ghostPresenceVector[id]};
2164 while(r < static_cast<int>(ghost.size())) {
2165 if(ghost[r].rank_ == saddleRank) {
2166 ghost[r].saddleIds_.emplace_back(saddleId);
2167 break;
2168 }
2169 r++;
2170 }
2171 if(r == static_cast<int>(ghost.size())) {
2172 ghost.emplace_back(saddleIdPerProcess{
2173 std::vector<ttk::SimplexId>{saddleId}, saddleRank});
2174 }
2175 extremaLocks[id].unlock();
2176 if(saddleRank == ttk::MPIrank_) {
2177 ttk::SimplexId vOrd[] = {offsets[lastCell.id_]};
2178 extremaNode<1> n(extremaId, -1, offsets[lastCell.id_], Rep{-1, -1},
2179 static_cast<char>(ttk::MPIrank_), vOrd);
2180 // We store it in the current rank
2181 char saddleLocalId;
2182#pragma omp atomic capture
2183 saddleLocalId = saddleAtomic[saddleId]++;
2184 res[saddleId][saddleLocalId] = n;
2185 } else {
2186 // We store it to send it back to whoever will own the extrema
2187 sendFinishedVPathBufferThread[threadNumber][saddleRank]
2188 .emplace_back(vpathFinished<1>(saddleId, extremaId,
2189 static_cast<char>(ttk::MPIrank_),
2190 offsets[lastCell.id_]));
2191 }
2192 }
2193 }
2194 } else {
2195 elementNumber++;
2196 }
2197 };
2198 ttk::SimplexId elementNumber = 0;
2199 // follow vpaths from 1-saddles to minima
2200#pragma omp parallel shared(extremaLocks, saddleAtomic) reduction(+: elementNumber) \
2201 num_threads(localThreadNumber)
2202 {
2203 int threadNumber = omp_get_thread_num();
2204#pragma omp for schedule(static)
2205 for(size_t i = 0; i < criticalEdges.size(); ++i) {
2206 // critical edge vertices
2207 SimplexId v0{}, v1{};
2208 triangulation.getEdgeVertex(criticalEdges[i], 0, v0);
2209 triangulation.getEdgeVertex(criticalEdges[i], 1, v1);
2210
2211 // follow vpath from each vertex of the critical edge
2212 followVPath(v0, i, ttk::MPIrank_, threadNumber, elementNumber);
2213 followVPath(v1, i, ttk::MPIrank_, threadNumber, elementNumber);
2214 }
2215 }
2216 localElementNumber += elementNumber;
2217 elementNumber = 0;
2218 // Send receive elements
2219 MPI_Datatype MPI_MessageType;
2220 this->createVpathMPIType(MPI_MessageType);
2221 MPI_Allreduce(
2222 &localElementNumber, &totalElement, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
2223 std::vector<std::vector<vpathToSend>> sendBuffer(neighborNumber);
2224 std::vector<std::vector<vpathToSend>> recvBuffer(neighborNumber);
2225 bool keepWorking = (totalElement != totalFinishedElement);
2226 while(keepWorking) {
2227#pragma omp parallel for schedule(static, 1) num_threads(localThreadNumber)
2228 for(int j = 0; j < neighborNumber; j++) {
2229 sendBuffer[j].clear();
2230 for(int i = 0; i < this->threadNumber_; i++) {
2231 sendBuffer[j].insert(sendBuffer[j].end(),
2232 sendBufferThread[i][j].begin(),
2233 sendBufferThread[i][j].end());
2234 sendBufferThread[i][j].clear();
2235 }
2236 }
2237 std::vector<MPI_Request> sendRequests(neighborNumber);
2238 std::vector<MPI_Request> recvRequests(neighborNumber);
2239 std::vector<MPI_Status> sendStatus(neighborNumber);
2240 std::vector<MPI_Status> recvStatus(neighborNumber);
2241 std::vector<ttk::SimplexId> sendMessageSize(neighborNumber, 0);
2242 std::vector<ttk::SimplexId> recvMessageSize(neighborNumber, 0);
2243 std::vector<int> recvCompleted(neighborNumber, 0);
2244 std::vector<int> sendCompleted(neighborNumber, 0);
2245 int sendPerformedCount = 0;
2246 int recvPerformedCount = 0;
2247 int sendPerformedCountTotal = 0;
2248 int recvPerformedCountTotal = 0;
2249 for(int i = 0; i < neighborNumber; i++) {
2250 // Send size of sendbuffer
2251 sendMessageSize[i] = sendBuffer[i].size();
2252 MPI_Isend(&sendMessageSize[i], 1, MPI_SimplexId, neighbors[i], 0, MPIcomm,
2253 &sendRequests[i]);
2254 MPI_Irecv(&recvMessageSize[i], 1, MPI_SimplexId, neighbors[i], 0, MPIcomm,
2255 &recvRequests[i]);
2256 }
2257 std::vector<MPI_Request> sendRequestsData(neighborNumber);
2258 std::vector<MPI_Request> recvRequestsData(neighborNumber);
2259 std::vector<MPI_Status> recvStatusData(neighborNumber);
2260 int recvCount = 0;
2261 int sendCount = 0;
2262 int r;
2263 while((sendPerformedCountTotal < neighborNumber
2264 || recvPerformedCountTotal < neighborNumber)) {
2265 if(sendPerformedCountTotal < neighborNumber) {
2266 MPI_Waitsome(neighborNumber, sendRequests.data(), &sendPerformedCount,
2267 sendCompleted.data(), sendStatus.data());
2268 if(sendPerformedCount > 0) {
2269 for(int i = 0; i < sendPerformedCount; i++) {
2270 int rankId = sendCompleted[i];
2271 r = neighbors[rankId];
2272 if((sendMessageSize[rankId] > 0)) {
2273 MPI_Isend(sendBuffer[rankId].data(), sendMessageSize[rankId],
2274 MPI_MessageType, r, 1, MPIcomm,
2275 &sendRequestsData[sendCount]);
2276 sendCount++;
2277 }
2278 }
2279 sendPerformedCountTotal += sendPerformedCount;
2280 }
2281 }
2282 if(recvPerformedCountTotal < neighborNumber) {
2283 MPI_Waitsome(neighborNumber, recvRequests.data(), &recvPerformedCount,
2284 recvCompleted.data(), recvStatus.data());
2285 if(recvPerformedCount > 0) {
2286 for(int i = 0; i < recvPerformedCount; i++) {
2287 r = recvStatus[i].MPI_SOURCE;
2288 int rankId = neighborsToId.find(r)->second;
2289 if((recvMessageSize[rankId] > 0)) {
2290 recvBuffer[rankId].resize(recvMessageSize[rankId]);
2291 MPI_Irecv(recvBuffer[rankId].data(), recvMessageSize[rankId],
2292 MPI_MessageType, r, 1, MPIcomm,
2293 &recvRequestsData[recvCount]);
2294
2295 recvCount++;
2296 }
2297 }
2298 recvPerformedCountTotal += recvPerformedCount;
2299 }
2300 }
2301 }
2302 recvPerformedCountTotal = 0;
2303 while(recvPerformedCountTotal < recvCount) {
2304 MPI_Waitsome(recvCount, recvRequestsData.data(), &recvPerformedCount,
2305 recvCompleted.data(), recvStatusData.data());
2306 if(recvPerformedCount > 0) {
2307 for(int i = 0; i < recvPerformedCount; i++) {
2308 r = recvStatusData[i].MPI_SOURCE;
2309 int rankId = neighborsToId.find(r)->second;
2310#pragma omp parallel num_threads(localThreadNumber) \
2311 shared(extremaLocks, saddleAtomic)
2312 {
2313 int threadNumber = omp_get_thread_num();
2314#pragma omp for schedule(static) reduction(+ : elementNumber)
2315 for(ttk::SimplexId j = 0; j < recvMessageSize[rankId]; j++) {
2316 struct vpathToSend element = recvBuffer[rankId][j];
2318 = triangulation.getVertexLocalId(element.extremaId_);
2319 followVPath(v, element.saddleId_, element.saddleRank_,
2320 threadNumber, elementNumber);
2321 }
2322 }
2323 localElementNumber += elementNumber;
2324 elementNumber = 0;
2325 }
2326 recvPerformedCountTotal += recvPerformedCount;
2327 }
2328 }
2329 MPI_Waitall(sendCount, sendRequestsData.data(), MPI_STATUSES_IGNORE);
2330 // Stop condition computation
2331 MPI_Allreduce(
2332 &localElementNumber, &totalElement, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
2333 keepWorking = (totalElement != totalFinishedElement);
2334 }
2335 // Create ghostPresence and send finished VPath back
2336 std::vector<std::vector<char>> ghostPresenceToSend(ttk::MPIsize_);
2337 std::vector<std::vector<vpathFinished<1>>> finishedVPathToSend(ttk::MPIsize_);
2338 std::vector<std::vector<std::vector<char>>> ghostPresenceToSendThread(
2339 threadNumber_);
2340 std::vector<std::vector<std::vector<vpathFinished<1>>>>
2341 finishedVPathToSendThread(threadNumber_);
2342 std::vector<std::vector<ttk::SimplexId>> ghostCounterThread(threadNumber_);
2343 for(int i = 0; i < threadNumber_; i++) {
2344 ghostPresenceToSendThread[i].resize(ttk::MPIsize_);
2345 finishedVPathToSendThread[i].resize(ttk::MPIsize_);
2346 ghostCounterThread[i].resize(ttk::MPIsize_, 0);
2347 }
2348 // Transform the ghostPresenceVector in proper ghostPresence
2349 this->extractGhost(ghostPresence, ghostPresenceVector, localThreadNumber);
2350
2351 // Package the ghostPresence to send it back
2352 this->packageGhost<1>(
2353 finishedVPathToSendThread, ghostPresenceToSendThread, ghostCounterThread,
2354 sendFinishedVPathBufferThread, localTriangToLocalVectExtrema,
2355 [&triangulation](const SimplexId a) {
2356 return triangulation.getVertexLocalId(a);
2357 },
2358 ghostPresence, localThreadNumber);
2359
2360 this->mergeThreadVectors(finishedVPathToSend, finishedVPathToSendThread,
2361 ghostPresenceToSend, ghostPresenceToSendThread,
2362 ghostCounterThread, localThreadNumber);
2363 // Send/Recv them
2364 std::vector<std::vector<vpathFinished<1>>> recvVPathFinished(ttk::MPIsize_);
2365 std::vector<std::vector<char>> recvGhostPresence(ttk::MPIsize_);
2366 // Send back the ghostpresence
2367 this->exchangeFinalVPathAndGhosts(ghostPresenceToSend, finishedVPathToSend,
2368 recvGhostPresence, recvVPathFinished,
2369 MPI_SimplexId, MPIcomm);
2370
2371 // Unpack the received ghostPresence
2372 this->unpackGhostPresence<1, 2>(
2373 recvGhostPresence, extremaLocks, ghostPresence, localGhostPresenceMap,
2374 localTriangToLocalVectExtrema, recvVPathFinished, saddleAtomic, res,
2375 [&triangulation](const SimplexId a) {
2376 return triangulation.getVertexLocalId(a);
2377 },
2378 [&triangulation](const SimplexId a) {
2379 return triangulation.getVertexRank(a);
2380 },
2381 localThreadNumber);
2382
2383 return 0;
2384}
2385
2386template <int sizeExtr,
2387 int sizeSad,
2388 typename triangulationType,
2389 typename GFS,
2390 typename GFSN,
2391 typename OB,
2392 typename FEO>
2393void ttk::DiscreteMorseSandwichMPI::getSaddle2ToMaxima(
2394 const std::vector<SimplexId> &criticalSaddles,
2395 const GFS &getFaceStar,
2396 const GFSN &getFaceStarNumber,
2397 const OB &isOnBoundary,
2398 const FEO &fillExtremaOrder,
2399 const triangulationType &triangulation,
2400 std::vector<std::array<extremaNode<sizeExtr>, sizeSad + 1>> &res,
2401 const std::unordered_map<ttk::SimplexId, ttk::SimplexId>
2402 &localTriangToLocalVectExtrema,
2403 std::vector<std::vector<char>> &ghostPresence,
2404 std::unordered_map<ttk::SimplexId, std::vector<char>> &localGhostPresenceMap,
2405 std::vector<std::vector<saddleIdPerProcess>> &ghostPresenceVector,
2406 const std::vector<SimplexId> &critMaxsOrder,
2407 MPI_Comm &MPIcomm,
2408 int localThreadNumber) const {
2409
2410 Timer tm{};
2411 const auto dim = this->dg_.getDimensionality();
2412 ttk::SimplexId criticalExtremasNumber = localTriangToLocalVectExtrema.size();
2413 const std::vector<int> neighbors = triangulation.getNeighborRanks();
2414 const std::map<int, int> neighborsToId = triangulation.getNeighborsToId();
2415 int neighborNumber = neighbors.size();
2416 std::vector<std::vector<std::vector<vpathToSend>>> sendBufferThread(
2417 threadNumber_);
2418 std::vector<std::vector<std::vector<vpathFinished<sizeExtr>>>>
2419 sendFinishedVPathBufferThread(threadNumber_);
2420 std::vector<char> saddleAtomic(criticalSaddles.size(), 0);
2421 std::vector<Lock> extremaLocks(criticalExtremasNumber);
2422 MPI_Datatype MPI_SimplexId = getMPIType(static_cast<ttk::SimplexId>(0));
2423 for(int i = 0; i < this->threadNumber_; i++) {
2424 sendBufferThread[i].resize(neighborNumber);
2425 sendFinishedVPathBufferThread[i].resize(ttk::MPIsize_);
2426 }
2427 ttk::SimplexId localElementNumber{0};
2428 ttk::SimplexId totalFinishedElement{0};
2429 ttk::SimplexId totalElement{0};
2430#ifdef TTK_ENABLE_OPENMP
2431#pragma omp parallel for num_threads(localThreadNumber) reduction(+:totalFinishedElement)
2432#endif
2433 for(size_t i = 0; i < criticalSaddles.size(); ++i) {
2434 totalFinishedElement += getFaceStarNumber(criticalSaddles[i]);
2435 }
2436 localElementNumber = 0;
2437 ttk::SimplexId elementNumber = 0;
2438 MPI_Allreduce(
2439 MPI_IN_PLACE, &totalFinishedElement, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
2440 // follow vpaths from 2-saddles to maxima
2441 const auto followVPath = [this, dim, &triangulation, &neighborsToId,
2442 &extremaLocks, &res, &saddleAtomic,
2443 &ghostPresenceVector, &sendBufferThread,
2444 &sendFinishedVPathBufferThread,
2445 &localTriangToLocalVectExtrema, &fillExtremaOrder,
2446 &critMaxsOrder](const SimplexId v,
2447 ttk::SimplexId saddleId,
2448 char saddleRank, int threadNumber,
2449 ttk::SimplexId &eltNumber) {
2450 std::vector<Cell> vpath{};
2451 this->dg_.getAscendingPath(Cell{dim, v}, vpath, triangulation);
2452 const Cell &lastCell = vpath.back();
2453 char saddleLocalId;
2454 if(lastCell.dim_ == dim) {
2455 ttk::SimplexId extremaId = triangulation.getCellGlobalId(lastCell.id_);
2456 int rank = triangulation.getCellRank(lastCell.id_);
2457 if(rank != ttk::MPIrank_) {
2458 sendBufferThread[threadNumber][neighborsToId.find(rank)->second]
2459 .emplace_back(vpathToSend{.saddleId_ = saddleId,
2460 .extremaId_ = extremaId,
2461 .saddleRank_ = saddleRank});
2462 } else {
2463 eltNumber++;
2464 if(this->dg_.isCellCritical(lastCell)) {
2466 = localTriangToLocalVectExtrema.find(lastCell.id_)->second;
2467 size_t r{0};
2468 extremaLocks[id].lock();
2469 auto &ghost{ghostPresenceVector[id]};
2470 while(r < ghost.size()) {
2471 if(ghost[r].rank_ == saddleRank) {
2472 ghost[r].saddleIds_.emplace_back(saddleId);
2473 break;
2474 }
2475 r++;
2476 }
2477 if(r == ghost.size()) {
2478 ghost.emplace_back(saddleIdPerProcess{
2479 std::vector<ttk::SimplexId>{saddleId}, saddleRank});
2480 }
2481 extremaLocks[id].unlock();
2482 if(saddleRank == ttk::MPIrank_) {
2483 extremaNode<sizeExtr> n(extremaId, -1, critMaxsOrder[lastCell.id_],
2484 Rep{-1, -1},
2485 static_cast<char>(ttk::MPIrank_));
2486 fillExtremaOrder(lastCell.id_, n.vOrder_);
2487 // We store it in the current rank
2488#pragma omp atomic capture
2489 saddleLocalId = saddleAtomic[saddleId]++;
2490 res[saddleId][saddleLocalId] = n;
2491 } else {
2492 auto vp{vpathFinished<sizeExtr>(
2493 saddleId, extremaId, static_cast<char>(ttk::MPIrank_))};
2494 fillExtremaOrder(lastCell.id_, vp.vOrder_);
2495 // We store it to send it back to whoever will own the extrema
2496 sendFinishedVPathBufferThread[threadNumber][saddleRank]
2497 .emplace_back(vp);
2498 }
2499 }
2500 }
2501 } else {
2502 if(lastCell.dim_ == dim - 1) {
2503 if(saddleRank == ttk::MPIrank_) {
2504 extremaNode<sizeExtr> n(
2505 -1, -1, -1, Rep{-1, -1}, static_cast<char>(ttk::MPIrank_));
2506 // We store it in the current rank
2507#pragma omp atomic capture
2508 saddleLocalId = saddleAtomic[saddleId]++;
2509 res[saddleId][saddleLocalId] = n;
2510 } else {
2511 // We store it to send it back to whoever will own the extrema
2512 sendFinishedVPathBufferThread[threadNumber][saddleRank].emplace_back(
2513 vpathFinished<sizeExtr>(
2514 saddleId, -1, static_cast<char>(ttk::MPIrank_)));
2515 }
2516 }
2517 eltNumber++;
2518 }
2519 };
2520 // follow vpaths from 2-saddles to maxima
2521 char saddleLocalId;
2522#pragma omp parallel shared(extremaLocks, saddleAtomic) reduction(+: elementNumber) \
2523 num_threads(localThreadNumber)
2524 {
2525 int threadNumber = omp_get_thread_num();
2526#pragma omp for schedule(static)
2527 for(size_t i = 0; i < criticalSaddles.size(); ++i) {
2528 const auto sid = criticalSaddles[i];
2529 for(int j = 0; j < sizeSad + 1; j++) {
2530 res[i][j].gid_ = -2;
2531 }
2532 const auto starNumber = getFaceStarNumber(sid);
2533
2534 for(SimplexId j = 0; j < starNumber; ++j) {
2535 SimplexId cellId{};
2536 getFaceStar(sid, j, cellId);
2537 followVPath(cellId, i, ttk::MPIrank_, threadNumber, elementNumber);
2538 }
2539
2540 if(isOnBoundary(sid)) {
2541 // critical saddle is on boundary
2542 extremaNode<sizeExtr> n(
2543 -1, -1, -1, Rep{-1, -1}, static_cast<char>(ttk::MPIrank_));
2544#pragma omp atomic capture
2545 saddleLocalId = saddleAtomic[i]++;
2546 res[i][saddleLocalId] = n;
2547 }
2548 }
2549 }
2550 localElementNumber += elementNumber;
2551 elementNumber = 0;
2552 // Send receive elements
2553 MPI_Datatype MPI_MessageType;
2554 this->createVpathMPIType(MPI_MessageType);
2555 MPI_Allreduce(
2556 &localElementNumber, &totalElement, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
2557 std::vector<std::vector<vpathToSend>> sendBuffer(neighborNumber);
2558 std::vector<std::vector<vpathToSend>> recvBuffer(neighborNumber);
2559 bool keepWorking = (totalElement != totalFinishedElement);
2560 while(keepWorking) {
2561#pragma omp parallel for schedule(static, 1) num_threads(localThreadNumber)
2562 for(int j = 0; j < neighborNumber; j++) {
2563 sendBuffer[j].clear();
2564 for(int i = 0; i < this->threadNumber_; i++) {
2565 sendBuffer[j].insert(sendBuffer[j].end(),
2566 sendBufferThread[i][j].begin(),
2567 sendBufferThread[i][j].end());
2568 sendBufferThread[i][j].clear();
2569 }
2570 }
2571 std::vector<MPI_Request> sendRequests(neighborNumber);
2572 std::vector<MPI_Request> recvRequests(neighborNumber);
2573 std::vector<MPI_Status> sendStatus(neighborNumber);
2574 std::vector<MPI_Status> recvStatus(neighborNumber);
2575 std::vector<ttk::SimplexId> sendMessageSize(neighborNumber, 0);
2576 std::vector<ttk::SimplexId> recvMessageSize(neighborNumber, 0);
2577 std::vector<int> recvCompleted(neighborNumber, 0);
2578 std::vector<int> sendCompleted(neighborNumber, 0);
2579 int sendPerformedCount = 0;
2580 int recvPerformedCount = 0;
2581 int sendPerformedCountTotal = 0;
2582 int recvPerformedCountTotal = 0;
2583 for(int i = 0; i < neighborNumber; i++) {
2584 // Send size of sendbuffer
2585 sendMessageSize[i] = sendBuffer[i].size();
2586 MPI_Isend(&sendMessageSize[i], 1, MPI_SimplexId, neighbors[i], 0, MPIcomm,
2587 &sendRequests[i]);
2588 MPI_Irecv(&recvMessageSize[i], 1, MPI_SimplexId, neighbors[i], 0, MPIcomm,
2589 &recvRequests[i]);
2590 }
2591 std::vector<MPI_Request> sendRequestsData(neighborNumber);
2592 std::vector<MPI_Request> recvRequestsData(neighborNumber);
2593 std::vector<MPI_Status> recvStatusData(neighborNumber);
2594 int recvCount = 0;
2595 int sendCount = 0;
2596 int r;
2597 while((sendPerformedCountTotal < neighborNumber
2598 || recvPerformedCountTotal < neighborNumber)) {
2599 if(sendPerformedCountTotal < neighborNumber) {
2600 MPI_Waitsome(neighborNumber, sendRequests.data(), &sendPerformedCount,
2601 sendCompleted.data(), sendStatus.data());
2602 if(sendPerformedCount > 0) {
2603 for(int i = 0; i < sendPerformedCount; i++) {
2604 int rankId = sendCompleted[i];
2605 r = neighbors[rankId];
2606 if((sendMessageSize[rankId] > 0)) {
2607 MPI_Isend(sendBuffer[rankId].data(), sendMessageSize[rankId],
2608 MPI_MessageType, r, 1, MPIcomm,
2609 &sendRequestsData[sendCount]);
2610 sendCount++;
2611 }
2612 }
2613 sendPerformedCountTotal += sendPerformedCount;
2614 }
2615 }
2616 if(recvPerformedCountTotal < neighborNumber) {
2617 MPI_Waitsome(neighborNumber, recvRequests.data(), &recvPerformedCount,
2618 recvCompleted.data(), recvStatus.data());
2619 if(recvPerformedCount > 0) {
2620 for(int i = 0; i < recvPerformedCount; i++) {
2621 r = recvStatus[i].MPI_SOURCE;
2622 int rankId = neighborsToId.find(r)->second;
2623 if((recvMessageSize[rankId] > 0)) {
2624 recvBuffer[rankId].resize(recvMessageSize[rankId]);
2625 MPI_Irecv(recvBuffer[rankId].data(), recvMessageSize[rankId],
2626 MPI_MessageType, r, 1, MPIcomm,
2627 &recvRequestsData[recvCount]);
2628
2629 recvCount++;
2630 }
2631 }
2632 recvPerformedCountTotal += recvPerformedCount;
2633 }
2634 }
2635 }
2636 recvPerformedCountTotal = 0;
2637 while(recvPerformedCountTotal < recvCount) {
2638 MPI_Waitsome(recvCount, recvRequestsData.data(), &recvPerformedCount,
2639 recvCompleted.data(), recvStatusData.data());
2640 if(recvPerformedCount > 0) {
2641 for(int i = 0; i < recvPerformedCount; i++) {
2642 r = recvStatusData[i].MPI_SOURCE;
2643 int rankId = neighborsToId.find(r)->second;
2644#pragma omp parallel num_threads(localThreadNumber) \
2645 shared(extremaLocks, saddleAtomic)
2646 {
2647 int threadNumber = omp_get_thread_num();
2648#pragma omp for schedule(static) reduction(+ : elementNumber)
2649 for(ttk::SimplexId j = 0; j < recvMessageSize[rankId]; j++) {
2650 struct vpathToSend element = recvBuffer[rankId][j];
2652 = triangulation.getCellLocalId(element.extremaId_);
2653 followVPath(v, element.saddleId_, element.saddleRank_,
2654 threadNumber, elementNumber);
2655 }
2656 }
2657 localElementNumber += elementNumber;
2658 elementNumber = 0;
2659 }
2660 recvPerformedCountTotal += recvPerformedCount;
2661 }
2662 }
2663 MPI_Waitall(sendCount, sendRequestsData.data(), MPI_STATUSES_IGNORE);
2664 // Stop condition computation
2665 MPI_Allreduce(
2666 &localElementNumber, &totalElement, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
2667 keepWorking = (totalElement != totalFinishedElement);
2668 }
2669 // Create ghostPresence and send finished VPath back
2670 std::vector<std::vector<char>> ghostPresenceToSend(ttk::MPIsize_);
2671 std::vector<std::vector<vpathFinished<sizeExtr>>> finishedVPathToSend(
2673 std::vector<std::vector<std::vector<char>>> ghostPresenceToSendThread(
2674 threadNumber_);
2675 std::vector<std::vector<std::vector<vpathFinished<sizeExtr>>>>
2676 finishedVPathToSendThread(threadNumber_);
2677 std::vector<std::vector<ttk::SimplexId>> ghostCounterThread(threadNumber_);
2678 for(int i = 0; i < threadNumber_; i++) {
2679 ghostPresenceToSendThread[i].resize(ttk::MPIsize_);
2680 finishedVPathToSendThread[i].resize(ttk::MPIsize_);
2681 ghostCounterThread[i].resize(ttk::MPIsize_, 0);
2682 }
2683 // Transform the ghostPresenceVector in proper ghostPresence
2684 this->extractGhost(ghostPresence, ghostPresenceVector, localThreadNumber);
2685
2686 // Package the ghostPresence to send it back
2687 this->packageGhost<sizeExtr>(
2688 finishedVPathToSendThread, ghostPresenceToSendThread, ghostCounterThread,
2689 sendFinishedVPathBufferThread, localTriangToLocalVectExtrema,
2690 [&triangulation](const SimplexId a) {
2691 return triangulation.getCellLocalId(a);
2692 },
2693 ghostPresence, localThreadNumber);
2694
2695 // Merge the vectors
2696 this->mergeThreadVectors(finishedVPathToSend, finishedVPathToSendThread,
2697 ghostPresenceToSend, ghostPresenceToSendThread,
2698 ghostCounterThread, localThreadNumber);
2699
2700 // Send/Recv them
2701 std::vector<std::vector<vpathFinished<sizeExtr>>> recvVPathFinished(
2703 std::vector<std::vector<char>> recvGhostPresence(ttk::MPIsize_);
2704
2705 // Send back the ghostpresence
2706 this->exchangeFinalVPathAndGhosts<sizeExtr>(
2707 ghostPresenceToSend, finishedVPathToSend, recvGhostPresence,
2708 recvVPathFinished, MPI_SimplexId, MPIcomm);
2709
2710 // Unpack the received ghostPresence
2711 this->unpackGhostPresence<sizeExtr, sizeSad + 1>(
2712 recvGhostPresence, extremaLocks, ghostPresence, localGhostPresenceMap,
2713 localTriangToLocalVectExtrema, recvVPathFinished, saddleAtomic, res,
2714 [&triangulation](const SimplexId a) {
2715 return triangulation.getCellLocalId(a);
2716 },
2717 [&triangulation](const SimplexId a) {
2718 return triangulation.getCellRank(a);
2719 },
2720 localThreadNumber);
2721}
2722
2723template <int sizeExtr, int sizeRes, typename GLI, typename GSR>
2724void ttk::DiscreteMorseSandwichMPI::unpackGhostPresence(
2725 std::vector<std::vector<char>> &recvGhostPresence,
2726 std::vector<Lock> &extremaLocks,
2727 std::vector<std::vector<char>> &ghostPresence,
2728 std::unordered_map<ttk::SimplexId, std::vector<char>> &localGhostPresenceMap,
2729 const std::unordered_map<ttk::SimplexId, ttk::SimplexId>
2730 &localTriangToLocalVectExtrema,
2731 std::vector<std::vector<vpathFinished<sizeExtr>>> &recvVPathFinished,
2732 std::vector<char> &saddleAtomic,
2733 std::vector<std::array<extremaNode<sizeExtr>, sizeRes>> &res,
2734 const GLI getSimplexLocalId,
2735 const GSR getSimplexRank,
2736 int localThreadNumber) const {
2737 for(int i = 0; i < ttk::MPIsize_; i++) {
2738#pragma omp parallel for schedule(static) shared(localGhostPresenceMap) \
2739 num_threads(localThreadNumber)
2740 for(size_t j = 0; j < recvVPathFinished[i].size(); j++) {
2741 // Receive element: create VPath and add it to the list
2742 auto &vp{recvVPathFinished[i][j]};
2743 ttk::SimplexId beginGhost
2744 = (j == 0) ? 0 : recvVPathFinished[i][j - 1].ghostPresenceSize_;
2745 ttk::SimplexId order{-1};
2746 if(sizeExtr == 1) {
2747 order = vp.vOrder_[0];
2748 }
2749 extremaNode<sizeExtr> n(
2750 vp.extremaId_, -1, order, Rep{-1, -1}, vp.extremaRank_, vp.vOrder_);
2751 // We store it in the current rank
2752 char saddleLocalId;
2753#pragma omp atomic capture
2754 saddleLocalId = saddleAtomic[vp.saddleId_]++;
2755 res[vp.saddleId_][saddleLocalId] = n;
2756 if(vp.ghostPresenceSize_ != 0) {
2757 // Add the received ghostPresence to the local ghostPresence
2758 // If there is only one process, then the extrema won't be on the
2759 // boundary of the new graph, there is no need to record it
2760 if(vp.ghostPresenceSize_ - beginGhost > 1) {
2761 std::vector<char> ghost{};
2762 ghost.insert(ghost.end(), recvGhostPresence[i].begin() + beginGhost,
2763 recvGhostPresence[i].begin()
2764 + static_cast<ttk::SimplexId>(vp.ghostPresenceSize_));
2765 ttk::SimplexId lid = getSimplexLocalId(vp.extremaId_);
2766 // If the extrema is not locally present in the triangulation,
2767 // Add the entry to the map
2768 if(lid == -1 || getSimplexRank(lid) != ttk::MPIrank_) {
2769#pragma omp critical
2770 { localGhostPresenceMap[vp.extremaId_] = ghost; }
2771 } else {
2772 lid = localTriangToLocalVectExtrema.find(lid)->second;
2773 extremaLocks[lid].lock();
2774 ghostPresence[lid] = ghost;
2775 extremaLocks[lid].unlock();
2776 }
2777 }
2778 }
2779 }
2780 }
2781};
2782
2783template <int sizeExtr, typename GLI>
2784void ttk::DiscreteMorseSandwichMPI::packageGhost(
2785 std::vector<std::vector<std::vector<vpathFinished<sizeExtr>>>>
2786 &finishedVPathToSendThread,
2787 std::vector<std::vector<std::vector<char>>> &ghostPresenceToSendThread,
2788 std::vector<std::vector<ttk::SimplexId>> &ghostCounterThread,
2789 std::vector<std::vector<std::vector<vpathFinished<sizeExtr>>>>
2790 &sendFinishedVPathBufferThread,
2791 const std::unordered_map<ttk::SimplexId, ttk::SimplexId>
2792 &localTriangToLocalVectExtrema,
2793 const GLI getSimplexLocalId,
2794 std::vector<std::vector<char>> &ghostPresence,
2795 int localThreadNumber) const {
2796#pragma omp parallel for schedule(static, 1) num_threads(localThreadNumber) \
2797 shared(ghostCounterThread)
2798 for(int j = 0; j < localThreadNumber; j++) {
2799 for(int i = 0; i < ttk::MPIsize_; i++) {
2800 for(size_t k = 0; k < sendFinishedVPathBufferThread[j][i].size(); k++) {
2801 // Find owner by applying the following rule:
2802 // if the current rank is in ghostPresence, then the current rank is
2803 // the owner if not, it is the rank with the lowest rank id that is
2804 // the owner
2805 auto vp = sendFinishedVPathBufferThread[j][i][k];
2806 if(vp.extremaId_ > -1) {
2807 ttk::SimplexId lid = localTriangToLocalVectExtrema
2808 .find(getSimplexLocalId(vp.extremaId_))
2809 ->second;
2810 auto &ghost{ghostPresence[lid]};
2811 auto it = std::find(
2812 ghost.begin(), ghost.end(), static_cast<char>(ttk::MPIrank_));
2813 if(it != ghost.end()) {
2814 // The rank of the extrema is the current rank
2815 // We store to send the finished vpath
2816 vp.extremaRank_ = ttk::MPIrank_;
2817 vp.ghostPresenceSize_ = ghostCounterThread[j][i];
2818 } else {
2819 // The rank of the extrema is NOT the current rank
2820 // We find the smallest rank
2821 auto minRank = std::min_element(ghost.begin(), ghost.end());
2822 vp.extremaRank_ = (*minRank);
2823 if(i == (*minRank) && ghost.size() > 1) {
2824 ghostCounterThread[j][i] += ghost.size();
2825 }
2826 vp.ghostPresenceSize_ = ghostCounterThread[j][i];
2827 // Send the ghostPresence to that rank
2828 if(ghost.size() > 1) {
2829 ghostPresenceToSendThread[j][i].insert(
2830 ghostPresenceToSendThread[j][i].end(), ghost.begin(),
2831 ghost.end());
2832 }
2833 }
2834 } else {
2835 vp.ghostPresenceSize_ = ghostCounterThread[j][i];
2836 }
2837 finishedVPathToSendThread[j][i].emplace_back(vp);
2838 }
2839 sendFinishedVPathBufferThread[j][i].clear();
2840 }
2841 }
2842}
2843
2844template <typename triangulationType>
2845void ttk::DiscreteMorseSandwichMPI::getMinSaddlePairs(
2846 std::vector<PersistencePair> &pairs,
2847 const std::vector<ttk::SimplexId> &criticalEdges,
2848 const std::vector<ttk::SimplexId> &critEdgesOrder,
2849 const std::vector<ttk::SimplexId> &criticalExtremas,
2850 const SimplexId *const offsets,
2851 size_t &nConnComp,
2852 const triangulationType &triangulation,
2853 MPI_Comm &MPIcomm,
2854 int localThreadNumber) const {
2855 ttk::SimplexId totalNumberOfVertices{-1};
2856 ttk::SimplexId criticalExtremasNumber = criticalExtremas.size();
2857 ttk::SimplexId criticalEdgesNumber
2858 = static_cast<ttk::SimplexId>(criticalEdges.size());
2859 MPI_Datatype MPI_SimplexId = getMPIType(totalNumberOfVertices);
2860 ttk::SimplexId localNumberOfVertices = triangulation.getNumberOfVertices();
2861
2862 MPI_Allreduce(&localNumberOfVertices, &totalNumberOfVertices, 1,
2863 MPI_SimplexId, MPI_SUM, MPIcomm);
2864
2865 ttk::SimplexId globalMinOffset{-1};
2866 std::pair<ttk::SimplexId, ttk::SimplexId> localMin(-1, totalNumberOfVertices);
2867
2868 if(criticalExtremasNumber > 0) {
2869 // extracts the global min
2870#pragma omp declare reduction(get_min : std::pair<ttk::SimplexId, ttk::SimplexId> :omp_out = omp_out.second < omp_in.second ? omp_out : omp_in)
2871#pragma omp parallel for reduction(get_min \
2872 : localMin) num_threads(localThreadNumber)
2873 for(ttk::SimplexId i = 0; i < criticalExtremasNumber; i++) {
2874 if(offsets[criticalExtremas[i]] < localMin.second) {
2875 localMin.first = criticalExtremas[i];
2876 localMin.second = offsets[localMin.first];
2877 }
2878 }
2879 }
2880
2881 MPI_Allreduce(
2882 &localMin.second, &globalMinOffset, 1, MPI_SimplexId, MPI_MIN, MPIcomm);
2883 if(this->ComputeMinSad) {
2884 // minima - saddle pairs
2885 Timer tm{};
2886 std::vector<std::array<extremaNode<1>, 2>> saddle1ToMinima;
2887 std::vector<std::vector<saddleIdPerProcess>> ghostPresenceVector;
2888 std::unordered_map<ttk::SimplexId, std::vector<char>> localGhostPresenceMap;
2889 std::vector<std::vector<char>> localGhostPresenceVector;
2890 std::unordered_map<ttk::SimplexId, ttk::SimplexId>
2891 localTriangToLocalVectExtrema;
2892 std::vector<ttk::SimplexId> extremasGid;
2893 std::vector<saddleEdge<2>> saddles;
2894 extremasGid.reserve(criticalEdgesNumber * 2);
2895#pragma omp parallel master num_threads(localThreadNumber)
2896 {
2897#pragma omp task
2898 for(ttk::SimplexId i = 0; i < criticalExtremasNumber; i++) {
2899 localTriangToLocalVectExtrema[criticalExtremas[i]] = i;
2900 }
2901#pragma omp task
2902 ghostPresenceVector.resize(
2903 criticalExtremasNumber, std::vector<saddleIdPerProcess>());
2904#pragma omp task
2905 saddle1ToMinima.resize(
2906 criticalEdges.size(), std::array<extremaNode<1>, 2>());
2907#pragma omp task
2908 localGhostPresenceVector.resize(
2909 criticalExtremasNumber, std::vector<char>());
2910#pragma omp task
2911 saddles.resize(criticalEdgesNumber);
2912 }
2913
2914 this->getSaddle1ToMinima(criticalEdges, localTriangToLocalVectExtrema,
2915 triangulation, offsets, saddle1ToMinima,
2916 localGhostPresenceVector, localGhostPresenceMap,
2917 ghostPresenceVector, MPIcomm, localThreadNumber);
2918 // Timer tmseq{};
2919 auto &saddleToPairedExtrema{this->saddleToPairedMin_};
2920 auto &extremaToPairedSaddle{this->minToPairedSaddle_};
2921 auto &globalToLocalSaddle{this->globalToLocalSaddle1_};
2922 std::vector<ttk::SimplexId> globalMinLid;
2923 ttk::SimplexId totalNumberOfPairs = criticalExtremasNumber;
2924 MPI_Allreduce(
2925 MPI_IN_PLACE, &totalNumberOfPairs, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
2926 globalToLocalSaddle.reserve(criticalEdgesNumber);
2927
2928#pragma omp declare reduction (merge : std::vector<ttk::SimplexId>: omp_out.insert(omp_out.end(), omp_in.begin(), omp_in.end()))
2929#pragma omp parallel for reduction(merge \
2930 : extremasGid) schedule(static) \
2931 shared(saddles) num_threads(localThreadNumber)
2932 for(ttk::SimplexId i = 0; i < criticalEdgesNumber; ++i) {
2933 auto &mins = saddle1ToMinima[i];
2934 const auto s1 = criticalEdges[i];
2935 // remove duplicates
2936 std::sort(mins.begin(), mins.end());
2937 const auto last = std::unique(mins.begin(), mins.end());
2938 if(last != mins.end()) {
2939 continue;
2940 }
2941 saddles[i].lid_ = i;
2942 ttk::SimplexId gid = triangulation.getEdgeGlobalId(s1);
2943 saddles[i].gid_ = gid;
2944 for(int j = 0; j < 2; j++) {
2945 extremasGid.emplace_back(mins[j].gid_);
2946 }
2947 }
2948 TTK_PSORT(localThreadNumber, extremasGid.begin(), extremasGid.end());
2949 const auto lastGid = std::unique(extremasGid.begin(), extremasGid.end());
2950 extremasGid.erase(lastGid, extremasGid.end());
2951 std::unordered_map<ttk::SimplexId, ttk::SimplexId> globalToLocalExtrema{};
2952 globalToLocalExtrema.reserve(extremasGid.size());
2953 std::vector<std::vector<char>> ghostPresence(
2954 extremasGid.size(), std::vector<char>());
2955 std::vector<int> extremaLocks(extremasGid.size(), 0);
2956 std::vector<extremaNode<1>> extremas(extremasGid.size(), extremaNode<1>());
2957#pragma omp parallel master shared(extremaLocks, extremas, globalMinLid, \
2958 ghostPresence, saddles, globalMinOffset) \
2959 num_threads(localThreadNumber)
2960 {
2961#pragma omp task
2962 {
2963 for(ttk::SimplexId i = 0;
2964 i < static_cast<ttk::SimplexId>(extremasGid.size()); i++) {
2965 globalToLocalExtrema.emplace(extremasGid[i], i);
2966 }
2967 }
2968#pragma omp task
2969 {
2970 for(ttk::SimplexId i = 0; i < criticalEdgesNumber; i++) {
2971 if(saddles[i].gid_ != -1) {
2972 globalToLocalSaddle.emplace(saddles[i].gid_, i);
2973 }
2974 }
2975 }
2976 ttk::SimplexId numTask
2977 = static_cast<ttk::SimplexId>(std::max(localThreadNumber - 2, 1));
2978#pragma omp taskloop num_tasks(numTask)
2979 for(size_t i = 0; i < static_cast<size_t>(criticalEdgesNumber); ++i) {
2980 auto &mins = saddle1ToMinima[i];
2981 const auto s1 = criticalEdges[i];
2982 const auto last = std::unique(mins.begin(), mins.end());
2983 // mins.erase(last, mins.end());
2984 if(last != mins.end()) {
2985 continue;
2986 }
2987 saddleEdge<2> &e{saddles[i]};
2988 fillEdgeOrder(s1, offsets, triangulation, e.vOrder_);
2989 e.order_ = critEdgesOrder[s1];
2990 for(int j = 0; j < 2; j++) {
2991 int extremaExists;
2992 ttk::SimplexId lid = std::lower_bound(extremasGid.begin(),
2993 extremasGid.end(), mins[j].gid_)
2994 - extremasGid.begin();
2995#pragma omp atomic capture
2996 extremaExists = extremaLocks[lid]++;
2997 if(extremaExists == 0) {
2998 std::vector<char> ghosts{};
2999 mins[j].lid_ = lid;
3000 mins[j].rep_.extremaId_ = lid;
3001 extremas[lid] = mins[j];
3002 if(globalMinOffset == mins[j].vOrder_[0]) {
3003#pragma omp critical
3004 globalMinLid.emplace_back(lid);
3005 }
3006 ttk::SimplexId triangLid
3007 = triangulation.getVertexLocalId(mins[j].gid_);
3008 if((triangLid == -1)
3009 || (triangulation.getVertexRank(triangLid) != ttk::MPIrank_)) {
3010 auto it = localGhostPresenceMap.find(mins[j].gid_);
3011 if(it != localGhostPresenceMap.end()) {
3012 ghosts = localGhostPresenceMap[mins[j].gid_];
3013 } else {
3014 ghosts.resize(0);
3015 }
3016 } else {
3017 triangLid = localTriangToLocalVectExtrema.find(triangLid)->second;
3018 ghosts = localGhostPresenceVector[triangLid];
3019 }
3020 ghostPresence[lid] = ghosts;
3021 }
3022 e.t_[j] = lid;
3023 }
3024 }
3025 }
3026 const auto cmpSadMin
3027 = [=, &extremas, &saddles](
3028 const ttk::SimplexId &id1, const ttk::SimplexId &id2) -> bool {
3029 const saddleEdge<2> &s0{saddles[id1]};
3030 const saddleEdge<2> &s1{saddles[id2]};
3031 if(s0.gid_ != s1.gid_) {
3032 if(s0.order_ != -1 && s1.order_ != -1) {
3033 return s0.order_ < s1.order_;
3034 }
3035 for(int i = 0; i < 2; i++) {
3036 if(s0.vOrder_[i] != s1.vOrder_[i]) {
3037 return s0.vOrder_[i] < s1.vOrder_[i];
3038 }
3039 }
3040 }
3041 if(s0.gid_ == -1 && s1.gid_ == -1) {
3042 return false;
3043 }
3044 return extremas[s0.t_[0]].vOrder_[0] > extremas[s1.t_[0]].vOrder_[0];
3045 };
3046
3047 // Sorting arcs
3048 std::vector<ttk::SimplexId> saddleIds(saddles.size());
3049 std::iota(saddleIds.begin(), saddleIds.end(), 0);
3050 TTK_PSORT(localThreadNumber, saddleIds.begin(), saddleIds.end(), cmpSadMin);
3051 // Setting up lid for arcs
3052 extremaToPairedSaddle.resize(globalToLocalExtrema.size(), -1);
3053 saddleToPairedExtrema.resize(saddles.size(), -1);
3054 MPI_Datatype MPI_MessageType;
3055 createMPIMessageType<1, 2>(MPI_MessageType);
3056 tripletsToPersistencePairs<1, 2>(
3057 0, extremas, saddles, saddleIds, saddleToPairedExtrema,
3058 extremaToPairedSaddle, globalToLocalSaddle, globalToLocalExtrema,
3059 ghostPresence, MPI_MessageType, true, MPIcomm, localThreadNumber);
3060 ttk::SimplexId nMinSadPairs = computePairNumbers<1, 2>(
3061 saddles, saddleToPairedExtrema, localThreadNumber);
3062 char rerunNeeded{0};
3063 for(const auto lid : globalMinLid) {
3064 if(extremaToPairedSaddle[lid] != -1
3065 && saddles[extremaToPairedSaddle[lid]].rank_ == ttk::MPIrank_) {
3066 saddleToPairedExtrema[extremaToPairedSaddle[lid]] = -2;
3067 rerunNeeded = 1;
3068 }
3069 }
3070 MPI_Allreduce(MPI_IN_PLACE, &rerunNeeded, 1, MPI_CHAR, MPI_LOR, MPIcomm);
3071 MPI_Allreduce(
3072 MPI_IN_PLACE, &nMinSadPairs, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
3073
3074 while((nMinSadPairs != totalNumberOfPairs - 1) || rerunNeeded) {
3075 printMsg("Re-computation, rerun needed: " + std::to_string(rerunNeeded)
3076 + " nMinSadPairs: " + std::to_string(nMinSadPairs) + ", "
3077 + std::to_string(totalNumberOfPairs));
3078 tripletsToPersistencePairs<1, 2>(
3079 0, extremas, saddles, saddleIds, saddleToPairedExtrema,
3080 extremaToPairedSaddle, globalToLocalSaddle, globalToLocalExtrema,
3081 ghostPresence, MPI_MessageType, false, MPIcomm, localThreadNumber);
3082 nMinSadPairs = computePairNumbers<1, 2>(
3083 saddles, saddleToPairedExtrema, localThreadNumber);
3084 rerunNeeded = 0;
3085 for(const auto lid : globalMinLid) {
3086 if(extremaToPairedSaddle[lid] != -1
3087 && saddles[extremaToPairedSaddle[lid]].rank_ == ttk::MPIrank_) {
3088 saddleToPairedExtrema[extremaToPairedSaddle[lid]] = -2;
3089 rerunNeeded = 1;
3090 }
3091 }
3092 MPI_Allreduce(MPI_IN_PLACE, &rerunNeeded, 1, MPI_CHAR, MPI_LOR, MPIcomm);
3093 MPI_Allreduce(
3094 MPI_IN_PLACE, &nMinSadPairs, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
3095 }
3096 extractPairs<1, 2>(pairs, extremas, saddles, saddleToPairedExtrema, false,
3097 0, localThreadNumber);
3098 // non-paired minima
3099 if(totalNumberOfPairs > 1) {
3100#pragma omp parallel for shared(pairs, nConnComp) num_threads(localThreadNumber)
3101 for(const auto extr : extremas) {
3102 if(extr.rank_ == ttk::MPIrank_) {
3103 if(extremaToPairedSaddle[extr.lid_] < 0) {
3104#pragma omp critical
3105 {
3106 pairs.emplace_back(extr.gid_, -1, 0);
3107 nConnComp++;
3108 }
3109 }
3110 }
3111 }
3112 } else {
3113 if(globalMinOffset == localMin.second) {
3114 pairs.emplace_back(
3115 triangulation.getVertexGlobalId(localMin.first), -1, 0);
3116 nConnComp++;
3117 }
3118 }
3119 if(ttk::MPIrank_ == 0)
3120 this->printMsg(
3121 "Computed " + std::to_string(nMinSadPairs) + " min-saddle pairs", 1.0,
3122 tm.getElapsedTime(), localThreadNumber);
3123 } else {
3124 if(globalMinOffset == localMin.second) {
3125 pairs.emplace_back(
3126 triangulation.getVertexGlobalId(localMin.first), -1, 0);
3127 nConnComp++;
3128 }
3129 }
3130}
3131
3132template <int sizeExtr,
3133 int sizeSad,
3134 typename triangulationType,
3135 typename GFS,
3136 typename GFSN,
3137 typename OB,
3138 typename FEO,
3139 typename GSGID,
3140 typename FSO>
3141void ttk::DiscreteMorseSandwichMPI::computeMaxSaddlePairs(
3142 std::vector<PersistencePair> &pairs,
3143 const std::vector<SimplexId> &criticalSaddles,
3144 const std::vector<SimplexId> &critSaddlesOrder,
3145 const std::vector<ttk::SimplexId> &criticalExtremas,
3146 const std::vector<SimplexId> &critMaxsOrder,
3147 const triangulationType &triangulation,
3148 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalSaddle,
3149 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalExtrema,
3150 const GFS &getFaceStar,
3151 const GFSN &getFaceStarNumber,
3152 const OB &isOnBoundary,
3153 const FEO &fillExtremaOrder,
3154 const GSGID &getSaddleGlobalId,
3155 const FSO &fillSaddleOrder,
3156 MPI_Comm &MPIcomm,
3157 int localThreadNumber) {
3158 Timer tm{};
3159 const auto dim = this->dg_.getDimensionality();
3160 std::vector<std::array<extremaNode<sizeExtr>, sizeSad + 1>> saddle2ToMaxima;
3161 std::vector<std::vector<saddleIdPerProcess>> ghostPresenceVector;
3162 ttk::SimplexId criticalExtremasNumber = criticalExtremas.size();
3163 ttk::SimplexId criticalSaddlesNumber = criticalSaddles.size();
3164 std::unordered_map<ttk::SimplexId, std::vector<char>> localGhostPresenceMap;
3165 std::vector<std::vector<char>> localGhostPresenceVector;
3166 std::unordered_map<ttk::SimplexId, ttk::SimplexId>
3167 localTriangToLocalVectExtrema;
3168 std::vector<ttk::SimplexId> extremasGid;
3169 std::vector<saddleEdge<sizeSad>> saddles;
3170 MPI_Datatype MPI_SimplexId = getMPIType(criticalExtremasNumber);
3171 extremasGid.reserve(criticalSaddlesNumber * 2);
3172#pragma omp parallel master num_threads(localThreadNumber)
3173 {
3174#pragma omp task
3175 for(ttk::SimplexId i = 0; i < criticalExtremasNumber; i++) {
3176 localTriangToLocalVectExtrema[criticalExtremas[i]] = i;
3177 }
3178#pragma omp task
3179 ghostPresenceVector.resize(
3180 criticalExtremasNumber, std::vector<saddleIdPerProcess>());
3181#pragma omp task
3182 saddle2ToMaxima.resize(
3183 criticalSaddles.size(), std::array<extremaNode<sizeExtr>, sizeSad + 1>());
3184#pragma omp task
3185 localGhostPresenceVector.resize(
3186 criticalExtremasNumber, std::vector<char>());
3187#pragma omp task
3188 saddles.resize(criticalSaddlesNumber);
3189 }
3190 this->getSaddle2ToMaxima<sizeExtr, sizeSad>(
3191 criticalSaddles, getFaceStar, getFaceStarNumber, isOnBoundary,
3192 fillExtremaOrder, triangulation, saddle2ToMaxima,
3193 localTriangToLocalVectExtrema, localGhostPresenceVector,
3194 localGhostPresenceMap, ghostPresenceVector, critMaxsOrder, MPIcomm,
3195 localThreadNumber);
3196
3197 Timer tmseq{};
3198
3199 auto &saddleToPairedExtrema{this->saddleToPairedMax_};
3200 auto &extremaToPairedSaddle{this->maxToPairedSaddle_};
3201
3202 globalToLocalExtrema.reserve(2 * saddle2ToMaxima.size());
3203 ttk::SimplexId totalNumberOfPairs = criticalExtremasNumber;
3204 MPI_Allreduce(
3205 MPI_IN_PLACE, &totalNumberOfPairs, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
3206 globalToLocalSaddle.reserve(criticalSaddlesNumber);
3207#pragma omp declare reduction (merge : std::vector<ttk::SimplexId>: omp_out.insert(omp_out.end(), omp_in.begin(), omp_in.end()))
3208#pragma omp parallel for reduction(merge \
3209 : extremasGid) schedule(static) \
3210 shared(saddles, saddle2ToMaxima) num_threads(localThreadNumber)
3211 for(ttk::SimplexId i = 0; i < criticalSaddlesNumber; ++i) {
3212 auto &maxs = saddle2ToMaxima[i];
3213 const auto s2 = criticalSaddles[i];
3214 std::sort(
3215 maxs.begin(), maxs.end(),
3216 [](const extremaNode<sizeExtr> &a, const extremaNode<sizeExtr> &b) {
3217 // positive values (actual maxima) before negative ones
3218 // (boundary component id)
3219 if(a.gid_ * b.gid_ >= 0) {
3220 return std::abs(a.gid_) < std::abs(b.gid_);
3221 } else {
3222 return a.gid_ > b.gid_;
3223 }
3224 });
3225 auto last = std::unique(maxs.begin(), maxs.end());
3226
3227 if(last->gid_ == -2) {
3228 last--;
3229 }
3230 // store the size to reuse later
3231 maxs[sizeSad].gid_ = std::distance(maxs.begin(), last);
3232
3233 // remove "doughnut" configurations: two ascending separatrices
3234 // leading to the same maximum/boundary component
3235 if(maxs[sizeSad].gid_ != 2) {
3236 continue;
3237 }
3238 saddles[i].lid_ = i;
3239 ttk::SimplexId gid = getSaddleGlobalId(s2);
3240 saddles[i].gid_ = gid;
3241 for(int j = 0; j < 2; j++) {
3242 if(maxs[j].gid_ != -1) {
3243 extremasGid.emplace_back(maxs[j].gid_);
3244 }
3245 }
3246 }
3247 TTK_PSORT(localThreadNumber, extremasGid.begin(), extremasGid.end());
3248 const auto last = std::unique(extremasGid.begin(), extremasGid.end());
3249 extremasGid.erase(last, extremasGid.end());
3250 globalToLocalExtrema.reserve(extremasGid.size());
3251 std::vector<std::vector<char>> ghostPresence(
3252 extremasGid.size(), std::vector<char>());
3253 std::vector<int> extremaLocks(extremasGid.size(), 0);
3254 std::vector<extremaNode<sizeExtr>> extremas(
3255 extremasGid.size(), extremaNode<sizeExtr>());
3256#pragma omp parallel master shared(extremaLocks, extremas, ghostPresence, \
3257 saddles) num_threads(localThreadNumber)
3258 {
3259#pragma omp task
3260 {
3261 for(ttk::SimplexId i = 0;
3262 i < static_cast<ttk::SimplexId>(extremasGid.size()); i++) {
3263 globalToLocalExtrema.emplace(extremasGid[i], i);
3264 }
3265 }
3266#pragma omp task
3267 {
3268 for(ttk::SimplexId i = 0; i < criticalSaddlesNumber; i++) {
3269 if(saddles[i].gid_ != -1) {
3270 globalToLocalSaddle.emplace(saddles[i].gid_, i);
3271 }
3272 }
3273 }
3274 int numTask = std::max(localThreadNumber - 2, 1);
3275#pragma omp taskloop num_tasks(numTask)
3276 for(size_t i = 0; i < static_cast<size_t>(criticalSaddlesNumber); ++i) {
3277 auto &maxs = saddle2ToMaxima[i];
3278 const auto s2 = criticalSaddles[i];
3279 if(maxs[sizeSad].gid_ != 2) {
3280 continue;
3281 }
3282 saddleEdge<sizeSad> &e{saddles[i]};
3283 fillSaddleOrder(s2, e.vOrder_);
3284 e.order_ = critSaddlesOrder[s2];
3285 for(int j = 0; j < 2; j++) {
3286 if(maxs[j].gid_ > -1) {
3287 int extremaExists;
3288 ttk::SimplexId lid = std::lower_bound(extremasGid.begin(),
3289 extremasGid.end(), maxs[j].gid_)
3290 - extremasGid.begin();
3291#pragma omp atomic capture
3292 extremaExists = extremaLocks[lid]++;
3293 if(extremaExists == 0) {
3294 std::vector<char> ghosts{};
3295 maxs[j].lid_ = lid;
3296 maxs[j].rep_.extremaId_ = lid;
3297 extremas[lid] = maxs[j];
3298 ttk::SimplexId triangLid
3299 = triangulation.getCellLocalId(maxs[j].gid_);
3300 if(triangLid == -1
3301 || triangulation.getCellRank(triangLid) != ttk::MPIrank_) {
3302 auto it = localGhostPresenceMap.find(maxs[j].gid_);
3303 if(it != localGhostPresenceMap.end()) {
3304 ghosts = localGhostPresenceMap[maxs[j].gid_];
3305 } else {
3306 ghosts.resize(0);
3307 }
3308 } else {
3309 triangLid = localTriangToLocalVectExtrema.find(triangLid)->second;
3310 ghosts = localGhostPresenceVector[triangLid];
3311 }
3312 ghostPresence[lid] = ghosts;
3313 }
3314 e.t_[j] = lid;
3315 }
3316 }
3317 }
3318 }
3319
3320 const auto cmpSadMax
3321 = [&extremas, &saddles](
3322 const ttk::SimplexId &id1, const ttk::SimplexId &id2) -> bool {
3323 const auto &s0{saddles[id1]};
3324 const auto &s1{saddles[id2]};
3325 if(&s0 != &s1) {
3326 if(s0.order_ != -1 && s1.order_ != -1) {
3327 return s0.order_ > s1.order_;
3328 }
3329 for(size_t i = 0; i < sizeSad; i++) {
3330 if(s0.vOrder_[i] != s1.vOrder_[i]) {
3331 return s0.vOrder_[i] > s1.vOrder_[i];
3332 }
3333 }
3334 return s0.gid_ > s1.gid_;
3335 }
3336 if(s0.gid_ == -1 && s1.gid_ == -1) {
3337 return false;
3338 }
3339 if(s0.t_[1] != s1.t_[1]) {
3340 if(s0.t_[1] == -1) {
3341 return false;
3342 }
3343 if(s1.t_[1] == -1) {
3344 return true;
3345 }
3346 auto &t0 = extremas[s0.t_[1]];
3347 auto &t1 = extremas[s1.t_[1]];
3348 if(t0.order_ != -1 && t1.order_ != -1) {
3349 return t0.order_ < t1.order_;
3350 }
3351 for(size_t i = 0; i < sizeExtr; i++) {
3352 if(t0.vOrder_[i] != t1.vOrder_[i]) {
3353 return t0.vOrder_[i] < t1.vOrder_[i];
3354 }
3355 }
3356 }
3357 return true;
3358 };
3359 // Sorting arcs
3360 std::vector<ttk::SimplexId> saddleIds(saddles.size());
3361 std::iota(saddleIds.begin(), saddleIds.end(), 0);
3362 TTK_PSORT(localThreadNumber, saddleIds.begin(), saddleIds.end(), cmpSadMax);
3363 // Setting up lid for arcs
3364 extremaToPairedSaddle.resize(globalToLocalExtrema.size(), -1);
3365 saddleToPairedExtrema.resize(saddles.size(), -1);
3366
3367 MPI_Datatype MPI_MessageType;
3368 createMPIMessageType<sizeExtr, sizeSad>(MPI_MessageType);
3369 tripletsToPersistencePairs<sizeExtr, sizeSad>(
3370 dim - 1, extremas, saddles, saddleIds, saddleToPairedExtrema,
3371 extremaToPairedSaddle, globalToLocalSaddle, globalToLocalExtrema,
3372 ghostPresence, MPI_MessageType, true, MPIcomm, localThreadNumber);
3373 ttk::SimplexId nMinSadPairs = pairs.size();
3374 extractPairs<sizeExtr, sizeSad>(pairs, extremas, saddles,
3375 saddleToPairedExtrema, true, dim - 1,
3376 localThreadNumber);
3377 ttk::SimplexId nSadMaxPairs = pairs.size() - nMinSadPairs;
3378 MPI_Allreduce(
3379 MPI_IN_PLACE, &nSadMaxPairs, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
3380
3381 if(ttk::MPIrank_ == 0)
3382 this->printMsg(
3383 "Computed " + std::to_string(nSadMaxPairs) + " saddle-max pairs", 1.0,
3384 tm.getElapsedTime(), localThreadNumber);
3385}
3386template <typename triangulationType>
3387void ttk::DiscreteMorseSandwichMPI::getMaxSaddlePairs(
3388 std::vector<PersistencePair> &pairs,
3389 const std::vector<SimplexId> &criticalSaddles,
3390 const std::vector<SimplexId> &critSaddlesOrder,
3391 const std::vector<ttk::SimplexId> &criticalExtremas,
3392 const std::vector<SimplexId> &critMaxsOrder,
3393 const triangulationType &triangulation,
3394 const bool ignoreBoundary,
3395 const SimplexId *const offsets,
3396 MPI_Comm &MPIcomm,
3397 int localThreadNumber) {
3398 Timer t{};
3399 const auto dim = this->dg_.getDimensionality();
3400 auto &globalToLocalSaddle{dim == 3 ? this->globalToLocalSaddle2_
3401 : this->globalToLocalSaddle1_};
3402 std::unordered_map<ttk::SimplexId, ttk::SimplexId> globalToLocalExtrema{};
3403
3404 ttk::SimplexId criticalExtremasNumber = criticalExtremas.size();
3405 ttk::SimplexId vertexNumber = triangulation.getNumberOfVertices();
3406 MPI_Datatype MPI_SimplexId = getMPIType(criticalExtremasNumber);
3407 std::vector<ttk::SimplexId> localMaxId;
3408 std::vector<ttk::SimplexId> globalMaxId;
3409 ttk::SimplexId globalMaxOffset{0};
3410 if(criticalExtremasNumber > 0) {
3411 // extracts the global max
3412#pragma omp parallel for reduction(max \
3413 : globalMaxOffset) \
3414 num_threads(localThreadNumber)
3415 for(ttk::SimplexId i = 0; i < vertexNumber; i++) {
3416 if(globalMaxOffset < offsets[i]) {
3417 globalMaxOffset = offsets[i];
3418 }
3419 }
3420 }
3421
3422 MPI_Allreduce(
3423 MPI_IN_PLACE, &globalMaxOffset, 1, MPI_SimplexId, MPI_MAX, MPIcomm);
3424
3425#pragma omp declare reduction (merge : std::vector<ttk::SimplexId> : omp_out.insert(omp_out.end(), omp_in.begin(), omp_in.end()))
3426#pragma omp parallel for reduction(merge \
3427 : localMaxId) \
3428 num_threads(localThreadNumber)
3429 for(ttk::SimplexId i = 0; i < criticalExtremasNumber; i++) {
3430 if(triangulation.getCellRank(criticalExtremas[i]) == ttk::MPIrank_) {
3431 const Cell cmax{dim, criticalExtremas[i]};
3432 const auto vmax{this->getCellGreaterVertex(cmax, triangulation)};
3433 if(offsets[vmax] == globalMaxOffset - 1) {
3434 localMaxId.emplace_back(
3435 triangulation.getCellGlobalId(criticalExtremas[i]));
3436 }
3437 }
3438 }
3439 int localMaxIdSize = localMaxId.size();
3440 std::vector<int> recvCount(ttk::MPIsize_, 0);
3441 std::vector<int> displs(ttk::MPIsize_, 0);
3442 MPI_Allgather(
3443 &localMaxIdSize, 1, MPI_INTEGER, recvCount.data(), 1, MPI_INTEGER, MPIcomm);
3444 std::partial_sum(
3445 recvCount.data(), recvCount.data() + ttk::MPIsize_ - 1, displs.data() + 1);
3446 globalMaxId.resize(std::accumulate(recvCount.begin(), recvCount.end(), 0));
3447 MPI_Allgatherv(localMaxId.data(), localMaxIdSize, MPI_SimplexId,
3448 globalMaxId.data(), recvCount.data(), displs.data(),
3449 MPI_SimplexId, MPIcomm);
3450 if(dim > 1 && this->ComputeSadMax) {
3451 if(dim == 3) {
3452 computeMaxSaddlePairs<4, 3>(
3453 pairs, criticalSaddles, critSaddlesOrder, criticalExtremas,
3454 critMaxsOrder, triangulation, globalToLocalSaddle, globalToLocalExtrema,
3455 [&triangulation](const SimplexId a, const SimplexId i, SimplexId &r) {
3456 return triangulation.getTriangleStar(a, i, r);
3457 },
3458 [&triangulation](const SimplexId a) {
3459 return triangulation.getTriangleStarNumber(a);
3460 },
3461 [&triangulation](const SimplexId a) {
3462 return triangulation.isTriangleOnBoundary(a);
3463 },
3464 [this, &triangulation, offsets](
3465 const ttk::SimplexId id, ttk::SimplexId *vertsOrder) {
3466 return fillTetraOrder(id, offsets, triangulation, vertsOrder);
3467 },
3468 [&triangulation](ttk::SimplexId lid) {
3469 return triangulation.getTriangleGlobalId(lid);
3470 },
3471 [this, &triangulation, offsets](
3472 const ttk::SimplexId id, ttk::SimplexId *vOrd) {
3473 return fillTriangleOrder(id, offsets, triangulation, vOrd);
3474 },
3475 MPIcomm, localThreadNumber);
3476 } else {
3477 computeMaxSaddlePairs<3, 2>(
3478 pairs, criticalSaddles, critSaddlesOrder, criticalExtremas,
3479 critMaxsOrder, triangulation, globalToLocalSaddle, globalToLocalExtrema,
3480 [&triangulation](const SimplexId a, const SimplexId i, SimplexId &r) {
3481 return triangulation.getEdgeStar(a, i, r);
3482 },
3483 [&triangulation](const SimplexId a) {
3484 return triangulation.getEdgeStarNumber(a);
3485 },
3486 [&triangulation](const SimplexId a) {
3487 return triangulation.isEdgeOnBoundary(a);
3488 },
3489 [this, &triangulation, offsets](
3490 const ttk::SimplexId id, ttk::SimplexId *vertsOrder) {
3491 return fillTriangleOrder(id, offsets, triangulation, vertsOrder);
3492 },
3493 [&triangulation](ttk::SimplexId lid) {
3494 return triangulation.getEdgeGlobalId(lid);
3495 },
3496 [this, &triangulation, offsets](
3497 const ttk::SimplexId id, ttk::SimplexId *vOrd) {
3498 return fillEdgeOrder(id, offsets, triangulation, vOrd);
3499 },
3500 MPIcomm, localThreadNumber);
3501 }
3502 }
3503 if(ignoreBoundary) {
3504 std::vector<ttk::SimplexId> locatedId;
3505 // post-process saddle-max pairs: remove the one with the global
3506 // maximum (if it exists) to be (more) compatible with FTM
3507#pragma omp parallel for shared(saddleToPairedMax_, maxToPairedSaddle_) \
3508 num_threads(localThreadNumber)
3509 for(ttk::SimplexId i = 0; i < static_cast<ttk::SimplexId>(pairs.size());
3510 i++) {
3511 if(pairs[i].type < dim - 1) {
3512 continue;
3513 }
3514 const auto it
3515 = std::find(globalMaxId.begin(), globalMaxId.end(), pairs[i].death);
3516 if(it != globalMaxId.end()) {
3517#pragma omp critical
3518 locatedId.emplace_back(i);
3519 }
3520 }
3521 for(ttk::SimplexId i = 0; i < static_cast<ttk::SimplexId>(locatedId.size());
3522 i++) {
3523 ttk::SimplexId id = locatedId[i] - i;
3524 this->saddleToPairedMax_[globalToLocalSaddle[pairs[id].death]] = -1;
3525 this->maxToPairedSaddle_[globalToLocalExtrema[pairs[id].birth]] = -1;
3526 pairs.erase(pairs.begin() + id);
3527 }
3528 }
3529}
3530
3531// get representative of current extremum
3532template <int sizeExtr, int sizeSad>
3533ttk::SimplexId ttk::DiscreteMorseSandwichMPI::getRep(
3534 extremaNode<sizeExtr> extr,
3535 saddleEdge<sizeSad> sv,
3536 bool increasing,
3537 std::vector<extremaNode<sizeExtr>> &extremas,
3538 std::vector<saddleEdge<sizeSad>> &saddles) const {
3539 auto currentNode = extr;
3540 if(currentNode.rep_.extremaId_ == -1) {
3541 return currentNode.lid_;
3542 }
3543 ttk::SimplexId count{0};
3544 auto rep = extremas[extr.rep_.extremaId_];
3545 saddleEdge<sizeSad> s;
3546 while(rep != currentNode && count < overflow_) {
3547 count++;
3548 if(currentNode.rep_.extremaId_ == -1) {
3549 break;
3550 }
3551 if(currentNode.rep_.saddleId_ > -1) {
3552 s = saddles[currentNode.rep_.saddleId_];
3553 if((s.gid_ == sv.gid_) || ((s < sv) == increasing)) {
3554 break;
3555 }
3556 }
3557 if(count >= overflow_) {
3558 printMsg("Overflow reached for " + std::to_string(extr.gid_) + " ("
3559 + std::to_string(extr.rank_) + ") and " + std::to_string(sv.gid_)
3560 + " (" + std::to_string(sv.rank_) + ")");
3561 }
3562 currentNode = rep;
3563 if(currentNode.rep_.extremaId_ == -1) {
3564 break;
3565 }
3566 rep = extremas[currentNode.rep_.extremaId_];
3567 }
3568 return currentNode.lid_;
3569};
3570
3571template <int sizeExtr, int sizeSad>
3572void ttk::DiscreteMorseSandwichMPI::addPair(
3573 const saddleEdge<sizeSad> &sad,
3574 const extremaNode<sizeExtr> &extr,
3575 std::vector<ttk::SimplexId> &saddleToPairedExtrema,
3576 std::vector<ttk::SimplexId> &extremaToPairedSaddle) const {
3577 saddleToPairedExtrema[sad.lid_] = extr.lid_;
3578 extremaToPairedSaddle[extr.lid_] = sad.lid_;
3579};
3580
3581template <int sizeExtr, int sizeSad>
3582void ttk::DiscreteMorseSandwichMPI::addToRecvBuffer(
3583 saddleEdge<sizeSad> &sad,
3584 std::set<messageType<sizeExtr, sizeSad>,
3585 std::function<bool(const messageType<sizeExtr, sizeSad> &,
3586 const messageType<sizeExtr, sizeSad> &)>>
3587 &recomputations,
3588 const std::function<bool(const messageType<sizeExtr, sizeSad> &,
3589 const messageType<sizeExtr, sizeSad> &)>
3590 &cmpMessages,
3591 std::vector<messageType<sizeExtr, sizeSad>> &recvBuffer,
3592 ttk::SimplexId beginVect) const {
3593 messageType<sizeExtr, sizeSad> m
3594 = messageType<sizeExtr, sizeSad>(sad.gid_, sad.vOrder_, sad.rank_);
3595 auto it = std::lower_bound(
3596 recvBuffer.begin() + beginVect, recvBuffer.end(), m, cmpMessages);
3597 if(it == recvBuffer.end() || it->s_ != m.s_) {
3598 recomputations.insert(m);
3599 } else {
3600 if(it->t1_ != -1) {
3601 it->t1_ = -1;
3602 it->t2_ = -1;
3603 for(int i = 0; i < sizeExtr; i++) {
3604 it->t1Order_[i] = 0;
3605 it->t2Order_[i] = 0;
3606 }
3607 }
3608 }
3609};
3610
3611template <int sizeExtr, int sizeSad>
3612void ttk::DiscreteMorseSandwichMPI::removePair(
3613 const saddleEdge<sizeSad> &sad,
3614 const extremaNode<sizeExtr> &extr,
3615 std::vector<ttk::SimplexId> &saddleToPairedExtrema,
3616 std::vector<ttk::SimplexId> &extremaToPairedSaddle) const {
3617 if(extremaToPairedSaddle[extr.lid_] == sad.lid_) {
3618 extremaToPairedSaddle[extr.lid_] = -1;
3619 }
3620 saddleToPairedExtrema[sad.lid_] = -1;
3621};
3622
3623template <int sizeExtr, int sizeSad>
3624void ttk::DiscreteMorseSandwichMPI::tripletsToPersistencePairs(
3625 const SimplexId pairDim,
3626 std::vector<extremaNode<sizeExtr>> &extremas,
3627 std::vector<saddleEdge<sizeSad>> &saddles,
3628 std::vector<ttk::SimplexId> &saddleIds,
3629 std::vector<ttk::SimplexId> &saddleToPairedExtrema,
3630 std::vector<ttk::SimplexId> &extremaToPairedSaddle,
3631 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalSaddle,
3632 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalExtrema,
3633 std::vector<std::vector<char>> ghostPresence,
3634 MPI_Datatype &MPI_MessageType,
3635 bool isFirstTime,
3636 MPI_Comm &MPIcomm,
3637 int localThreadNumber) const {
3638 // The use of localThreadNumber in TTK_PSORT is not detected by the compiler,
3639 // hence the following hack
3640 TTK_FORCE_USE(localThreadNumber);
3641 std::array<std::vector<std::vector<messageType<sizeExtr, sizeSad>>>, 2>
3642 sendBuffer;
3643 sendBuffer[0].resize(
3644 ttk::MPIsize_, std::vector<messageType<sizeExtr, sizeSad>>());
3645 sendBuffer[1].resize(
3646 ttk::MPIsize_, std::vector<messageType<sizeExtr, sizeSad>>());
3647 const bool increasing = (pairDim > 0);
3648 std::vector<std::vector<messageType<sizeExtr, sizeSad>>> recvBuffer(
3649 ttk::MPIsize_, std::vector<messageType<sizeExtr, sizeSad>>());
3650 std::function<bool(const messageType<sizeExtr, sizeSad> &,
3651 const messageType<sizeExtr, sizeSad> &)>
3652 cmpMessages;
3653 if(increasing) {
3654 cmpMessages = [=](const messageType<sizeExtr, sizeSad> &elt0,
3655 const messageType<sizeExtr, sizeSad> &elt1) -> bool {
3656 if(elt0.s_ != elt1.s_) {
3657 for(int i = 0; i < sizeSad; i++) {
3658 if(elt0.sOrder_[i] != elt1.sOrder_[i]) {
3659 return elt0.sOrder_[i] > elt1.sOrder_[i];
3660 }
3661 }
3662 }
3663 return elt0.t1Order_[0] < elt1.t1Order_[0];
3664 };
3665 } else {
3666 cmpMessages = [=](const messageType<sizeExtr, sizeSad> &elt0,
3667 const messageType<sizeExtr, sizeSad> &elt1) -> bool {
3668 if(elt0.s_ != elt1.s_) {
3669 for(int i = 0; i < sizeSad; i++) {
3670 if(elt0.sOrder_[i] != elt1.sOrder_[i]) {
3671 return elt0.sOrder_[i] < elt1.sOrder_[i];
3672 }
3673 }
3674 }
3675 return elt0.t1Order_[0] > elt1.t1Order_[0];
3676 };
3677 }
3678 std::set<messageType<sizeExtr, sizeSad>,
3679 std::function<bool(const messageType<sizeExtr, sizeSad> &,
3680 const messageType<sizeExtr, sizeSad> &)>>
3681 recomputations(cmpMessages);
3682 if(isFirstTime) {
3683 for(const auto &sid : saddleIds) {
3684 if(saddles[sid].gid_ != -1) {
3685 processTriplet<sizeExtr, sizeSad>(
3686 saddles[sid], saddleToPairedExtrema, extremaToPairedSaddle, saddles,
3687 extremas, increasing, ghostPresence, sendBuffer[0], recomputations,
3688 cmpMessages, recvBuffer[ttk::MPIrank_], 0);
3689 }
3690 }
3691 } else {
3692 for(const auto &sid : saddleIds) {
3693 if(saddleToPairedExtrema[sid] == -2 && saddles[sid].gid_ != -1) {
3694 processTriplet<sizeExtr, sizeSad>(
3695 saddles[sid], saddleToPairedExtrema, extremaToPairedSaddle, saddles,
3696 extremas, increasing, ghostPresence, sendBuffer[0], recomputations,
3697 cmpMessages, recvBuffer[ttk::MPIrank_], 0);
3698 }
3699 }
3700 }
3701
3702 const auto equalSadMin
3703 = [=](const messageType<sizeExtr, sizeSad> &elt0,
3704 const messageType<sizeExtr, sizeSad> &elt1) -> bool {
3705 return (elt0.s_ == elt1.s_) && (elt0.t1_ == elt1.t1_)
3706 && (elt0.t2_ == elt1.t2_) && (elt0.s1_ == elt1.s1_)
3707 && (elt0.s2_ == elt1.s2_)
3708 && (elt0.hasBeenModified_ == elt1.hasBeenModified_);
3709 };
3710 // Receive elements
3711 ttk::SimplexId hasSentMessages{10};
3712 MPI_Datatype MPI_SimplexId = getMPIType(hasSentMessages);
3713 char currentSendBuffer{0};
3714 while(hasSentMessages > 0) {
3715 ttk::SimplexId localSentMessageNumber{0};
3716 std::vector<MPI_Request> sendRequests(ttk::MPIsize_ - 1);
3717 std::vector<MPI_Request> recvRequests(ttk::MPIsize_ - 1);
3718 std::vector<MPI_Status> sendStatus(ttk::MPIsize_ - 1);
3719 std::vector<MPI_Status> recvStatus(ttk::MPIsize_ - 1);
3720 std::vector<ttk::SimplexId> sendMessageSize(ttk::MPIsize_, 0);
3721 std::vector<ttk::SimplexId> recvMessageSize(ttk::MPIsize_, 0);
3722 std::vector<int> recvCompleted(ttk::MPIsize_ - 1, 0);
3723 std::vector<int> sendCompleted(ttk::MPIsize_ - 1, 0);
3724 int recvPerformedCount = 0;
3725 int recvPerformedCountTotal = 0;
3726 for(int i = 0; i < ttk::MPIsize_; i++) {
3727 // Send size of Sendbuffer
3728 if(i != ttk::MPIrank_) {
3729 sendMessageSize[i] = sendBuffer[currentSendBuffer][i].size();
3730 localSentMessageNumber += sendMessageSize[i];
3731 }
3732 }
3733 MPI_Alltoall(sendMessageSize.data(), 1, MPI_SimplexId,
3734 recvMessageSize.data(), 1, MPI_SimplexId, MPIcomm);
3735 std::vector<MPI_Request> sendRequestsData(ttk::MPIsize_ - 1);
3736 std::vector<MPI_Request> recvRequestsData(ttk::MPIsize_ - 1);
3737 std::vector<MPI_Status> recvStatusData(ttk::MPIsize_ - 1);
3738 int recvCount = 0;
3739 int sendCount = 0;
3740 int r = 0;
3741 for(int i = 0; i < ttk::MPIsize_; i++) {
3742 if((sendMessageSize[i] > 0)) {
3743 MPI_Isend(sendBuffer[currentSendBuffer][i].data(), sendMessageSize[i],
3744 MPI_MessageType, i, 1, MPIcomm, &sendRequestsData[sendCount]);
3745 sendCount++;
3746 }
3747 if((recvMessageSize[i] > 0)) {
3748 recvBuffer[i].resize(recvMessageSize[i]);
3749 MPI_Irecv(recvBuffer[i].data(), recvMessageSize[i], MPI_MessageType, i,
3750 1, MPIcomm, &recvRequestsData[recvCount]);
3751 recvCount++;
3752 }
3753 }
3754 recvPerformedCountTotal = 0;
3755 while(recvPerformedCountTotal < recvCount) {
3756 MPI_Waitsome(recvCount, recvRequestsData.data(), &recvPerformedCount,
3757 recvCompleted.data(), recvStatusData.data());
3758
3759 if(recvPerformedCount > 0) {
3760 for(int i = 0; i < recvPerformedCount; i++) {
3761 r = recvStatusData[i].MPI_SOURCE;
3762 TTK_PSORT(localThreadNumber, recvBuffer[r].begin(),
3763 recvBuffer[r].end(), cmpMessages);
3764 }
3765 recvPerformedCountTotal += recvPerformedCount;
3766 }
3767 }
3768 MPI_Waitall(sendCount, sendRequestsData.data(), MPI_STATUSES_IGNORE);
3769 // Stop condition computation
3770 for(int i = 0; i < ttk::MPIsize_; i++) {
3771 ttk::SimplexId sid{-1};
3772 size_t j{0};
3773 // ttk::SimplexId recomp{0};
3774 recomputations.clear();
3775 while(j < recvBuffer[i].size()) {
3776 if((j == 0 || !equalSadMin(recvBuffer[i][j], recvBuffer[i][j - 1]))) {
3777 messageType<sizeExtr, sizeSad> elt;
3778 if(!recomputations.empty()) {
3779 elt = (*recomputations.begin());
3780 if(cmpMessages(elt, recvBuffer[i][j])) {
3781 recomputations.erase(recomputations.begin());
3782 } else {
3783 elt = recvBuffer[i][j];
3784 j++;
3785 }
3786 } else {
3787 elt = recvBuffer[i][j];
3788 j++;
3789 }
3790 // Condition sur le premier élément de la liste
3791 if(elt.s_ != sid) {
3792 receiveElement<sizeExtr, sizeSad>(
3793 elt, globalToLocalSaddle, globalToLocalExtrema, saddles, extremas,
3794 extremaToPairedSaddle, saddleToPairedExtrema,
3795 sendBuffer[1 - currentSendBuffer], ghostPresence,
3796 static_cast<char>(i), increasing, recomputations, cmpMessages,
3797 recvBuffer[i], j + 1);
3798 if(elt.t1_ == -1) {
3799 sid = elt.s_;
3800 }
3801 }
3802 } else {
3803 j++;
3804 }
3805 }
3806 auto it = recomputations.begin();
3807 while(it != recomputations.end()) {
3808 receiveElement<sizeExtr, sizeSad>(
3809 (*it), globalToLocalSaddle, globalToLocalExtrema, saddles, extremas,
3810 extremaToPairedSaddle, saddleToPairedExtrema,
3811 sendBuffer[1 - currentSendBuffer], ghostPresence,
3812 static_cast<char>(i), increasing, recomputations, cmpMessages,
3813 recvBuffer[i], recvBuffer[i].size());
3814 it++;
3815 }
3816 recomputations.clear();
3817 }
3818
3819 MPI_Allreduce(&localSentMessageNumber, &hasSentMessages, 1, MPI_SimplexId,
3820 MPI_SUM, MPIcomm);
3821 if(hasSentMessages) {
3822 for(int i = 0; i < ttk::MPIsize_; i++) {
3823 sendBuffer[currentSendBuffer][i].clear();
3824 recvBuffer[i].clear();
3825 }
3826 currentSendBuffer = 1 - currentSendBuffer;
3827 }
3828 }
3829}
3830
3831template <int sizeExtr, int sizeSad>
3832void ttk::DiscreteMorseSandwichMPI::extractPairs(
3833 std::vector<PersistencePair> &pairs,
3834 std::vector<extremaNode<sizeExtr>> &extremas,
3835 std::vector<saddleEdge<sizeSad>> &saddles,
3836 std::vector<ttk::SimplexId> &saddleToPairedExtrema,
3837 bool increasing,
3838 const int pairDim,
3839 int localThreadNumber) const {
3840 ttk::SimplexId saddleNumber = saddleToPairedExtrema.size();
3841
3842#ifdef TTK_ENABLE_OPENMP
3843#pragma omp declare reduction (merge : std::vector<PersistencePair> : omp_out.insert(omp_out.end(), omp_in.begin(), omp_in.end()))
3844#pragma omp parallel for reduction(merge \
3845 : pairs) schedule(static) \
3846 num_threads(localThreadNumber)
3847#endif
3848 for(ttk::SimplexId i = 0; i < saddleNumber; i++) {
3849 if(saddleToPairedExtrema[i] > -1 && saddles[i].rank_ == ttk::MPIrank_) {
3850 if(increasing) {
3851 if(saddles[i].rank_ == ttk::MPIrank_) {
3852 pairs.emplace_back(
3853 saddles[i].gid_, extremas[saddleToPairedExtrema[i]].gid_, pairDim);
3854 }
3855 } else {
3856 if(saddles[i].rank_ == ttk::MPIrank_) {
3857 pairs.emplace_back(
3858 extremas[saddleToPairedExtrema[i]].gid_, saddles[i].gid_, pairDim);
3859 }
3860 }
3861 }
3862 }
3863}
3864
3865template <int sizeExtr, int sizeSad>
3866ttk::SimplexId ttk::DiscreteMorseSandwichMPI::computePairNumbers(
3867 std::vector<saddleEdge<sizeSad>> &saddles,
3868 std::vector<ttk::SimplexId> &saddleToPairedExtrema,
3869 int localThreadNumber) const {
3870 ttk::SimplexId saddleNumber = saddleToPairedExtrema.size();
3871 ttk::SimplexId computedSaddleNumber{0};
3872#ifdef TTK_ENABLE_OPENMP
3873#pragma omp parallel for reduction(+ : computedSaddleNumber) schedule(static) num_threads(localThreadNumber)
3874#endif
3875 for(ttk::SimplexId i = 0; i < saddleNumber; i++) {
3876 if(saddleToPairedExtrema[i] > -1 && saddles[i].rank_ == ttk::MPIrank_) {
3877 computedSaddleNumber++;
3878 }
3879 }
3880 return computedSaddleNumber;
3881}
3882
3883template <int sizeSad>
3884struct ttk::DiscreteMorseSandwichMPI::saddleEdge<sizeSad>
3885 ttk::DiscreteMorseSandwichMPI::addSaddle(
3886 saddleEdge<sizeSad> s,
3887 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalSaddle,
3888 std::vector<saddleEdge<sizeSad>> &saddles,
3889 std::vector<ttk::SimplexId> &saddleToPairedExtrema) const {
3890 if(s.lid_ == -1 && s.gid_ > -1) {
3891 globalToLocalSaddle[s.gid_] = saddles.size();
3892 s.lid_ = saddles.size();
3893 saddles.emplace_back(s);
3894 s = saddles.back();
3895 saddleToPairedExtrema.emplace_back(static_cast<ttk::SimplexId>(-1));
3896 }
3897 return s;
3898};
3899
3900template <int sizeExtr, int sizeSad>
3901ttk::SimplexId ttk::DiscreteMorseSandwichMPI::getUpdatedT1(
3902 const ttk::SimplexId extremaGid,
3903 messageType<sizeExtr, sizeSad> &elt,
3904 saddleEdge<sizeSad> s,
3905 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalExtrema,
3906 std::vector<extremaNode<sizeExtr>> &extremas,
3907 std::vector<saddleEdge<sizeSad>> &saddles,
3908 bool increasing) const {
3909 ttk::SimplexId lid{-1};
3910 if(extremaGid > -1) {
3911 auto it = globalToLocalExtrema.find(extremaGid);
3912 if(it != globalToLocalExtrema.end()) {
3913 lid = it->second;
3914 auto e{extremas[lid]};
3915 ttk::SimplexId repLid
3916 = getRep(extremas[lid], s, increasing, extremas, saddles);
3917 if(extremas[repLid].gid_ != e.gid_) {
3918 lid = repLid;
3919 elt.t1_ = extremas[repLid].gid_;
3920 for(int i = 0; i < sizeExtr; i++) {
3921 elt.t1Order_[i] = extremas[repLid].vOrder_[i];
3922 }
3923 elt.t1Rank_ = extremas[repLid].rank_;
3924 elt.hasBeenModified_ = 1;
3925 saddleEdge<sizeSad> s1;
3926 if(extremas[repLid].rep_.saddleId_ > -1) {
3927 s1 = saddles[extremas[repLid].rep_.saddleId_];
3928 }
3929 elt.s1Rank_ = s1.rank_;
3930 elt.s1_ = s1.gid_;
3931 for(int i = 0; i < sizeSad; i++) {
3932 elt.s1Order_[i] = s1.vOrder_[i];
3933 }
3934 } else {
3935 // Update s1 anyway
3936 if(elt.t1_ != -1) {
3937 saddleEdge<sizeSad> s1Loc;
3938 if(e.rep_.saddleId_ > -1) {
3939 s1Loc = saddles[e.rep_.saddleId_];
3940 if((elt.s1_ == -1 && s1Loc.gid_ != -1)
3941 || (compareArray(elt.s1Order_, s1Loc.vOrder_, sizeSad)
3942 == increasing)) {
3943 elt.s1_ = s1Loc.gid_;
3944 elt.s1Rank_ = s1Loc.rank_;
3945 for(int i = 0; i < sizeSad; i++) {
3946 elt.s1Order_[i] = s1Loc.vOrder_[i];
3947 }
3948 }
3949 }
3950 }
3951 }
3952 }
3953 }
3954 return lid;
3955};
3956
3957template <int sizeExtr, int sizeSad>
3958ttk::SimplexId ttk::DiscreteMorseSandwichMPI::getUpdatedT2(
3959 const ttk::SimplexId extremaGid,
3960 messageType<sizeExtr, sizeSad> &elt,
3961 saddleEdge<sizeSad> s,
3962 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalExtrema,
3963 std::vector<extremaNode<sizeExtr>> &extremas,
3964 std::vector<saddleEdge<sizeSad>> &saddles,
3965 bool increasing) const {
3966 ttk::SimplexId lid{-1};
3967 if(extremaGid > -1) {
3968 auto it = globalToLocalExtrema.find(extremaGid);
3969 if(it != globalToLocalExtrema.end()) {
3970 lid = it->second;
3971 auto e{extremas[lid]};
3972 ttk::SimplexId repLid = getRep(e, s, increasing, extremas, saddles);
3973 if(extremas[repLid].gid_ != e.gid_) {
3974 lid = repLid;
3975 elt.t2_ = extremas[repLid].gid_;
3976 for(int i = 0; i < sizeExtr; i++) {
3977 elt.t2Order_[i] = extremas[repLid].vOrder_[i];
3978 }
3979 elt.t2Rank_ = extremas[repLid].rank_;
3980 elt.hasBeenModified_ = 1;
3981 saddleEdge<sizeSad> s2;
3982 if(extremas[repLid].rep_.saddleId_ > -1) {
3983 s2 = saddles[extremas[repLid].rep_.saddleId_];
3984 }
3985 elt.s2Rank_ = s2.rank_;
3986 elt.s2_ = s2.gid_;
3987 for(int i = 0; i < sizeSad; i++) {
3988 elt.s2Order_[i] = s2.vOrder_[i];
3989 }
3990 } else {
3991 // Update s2 anyway
3992 if(elt.t2_ != -1) {
3993 saddleEdge<sizeSad> s2Loc;
3994 if(e.rep_.saddleId_ > -1) {
3995 s2Loc = saddles[e.rep_.saddleId_];
3996 if((elt.s2_ == -1 && s2Loc.gid_ != -1)
3997 || compareArray(elt.s2Order_, s2Loc.vOrder_, sizeSad)
3998 == increasing) {
3999 elt.s2_ = s2Loc.gid_;
4000 elt.s2Rank_ = s2Loc.rank_;
4001 for(int i = 0; i < sizeSad; i++) {
4002 elt.s2Order_[i] = s2Loc.vOrder_[i];
4003 }
4004 // elt.hasBeenModified_ = 1;
4005 }
4006 }
4007 }
4008 }
4009 }
4010 }
4011 return lid;
4012};
4013
4014template <int sizeExtr, int sizeSad>
4015void ttk::DiscreteMorseSandwichMPI::swapT1T2(
4016 messageType<sizeExtr, sizeSad> &elt,
4017 ttk::SimplexId &t1Lid,
4018 ttk::SimplexId &t2Lid,
4019 bool increasing) const {
4020 if(elt.t1_ != elt.t2_ && elt.t2_ != -1) {
4021 bool pairedR2 = elt.s2_ != -1 && elt.s2_ != elt.s_;
4022 bool isR2Invalid
4023 = ((elt.s2_ != -1)
4024 && (compareArray(elt.s2Order_, elt.sOrder_, sizeSad) == increasing));
4025 if(isR2Invalid)
4026 pairedR2 = false;
4027 bool pairedR1 = elt.s1_ != -1 && elt.s1_ != elt.s_;
4028 bool isR1Invalid
4029 = ((elt.s1_ != -1)
4030 && (compareArray(elt.s1Order_, elt.sOrder_, sizeSad) == increasing));
4031 if(isR1Invalid)
4032 pairedR1 = false;
4033 if((((compareArray(elt.t2Order_, elt.t1Order_, sizeExtr)) == increasing)
4034 || pairedR1)
4035 && !pairedR2) {
4036 std::swap(t1Lid, t2Lid);
4037 std::swap(elt.t1_, elt.t2_);
4038 std::swap(elt.t1Order_, elt.t2Order_);
4039 std::swap(elt.s1Order_, elt.s2Order_);
4040 std::swap(elt.t1Rank_, elt.t2Rank_);
4041 std::swap(elt.s2Rank_, elt.s1Rank_);
4042 std::swap(elt.s1_, elt.s2_);
4043 elt.hasBeenModified_ = 1;
4044 }
4045 }
4046};
4047
4048template <int sizeExtr>
4049void ttk::DiscreteMorseSandwichMPI::addLocalExtrema(
4050 ttk::SimplexId &lid,
4051 const ttk::SimplexId gid,
4052 char rank,
4053 ttk::SimplexId *vOrder,
4054 std::vector<extremaNode<sizeExtr>> &extremas,
4055 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalExtrema,
4056 std::vector<ttk::SimplexId> &extremaToPairedSaddle) const {
4057 if(lid == -1) {
4058 lid = extremas.size();
4059 extremaNode n
4060 = extremaNode<sizeExtr>(gid, lid, -1, Rep{-1, -2}, rank, vOrder);
4061 extremas.emplace_back(n);
4062 extremaToPairedSaddle.emplace_back(-1);
4063 globalToLocalExtrema[gid] = lid;
4064 }
4065};
4066
4067template <int sizeExtr, int sizeSad>
4068void ttk::DiscreteMorseSandwichMPI::receiveElement(
4069 messageType<sizeExtr, sizeSad> element,
4070 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalSaddle,
4071 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalExtrema,
4072 std::vector<saddleEdge<sizeSad>> &saddles,
4073 std::vector<extremaNode<sizeExtr>> &extremas,
4074 std::vector<ttk::SimplexId> &extremaToPairedSaddle,
4075 std::vector<ttk::SimplexId> &saddleToPairedExtrema,
4076 std::vector<std::vector<messageType<sizeExtr, sizeSad>>> &sendBuffer,
4077 std::vector<std::vector<char>> &ghostPresence,
4078 char sender,
4079 bool increasing,
4080 std::set<messageType<sizeExtr, sizeSad>,
4081 std::function<bool(const messageType<sizeExtr, sizeSad> &,
4082 const messageType<sizeExtr, sizeSad> &)>>
4083 &recomputations,
4084 const std::function<bool(const messageType<sizeExtr, sizeSad> &,
4085 const messageType<sizeExtr, sizeSad> &)>
4086 &cmpMessages,
4087 std::vector<messageType<sizeExtr, sizeSad>> &recvBuffer,
4088 ttk::SimplexId beginVect) const {
4089 struct saddleEdge<sizeSad> s;
4090 auto it = globalToLocalSaddle.find(element.s_);
4091 if(it != globalToLocalSaddle.end()) {
4092 s = saddles[it->second];
4093 } else {
4094 s = saddleEdge<sizeSad>(element.s_, -1, element.sOrder_, element.sRank_);
4095 }
4096 if(s.rank_ == ttk::MPIrank_ && element.t1_ == -1 && element.t2_ == -1) {
4097 if(saddleToPairedExtrema[s.lid_] > -1) {
4098 if(extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_ != -1) {
4099 extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_
4100 = extremas[saddleToPairedExtrema[s.lid_]].lid_;
4101 extremas[saddleToPairedExtrema[s.lid_]].rep_.saddleId_ = -1;
4102 }
4103 removePair(saddles[s.lid_], extremas[saddleToPairedExtrema[s.lid_]],
4104 saddleToPairedExtrema, extremaToPairedSaddle);
4105 }
4106 processTriplet<sizeExtr>(
4107 saddles[s.lid_], saddleToPairedExtrema, extremaToPairedSaddle, saddles,
4108 extremas, increasing, ghostPresence, sendBuffer, recomputations,
4109 cmpMessages, recvBuffer, beginVect);
4110 return;
4111 }
4112 ttk::SimplexId t1Lid
4113 = getUpdatedT1(element.t1_, element, s, globalToLocalExtrema, extremas,
4114 saddles, increasing);
4115 ttk::SimplexId t2Lid
4116 = getUpdatedT2(element.t2_, element, s, globalToLocalExtrema, extremas,
4117 saddles, increasing);
4118
4119 swapT1T2(element, t1Lid, t2Lid, increasing);
4120 if(element.sRank_ != ttk::MPIrank_) {
4121 // Send now if the extrema rep is -1
4122 if(t1Lid == -1 || extremas[t1Lid].rep_.extremaId_ == -1) {
4123 element.hasBeenModified_ = 1;
4124 sendBuffer[element.t1Rank_].emplace_back(element);
4125 } else {
4126 // Send back to owner of s if hasBeenModified
4127 if(element.hasBeenModified_) {
4128 element.hasBeenModified_ = 0;
4129 sendBuffer[s.rank_].emplace_back(element);
4130 element.hasBeenModified_ = 1;
4131 }
4132 // If t1 present locally
4133 // Who is t1 paired with?
4134 ttk::SimplexId ls1Id = extremaToPairedSaddle[t1Lid];
4135 if(element.t1_ == element.t2_) {
4136 if(s.lid_ > -1 && saddleToPairedExtrema[s.lid_] > -1) {
4137 extremaToPairedSaddle[saddleToPairedExtrema[s.lid_]] = -1;
4138 extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_
4139 = extremas[saddleToPairedExtrema[s.lid_]].lid_;
4140 extremas[saddleToPairedExtrema[s.lid_]].rep_.saddleId_ = -1;
4141 if((element.t1Rank_ != ttk::MPIrank_)
4142 && (element.t1Rank_ != sender)) {
4143 sendBuffer[element.t1Rank_].emplace_back(element);
4144 } else {
4145 if(extremas[saddleToPairedExtrema[s.lid_]].rank_ == ttk::MPIrank_
4146 && ghostPresence[extremas[saddleToPairedExtrema[s.lid_]].lid_]
4147 .size()
4148 > 1) {
4149 for(const auto rank :
4150 ghostPresence[extremas[saddleToPairedExtrema[s.lid_]].lid_]) {
4151 if((rank != ttk::MPIrank_)
4152 && ((rank == sender && element.hasBeenModified_)
4153 || rank != sender)) {
4154 sendBuffer[rank].emplace_back(element);
4155 }
4156 }
4157 }
4158 }
4159 saddleToPairedExtrema[s.lid_] = static_cast<ttk::SimplexId>(-2);
4160 }
4161 } else {
4162 if(ls1Id != -1 && ls1Id != s.lid_) {
4163 // To s1:
4164 auto ls1{saddles[ls1Id]};
4165 // s1 is owned by the process
4166 if((ls1.gid_ != s.gid_) && ((ls1 < s) == increasing)) {
4167 // s1 is incorrect
4168 ttk::SimplexId oldSaddleId = extremas[t1Lid].rep_.saddleId_;
4169 removePair(ls1, extremas[t1Lid], saddleToPairedExtrema,
4170 extremaToPairedSaddle);
4171 s = addSaddle(
4172 s, globalToLocalSaddle, saddles, saddleToPairedExtrema);
4173 if(saddleToPairedExtrema[s.lid_] > -1) {
4174 if(extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_ != -1
4175 && extremas[saddleToPairedExtrema[s.lid_]].rep_.saddleId_
4176 == s.lid_) {
4177 extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_
4178 = extremas[saddleToPairedExtrema[s.lid_]].lid_;
4179 extremas[saddleToPairedExtrema[s.lid_]].rep_.saddleId_ = -1;
4180 }
4181 removePair(s, extremas[saddleToPairedExtrema[s.lid_]],
4182 saddleToPairedExtrema, extremaToPairedSaddle);
4183 }
4184 if(element.t2_ > -1) {
4185 addLocalExtrema(t2Lid, element.t2_, element.t2Rank_,
4186 element.t2Order_, extremas, globalToLocalExtrema,
4187 extremaToPairedSaddle);
4188 }
4189 addPair(
4190 s, extremas[t1Lid], saddleToPairedExtrema, extremaToPairedSaddle);
4191 if(element.t2_ > -1) {
4192 extremas[t1Lid].rep_.extremaId_ = t2Lid;
4193 } else {
4194 extremas[t1Lid].rep_.extremaId_ = t1Lid;
4195 }
4196 extremas[t1Lid].rep_.saddleId_ = s.lid_;
4197 // If t1 owned but with !ghostPresence.empty() -> send to
4198 // ghostPresence
4199 saddleEdge<sizeSad> lst1;
4200 if(extremas[t1Lid].rep_.saddleId_ > -1) {
4201 lst1 = saddles[extremas[t1Lid].rep_.saddleId_];
4202 } else {
4203 if(element.s1_ != -1) {
4204 lst1 = saddleEdge<sizeSad>(
4205 element.s1_, element.s1Order_, element.s1Rank_);
4206 }
4207 }
4208 if(element.t2_ > -1) {
4209 saddleEdge<sizeSad> lst2;
4210 if(extremas[t2Lid].rep_.saddleId_ > -1) {
4211 lst2 = saddles[extremas[t2Lid].rep_.saddleId_];
4212 } else {
4213 if(element.s2_ != -1) {
4214 lst2 = saddleEdge<sizeSad>(
4215 element.s2_, element.s2Order_, element.s2Rank_);
4216 }
4217 }
4218 storeMessageToSend<sizeExtr, sizeSad>(
4219 ghostPresence, sendBuffer, s, lst1, lst2, extremas[t1Lid],
4220 extremas[t2Lid], sender, element.hasBeenModified_);
4221 } else {
4222 storeMessageToSend<sizeExtr, sizeSad>(
4223 ghostPresence, sendBuffer, s, lst1, extremas[t1Lid], sender,
4224 element.hasBeenModified_);
4225 }
4226 // Re-compute for incorrect s1
4227 if(saddles[ls1.lid_].rank_ == ttk::MPIrank_) {
4228 addToRecvBuffer(
4229 ls1, recomputations, cmpMessages, recvBuffer, beginVect);
4230 } else {
4231 // Send s1 for re-computation
4232 storeRerunToSend<sizeExtr>(sendBuffer, saddles[oldSaddleId]);
4233 }
4234 }
4235 } else {
4236 bool changesApplied{true};
4237 // Update rep + send update to other processes
4238 s = addSaddle(s, globalToLocalSaddle, saddles, saddleToPairedExtrema);
4239 if(saddleToPairedExtrema[s.lid_] > -1) {
4240 if((t2Lid > -1)
4241 && (extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_
4242 == t2Lid)
4243 && (saddleToPairedExtrema[s.lid_] == t1Lid)) {
4244 changesApplied = false;
4245 }
4246 if(extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_ != -1
4247 && extremas[saddleToPairedExtrema[s.lid_]].rep_.saddleId_
4248 == s.lid_) {
4249 extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_
4250 = extremas[saddleToPairedExtrema[s.lid_]].lid_;
4251 extremas[saddleToPairedExtrema[s.lid_]].rep_.saddleId_ = -1;
4252 }
4253 removePair(s, extremas[saddleToPairedExtrema[s.lid_]],
4254 saddleToPairedExtrema, extremaToPairedSaddle);
4255 }
4256 if(element.t2_ > -1) {
4257 addLocalExtrema(t2Lid, element.t2_, element.t2Rank_,
4258 element.t2Order_, extremas, globalToLocalExtrema,
4259 extremaToPairedSaddle);
4260 }
4261 addPair(
4262 s, extremas[t1Lid], saddleToPairedExtrema, extremaToPairedSaddle);
4263 if(extremas[t1Lid].rep_.extremaId_ != -1) {
4264 if(element.t2_ > -1) {
4265 extremas[t1Lid].rep_.extremaId_ = t2Lid;
4266 } else {
4267 extremas[t1Lid].rep_.extremaId_ = t1Lid;
4268 }
4269 extremas[t1Lid].rep_.saddleId_ = s.lid_;
4270 }
4271 if(changesApplied) {
4272 // If t1 owned but with !ghostPresence.empty() -> send to
4273 // ghostPresence
4274 saddleEdge<sizeSad> lst1;
4275 if(extremas[t1Lid].rep_.saddleId_ > -1) {
4276 lst1 = saddles[extremas[t1Lid].rep_.saddleId_];
4277 } else {
4278 if(element.s1_ != -1) {
4279 lst1 = saddleEdge<sizeSad>(
4280 element.s1_, element.s1Order_, element.s1Rank_);
4281 }
4282 }
4283 if(element.t2_ > -1) {
4284 saddleEdge<sizeSad> lst2;
4285 if(extremas[t2Lid].rep_.saddleId_ > -1) {
4286 lst2 = saddles[extremas[t2Lid].rep_.saddleId_];
4287 } else {
4288 if(element.s2_ != -1) {
4289 lst2 = saddleEdge<sizeSad>(
4290 element.s2_, element.s2Order_, element.s2Rank_);
4291 }
4292 }
4293 storeMessageToSend<sizeExtr, sizeSad>(
4294 ghostPresence, sendBuffer, s, lst1, lst2, extremas[t1Lid],
4295 extremas[t2Lid], sender, element.hasBeenModified_);
4296 } else {
4297 storeMessageToSend<sizeExtr, sizeSad>(
4298 ghostPresence, sendBuffer, s, lst1, extremas[t1Lid], sender,
4299 element.hasBeenModified_);
4300 }
4301 }
4302 }
4303 }
4304 }
4305 } else {
4306 if(((t1Lid != -1 && extremas[t1Lid].rep_.extremaId_ == -1) || t1Lid == -1)
4307 && element.hasBeenModified_ == 1) {
4308 sendBuffer[element.t1Rank_].emplace_back(element);
4309 } else {
4310 if(element.t1_ == element.t2_) {
4311 if(saddleToPairedExtrema[s.lid_] > -1) {
4312 extremaToPairedSaddle[saddleToPairedExtrema[s.lid_]] = -1;
4313 if(extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_ != -1) {
4314 extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_
4315 = extremas[saddleToPairedExtrema[s.lid_]].lid_;
4316 extremas[saddleToPairedExtrema[s.lid_]].rep_.saddleId_ = -1;
4317 }
4318 if(extremas[saddleToPairedExtrema[s.lid_]].rank_ == ttk::MPIrank_
4319 && ghostPresence[extremas[saddleToPairedExtrema[s.lid_]].lid_]
4320 .size()
4321 > 1) {
4322 for(const auto rank :
4323 ghostPresence[extremas[saddleToPairedExtrema[s.lid_]].lid_]) {
4324 if((rank != ttk::MPIrank_)
4325 && ((rank == sender && element.hasBeenModified_)
4326 || rank != sender)) {
4327 sendBuffer[rank].emplace_back(element);
4328 }
4329 }
4330 }
4331 }
4332 saddleToPairedExtrema[s.lid_] = static_cast<ttk::SimplexId>(-2);
4333 } else {
4334 if((element.s1_ != -1) && (element.s1_ != element.s_)
4335 && (!(compareArray(element.s1Order_, element.sOrder_, sizeSad)
4336 == increasing))) {
4337 if(saddleToPairedExtrema[s.lid_] > -1) {
4338 extremaToPairedSaddle[saddleToPairedExtrema[s.lid_]] = -1;
4339 if(extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_ != -1) {
4340 extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_
4341 = extremas[saddleToPairedExtrema[s.lid_]].lid_;
4342 extremas[saddleToPairedExtrema[s.lid_]].rep_.saddleId_ = -1;
4343 }
4344 saddleToPairedExtrema[s.lid_] = static_cast<ttk::SimplexId>(-2);
4345 }
4346 } else {
4347 addLocalExtrema(t1Lid, element.t1_, element.t1Rank_, element.t1Order_,
4348 extremas, globalToLocalExtrema,
4349 extremaToPairedSaddle);
4350
4351 bool isPairedWithWrongExtrema
4352 = ((saddleToPairedExtrema[s.lid_] > -1)
4353 && (saddleToPairedExtrema[s.lid_] != t1Lid));
4354 if(((saddleToPairedExtrema[s.lid_] > -1)
4355 && (saddleToPairedExtrema[s.lid_] == t1Lid))
4356 && ((extremaToPairedSaddle[t1Lid] > -1)
4357 && (extremaToPairedSaddle[t1Lid] == s.lid_))) {
4358 if(extremas[t1Lid].rep_.extremaId_ != -1
4359 && extremas[t1Lid].rep_.extremaId_ != t2Lid) {
4360 if(element.t2_ > -1) {
4361 addLocalExtrema(t2Lid, element.t2_, element.t2Rank_,
4362 element.t2Order_, extremas,
4363 globalToLocalExtrema, extremaToPairedSaddle);
4364 extremas[t1Lid].rep_.extremaId_ = t2Lid;
4365 } else {
4366 extremas[t1Lid].rep_.extremaId_ = t1Lid;
4367 }
4368 }
4369 }
4370 if(isPairedWithWrongExtrema) {
4371 if(extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_ != -1) {
4372 extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_
4373 = extremas[saddleToPairedExtrema[s.lid_]].lid_;
4374 extremas[saddleToPairedExtrema[s.lid_]].rep_.saddleId_ = -1;
4375 }
4376 auto extr{extremas[saddleToPairedExtrema[s.lid_]]};
4377 removePair(s, extremas[saddleToPairedExtrema[s.lid_]],
4378 saddleToPairedExtrema, extremaToPairedSaddle);
4379 if(extr.rank_ == ttk::MPIrank_
4380 && ghostPresence[extr.lid_].size() > 1) {
4381 for(const auto rank : ghostPresence[extr.lid_]) {
4382 if((rank != ttk::MPIrank_)
4383 && ((rank == sender && element.hasBeenModified_)
4384 || rank != sender)) {
4385 sendBuffer[rank].emplace_back(element);
4386 }
4387 }
4388 }
4389 }
4390 // If extrema is not paired at all
4391 if(extremaToPairedSaddle[t1Lid] < 0) {
4392 addPair(
4393 s, extremas[t1Lid], saddleToPairedExtrema, extremaToPairedSaddle);
4394 ttk::SimplexId oldSaddle = extremas[t1Lid].rep_.saddleId_;
4395 if(element.t2_ > -1) {
4396 addLocalExtrema(t2Lid, element.t2_, element.t2Rank_,
4397 element.t2Order_, extremas, globalToLocalExtrema,
4398 extremaToPairedSaddle);
4399 }
4400 if(extremas[t1Lid].rep_.extremaId_ != -1) {
4401 if(element.t2_ > -1) {
4402 extremas[t1Lid].rep_.extremaId_ = t2Lid;
4403 } else {
4404 extremas[t1Lid].rep_.extremaId_ = t1Lid;
4405 }
4406 extremas[t1Lid].rep_.saddleId_ = s.lid_;
4407 }
4408 if(extremas[t1Lid].rank_ != ttk::MPIrank_
4409 || (ghostPresence[extremas[t1Lid].lid_].size() > 1)) {
4410 saddleEdge<sizeSad> ls1;
4411 saddleEdge<sizeSad> ls2;
4412 if(oldSaddle > -1) {
4413 ls1 = saddles[oldSaddle];
4414 }
4415 if(element.t2_ > -1) {
4416 if(extremas[t2Lid].rep_.saddleId_ > -1) {
4417 ls2 = saddles[extremas[t2Lid].rep_.saddleId_];
4418 } else {
4419 if(element.s2_ != -1) {
4420 ls2 = saddleEdge<sizeSad>(
4421 element.s2_, element.s2Order_, element.s2Rank_);
4422 }
4423 }
4424 storeMessageToSend<sizeExtr, sizeSad>(
4425 ghostPresence, sendBuffer, s, ls1, ls2, extremas[t1Lid],
4426 extremas[t2Lid]);
4427 } else {
4428 storeMessageToSend<sizeExtr, sizeSad>(
4429 ghostPresence, sendBuffer, s, ls1, extremas[t1Lid]);
4430 }
4431 }
4432 }
4433 bool isPairedWithWrongSaddle
4434 = ((extremaToPairedSaddle[t1Lid] > -1)
4435 && (extremaToPairedSaddle[t1Lid] != s.lid_));
4436 struct saddleEdge<sizeSad> sCurrent;
4437 if(extremaToPairedSaddle[t1Lid] > -1) {
4438 sCurrent = saddles[extremaToPairedSaddle[t1Lid]];
4439 }
4440 // extrema paired to wrong saddle
4441 if(isPairedWithWrongSaddle && ((sCurrent < s) == increasing)) {
4442 // If the paired extrema is the global min, re-computation is
4443 // triggered The new saddle is right, the old one is false
4444 removePair(sCurrent, extremas[t1Lid], saddleToPairedExtrema,
4445 extremaToPairedSaddle);
4446 }
4447 if(isPairedWithWrongSaddle) {
4448 if((sCurrent < s) == increasing) {
4449 addPair(s, extremas[t1Lid], saddleToPairedExtrema,
4450 extremaToPairedSaddle);
4451 ttk::SimplexId oldSaddle = extremas[t1Lid].rep_.saddleId_;
4452 if(element.t2_ > -1) {
4453 addLocalExtrema(t2Lid, element.t2_, element.t2Rank_,
4454 element.t2Order_, extremas,
4455 globalToLocalExtrema, extremaToPairedSaddle);
4456 }
4457 if(extremas[t1Lid].rep_.extremaId_ != -1) {
4458 if(element.t2_ > -1) {
4459 extremas[t1Lid].rep_.extremaId_ = t2Lid;
4460 } else {
4461 extremas[t1Lid].rep_.extremaId_ = t1Lid;
4462 }
4463 extremas[t1Lid].rep_.saddleId_ = s.lid_;
4464 }
4465 if(extremas[t1Lid].rank_ != ttk::MPIrank_
4466 || (ghostPresence[extremas[t1Lid].lid_].size() > 1)) {
4467 saddleEdge<sizeSad> ls1;
4468 saddleEdge<sizeSad> ls2;
4469 if(oldSaddle > -1) {
4470 ls1 = saddles[oldSaddle];
4471 }
4472 if(element.t2_ > -1) {
4473 if(extremas[t2Lid].rep_.saddleId_ > -1) {
4474 ls2 = saddles[extremas[t2Lid].rep_.saddleId_];
4475 } else {
4476 if(element.s2_ != -1) {
4477 ls2 = saddleEdge<sizeSad>(
4478 element.s2_, element.s2Order_, element.s2Rank_);
4479 }
4480 }
4481 storeMessageToSend<sizeExtr, sizeSad>(
4482 ghostPresence, sendBuffer, s, ls1, ls2, extremas[t1Lid],
4483 extremas[t2Lid]);
4484 } else {
4485 storeMessageToSend<sizeExtr, sizeSad>(
4486 ghostPresence, sendBuffer, s, ls1, extremas[t1Lid]);
4487 }
4488 }
4489 if(sCurrent.gid_ > -1 && sCurrent.gid_ != s.gid_) {
4490 if(sCurrent.rank_ == ttk::MPIrank_) {
4491 addToRecvBuffer(sCurrent, recomputations, cmpMessages,
4492 recvBuffer, beginVect);
4493 } else {
4494 storeRerunToSend<sizeExtr>(sendBuffer, sCurrent);
4495 }
4496 }
4497 } else {
4498 if(s.rank_ == ttk::MPIrank_) {
4499 addToRecvBuffer(
4500 s, recomputations, cmpMessages, recvBuffer, beginVect);
4501 } else {
4502 storeRerunToSend<sizeExtr>(sendBuffer, s);
4503 }
4504 }
4505 }
4506 }
4507 }
4508 }
4509 }
4510}
4511
4512template <int sizeExtr, int sizeSad>
4513void ttk::DiscreteMorseSandwichMPI::storeMessageToSend(
4514 std::vector<std::vector<char>> &ghostPresence,
4515 std::vector<std::vector<messageType<sizeExtr, sizeSad>>> &sendBuffer,
4516 saddleEdge<sizeSad> &sv,
4517 saddleEdge<sizeSad> &s1,
4518 extremaNode<sizeExtr> &rep1,
4519 char sender,
4520 char hasBeenModified) const {
4521 struct messageType<sizeExtr, sizeSad> m = messageType<sizeExtr, sizeSad>(
4522 rep1.gid_, rep1.vOrder_, sv.gid_, sv.vOrder_, s1.gid_, s1.vOrder_,
4523 rep1.rank_, sv.rank_, s1.rank_, 0);
4524 if(rep1.rank_ != ttk::MPIrank_ && (rep1.rank_ != sender || hasBeenModified)) {
4525 sendBuffer[rep1.rank_].emplace_back(m);
4526 } else {
4527 for(auto r : ghostPresence[rep1.lid_]) {
4528 sendBuffer[r].emplace_back(m);
4529 }
4530 }
4531};
4532
4533template <int sizeExtr, int sizeSad>
4534void ttk::DiscreteMorseSandwichMPI::storeMessageToSend(
4535 std::vector<std::vector<char>> &ghostPresence,
4536 std::vector<std::vector<messageType<sizeExtr, sizeSad>>> &sendBuffer,
4537 saddleEdge<sizeSad> &sv,
4538 saddleEdge<sizeSad> &s1,
4539 saddleEdge<sizeSad> &s2,
4540 extremaNode<sizeExtr> &rep1,
4541 extremaNode<sizeExtr> &rep2,
4542 char sender,
4543 char hasBeenModified) const {
4544 messageType<sizeExtr, sizeSad> m = messageType<sizeExtr, sizeSad>(
4545 rep1.gid_, rep1.vOrder_, rep2.gid_, rep2.vOrder_, sv.gid_, sv.vOrder_,
4546 s1.gid_, s1.vOrder_, s2.gid_, s2.vOrder_, rep1.rank_, rep2.rank_, sv.rank_,
4547 s1.rank_, s2.rank_, 0);
4548 if(rep1.rank_ != ttk::MPIrank_ && (rep1.rank_ != sender || hasBeenModified)) {
4549 sendBuffer[rep1.rank_].emplace_back(m);
4550 } else {
4551 for(auto r : ghostPresence[rep1.lid_]) {
4552 sendBuffer[r].emplace_back(m);
4553 }
4554 }
4555};
4556
4557template <int sizeExtr, int sizeSad>
4558void ttk::DiscreteMorseSandwichMPI::storeMessageToSendToRepOwner(
4559 std::vector<std::vector<messageType<sizeExtr, sizeSad>>> &sendBuffer,
4560 saddleEdge<sizeSad> &sv,
4561 std::vector<saddleEdge<sizeSad>> &saddles,
4562 extremaNode<sizeExtr> &rep1,
4563 extremaNode<sizeExtr> &rep2) const {
4564 saddleEdge<sizeSad> s1;
4565 saddleEdge<sizeSad> s2;
4566 if(rep1.rep_.saddleId_ > -1) {
4567 s1 = saddles[rep1.rep_.saddleId_];
4568 }
4569 if(rep2.rep_.saddleId_ > -1) {
4570 s2 = saddles[rep2.rep_.saddleId_];
4571 }
4572 messageType<sizeExtr, sizeSad> m = messageType<sizeExtr, sizeSad>(
4573 rep1.gid_, rep1.vOrder_, rep2.gid_, rep2.vOrder_, sv.gid_, sv.vOrder_,
4574 s1.gid_, s1.vOrder_, s2.gid_, s2.vOrder_, rep1.rank_, rep2.rank_, sv.rank_,
4575 s1.rank_, s2.rank_, 1);
4576 sendBuffer[rep1.rank_].emplace_back(m);
4577};
4578
4579template <int sizeExtr, int sizeSad>
4580void ttk::DiscreteMorseSandwichMPI::storeMessageToSendToRepOwner(
4581 std::vector<std::vector<messageType<sizeExtr, sizeSad>>> &sendBuffer,
4582 saddleEdge<sizeSad> &sv,
4583 std::vector<saddleEdge<sizeSad>> &saddles,
4584 extremaNode<sizeExtr> &rep1) const {
4585 saddleEdge<sizeSad> s1;
4586 if(rep1.rep_.saddleId_ > -1) {
4587 s1 = saddles[rep1.rep_.saddleId_];
4588 }
4589 struct messageType<sizeExtr, sizeSad> m = messageType<sizeExtr, sizeSad>(
4590 rep1.gid_, rep1.vOrder_, sv.gid_, sv.vOrder_, s1.gid_, s1.vOrder_,
4591 rep1.rank_, sv.rank_, s1.rank_, 1);
4592 sendBuffer[rep1.rank_].emplace_back(m);
4593};
4594
4595template <int sizeExtr, int sizeSad>
4596void ttk::DiscreteMorseSandwichMPI::storeRerunToSend(
4597 std::vector<std::vector<messageType<sizeExtr, sizeSad>>> &sendBuffer,
4598 saddleEdge<sizeSad> &sv) const {
4599 messageType<sizeExtr, sizeSad> m
4600 = messageType<sizeExtr, sizeSad>(sv.gid_, sv.vOrder_, sv.rank_);
4601 sendBuffer[sv.rank_].emplace_back(m);
4602};
4603
4604template <int sizeExtr, int sizeSad>
4605int ttk::DiscreteMorseSandwichMPI::processTriplet(
4606 saddleEdge<sizeSad> sv,
4607 std::vector<ttk::SimplexId> &saddleToPairedExtrema,
4608 std::vector<ttk::SimplexId> &extremaToPairedSaddle,
4609 std::vector<saddleEdge<sizeSad>> &saddles,
4610 std::vector<extremaNode<sizeExtr>> &extremas,
4611 bool increasing,
4612 std::vector<std::vector<char>> &ghostPresence,
4613 std::vector<std::vector<messageType<sizeExtr, sizeSad>>> &sendBuffer,
4614 std::set<messageType<sizeExtr, sizeSad>,
4615 std::function<bool(const messageType<sizeExtr, sizeSad> &,
4616 const messageType<sizeExtr, sizeSad> &)>>
4617 &recomputations,
4618 const std::function<bool(const messageType<sizeExtr, sizeSad> &,
4619 const messageType<sizeExtr, sizeSad> &)>
4620 &cmpMessages,
4621 std::vector<messageType<sizeExtr, sizeSad>> &recvBuffer,
4622 ttk::SimplexId beginVect) const {
4623 // rep1 is either last correct in local or a ghost
4624 ttk::SimplexId r1Lid
4625 = getRep(extremas[sv.t_[0]], sv, increasing, extremas, saddles);
4626
4627 bool pairedR1 = extremaToPairedSaddle[r1Lid] > -1;
4628 bool isR1Invalid
4629 = (pairedR1
4630 && ((saddles[extremaToPairedSaddle[r1Lid]] < sv) == increasing));
4631 ttk::SimplexId oldSaddle{-1};
4632 if(isR1Invalid)
4633 pairedR1 = false;
4634 if(sv.t_[1] < 0) {
4635 // deal with "shadow" triplets (a 2-saddle with only one
4636 // ascending 1-separatrix leading to an unique maximum)
4637 if(extremas[r1Lid].rep_.extremaId_ == -1) {
4638 // Send to owner to continue computation
4639 storeMessageToSendToRepOwner(sendBuffer, sv, saddles, extremas[r1Lid]);
4640 return 0;
4641 }
4642 if(!pairedR1) {
4643
4644 // when considering the boundary, the "-1" of the triplets
4645 // indicate a virtual maximum of infinite persistence on the
4646 // boundary component. a pair is created with the other
4647 // maximum
4648 if(isR1Invalid) {
4649 oldSaddle = extremaToPairedSaddle[r1Lid];
4650 removePair(saddles[extremaToPairedSaddle[r1Lid]], extremas[r1Lid],
4651 saddleToPairedExtrema, extremaToPairedSaddle);
4652 }
4653 addPair(
4654 sv, extremas[r1Lid], saddleToPairedExtrema, extremaToPairedSaddle);
4655 // If extrema is has local id, then is present in local
4656 if(extremas[r1Lid].rank_ != ttk::MPIrank_
4657 || (ghostPresence[r1Lid].size() > 1)) {
4658 saddleEdge<sizeSad> s1;
4659 if(oldSaddle > -1) {
4660 s1 = saddles[oldSaddle];
4661 }
4662 storeMessageToSend<sizeExtr, sizeSad>(
4663 ghostPresence, sendBuffer, sv, s1, extremas[r1Lid]);
4664 }
4665 // If ghost or on the border: send message, do what happens next?
4666 if(extremas[r1Lid].rep_.extremaId_ != -1) {
4667 extremas[r1Lid].rep_.extremaId_ = r1Lid;
4668 extremas[r1Lid].rep_.saddleId_ = sv.lid_;
4669 }
4670 if(isR1Invalid) {
4671 if(saddles[oldSaddle].rank_ == ttk::MPIrank_) {
4672 addToRecvBuffer(saddles[oldSaddle], recomputations, cmpMessages,
4673 recvBuffer, beginVect);
4674 } else {
4675 storeRerunToSend<sizeExtr>(sendBuffer, saddles[oldSaddle]);
4676 }
4677 }
4678 }
4679 return 0;
4680 }
4681 ttk::SimplexId r2Lid
4682 = getRep(extremas[sv.t_[1]], sv, increasing, extremas, saddles);
4683 bool pairedR2 = extremaToPairedSaddle[r2Lid] > -1;
4684 bool isR2Invalid
4685 = (pairedR2
4686 && ((saddles[extremaToPairedSaddle[r2Lid]] < sv) == increasing));
4687 if(isR2Invalid)
4688 pairedR2 = false;
4689 if(extremas[r2Lid].rep_.extremaId_ == -1) {
4690 // Send to owner to continue computation
4691 storeMessageToSendToRepOwner(
4692 sendBuffer, sv, saddles, extremas[r2Lid], extremas[r1Lid]);
4693 return 0;
4694 } else {
4695 if(extremas[r1Lid].rep_.extremaId_ == -1) {
4696 // Send to owner to continue computation
4697 storeMessageToSendToRepOwner(
4698 sendBuffer, sv, saddles, extremas[r1Lid], extremas[r2Lid]);
4699 return 0;
4700 }
4701 }
4702 if(extremas[r1Lid].gid_ != extremas[r2Lid].gid_) {
4703 if((((extremas[r2Lid] < extremas[r1Lid]) == increasing) || pairedR1)
4704 && !pairedR2) {
4705 if(isR2Invalid) {
4706 oldSaddle = extremaToPairedSaddle[r2Lid];
4707 removePair(saddles[extremaToPairedSaddle[r2Lid]], extremas[r2Lid],
4708 saddleToPairedExtrema, extremaToPairedSaddle);
4709 }
4710 addPair(
4711 sv, extremas[r2Lid], saddleToPairedExtrema, extremaToPairedSaddle);
4712 extremas[r2Lid].rep_.extremaId_ = r1Lid;
4713 extremas[r2Lid].rep_.saddleId_ = sv.lid_;
4714 // send to other processes if necessary
4715 if(extremas[r2Lid].rank_ != ttk::MPIrank_
4716 || (ghostPresence[r2Lid].size() > 1)) {
4717 saddleEdge<sizeSad> s1;
4718 saddleEdge<sizeSad> s2;
4719 if(extremaToPairedSaddle[r1Lid] > -1) {
4720 s1 = saddles[extremaToPairedSaddle[r1Lid]];
4721 }
4722 if(oldSaddle > -1) {
4723 s2 = saddles[oldSaddle];
4724 }
4725 storeMessageToSend<sizeExtr, sizeSad>(ghostPresence, sendBuffer, sv, s2,
4726 s1, extremas[r2Lid],
4727 extremas[r1Lid]);
4728 }
4729 if(isR2Invalid) {
4730 if(saddles[oldSaddle].rank_ == ttk::MPIrank_
4731 && saddles[oldSaddle].gid_ != sv.gid_) {
4732 addToRecvBuffer(saddles[oldSaddle], recomputations, cmpMessages,
4733 recvBuffer, beginVect);
4734 } else {
4735 storeRerunToSend<sizeExtr>(sendBuffer, saddles[oldSaddle]);
4736 }
4737 }
4738 } else {
4739 if(!pairedR1) {
4740 if(isR1Invalid) {
4741 oldSaddle = extremaToPairedSaddle[r1Lid];
4742 removePair(saddles[extremaToPairedSaddle[r1Lid]], extremas[r1Lid],
4743 saddleToPairedExtrema, extremaToPairedSaddle);
4744 }
4745 addPair(
4746 sv, extremas[r1Lid], saddleToPairedExtrema, extremaToPairedSaddle);
4747 extremas[r1Lid].rep_.extremaId_ = r2Lid;
4748 extremas[r1Lid].rep_.saddleId_ = sv.lid_;
4749 if(extremas[r1Lid].rank_ != ttk::MPIrank_
4750 || (ghostPresence[r1Lid].size() > 1)) {
4751 saddleEdge<sizeSad> s1;
4752 saddleEdge<sizeSad> s2;
4753 if(oldSaddle > -1) {
4754 s1 = saddles[oldSaddle];
4755 }
4756 if(extremaToPairedSaddle[r2Lid] > -1) {
4757 s2 = saddles[extremaToPairedSaddle[r2Lid]];
4758 }
4759 storeMessageToSend<sizeExtr, sizeSad>(ghostPresence, sendBuffer, sv,
4760 s1, s2, extremas[r1Lid],
4761 extremas[r2Lid]);
4762 }
4763 if(isR1Invalid) {
4764 if(saddles[oldSaddle].rank_ == ttk::MPIrank_
4765 && saddles[oldSaddle].gid_ != sv.gid_) {
4766 addToRecvBuffer(saddles[oldSaddle], recomputations, cmpMessages,
4767 recvBuffer, beginVect);
4768 } else {
4769 storeRerunToSend<sizeExtr>(sendBuffer, saddles[oldSaddle]);
4770 }
4771 }
4772 }
4773 }
4774 } else {
4775 if(saddleToPairedExtrema[sv.lid_] > -1) {
4776 extremaToPairedSaddle[saddleToPairedExtrema[sv.lid_]] = -1;
4777 extremas[saddleToPairedExtrema[sv.lid_]].rep_.extremaId_
4778 = extremas[saddleToPairedExtrema[sv.lid_]].lid_;
4779 extremas[saddleToPairedExtrema[sv.lid_]].rep_.saddleId_ = -1;
4780 if(extremas[saddleToPairedExtrema[sv.lid_]].rank_ != ttk::MPIrank_
4781 || (ghostPresence[extremas[saddleToPairedExtrema[sv.lid_]].lid_].size()
4782 > 1)) {
4783 saddleEdge<sizeSad> s1;
4784 if(extremaToPairedSaddle[saddleToPairedExtrema[sv.lid_]] > -1) {
4785 s1 = saddles[extremaToPairedSaddle[saddleToPairedExtrema[sv.lid_]]];
4786 }
4787 storeMessageToSend<sizeExtr, sizeSad>(
4788 ghostPresence, sendBuffer, sv, s1, s1,
4789 extremas[saddleToPairedExtrema[sv.lid_]],
4790 extremas[saddleToPairedExtrema[sv.lid_]]);
4791 }
4792 saddleToPairedExtrema[sv.lid_] = static_cast<ttk::SimplexId>(-2);
4793 }
4794 }
4795
4796 return 0;
4797};
4798
4799template <typename GlobalBoundary>
4800void ttk::DiscreteMorseSandwichMPI::updateMax(
4801 maxPerProcess m, GlobalBoundary &maxBoundary) const {
4802 auto it = std::find(maxBoundary.begin(), maxBoundary.end(), m);
4803 if(it != maxBoundary.end()) {
4804 maxBoundary.erase(it);
4805 }
4806 maxBoundary.emplace(m);
4807};
4808
4809template <typename GlobalBoundary>
4810void ttk::DiscreteMorseSandwichMPI::getMaxOfProc(
4811 ttk::SimplexId rank,
4812 ttk::SimplexId *currentMax,
4813 GlobalBoundary &maxBoundary) const {
4814 auto it
4815 = std::find(maxBoundary.begin(), maxBoundary.end(), maxPerProcess(rank));
4816 if(it != maxBoundary.end()) {
4817 currentMax[0] = it->max_[0];
4818 currentMax[1] = it->max_[1];
4819 }
4820};
4821
4822template <typename GlobalBoundary, typename LocalBoundary>
4823bool ttk::DiscreteMorseSandwichMPI::isEmpty(
4824 LocalBoundary &localBoundary, GlobalBoundary &globalBoundary) const {
4825 if(localBoundary.empty()) {
4826 for(const auto elt : globalBoundary) {
4827 if(elt.max_[0] != -1) {
4828 return false;
4829 }
4830 }
4831 } else {
4832 return false;
4833 }
4834 return true;
4835};
4836
4837template <typename LocalBoundary>
4838bool ttk::DiscreteMorseSandwichMPI::addBoundary(
4839 const SimplexId e, bool isOnBoundary, LocalBoundary &localBoundary) const {
4840 // add edge e to boundaryIds/onBoundary modulo 2
4841 if(!isOnBoundary) {
4842 localBoundary.emplace(e);
4843 isOnBoundary = true;
4844 } else {
4845 const auto it = localBoundary.find(e);
4846 localBoundary.erase(it);
4847 isOnBoundary = false;
4848 }
4849 return isOnBoundary;
4850};
4851
4852template <typename GlobalBoundary>
4853void ttk::DiscreteMorseSandwichMPI::updateMaxBoundary(
4854 const SimplexId s2,
4855 const ttk::SimplexId *tauOrder,
4856 GlobalBoundary &globalBoundary,
4857 ttk::SimplexId rank,
4858 ttk::SimplexId computeProc) const {
4859 if(!globalBoundary.empty()) {
4860 std::vector<ttk::SimplexId> vect(5);
4861 vect[0] = -4;
4862 vect[1] = s2;
4863 vect[2] = rank;
4864 vect[3] = tauOrder[0];
4865 vect[4] = tauOrder[1];
4866 int tempCurrentBuffer;
4867 for(const auto max : globalBoundary) {
4868 if(max.proc_ != rank) {
4869 sendBoundaryBufferLock_[max.proc_].lock();
4870#pragma omp atomic read
4871 tempCurrentBuffer = currentBuffer_;
4872 sendBoundaryBuffer_[tempCurrentBuffer][max.proc_].insert(
4873 sendBoundaryBuffer_[tempCurrentBuffer][max.proc_].end(), vect.begin(),
4874 vect.end());
4875 sendBoundaryBufferLock_[max.proc_].unlock();
4876 if(max.proc_ == computeProc) {
4877 sendComputeBufferLock_[max.proc_].lock();
4878#pragma omp atomic read
4879 tempCurrentBuffer = currentBuffer_;
4880 sendComputeBuffer_[tempCurrentBuffer][max.proc_].emplace_back(s2);
4881 sendComputeBufferLock_[max.proc_].unlock();
4882 }
4883 }
4884 }
4885#pragma omp atomic update
4886 messageCounter_++;
4887 }
4888};
4889
4890template <typename GlobalBoundary>
4891void ttk::DiscreteMorseSandwichMPI::updateLocalBoundary(
4892 const saddle<3> &s2,
4893 const SimplexId egid1,
4894 const SimplexId egid2,
4895 const ttk::SimplexId *tauOrder,
4896 GlobalBoundary &globalBoundary,
4897 ttk::SimplexId rank) const {
4898 std::vector<ttk::SimplexId> vect(10);
4899 vect[1] = s2.gid_;
4900 for(int i = 0; i < 3; i++) {
4901 vect[2 + i] = s2.vOrder_[i];
4902 }
4903 vect[5] = egid1;
4904 vect[6] = egid2;
4905 vect[7] = ttk::MPIrank_;
4906 vect[8] = tauOrder[0];
4907 vect[9] = tauOrder[1];
4908 for(const auto max : globalBoundary) {
4909 vect.emplace_back(max.proc_);
4910 vect.emplace_back(max.max_[0]);
4911 vect.emplace_back(max.max_[1]);
4912 }
4913 vect[0] = -(vect.size() - 1);
4914 int tempCurrentBuffer;
4915 sendBoundaryBufferLock_[rank].lock();
4916#pragma omp atomic read
4917 tempCurrentBuffer = currentBuffer_;
4918 sendBoundaryBuffer_[tempCurrentBuffer][rank].insert(
4919 sendBoundaryBuffer_[tempCurrentBuffer][rank].end(), vect.begin(),
4920 vect.end());
4921 sendBoundaryBufferLock_[rank].unlock();
4922#pragma omp atomic update
4923 messageCounter_++;
4924};
4925
4926template <typename LocalBoundary, typename GlobalBoundary>
4927bool ttk::DiscreteMorseSandwichMPI::mergeGlobalBoundaries(
4928 std::vector<bool> &onBoundary,
4929 LocalBoundary &s2LocalBoundary,
4930 GlobalBoundary &s2GlobalBoundary,
4931 LocalBoundary &pTauLocalBoundary,
4932 GlobalBoundary &pTauGlobalBoundary) const {
4933 for(const auto e : pTauLocalBoundary) {
4934 onBoundary[e] = addBoundary(e, onBoundary[e], s2LocalBoundary);
4935 }
4936 bool hasChanged{false};
4937 for(const auto &m : pTauGlobalBoundary) {
4938 auto it = std::find(s2GlobalBoundary.begin(), s2GlobalBoundary.end(), m);
4939 if(it != s2GlobalBoundary.end()) {
4940 if(compareArray(it->max_, m.max_, 2)) {
4941 hasChanged = true;
4942 s2GlobalBoundary.erase(it);
4943 s2GlobalBoundary.emplace(m);
4944 }
4945 } else {
4946 s2GlobalBoundary.emplace(m);
4947 hasChanged = true;
4948 }
4949 }
4950 return hasChanged;
4951};
4952
4953template <typename GlobalBoundary>
4954void ttk::DiscreteMorseSandwichMPI::updateMergedBoundary(
4955 const saddle<3> &s2,
4956 const SimplexId pTau,
4957 const ttk::SimplexId *tauOrder,
4958 GlobalBoundary &globalBoundary) const {
4959 if(!globalBoundary.empty()) {
4960 std::vector<ttk::SimplexId> vect(10);
4961 vect[1] = s2.gid_;
4962 for(int i = 0; i < 3; i++) {
4963 vect[2 + i] = s2.vOrder_[i];
4964 }
4965 vect[5] = -1;
4966 vect[6] = pTau;
4967 vect[7] = ttk::MPIrank_;
4968 vect[8] = tauOrder[0];
4969 vect[9] = tauOrder[1];
4970 for(const auto max : globalBoundary) {
4971 vect.emplace_back(max.proc_);
4972 vect.emplace_back(max.max_[0]);
4973 vect.emplace_back(max.max_[1]);
4974 }
4975 vect[0] = -(vect.size() - 1);
4976 int tempCurrentBuffer;
4977 for(const auto max : globalBoundary) {
4978 sendBoundaryBufferLock_[max.proc_].lock();
4979#pragma omp atomic read
4980 tempCurrentBuffer = currentBuffer_;
4981 sendBoundaryBuffer_[tempCurrentBuffer][max.proc_].insert(
4982 sendBoundaryBuffer_[tempCurrentBuffer][max.proc_].end(), vect.begin(),
4983 vect.end());
4984 sendBoundaryBufferLock_[max.proc_].unlock();
4985 }
4986#pragma omp atomic update
4987 messageCounter_++;
4988 }
4989};
4990
4991template <typename triangulationType,
4992 typename GlobalBoundary,
4993 typename LocalBoundary>
4994void ttk::DiscreteMorseSandwichMPI::addEdgeToBoundary(
4995 const ttk::SimplexId pTau,
4996 const ttk::SimplexId edgeId,
4997 std::vector<bool> &onBoundary,
4998 GlobalBoundary &globalBoundaryIds,
4999 LocalBoundary &localBoundaryIds,
5000 const triangulationType &triangulation,
5001 const SimplexId *const offsets,
5002 std::vector<std::pair<ttk::SimplexId, ttk::SimplexId>> &ghostEdges,
5003 std::vector<bool> &hasChangedMax) const {
5004 SimplexId e{};
5005 triangulation.getTriangleEdge(pTau, edgeId, e);
5006 ttk::SimplexId rank = triangulation.getEdgeRank(e);
5007 if(rank == ttk::MPIrank_) {
5008 onBoundary[e] = addBoundary(e, onBoundary[e], localBoundaryIds);
5009 } else {
5010 ghostEdges.emplace_back(std::make_pair(e, rank));
5011 hasChangedMax.emplace_back(false);
5012 ttk::SimplexId eOrder[2];
5013 fillEdgeOrder(e, offsets, triangulation, eOrder);
5014 ttk::SimplexId currentMax[] = {-1, -1};
5015 getMaxOfProc(rank, currentMax, globalBoundaryIds);
5016 if(currentMax[0] != -1) {
5017 if(compareArray(currentMax, eOrder, 2)) {
5018 updateMax(maxPerProcess(rank, eOrder), globalBoundaryIds);
5019 hasChangedMax[hasChangedMax.size() - 1] = true;
5020 }
5021 } else {
5022 updateMax(maxPerProcess(rank, eOrder), globalBoundaryIds);
5023 hasChangedMax[hasChangedMax.size() - 1] = true;
5024 }
5025 }
5026};
5027
5028template <typename triangulationType, typename GlobalBoundary>
5029void ttk::DiscreteMorseSandwichMPI::packageLocalBoundaryUpdate(
5030 const saddle<3> &s2,
5031 ttk::SimplexId *tauOrder,
5032 GlobalBoundary &globalBoundaryIds,
5033 const triangulationType &triangulation,
5034 std::vector<std::pair<ttk::SimplexId, ttk::SimplexId>> &ghostEdges,
5035 std::vector<bool> &hasChangedMax) const {
5036 switch(ghostEdges.size()) {
5037 case 0:
5038 break;
5039 case 1:
5040 updateLocalBoundary(
5041 s2, triangulation.getEdgeGlobalId(ghostEdges[0].first), -1, tauOrder,
5042 globalBoundaryIds, ghostEdges[0].second);
5043 if(hasChangedMax[0]) { // Add test if other rank of propagation (outside
5044 // current and updatedLocalBoundary)
5045 ttk::SimplexId currentMax[] = {-1, -1};
5046 getMaxOfProc(ghostEdges[0].second, currentMax, globalBoundaryIds);
5047 updateMaxBoundary(
5048 s2.gid_, currentMax, globalBoundaryIds, ghostEdges[0].second);
5049 }
5050 break;
5051 case 2:
5052 if(ghostEdges[0].second == ghostEdges[1].second) {
5053 updateLocalBoundary(s2,
5054 triangulation.getEdgeGlobalId(ghostEdges[0].first),
5055 triangulation.getEdgeGlobalId(ghostEdges[1].first),
5056 tauOrder, globalBoundaryIds, ghostEdges[0].second);
5057 if(hasChangedMax[0] || hasChangedMax[1]) {
5058 ttk::SimplexId currentMax[] = {-1, -1};
5059 getMaxOfProc(ghostEdges[0].second, currentMax, globalBoundaryIds);
5060 updateMaxBoundary(
5061 s2.gid_, currentMax, globalBoundaryIds, ghostEdges[0].second);
5062 }
5063 } else {
5064 updateLocalBoundary(
5065 s2, triangulation.getEdgeGlobalId(ghostEdges[0].first), -1, tauOrder,
5066 globalBoundaryIds, ghostEdges[0].second);
5067 updateLocalBoundary(
5068 s2, triangulation.getEdgeGlobalId(ghostEdges[1].first), -1, tauOrder,
5069 globalBoundaryIds, ghostEdges[1].second);
5070 if(hasChangedMax[0]) {
5071 ttk::SimplexId currentMax[] = {-1, -1};
5072 getMaxOfProc(ghostEdges[0].second, currentMax, globalBoundaryIds);
5073 updateMaxBoundary(
5074 s2.gid_, currentMax, globalBoundaryIds, ghostEdges[0].second);
5075 }
5076 if(hasChangedMax[1]) {
5077 ttk::SimplexId currentMax[] = {-1, -1};
5078 getMaxOfProc(ghostEdges[1].second, currentMax, globalBoundaryIds);
5079 updateMaxBoundary(
5080 s2.gid_, currentMax, globalBoundaryIds, ghostEdges[1].second);
5081 }
5082 }
5083 break;
5084 default:
5085 printErr("In packageLocalBoundaryUpdate:");
5086 printErr("This case is not supposed to be reached.");
5087 printErr("Something has gone wrong in the execution.");
5088 break;
5089 }
5090};
5091
5092void ttk::DiscreteMorseSandwichMPI::getLid(ttk::SimplexId lid,
5093 ttk::SimplexId &lidBlock,
5094 ttk::SimplexId &lidElement) const {
5095 if(lid < firstBlockSize_) {
5096 lidBlock = 0;
5097 lidElement = lid;
5098 } else {
5099 lidElement = (lid - firstBlockSize_) % blockSize_;
5100 lidBlock = 1 + (lid - firstBlockSize_ - lidElement) / blockSize_;
5101 }
5102}
5103
5104template <typename triangulationType,
5105 typename GlobalBoundary,
5106 typename LocalBoundary>
5107SimplexId ttk::DiscreteMorseSandwichMPI::eliminateBoundariesSandwich(
5108 const saddle<3> &s2,
5109 std::vector<std::vector<bool>> &onBoundaryThread,
5110 std::vector<std::vector<GlobalBoundary>> &s2GlobalBoundaries,
5111 std::vector<std::vector<LocalBoundary>> &s2LocalBoundaries,
5112 std::vector<SimplexId> &partners,
5113 std::vector<int> &s1Locks,
5114 std::vector<std::vector<int>> &s2Locks,
5115 const std::vector<std::vector<saddle<3>>> &saddles2,
5116 const std::vector<ttk::SimplexId> &localEdgeToSaddle1,
5117 const triangulationType &triangulation,
5118 const SimplexId *const offsets) const {
5119 int threadNumber = omp_get_thread_num();
5120 auto &onBoundary = onBoundaryThread[threadNumber];
5121 int lock;
5122 // lock the 2-saddle to ensure that only one thread can perform the
5123 // boundary expansion
5124 do {
5125#pragma omp atomic capture seq_cst
5126 {
5127 lock = s2Locks[s2.lidBlock_][s2.lidElement_];
5128 s2Locks[s2.lidBlock_][s2.lidElement_] = 1;
5129 };
5130 } while(lock == 1);
5131 bool tooFar = false;
5132 ttk::SimplexId localEdgeCounter{0};
5133 ttk::SimplexId tooFarCounter{0};
5134 auto &localBoundaryIds{s2LocalBoundaries[s2.lidBlock_][s2.lidElement_]};
5135 auto &globalBoundaryIds{s2GlobalBoundaries[s2.lidBlock_][s2.lidElement_]};
5136 const auto clearOnBoundary = [&localBoundaryIds, &onBoundary]() {
5137 // clear the onBoundary vector (set everything to false)
5138 for(const auto e : localBoundaryIds) {
5139 onBoundary[e] = false;
5140 }
5141 };
5142 if(!isEmpty(localBoundaryIds, globalBoundaryIds)) {
5143 // restore previously computed s2 boundary
5144 for(const auto e : localBoundaryIds) {
5145 onBoundary[e] = true;
5146 }
5147 } else {
5148 // init cascade with s2 triangle boundary (3 edges)
5149 std::vector<std::pair<ttk::SimplexId, ttk::SimplexId>> ghostEdges;
5150 std::vector<bool> hasChangedMax;
5151 for(ttk::SimplexId i = 0; i < 3; ++i) {
5152 addEdgeToBoundary(triangulation.getTriangleLocalId(s2.gid_), i,
5153 onBoundary, globalBoundaryIds, localBoundaryIds,
5154 triangulation, offsets, ghostEdges, hasChangedMax);
5155 }
5156 if(ghostEdges.size() > 0) {
5157 ttk::SimplexId tauOrder[2] = {-1, -1};
5158 // tau: youngest edge on boundary
5159 const auto tau{*localBoundaryIds.begin()};
5160 fillEdgeOrder(tau, offsets, triangulation, tauOrder);
5161 this->packageLocalBoundaryUpdate(s2, tauOrder, globalBoundaryIds,
5162 triangulation, ghostEdges,
5163 hasChangedMax);
5164 }
5165 }
5166
5167 while(!isEmpty(localBoundaryIds, globalBoundaryIds)) {
5168 ttk::SimplexId tauOrder[2] = {-1, -1};
5169 // tau: youngest edge on boundary
5170 ttk::SimplexId tau{-1};
5171 if(!localBoundaryIds.empty()) {
5172 auto it = localBoundaryIds.begin();
5173 if(tooFar) {
5174 ttk::SimplexId i{0};
5175 while(i < localEdgeCounter && it != localBoundaryIds.end()) {
5176 it++;
5177 }
5178 }
5179 if(it == localBoundaryIds.end()) {
5180 const auto globMax{*globalBoundaryIds.begin()};
5181 tau = *localBoundaryIds.begin();
5182 fillEdgeOrder(tau, offsets, triangulation, tauOrder);
5183 updateMaxBoundary(
5184 s2.gid_, tauOrder, globalBoundaryIds, ttk::MPIrank_, globMax.proc_);
5185 clearOnBoundary();
5186#pragma omp atomic write seq_cst
5187 s2Locks[s2.lidBlock_][s2.lidElement_] = 0;
5188#pragma omp atomic update
5189 taskCounter_--;
5190 return 0;
5191 }
5192 tau = *it;
5193 fillEdgeOrder(tau, offsets, triangulation, tauOrder);
5194 }
5195 if(!globalBoundaryIds.empty()) {
5196 const auto globMax{*globalBoundaryIds.begin()};
5197 if(localBoundaryIds.empty()) {
5198 updateMaxBoundary(
5199 s2.gid_, tauOrder, globalBoundaryIds, ttk::MPIrank_, globMax.proc_);
5200 clearOnBoundary();
5201 globalBoundaryIds.clear();
5202#pragma omp atomic write seq_cst
5203 s2Locks[s2.lidBlock_][s2.lidElement_] = 0;
5204#pragma omp atomic update
5205 taskCounter_--;
5206 return 0;
5207 } else {
5208 if(tooFar || !compareArray(globMax.max_, tauOrder, 2)) {
5209 tooFar = true;
5210 tooFarCounter++;
5211 if(tooFar && tooFarCounter > this->sadSadLimit_) {
5212 updateMaxBoundary(s2.gid_, tauOrder, globalBoundaryIds,
5213 ttk::MPIrank_, globMax.proc_);
5214 clearOnBoundary();
5215#pragma omp atomic write seq_cst
5216 s2Locks[s2.lidBlock_][s2.lidElement_] = 0;
5217#pragma omp atomic update
5218 taskCounter_--;
5219 return 0;
5220 }
5221 }
5222 }
5223 }
5224
5225 // use the Discrete Gradient to find a triangle paired to tau
5226 auto pTau{this->dg_.getPairedCell(Cell{1, tau}, triangulation)};
5227 // pTau is a regular triangle
5228 // add pTau triangle boundary (3 edges)
5229 if(pTau != -1) {
5230 std::vector<std::pair<ttk::SimplexId, ttk::SimplexId>> ghostEdges;
5231 std::vector<bool> hasChangedMax;
5232 std::vector<ttk::SimplexId> newMax;
5233 for(SimplexId i = 0; i < 3; ++i) {
5234 this->addEdgeToBoundary(pTau, i, onBoundary, globalBoundaryIds,
5235 localBoundaryIds, triangulation, offsets,
5236 ghostEdges, hasChangedMax);
5237 }
5238 this->packageLocalBoundaryUpdate(s2, tauOrder, globalBoundaryIds,
5239 triangulation, ghostEdges,
5240 hasChangedMax);
5241 }
5242 bool critical{false};
5243 ttk::SimplexId saddleTau{-1};
5244 ttk::SimplexId pTauLidBlock{-1};
5245 ttk::SimplexId pTauLidElement{-1};
5246 // pTau is critical
5247 if(pTau == -1) {
5248 saddleTau = localEdgeToSaddle1[tau];
5249 if(saddleTau == -1) {
5250 localEdgeCounter++;
5251 continue;
5252 }
5253 // maybe tau is critical and paired to a critical triangle
5254 do {
5255#ifdef TTK_ENABLE_OPENMP
5256#pragma omp atomic read seq_cst
5257#endif // TTK_ENABLE_OPENMP
5258 pTau = partners[saddleTau];
5259 if(pTau != -1) {
5260 getLid(pTau, pTauLidBlock, pTauLidElement);
5261 if(s2LocalBoundaries[pTauLidBlock][pTauLidElement].empty()) {
5262 break;
5263 }
5264 } else {
5265 break;
5266 }
5267 } while(*s2LocalBoundaries[pTauLidBlock][pTauLidElement].begin() != tau);
5268 critical = true;
5269 }
5270 if(pTau == -1) {
5271 // tau is critical and not paired
5272
5273 // compare-and-swap from "Towards Lockfree Persistent Homology"
5274 // using locks over 1-saddles instead of atomics (OpenMP compatibility)
5275 do {
5276#pragma omp atomic capture seq_cst
5277 {
5278 lock = s1Locks[saddleTau];
5279 s1Locks[saddleTau] = 1;
5280 };
5281 } while(lock == 1);
5282
5283 const auto cap = partners[saddleTau];
5284 if(partners[saddleTau] == -1) {
5285 if(tooFar) {
5286#pragma omp atomic write seq_cst
5287 s1Locks[saddleTau] = 0;
5288 const auto globMax{*globalBoundaryIds.begin()};
5289 updateMaxBoundary(
5290 s2.gid_, tauOrder, globalBoundaryIds, ttk::MPIrank_, globMax.proc_);
5291 clearOnBoundary();
5292#pragma omp atomic write seq_cst
5293 s2Locks[s2.lidBlock_][s2.lidElement_] = 0;
5294#pragma omp atomic update
5295 taskCounter_--;
5296 return 0;
5297 } else {
5298 if(s2.lidBlock_ == 0) {
5299 partners[saddleTau] = s2.lidElement_;
5300 } else {
5301 partners[saddleTau] = firstBlockSize_
5302 + blockSize_ * (s2.lidBlock_ - 1)
5303 + s2.lidElement_;
5304 }
5305#pragma omp atomic update
5306 finishedPropagationCounter_++;
5307 }
5308 }
5309#pragma omp atomic write seq_cst
5310 s1Locks[saddleTau] = 0;
5311 // cleanup before exiting
5312 clearOnBoundary();
5313 if(cap == -1) {
5314 // Update global boundary
5315 updateMaxBoundary(s2.gid_, tauOrder, globalBoundaryIds, ttk::MPIrank_);
5316#pragma omp atomic write seq_cst
5317 s2Locks[s2.lidBlock_][s2.lidElement_] = 0;
5318#pragma omp atomic update
5319 taskCounter_--;
5320 return tau;
5321 } else {
5322#pragma omp atomic write seq_cst
5323 s2Locks[s2.lidBlock_][s2.lidElement_] = 0;
5324 return this->eliminateBoundariesSandwich(
5325 s2, onBoundaryThread, s2GlobalBoundaries, s2LocalBoundaries, partners,
5326 s1Locks, s2Locks, saddles2, localEdgeToSaddle1, triangulation,
5327 offsets);
5328 }
5329
5330 } else {
5331 // expand boundary
5332 if(critical) {
5333 if(saddles2[pTauLidBlock][pTauLidElement] < s2) {
5334 // pTau is an already-paired 2-saddle
5335 // merge pTau boundary into s2 boundary
5336
5337 // make sure that pTau boundary is not modified by another
5338 // thread while we merge the two boundaries...
5339 do {
5340#pragma omp atomic capture seq_cst
5341 {
5342 lock = s2Locks[pTauLidBlock][pTauLidElement];
5343 s2Locks[pTauLidBlock][pTauLidElement] = 1;
5344 };
5345 } while(lock == 1);
5346 mergeGlobalBoundaries(
5347 onBoundary, localBoundaryIds, globalBoundaryIds,
5348 s2LocalBoundaries[pTauLidBlock][pTauLidElement],
5349 s2GlobalBoundaries[pTauLidBlock][pTauLidElement]);
5350 tau = *localBoundaryIds.begin();
5351 fillEdgeOrder(tau, offsets, triangulation, tauOrder);
5352 updateMergedBoundary(s2, saddles2[pTauLidBlock][pTauLidElement].gid_,
5353 tauOrder, globalBoundaryIds);
5354#pragma omp atomic write seq_cst
5355 s2Locks[pTauLidBlock][pTauLidElement] = 0;
5356 } else if(saddles2[pTauLidBlock][pTauLidElement] > s2) {
5357 // compare-and-swap from "Towards Lockfree Persistent
5358 // Homology" using locks over 1-saddles
5359 do {
5360#pragma omp atomic capture seq_cst
5361 {
5362 lock = s1Locks[saddleTau];
5363 s1Locks[saddleTau] = 1;
5364 };
5365 } while(lock == 1);
5366
5367 const auto cap = partners[saddleTau];
5368 if(partners[saddleTau] == pTau) {
5369 if(tooFar) {
5370#pragma omp atomic write seq_cst
5371 s1Locks[saddleTau] = 0;
5372 const auto globMax{*globalBoundaryIds.begin()};
5373 updateMaxBoundary(s2.gid_, tauOrder, globalBoundaryIds,
5374 ttk::MPIrank_, globMax.proc_);
5375 clearOnBoundary();
5376#pragma omp atomic write seq_cst
5377 s2Locks[s2.lidBlock_][s2.lidElement_] = 0;
5378#pragma omp atomic update
5379 taskCounter_--;
5380 return 0;
5381 } else {
5382 if(s2.lidBlock_ == 0) {
5383 partners[saddleTau] = s2.lidElement_;
5384 } else {
5385 partners[saddleTau] = firstBlockSize_
5386 + blockSize_ * (s2.lidBlock_ - 1)
5387 + s2.lidElement_;
5388 }
5389 }
5390 }
5391#pragma omp atomic write seq_cst
5392 s1Locks[saddleTau] = 0;
5393 updateMaxBoundary(
5394 s2.gid_, tauOrder, globalBoundaryIds, ttk::MPIrank_);
5395 if(cap == pTau) {
5396 // cleanup before exiting
5397 clearOnBoundary();
5398#pragma omp atomic write seq_cst
5399 s2Locks[s2.lidBlock_][s2.lidElement_] = 0;
5400 return this->eliminateBoundariesSandwich(
5401 saddles2[pTauLidBlock][pTauLidElement], onBoundaryThread,
5402 s2GlobalBoundaries, s2LocalBoundaries, partners, s1Locks, s2Locks,
5403 saddles2, localEdgeToSaddle1, triangulation, offsets);
5404 }
5405 }
5406 }
5407 }
5408 }
5409
5410 // cleanup before exiting
5411 clearOnBoundary();
5412#pragma omp atomic write seq_cst
5413 s2Locks[s2.lidBlock_][s2.lidElement_] = 0;
5414#pragma omp atomic update
5415 taskCounter_--;
5416 return -1;
5417}
5418
5419template <typename GlobalBoundary, typename LocalBoundary>
5420void ttk::DiscreteMorseSandwichMPI::mergeDistributedBoundary(
5421 std::vector<ttk::SimplexId> &recvBoundaryBuffer,
5422 std::vector<std::vector<int>> &s2Locks,
5423 std::vector<std::vector<GlobalBoundary>> &globalBoundaries,
5424 std::vector<std::vector<LocalBoundary>> &localBoundaries,
5425 std::vector<std::vector<saddle<3>>> &saddles2,
5427 ttk::SimplexId lidBlock,
5428 ttk::SimplexId lidElement,
5429 ttk::SimplexId pTauLidBlock,
5430 ttk::SimplexId pTauLidElement) const {
5431 int lock{0};
5432 saddle<3> &s{saddles2[lidBlock][lidElement]};
5433 do {
5434#pragma omp atomic capture
5435 {
5436 lock = s2Locks[lidBlock][lidElement];
5437 s2Locks[lidBlock][lidElement] = 1;
5438 };
5439 } while(lock == 1);
5440 GlobalBoundary &globalBoundary = globalBoundaries[lidBlock][lidElement];
5441 int size = -recvBoundaryBuffer[i];
5442 if(!globalBoundary.empty()) {
5443 for(int j = 7; j < size; j += 3) {
5444 if(recvBoundaryBuffer[i + j] != ttk::MPIrank_) {
5445 ttk::SimplexId newMax[]
5446 = {recvBoundaryBuffer[i + j + 1], recvBoundaryBuffer[i + j + 2]};
5447 updateMax(
5448 maxPerProcess(recvBoundaryBuffer[i + j], newMax), globalBoundary);
5449 }
5450 }
5451 } else {
5452 for(int j = 7; j < size; j += 3) {
5453 if(recvBoundaryBuffer[i + j] != ttk::MPIrank_) {
5454 ttk::SimplexId newMax[]
5455 = {recvBoundaryBuffer[i + j + 1], recvBoundaryBuffer[i + j + 2]};
5456 globalBoundary.emplace(
5457 maxPerProcess(recvBoundaryBuffer[i + j], newMax));
5458 }
5459 }
5460 }
5461
5462 do {
5463#pragma omp atomic capture
5464 {
5465 lock = s2Locks[pTauLidBlock][pTauLidElement];
5466 s2Locks[pTauLidBlock][pTauLidElement] = 1;
5467 };
5468 } while(lock == 1);
5469
5470 auto &pTauLocalBoundary = localBoundaries[pTauLidBlock][pTauLidElement];
5471 if(s.lidBlock_ != -1 && !localBoundaries[lidBlock][lidElement].empty()) {
5472 auto &s2LocalBoundary = localBoundaries[lidBlock][lidElement];
5473 for(const auto e : pTauLocalBoundary) {
5474 auto ite = s2LocalBoundary.find(e);
5475 if(ite == s2LocalBoundary.end()) {
5476 s2LocalBoundary.emplace(e);
5477 } else {
5478 s2LocalBoundary.erase(ite);
5479 }
5480 }
5481 } else {
5482 localBoundaries[lidBlock][lidElement].insert(
5483 pTauLocalBoundary.begin(), pTauLocalBoundary.end());
5484 }
5485#pragma omp atomic write
5486 s2Locks[pTauLidBlock][pTauLidElement] = 0;
5487#pragma omp atomic write
5488 s2Locks[lidBlock][lidElement] = 0;
5489 if(s.gid_ == -1) {
5490 s.gid_ = recvBoundaryBuffer[i + 1];
5491 for(int j = 0; j < 3; j++) {
5492 s.vOrder_[j] = recvBoundaryBuffer[i + 2 + j];
5493 }
5494 s.lidBlock_ = lidBlock;
5495 s.lidElement_ = lidElement;
5496 }
5497}
5498
5499template <typename GlobalBoundary, typename LocalBoundary>
5500void ttk::DiscreteMorseSandwichMPI::addDistributedEdgeToLocalBoundary(
5501 std::vector<ttk::SimplexId> &recvBoundaryBuffer,
5502 std::vector<std::vector<int>> &s2Locks,
5503 std::vector<std::vector<GlobalBoundary>> &globalBoundaries,
5504 std::vector<std::vector<LocalBoundary>> &localBoundaries,
5505 std::vector<std::vector<saddle<3>>> &saddles2,
5507 ttk::SimplexId lidBlock,
5508 ttk::SimplexId lidElement,
5509 ttk::SimplexId leid1,
5510 ttk::SimplexId leid2) const {
5511 int lock{0};
5512 saddle<3> &s{saddles2[lidBlock][lidElement]};
5513 int size = -recvBoundaryBuffer[i];
5514 do {
5515#pragma omp atomic capture
5516 {
5517 lock = s2Locks[lidBlock][lidElement];
5518 s2Locks[lidBlock][lidElement] = 1;
5519 };
5520 } while(lock == 1);
5521 LocalBoundary &localBoundary = localBoundaries[lidBlock][lidElement];
5522 bool isEmpty = localBoundary.empty();
5523 auto it = localBoundary.find(leid1);
5524 if(it != localBoundary.end()) {
5525 localBoundary.erase(it);
5526 } else {
5527 localBoundary.emplace(leid1);
5528 }
5529 if(leid2 != -1) {
5530 it = localBoundary.find(leid2);
5531 if(it != localBoundary.end()) {
5532 localBoundary.erase(it);
5533 } else {
5534 localBoundary.emplace(leid2);
5535 }
5536 }
5537 if(((s.lidBlock_ == -1) || isEmpty)) {
5538 GlobalBoundary &globalBoundary = globalBoundaries[lidBlock][lidElement];
5539 for(int j = 7; j < size; j += 3) {
5540 if(recvBoundaryBuffer[i + j] != ttk::MPIrank_) {
5541 ttk::SimplexId newMax[]
5542 = {recvBoundaryBuffer[i + j + 1], recvBoundaryBuffer[i + j + 2]};
5543 auto m = maxPerProcess(recvBoundaryBuffer[i + j], newMax);
5544 auto itg = std::find(globalBoundary.begin(), globalBoundary.end(), m);
5545 if(itg == globalBoundary.end()) {
5546 globalBoundary.emplace(m);
5547 }
5548 }
5549 }
5550 }
5551#pragma omp atomic write
5552 s2Locks[lidBlock][lidElement] = 0;
5553 if(s.gid_ == -1) {
5554 s.gid_ = recvBoundaryBuffer[i + 1];
5555 for(int j = 0; j < 3; j++) {
5556 s.vOrder_[j] = recvBoundaryBuffer[i + 2 + j];
5557 }
5558 s.lidBlock_ = lidBlock;
5559 s.lidElement_ = lidElement;
5560 }
5561}
5562
5563template <typename triangulationType,
5564 typename GlobalBoundary,
5565 typename LocalBoundary,
5566 typename compareEdges>
5567void ttk::DiscreteMorseSandwichMPI::receiveBoundaryUpdate(
5568 std::vector<ttk::SimplexId> &recvBoundaryBuffer,
5569 std::vector<std::vector<int>> &s2Locks,
5570 std::vector<std::vector<GlobalBoundary>> &globalBoundaries,
5571 std::vector<std::vector<LocalBoundary>> &localBoundaries,
5572 std::vector<std::vector<saddle<3>>> &saddles2,
5573 triangulationType &triangulation,
5574 compareEdges &cmpEdges) const {
5575 for(ttk::SimplexId i = 0;
5576 i < static_cast<ttk::SimplexId>(recvBoundaryBuffer.size()); i++) {
5577 if(recvBoundaryBuffer[i] < -1) {
5578 int lock;
5579 ttk::SimplexId size = -recvBoundaryBuffer[i];
5580 auto it = globalToLocalSaddle2_.find(recvBoundaryBuffer[i + 1]);
5581 ttk::SimplexId lid;
5582 if(it != globalToLocalSaddle2_.end()) {
5583 lid = it->second;
5584 } else {
5585 if(currentLastElement_ >= blockSize_) {
5586 currentLastBlock_++;
5587 saddles2[currentLastBlock_].resize(blockSize_);
5588 s2Locks[currentLastBlock_].resize(blockSize_, 0);
5589 globalBoundaries[currentLastBlock_].resize(blockSize_);
5590 localBoundaries[currentLastBlock_].resize(
5591 blockSize_, LocalBoundary(cmpEdges));
5592 currentLastElement_ = 0;
5593 }
5594 lid = firstBlockSize_ + currentLastElement_
5595 + blockSize_ * (currentLastBlock_ - 1);
5596 globalToLocalSaddle2_[recvBoundaryBuffer[i + 1]] = lid;
5597 currentLastElement_++;
5598 }
5599 ttk::SimplexId lidBlock;
5600 ttk::SimplexId lidElement;
5601 getLid(lid, lidBlock, lidElement);
5602 // This is a simple max update
5603 if(size == 4) {
5604 ttk::SimplexId rank = recvBoundaryBuffer[i + 2];
5605 ttk::SimplexId newMax[]
5606 = {recvBoundaryBuffer[i + 3], recvBoundaryBuffer[i + 4]};
5607 // atomic lock
5608 do {
5609#pragma omp atomic capture
5610 {
5611 lock = s2Locks[lidBlock][lidElement];
5612 s2Locks[lidBlock][lidElement] = 1;
5613 };
5614 } while(lock == 1);
5615 auto m = maxPerProcess(rank, newMax);
5616 auto itg = std::find(globalBoundaries[lidBlock][lidElement].begin(),
5617 globalBoundaries[lidBlock][lidElement].end(), m);
5618
5619 if(itg != globalBoundaries[lidBlock][lidElement].end()) {
5620 globalBoundaries[lidBlock][lidElement].erase(itg);
5621 }
5622 if(!(newMax[0] == -1 && newMax[1] == -1)) {
5623 globalBoundaries[lidBlock][lidElement].emplace(
5624 maxPerProcess(rank, newMax));
5625 }
5626#pragma omp atomic write
5627 s2Locks[lidBlock][lidElement] = 0;
5628 } else {
5629 // This is either a merge order or an addition of local edges
5630 if(recvBoundaryBuffer[i + 5] == -1) {
5631 // This is a merge order
5632 ttk::SimplexId pTau = recvBoundaryBuffer[i + 6];
5633 auto itgl = globalToLocalSaddle2_.find(pTau);
5634 if(itgl != globalToLocalSaddle2_.end()) {
5635 // pTau is present on this process, therefore both the local and
5636 // global boundary need to be updated
5637 ttk::SimplexId pTauLid = itgl->second;
5638 ttk::SimplexId pTauLidBlock;
5639 ttk::SimplexId pTauLidElement;
5640 getLid(pTauLid, pTauLidBlock, pTauLidElement);
5641 // s is present
5642 this->mergeDistributedBoundary(
5643 recvBoundaryBuffer, s2Locks, globalBoundaries, localBoundaries,
5644 saddles2, i, lidBlock, lidElement, pTauLidBlock, pTauLidElement);
5645 } else {
5646 // pTau is not present on this process, therefore only the global
5647 // boundary needs to be update s is necessarily present
5648 saddle<3> &s{saddles2[lidBlock][lidElement]};
5649 if(s.gid_ == -1) {
5650 s.gid_ = recvBoundaryBuffer[i + 1];
5651 for(int j = 0; j < 3; j++) {
5652 s.vOrder_[j] = recvBoundaryBuffer[i + 2 + j];
5653 }
5654 s.lidBlock_ = lidBlock;
5655 s.lidElement_ = lidElement;
5656 }
5657 do {
5658#pragma omp atomic capture
5659 {
5660 lock = s2Locks[lidBlock][lidElement];
5661 s2Locks[lidBlock][lidElement] = 1;
5662 };
5663 } while(lock == 1);
5664 GlobalBoundary &globalBoundary
5665 = globalBoundaries[lidBlock][lidElement];
5666 for(int j = 7; j < size; j += 3) {
5667 if(recvBoundaryBuffer[i + j] != ttk::MPIrank_) {
5668 ttk::SimplexId newMax[] = {
5669 recvBoundaryBuffer[i + j + 1], recvBoundaryBuffer[i + j + 2]};
5670 updateMax(maxPerProcess(recvBoundaryBuffer[i + j], newMax),
5671 globalBoundary);
5672 }
5673 }
5674#pragma omp atomic write
5675 s2Locks[lidBlock][lidElement] = 0;
5676 }
5677 } else {
5678 // This is an addition of local edges
5679 ttk::SimplexId leid1
5680 = triangulation.getEdgeLocalId(recvBoundaryBuffer[i + 5]);
5681 ttk::SimplexId leid2
5682 = triangulation.getEdgeLocalId(recvBoundaryBuffer[i + 6]);
5683
5684 this->addDistributedEdgeToLocalBoundary(
5685 recvBoundaryBuffer, s2Locks, globalBoundaries, localBoundaries,
5686 saddles2, i, lidBlock, lidElement, leid1, leid2);
5687 }
5688 }
5689 }
5690 }
5691}
5692
5693template <typename triangulationType>
5694void ttk::DiscreteMorseSandwichMPI::getSaddleSaddlePairs(
5695 std::vector<PersistencePair> &pairs,
5696 const std::vector<SimplexId> &critical1Saddles,
5697 const std::vector<SimplexId> &critical2Saddles,
5698 const std::vector<SimplexId> &crit1SaddlesOrder,
5699 const std::vector<SimplexId> &crit2SaddlesOrder,
5700 const triangulationType &triangulation,
5701 const SimplexId *const offsets) const {
5702
5703 Timer tm2{};
5704 const auto nSadExtrPairs = pairs.size();
5705
5706 // 1- and 2-saddles yet to be paired
5707 std::vector<SimplexId> saddles1Gid{}, saddles2Gid{};
5708 // filter out already paired 1-saddles (edge id)
5709#ifdef TTK_ENABLE_MPI_TIME
5710 ttk::Timer t_mpi;
5711 ttk::startMPITimer(t_mpi, ttk::MPIrank_, ttk::MPIsize_);
5712#endif
5713#pragma omp declare reduction (merge : std::vector<ttk::SimplexId>: omp_out.insert(omp_out.end(), omp_in.begin(), omp_in.end()))
5714#pragma omp parallel for reduction(merge : saddles1Gid) schedule(static)
5715 for(size_t i = 0; i < critical1Saddles.size(); i++) {
5716 const auto s1 = critical1Saddles[i];
5717 ttk::SimplexId gid = triangulation.getEdgeGlobalId(s1);
5718 auto it = globalToLocalSaddle1_.find(triangulation.getEdgeGlobalId(s1));
5719 if(it == globalToLocalSaddle1_.end()) {
5720 saddles1Gid.emplace_back(gid);
5721 } else {
5722 if(saddleToPairedMin_[it->second] < 0) {
5723 saddles1Gid.emplace_back(gid);
5724 }
5725 }
5726 }
5727
5728#pragma omp parallel for reduction(merge : saddles2Gid) schedule(static)
5729 for(size_t i = 0; i < critical2Saddles.size(); i++) {
5730 const auto s2 = critical2Saddles[i];
5731 ttk::SimplexId gid = triangulation.getTriangleGlobalId(s2);
5732 auto it = globalToLocalSaddle2_.find(triangulation.getTriangleGlobalId(s2));
5733 if(it == globalToLocalSaddle2_.end()) {
5734 saddles2Gid.emplace_back(gid);
5735 } else {
5736 if(saddleToPairedMax_[it->second] < 0) {
5737 saddles2Gid.emplace_back(gid);
5738 }
5739 }
5740 }
5741 ttk::SimplexId saddle1Number = saddles1Gid.size();
5742 ttk::SimplexId saddle2Number = saddles2Gid.size();
5743 firstBlockSize_ = saddle2Number;
5744 blockSize_ = std::max(static_cast<ttk::SimplexId>(0.05 * saddle2Number),
5745 static_cast<ttk::SimplexId>(1));
5746
5747 MPI_Datatype MPI_SimplexId = getMPIType(saddle1Number);
5748 MPI_Allreduce(&saddle2Number, &globalSaddle2Counter_, 1, MPI_SimplexId,
5749 MPI_SUM, ttk::MPIcomm_);
5750 this->messageSize_
5751 = std::max(static_cast<ttk::SimplexId>(saddle2Number * 0.0001),
5752 static_cast<ttk::SimplexId>(2));
5753 ttk::SimplexId overallSize
5754 = (globalSaddle2Counter_ - firstBlockSize_) / blockSize_ + 2;
5755 globalToLocalSaddle2_.clear();
5756 std::vector<std::vector<saddle<3>>> saddles2(overallSize);
5757 auto &edgeTrianglePartner{this->edgeTrianglePartner_};
5758 auto &onBoundaryThread{this->onBoundary_};
5759 onBoundaryThread.resize(
5760 threadNumber_, std::vector<bool>(triangulation.getNumberOfEdges(), false));
5761 // one lock per 1-saddle
5762 std::vector<int> s1Locks;
5763 // one lock per 2-saddle
5764 std::vector<std::vector<int>> s2Locks(overallSize, std::vector<int>());
5765#pragma omp parallel master
5766 {
5767#pragma omp task shared(globalToLocalSaddle2_, saddles2Gid)
5768 for(ttk::SimplexId i = 0; i < saddle2Number; i++) {
5769 globalToLocalSaddle2_.emplace(saddles2Gid[i], i);
5770 }
5771#pragma omp task
5772 this->minMaxClear();
5773#pragma omp task
5774 localEdgeToSaddle1_.resize(triangulation.getNumberOfEdges(), -1);
5775#pragma omp task shared(saddles2)
5776 saddles2[0].resize(saddle2Number);
5777#pragma omp task
5778 globalToLocalSaddle1_.clear();
5779#pragma omp task
5780 edgeTrianglePartner.resize(saddle1Number, -1);
5781#pragma omp task
5782 s1Locks.resize(saddle1Number, 0);
5783#pragma omp task
5784 s2Locks[0].resize(saddle2Number, 0);
5785 }
5786#pragma omp parallel for num_threads(threadNumber_) schedule(static)
5787 for(size_t i = 0; i < saddles1Gid.size(); i++) {
5788 ttk::SimplexId lid = triangulation.getEdgeLocalId(saddles1Gid[i]);
5789 localEdgeToSaddle1_[lid] = i;
5790 }
5791
5792#pragma omp parallel for num_threads(threadNumber_) schedule(static)
5793 for(ttk::SimplexId i = 0; i < saddle2Number; i++) {
5794 auto &s2{saddles2[0][i]};
5795 s2.gid_ = saddles2Gid[i];
5796 ttk::SimplexId lid = triangulation.getTriangleLocalId(s2.gid_);
5797 s2.lidElement_ = i;
5798 s2.lidBlock_ = 0;
5799 s2.order_ = crit2SaddlesOrder[lid];
5800 fillTriangleOrder(lid, offsets, triangulation, s2.vOrder_);
5801 }
5802 // sort every triangulation edges by filtration order
5803 const auto &edgesFiltrOrder{crit1SaddlesOrder};
5804
5805 std::function<bool(const ttk::SimplexId, const ttk::SimplexId)> cmpEdges
5806 = [&edgesFiltrOrder](const ttk::SimplexId a, const ttk::SimplexId b) {
5807 return edgesFiltrOrder[a] > edgesFiltrOrder[b];
5808 };
5809 sendBoundaryBufferLock_ = std::vector<Lock>(ttk::MPIsize_);
5810 sendComputeBufferLock_ = std::vector<Lock>(ttk::MPIsize_);
5811 for(int i = 0; i < 2; i++) {
5812 sendBoundaryBuffer_[i].resize(ttk::MPIsize_);
5813 sendComputeBuffer_[i].resize(ttk::MPIsize_);
5814 }
5815 using GlobalBoundary = std::set<maxPerProcess, std::less<>>;
5816 using LocalBoundary = std::set<ttk::SimplexId, decltype(cmpEdges)>;
5817 std::vector<std::vector<GlobalBoundary>> s2GlobalBoundaries(overallSize);
5818 s2GlobalBoundaries[0].resize(saddle2Number);
5819 taskCounter_ = saddle2Number;
5820 messageCounter_ = 0;
5821 finishedPropagationCounter_ = 0;
5822 currentLastElement_ = 0;
5823 currentLastBlock_ = 1;
5824 currentBuffer_ = 0;
5825 std::vector<std::vector<LocalBoundary>> s2LocalBoundaries(
5826 overallSize, std::vector<LocalBoundary>(0, LocalBoundary(cmpEdges)));
5827 s2LocalBoundaries[0].resize(saddle2Number, LocalBoundary(cmpEdges));
5828#ifdef TTK_ENABLE_MPI_TIME
5829 double elapsedTime = ttk::endMPITimer(t_mpi, ttk::MPIrank_, ttk::MPIsize_);
5830 if(ttk::MPIrank_ == 0) {
5831 printMsg("Total preprocessing performed using "
5832 + std::to_string(ttk::MPIsize_)
5833 + " MPI processes lasted: " + std::to_string(elapsedTime));
5834 }
5835 ttk::startMPITimer(t_mpi, ttk::MPIrank_, ttk::MPIsize_);
5836#endif
5837 ttk::SimplexId taskSize
5838 = std::min(saddle2Number + 1, static_cast<ttk::SimplexId>(10));
5839 ttk::SimplexId taskNum
5840 = static_cast<ttk::SimplexId>(saddle2Number / taskSize) + 1;
5841#pragma omp parallel num_threads(threadNumber_) shared( \
5842 onBoundaryThread, s1Locks, s2Locks, s2GlobalBoundaries, s2LocalBoundaries, \
5843 localEdgeToSaddle1_, saddles2, edgeTrianglePartner)
5844 {
5845#pragma omp single nowait
5846 {
5847 for(ttk::SimplexId i = 0; i < taskNum; i++) {
5848#pragma omp task firstprivate(i)
5849 {
5850 for(ttk::SimplexId j = 0; j < taskSize; j++) {
5851 ttk::SimplexId lid = i * taskSize + j;
5852 if(lid < saddle2Number) {
5853 const auto s2 = saddles2[0][lid];
5854 this->eliminateBoundariesSandwich(
5855 s2, onBoundaryThread, s2GlobalBoundaries, s2LocalBoundaries,
5856 edgeTrianglePartner, s1Locks, s2Locks, saddles2,
5857 localEdgeToSaddle1_, triangulation, offsets);
5858 }
5859 }
5860 }
5861 }
5862 // Start communication phase
5863 if(ttk::MPIsize_ > 1) {
5864 saddles2[currentLastBlock_].resize(blockSize_);
5865 s2Locks[currentLastBlock_].resize(blockSize_, 0);
5866 s2GlobalBoundaries[currentLastBlock_].resize(blockSize_);
5867 s2LocalBoundaries[currentLastBlock_].resize(
5868 blockSize_, LocalBoundary(cmpEdges));
5869 std::vector<std::vector<ttk::SimplexId>> recvBoundaryBuffer(
5871 std::vector<std::vector<ttk::SimplexId>> recvComputeBuffer(
5873 ttk::SimplexId totalFinishedPropagationCounter{0};
5874 ttk::SimplexId tempTask;
5875 ttk::SimplexId messageCnt;
5876 while(totalFinishedPropagationCounter < globalSaddle2Counter_) {
5877 bool flag = true;
5878 while(flag) {
5879#pragma omp atomic read
5880 messageCnt = messageCounter_;
5881 if(messageCnt > messageSize_) {
5882 flag = false;
5883 } else {
5884#pragma omp atomic read
5885 tempTask = taskCounter_;
5886 if(tempTask == 0) {
5887 flag = false;
5888 }
5889 }
5890 }
5891#pragma omp atomic write
5892 messageCounter_ = 0;
5893 std::vector<MPI_Request> sendRequests(ttk::MPIsize_ - 1);
5894 std::vector<MPI_Request> recvRequests(ttk::MPIsize_ - 1);
5895 std::vector<MPI_Status> sendStatus(ttk::MPIsize_ - 1);
5896 std::vector<MPI_Status> recvStatus(ttk::MPIsize_ - 1);
5897 std::vector<std::array<ttk::SimplexId, 2>> sendMessageSize(
5898 ttk::MPIsize_, {0, 0});
5899 std::vector<std::array<ttk::SimplexId, 2>> recvMessageSize(
5900 ttk::MPIsize_, {0, 0});
5901 std::vector<int> recvCompleted(ttk::MPIsize_ - 1, 0);
5902 std::vector<int> sendCompleted(ttk::MPIsize_ - 1, 0);
5903 int recvPerformedCount = 0;
5904 int recvPerformedCountTotal = 0;
5905 int tempCurrentBuffer;
5906#pragma omp atomic capture
5907 {
5908 tempCurrentBuffer = currentBuffer_;
5909 currentBuffer_ = 1 - currentBuffer_;
5910 }
5911#pragma omp atomic read
5912 totalFinishedPropagationCounter = finishedPropagationCounter_;
5913 for(int i = 0; i < ttk::MPIsize_; i++) {
5914 // Send size of Sendbuffer
5915 if(i != ttk::MPIrank_) {
5916 sendBoundaryBufferLock_[i].lock();
5917 sendMessageSize[i][0]
5918 = sendBoundaryBuffer_[tempCurrentBuffer][i].size();
5919 sendBoundaryBufferLock_[i].unlock();
5920 sendComputeBufferLock_[i].lock();
5921 sendMessageSize[i][1]
5922 = sendComputeBuffer_[tempCurrentBuffer][i].size();
5923 sendComputeBufferLock_[i].unlock();
5924 }
5925 }
5926 MPI_Alltoall(sendMessageSize.data(), 2, MPI_SimplexId,
5927 recvMessageSize.data(), 2, MPI_SimplexId, ttk::MPIcomm_);
5928 // Stop condition computation
5929 MPI_Allreduce(MPI_IN_PLACE, &totalFinishedPropagationCounter, 1,
5930 MPI_SimplexId, MPI_SUM, ttk::MPIcomm_);
5931 messageSize_ = std::max(
5932 static_cast<ttk::SimplexId>(2),
5933 std::min(messageSize_,
5934 static_cast<ttk::SimplexId>(
5935 (globalSaddle2Counter_ - totalFinishedPropagationCounter)
5936 * 0.1)));
5937 std::vector<MPI_Request> sendRequestsData(ttk::MPIsize_ - 1);
5938 std::vector<MPI_Request> recvRequestsData(ttk::MPIsize_ - 1);
5939 std::vector<MPI_Status> recvStatusData(ttk::MPIsize_ - 1);
5940 int recvCount = 0;
5941 int sendCount = 0;
5942 int r = 0;
5943 for(int i = 0; i < ttk::MPIsize_; i++) {
5944 if((sendMessageSize[i][0] > 0)) {
5945 MPI_Isend(sendBoundaryBuffer_[tempCurrentBuffer][i].data(),
5946 sendMessageSize[i][0], MPI_SimplexId, i, 1,
5947 ttk::MPIcomm_, &sendRequestsData[sendCount]);
5948 sendCount++;
5949 }
5950 if((recvMessageSize[i][0] > 0)) {
5951 recvBoundaryBuffer[i].resize(recvMessageSize[i][0]);
5952 MPI_Irecv(recvBoundaryBuffer[i].data(), recvMessageSize[i][0],
5953 MPI_SimplexId, i, 1, ttk::MPIcomm_,
5954 &recvRequestsData[recvCount]);
5955 recvCount++;
5956 }
5957 }
5958 recvPerformedCountTotal = 0;
5959 while(recvPerformedCountTotal < recvCount) {
5960 MPI_Waitsome(recvCount, recvRequestsData.data(),
5961 &recvPerformedCount, recvCompleted.data(),
5962 recvStatusData.data());
5963 if(recvPerformedCount > 0) {
5964 for(int i = 0; i < recvPerformedCount; i++) {
5965 r = recvStatusData[i].MPI_SOURCE;
5966 receiveBoundaryUpdate(recvBoundaryBuffer[r], s2Locks,
5967 s2GlobalBoundaries, s2LocalBoundaries,
5968 saddles2, triangulation, cmpEdges);
5969 }
5970 recvPerformedCountTotal += recvPerformedCount;
5971 }
5972 }
5973 MPI_Waitall(sendCount, sendRequestsData.data(), MPI_STATUSES_IGNORE);
5974 for(int i = 0; i < ttk::MPIsize_; i++) {
5975 // Send size of Sendbuffer
5976 if(i != ttk::MPIrank_) {
5977 recvBoundaryBuffer[i].clear();
5978 sendBoundaryBuffer_[tempCurrentBuffer][i].clear();
5979 }
5980 }
5981 // Exchange computation signals
5982 recvPerformedCount = 0;
5983 recvPerformedCountTotal = 0;
5984 recvCount = 0;
5985 sendCount = 0;
5986 for(int i = 0; i < ttk::MPIsize_; i++) {
5987 if((sendMessageSize[i][1] > 0)) {
5988 MPI_Isend(sendComputeBuffer_[tempCurrentBuffer][i].data(),
5989 sendMessageSize[i][1], MPI_SimplexId, i, 1,
5990 ttk::MPIcomm_, &sendRequestsData[sendCount]);
5991 sendCount++;
5992 }
5993 if((recvMessageSize[i][1] > 0)) {
5994 recvComputeBuffer[i].resize(recvMessageSize[i][1]);
5995 MPI_Irecv(recvComputeBuffer[i].data(), recvMessageSize[i][1],
5996 MPI_SimplexId, i, 1, ttk::MPIcomm_,
5997 &recvRequestsData[recvCount]);
5998 recvCount++;
5999 }
6000 }
6001 recvPerformedCountTotal = 0;
6002 while(recvPerformedCountTotal < recvCount) {
6003 MPI_Waitsome(recvCount, recvRequestsData.data(),
6004 &recvPerformedCount, recvCompleted.data(),
6005 recvStatusData.data());
6006 if(recvPerformedCount > 0) {
6007 for(int i = 0; i < recvPerformedCount; i++) {
6008 r = recvStatusData[i].MPI_SOURCE;
6009#pragma omp atomic update
6010 taskCounter_ += recvMessageSize[r][1];
6011 for(ttk::SimplexId j = 0; j < recvMessageSize[r][1]; j++) {
6012 ttk::SimplexId lid
6013 = globalToLocalSaddle2_.find(recvComputeBuffer[r][j])
6014 ->second;
6015#pragma omp task firstprivate(lid) \
6016 shared(s2GlobalBoundaries, s2LocalBoundaries, edgeTrianglePartner, s1Locks, \
6017 s2Locks, saddles2)
6018 {
6019 ttk::SimplexId lidBlock;
6020 ttk::SimplexId lidElement;
6021 getLid(lid, lidBlock, lidElement);
6022 const auto s2 = saddles2[lidBlock][lidElement];
6023 this->eliminateBoundariesSandwich(
6024 s2, onBoundaryThread, s2GlobalBoundaries,
6025 s2LocalBoundaries, edgeTrianglePartner, s1Locks, s2Locks,
6026 saddles2, localEdgeToSaddle1_, triangulation, offsets);
6027 }
6028 }
6029 }
6030 recvPerformedCountTotal += recvPerformedCount;
6031 }
6032 }
6033 MPI_Waitall(sendCount, sendRequestsData.data(), MPI_STATUSES_IGNORE);
6034 for(int i = 0; i < ttk::MPIsize_; i++) {
6035 if(i != ttk::MPIrank_) {
6036 sendComputeBuffer_[tempCurrentBuffer][i].clear();
6037 recvComputeBuffer[i].clear();
6038 }
6039 }
6040 }
6041 }
6042 }
6043 }
6044#ifdef TTK_ENABLE_MPI_TIME
6045 elapsedTime = ttk::endMPITimer(t_mpi, ttk::MPIrank_, ttk::MPIsize_);
6046 if(ttk::MPIrank_ == 0) {
6047 printMsg("EBS computation performed using " + std::to_string(ttk::MPIsize_)
6048 + " MPI processes lasted: " + std::to_string(elapsedTime));
6049 }
6050 ttk::startMPITimer(t_mpi, ttk::MPIrank_, ttk::MPIsize_);
6051#endif
6052 Timer tmseq{};
6053
6054 // extract saddle-saddle pairs from computed boundaries
6055#pragma omp declare reduction (merge : std::vector<PersistencePair>: omp_out.insert(omp_out.end(), omp_in.begin(), omp_in.end()))
6056#pragma omp parallel for reduction(merge : pairs) schedule(static)
6057 for(size_t i = 0; i < edgeTrianglePartner.size(); ++i) {
6058 if(edgeTrianglePartner[i] != -1) {
6059 const auto s1 = saddles1Gid[i];
6060 ttk::SimplexId lidBlock, lidElement;
6061 getLid(edgeTrianglePartner[i], lidBlock, lidElement);
6062 const auto s2 = saddles2[lidBlock][lidElement].gid_;
6063 // we found a pair
6064 pairs.emplace_back(s1, s2, 1);
6065 }
6066 }
6067#ifdef TTK_ENABLE_MPI_TIME
6068 elapsedTime = ttk::endMPITimer(t_mpi, ttk::MPIrank_, ttk::MPIsize_);
6069 if(ttk::MPIrank_ == 0) {
6070 printMsg("Pairs extraction performed using " + std::to_string(ttk::MPIsize_)
6071 + " MPI processes lasted: " + std::to_string(elapsedTime));
6072 }
6073 ttk::startMPITimer(t_mpi, ttk::MPIrank_, ttk::MPIsize_);
6074#endif
6075
6076 for(ttk::SimplexId i = 0; i < currentLastBlock_ + 1; i++) {
6077#pragma omp parallel for num_threads(threadNumber_)
6078 for(ttk::SimplexId j = 0;
6079 j < static_cast<ttk::SimplexId>(saddles2[i].size()); j++) {
6080 s2LocalBoundaries[i][j].clear();
6081 s2GlobalBoundaries[i][j].clear();
6082 }
6083 }
6084#pragma omp parallel master num_threads(threadNumber_)
6085 {
6086 if(ttk::MPIsize_ > 1) {
6087 for(int i = 0; i < ttk::MPIsize_; i++) {
6088#pragma omp task
6089 this->sendComputeBuffer_[0][i].clear();
6090#pragma omp task
6091 this->sendComputeBuffer_[1][i].clear();
6092#pragma omp task
6093 this->sendBoundaryBuffer_[0][i].clear();
6094#pragma omp task
6095 this->sendBoundaryBuffer_[1][i].clear();
6096 }
6097 }
6098#pragma omp taskwait
6099#pragma omp task
6100 sendComputeBuffer_ = {};
6101#pragma omp task
6102 sendBoundaryBuffer_ = {};
6103#pragma omp task
6104 s2LocalBoundaries = {};
6105#pragma omp task
6106 s2GlobalBoundaries = {};
6107#pragma omp task
6108 saddles2 = {};
6109#pragma omp task
6110 s1Locks = {};
6111#pragma omp task
6112 s2Locks = {};
6113#pragma omp task
6114 saddles1Gid = {};
6115#pragma omp task
6116 saddles2Gid = {};
6117#pragma omp task
6118 globalToLocalSaddle1_ = {};
6119#pragma omp task
6120 globalToLocalSaddle2_ = {};
6121#pragma omp task
6122 localEdgeToSaddle1_ = {};
6123#pragma omp task
6124 edgeTrianglePartner = {};
6125#pragma omp task
6126 onBoundaryThread = {};
6127 }
6128#ifdef TTK_ENABLE_MPI_TIME
6129 elapsedTime = ttk::endMPITimer(t_mpi, ttk::MPIrank_, ttk::MPIsize_);
6130 if(ttk::MPIrank_ == 0) {
6131 printMsg("D1 memory clean up performed using "
6132 + std::to_string(ttk::MPIsize_)
6133 + " MPI processes lasted: " + std::to_string(elapsedTime));
6134 }
6135#endif
6136 auto nSadSadPairs = pairs.size() - nSadExtrPairs;
6137 MPI_Allreduce(
6138 MPI_IN_PLACE, &nSadSadPairs, 1, MPI_SimplexId, MPI_SUM, ttk::MPIcomm_);
6139 if(ttk::MPIrank_ == 0)
6140 this->printMsg(
6141 "Computed " + std::to_string(nSadSadPairs) + " saddle-saddle pairs", 1.0,
6142 tm2.getElapsedTime(), this->threadNumber_);
6143}
6144
6145template <typename triangulationType>
6146void ttk::DiscreteMorseSandwichMPI::extractCriticalCells(
6147 std::array<std::vector<SimplexId>, 4> &criticalCellsByDim,
6148 std::array<std::vector<SimplexId>, 4> &critCellsOrder,
6149 const SimplexId *const offsets,
6150 const triangulationType &triangulation,
6151 const bool sortEdges) const {
6152
6153 Timer tm{};
6154 this->dg_.getCriticalPoints(criticalCellsByDim, triangulation);
6155
6156 const auto dim{this->dg_.getDimensionality()};
6157 std::vector<EdgeSimplex> critEdges;
6158 std::vector<TriangleSimplex> critTriangles;
6159 std::vector<TetraSimplex> critTetras;
6160 // memory allocations
6161#ifdef TTK_ENABLE_OPENMP
6162#pragma omp parallel master num_threads(threadNumber_) \
6163 shared(criticalCellsByDim)
6164#endif
6165 {
6166 if(dim > 2) {
6167 if(!sortEdges) {
6168#ifdef TTK_ENABLE_OPENMP
6169#pragma omp task shared(critEdges)
6170#endif
6171 critEdges.resize(criticalCellsByDim[1].size());
6172 } else {
6173#ifdef TTK_ENABLE_OPENMP
6174#pragma omp task shared(critEdges)
6175#endif
6176 critEdges.resize(triangulation.getNumberOfEdges());
6177 }
6178 }
6179#ifdef TTK_ENABLE_OPENMP
6180#pragma omp task shared(critTriangles)
6181#endif
6182 critTriangles.resize(criticalCellsByDim[2].size());
6183#ifdef TTK_ENABLE_OPENMP
6184#pragma omp task shared(critTetras)
6185#endif
6186 critTetras.resize(criticalCellsByDim[3].size());
6187#ifdef TTK_ENABLE_OPENMP
6188#pragma omp task shared(critCellsOrder_)
6189#endif
6190 this->critCellsOrder_[1].resize(
6191 this->dg_.getNumberOfCells(1, triangulation), -1);
6192 if(dim > 1) {
6193#ifdef TTK_ENABLE_OPENMP
6194#pragma omp task shared(critCellsOrder_)
6195#endif
6196 this->critCellsOrder_[2].resize(
6197 this->dg_.getNumberOfCells(2, triangulation), -1);
6198 }
6199
6200 if(dim > 2) {
6201#ifdef TTK_ENABLE_OPENMP
6202#pragma omp task shared(critCellsOrder_)
6203#endif
6204 this->critCellsOrder_[3].resize(
6205 this->dg_.getNumberOfCells(3, triangulation), -1);
6206 }
6207 }
6208#ifdef TTK_ENABLE_OPENMP
6209#pragma omp parallel num_threads(threadNumber_)
6210#endif // TTK_ENABLE_OPENMP
6211 {
6212 if(sortEdges) {
6213#ifdef TTK_ENABLE_OPENMP
6214#pragma omp for nowait
6215#endif // TTK_ENABLE_OPENMP
6216 for(size_t i = 0; i < critEdges.size(); ++i) {
6217 critEdges[i].fillEdge(i, offsets, triangulation);
6218 }
6219 } else {
6220#ifdef TTK_ENABLE_OPENMP
6221#pragma omp for nowait
6222#endif // TTK_ENABLE_OPENMP
6223 for(size_t i = 0; i < critEdges.size(); ++i) {
6224 critEdges[i].fillEdge(criticalCellsByDim[1][i], offsets, triangulation);
6225 }
6226 }
6227
6228#ifdef TTK_ENABLE_OPENMP
6229#pragma omp for nowait
6230#endif // TTK_ENABLE_OPENMP
6231 for(size_t i = 0; i < critTriangles.size(); ++i) {
6232 critTriangles[i].fillTriangle(
6233 criticalCellsByDim[2][i], offsets, triangulation);
6234 }
6235
6236#ifdef TTK_ENABLE_OPENMP
6237#pragma omp for
6238#endif // TTK_ENABLE_OPENMP
6239 for(size_t i = 0; i < critTetras.size(); ++i) {
6240 critTetras[i].fillTetra(criticalCellsByDim[3][i], offsets, triangulation);
6241 }
6242 }
6243
6244 TTK_PSORT(this->threadNumber_, critEdges.begin(), critEdges.end());
6245 TTK_PSORT(this->threadNumber_, critTriangles.begin(), critTriangles.end());
6246 TTK_PSORT(this->threadNumber_, critTetras.begin(), critTetras.end());
6247
6248#ifdef TTK_ENABLE_OPENMP
6249#pragma omp parallel num_threads(threadNumber_)
6250#endif // TTK_ENABLE_OPENMP
6251 {
6252#ifdef TTK_ENABLE_OPENMP
6253#pragma omp for nowait
6254#endif // TTK_ENABLE_OPENMP
6255 for(size_t i = 0; i < critEdges.size(); ++i) {
6256 critCellsOrder[1][critEdges[i].id_] = i;
6257 }
6258
6259#ifdef TTK_ENABLE_OPENMP
6260#pragma omp for nowait
6261#endif // TTK_ENABLE_OPENMP
6262 for(size_t i = 0; i < critTriangles.size(); ++i) {
6263 criticalCellsByDim[2][i] = critTriangles[i].id_;
6264 critCellsOrder[2][critTriangles[i].id_] = i;
6265 }
6266
6267#ifdef TTK_ENABLE_OPENMP
6268#pragma omp for
6269#endif // TTK_ENABLE_OPENMP
6270 for(size_t i = 0; i < critTetras.size(); ++i) {
6271 criticalCellsByDim[3][i] = critTetras[i].id_;
6272 critCellsOrder[3][critTetras[i].id_] = i;
6273 }
6274 }
6275
6276 if(sortEdges) {
6277 TTK_PSORT(this->threadNumber_, criticalCellsByDim[1].begin(),
6278 criticalCellsByDim[1].end(),
6279 [&critCellsOrder](const SimplexId a, const SimplexId b) {
6280 return critCellsOrder[1][a] < critCellsOrder[1][b];
6281 });
6282 } else {
6283#ifdef TTK_ENABLE_OPENMP
6284#pragma omp parallel for num_threads(threadNumber_)
6285#endif // TTK_ENABLE_OPENMP
6286 for(size_t i = 0; i < critEdges.size(); ++i) {
6287 criticalCellsByDim[1][i] = critEdges[i].id_;
6288 }
6289 }
6290}
6291
6292template <typename triangulationType>
6293int ttk::DiscreteMorseSandwichMPI::computePersistencePairs(
6294 std::vector<PersistencePair> &pairs,
6295 const SimplexId *const offsets,
6296 const triangulationType &triangulation,
6297 const bool ignoreBoundary,
6298 const bool compute2SaddlesChildren) {
6299
6300#ifdef TTK_ENABLE_MPI_TIME
6301 ttk::Timer t_mpi;
6302 ttk::startMPITimer(t_mpi, ttk::MPIrank_, ttk::MPIsize_);
6303#endif
6304 Timer tm{};
6305 pairs.clear();
6306 const auto dim = this->dg_.getDimensionality();
6307 this->Compute2SaddlesChildren = compute2SaddlesChildren;
6308
6309 // get every critical cell sorted them by dimension
6310 std::array<std::vector<SimplexId>, 4> criticalCellsByDim{};
6311 // holds the critical cells order
6312 auto &critCellsOrder{this->critCellsOrder_};
6313
6314 this->extractCriticalCells(
6315 criticalCellsByDim, critCellsOrder, offsets, triangulation, dim == 3);
6316
6317#ifdef TTK_ENABLE_MPI_TIME
6318 double elapsedTime = ttk::endMPITimer(t_mpi, ttk::MPIrank_, ttk::MPIsize_);
6319 if(ttk::MPIrank_ == 0) {
6320 printMsg("Extract critical cells performed using "
6321 + std::to_string(ttk::MPIsize_)
6322 + " MPI processes lasted: " + std::to_string(elapsedTime));
6323 }
6324#endif
6325#ifdef TTK_ENABLE_MPI_TIME
6326 ttk::Timer t_int;
6327 ttk::startMPITimer(t_int, ttk::MPIrank_, ttk::MPIsize_);
6328#endif
6329
6330 // connected components (global min/max pair)
6331 size_t nConnComp{};
6332 if(dim > 2 && UseTasks) {
6333 pairs.reserve(criticalCellsByDim[0].size()
6334 + criticalCellsByDim[dim].size());
6335 int minSadThreadNumber = std::max(1, static_cast<int>(threadNumber_ / 2));
6336 int maxSadThreadNumber = std::max(1, threadNumber_ - minSadThreadNumber);
6337 int taskNumber = std::min(2, threadNumber_);
6338 omp_set_max_active_levels(100);
6339 std::vector<PersistencePair> sadMaxPairs;
6340 MPI_Comm minSadComm;
6341 MPI_Comm_dup(ttk::MPIcomm_, &minSadComm);
6342 MPI_Comm sadMaxComm;
6343 MPI_Comm_dup(ttk::MPIcomm_, &sadMaxComm);
6344#pragma omp parallel master num_threads(taskNumber) \
6345 firstprivate(dim, sadMaxComm, minSadComm)
6346 {
6347#pragma omp task
6348 {
6349 this->getMinSaddlePairs(pairs, criticalCellsByDim[1], critCellsOrder[1],
6350 criticalCellsByDim[0], offsets, nConnComp,
6351 triangulation, minSadComm, minSadThreadNumber);
6352 }
6353 // saddle - maxima pairs
6354#pragma omp task
6355 {
6356 this->getMaxSaddlePairs(
6357 sadMaxPairs, criticalCellsByDim[dim - 1], critCellsOrder[dim - 1],
6358 criticalCellsByDim[dim], critCellsOrder[dim], triangulation,
6359 ignoreBoundary, offsets, sadMaxComm, maxSadThreadNumber);
6360 }
6361 }
6362 omp_set_max_active_levels(1);
6363 MPI_Comm_free(&minSadComm);
6364 MPI_Comm_free(&sadMaxComm);
6365 pairs.insert(pairs.end(), sadMaxPairs.begin(), sadMaxPairs.end());
6366 } else {
6367 // minima - saddle pairs
6368 this->getMinSaddlePairs(pairs, criticalCellsByDim[1], critCellsOrder[1],
6369 criticalCellsByDim[0], offsets, nConnComp,
6370 triangulation, ttk::MPIcomm_, threadNumber_);
6371 // saddle - maxima pairs
6372 this->getMaxSaddlePairs(pairs, criticalCellsByDim[dim - 1],
6373 critCellsOrder[dim - 1], criticalCellsByDim[dim],
6374 critCellsOrder[dim], triangulation, ignoreBoundary,
6375 offsets, ttk::MPIcomm_, threadNumber_);
6376 }
6377
6378#ifdef TTK_ENABLE_MPI_TIME
6379 elapsedTime = ttk::endMPITimer(t_int, ttk::MPIrank_, ttk::MPIsize_);
6380 if(ttk::MPIrank_ == 0) {
6381 printMsg("Computation of D0 and D2 pairs performed using "
6382 + std::to_string(ttk::MPIsize_)
6383 + " MPI processes lasted: " + std::to_string(elapsedTime));
6384 }
6385 ttk::startMPITimer(t_int, ttk::MPIrank_, ttk::MPIsize_);
6386#endif
6387 // saddle - saddle pairs
6388 if(dim == 3 && this->ComputeSadSad) {
6389 char computeSaddleSaddles
6390 = !criticalCellsByDim[1].empty() && !criticalCellsByDim[2].empty();
6391 MPI_Allreduce(
6392 MPI_IN_PLACE, &computeSaddleSaddles, 1, MPI_CHAR, MPI_LOR, ttk::MPIcomm_);
6393 if(computeSaddleSaddles) {
6394 this->sadSadLimit_ = static_cast<ttk::SimplexId>(
6395 triangulation.getNumberOfTriangles() * 0.0001);
6396 this->getSaddleSaddlePairs(pairs, criticalCellsByDim[1],
6397 criticalCellsByDim[2], critCellsOrder[1],
6398 critCellsOrder[2], triangulation, offsets);
6399 }
6400 } else {
6401 this->minMaxClear();
6402 }
6403#ifdef TTK_ENABLE_MPI_TIME
6404 elapsedTime = ttk::endMPITimer(t_int, ttk::MPIrank_, ttk::MPIsize_);
6405 if(ttk::MPIrank_ == 0) {
6406 printMsg("Computation of D1 pairs performed using "
6407 + std::to_string(ttk::MPIsize_)
6408 + " MPI processes lasted: " + std::to_string(elapsedTime));
6409 }
6410#endif
6411 /* TODO: implement the following for an execution with explicit triangulation.
6412 The following snippet of code is inherited from the DMS algorithm
6413 It has not been modified for distributed-memory execution.
6414 The following modifications should be performed:
6415 - Checking globally if a saddle has been paired instead of locally
6416 - Computing the statistics globally though reduce operations
6417 Other unforseen modifications may also be required.
6418
6419 if(std::is_same<triangulationType, ttk::ExplicitTriangulation>::value) {
6420 // create infinite pairs from non-paired 1-saddles, 2-saddles and maxima
6421 size_t nHandles{}, nCavities{}, nNonPairedMax{};
6422 if((dim == 2 && !ignoreBoundary && this->ComputeMinSad
6423 && this->ComputeSadMax)
6424 || (dim == 3 && this->ComputeMinSad && this->ComputeSadSad)) {
6425 // non-paired 1-saddles
6426 for(const auto s1 : criticalCellsByDim[1]) {
6427 if(!paired1Saddles[s1]) {
6428 paired1Saddles[s1] = true;
6429 // topological handles
6430 pairs.emplace_back(s1, -1, 1);
6431 nHandles++;
6432 }
6433 }
6434 }
6435 if(dim == 3 && !ignoreBoundary && this->ComputeSadMax
6436 && this->ComputeSadSad) {
6437 // non-paired 2-saddles
6438 for(const auto s2 : criticalCellsByDim[2]) {
6439 if(!paired2Saddles[s2]) {
6440 paired2Saddles[s2] = true;
6441 // cavities
6442 pairs.emplace_back(s2, -1, 2);
6443 nCavities++;
6444 }
6445 }
6446 }
6447 if(dim == 2 && !ignoreBoundary && this->ComputeSadMax) {
6448 // non-paired maxima
6449 for(const auto max : criticalCellsByDim[dim]) {
6450 if(!pairedMaxima[max]) {
6451 pairs.emplace_back(max, -1, 2);
6452 nNonPairedMax++;
6453 }
6454 }
6455 }
6456
6457 int nBoundComp
6458 = (dim == 3 ? nCavities : nHandles) + nConnComp - nNonPairedMax;
6459 nBoundComp = std::max(nBoundComp, 0);
6460
6461 // print Betti numbers
6462 const std::vector<std::vector<std::string>> rows{
6463 {" #Connected components", std::to_string(nConnComp)},
6464 {" #Topological handles", std::to_string(nHandles)},
6465 {" #Cavities", std::to_string(nCavities)},
6466 {" #Boundary components", std::to_string(nBoundComp)},
6467 };
6468
6469 this->printMsg(rows, debug::Priority::DETAIL);
6470 }*/
6471
6472 // free memory
6473 this->clear();
6474
6475#ifdef TTK_ENABLE_MPI_TIME
6476 elapsedTime = ttk::endMPITimer(t_mpi, ttk::MPIrank_, ttk::MPIsize_);
6477 if(ttk::MPIrank_ == 0) {
6478 printMsg("Computation of persistence pairs performed using "
6479 + std::to_string(ttk::MPIsize_)
6480 + " MPI processes lasted: " + std::to_string(elapsedTime));
6481 }
6482#endif
6483
6484 return 0;
6485}
6486#endif // TTK_ENABLE_MPI
#define TTK_FORCE_USE(x)
Force the compiler to use the function/method parameter.
Definition BaseClass.h:57
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
#define TTK_PSORT(NTHREADS,...)
Parallel sort macro.
Definition OpenMP.h:46
TTK DiscreteMorseSandwichMPI processing package.
TTK discreteGradient processing package.
FiltratedEdge max(const FiltratedEdge &a, const FiltratedEdge &b)
TTK base package defining the standard types.
COMMON_EXPORTS int MPIsize_
Definition BaseClass.cpp:10
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
T begin(std::pair< T, T > &p)
Definition ripser.cpp:499
SimplexId id_
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/| (_) |"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)