22#if defined(TTK_ENABLE_MPI) && defined(TTK_ENABLE_OPENMP)
30#include <unordered_map>
37 int setThreadNumber(
const int threadNumber) {
38 threadNumber_ = threadNumber;
40 if(threadNumber_ < 2) {
42 printWrn(
"The distributed persistence computation");
43 printWrn(
"needs at least 2 threads.");
44 printWrn(
"(dedicated communication thread)");
45 printWrn(
"Defaulting to 2 threads.");
56 struct PersistencePair {
65 : birth{b}, death{d}, type{t} {
77 template <
typename triangulationType>
80 const triangulationType &triangulation,
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>());
89 template <
typename triangulationType>
92 const triangulationType &triangulation,
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]];
101 std::sort(vertsOrder, vertsOrder + 3, std::greater<ttk::SimplexId>());
104 template <
typename triangulationType>
107 const triangulationType &triangulation,
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]];
118 std::sort(vertsOrder, vertsOrder + 4, std::greater<ttk::SimplexId>());
136 template <
int sizeExtr>
137 struct vpathFinished {
147 : saddleId_{saddleId}, extremaId_{extremaId}, extremaRank_{rank} {
148 for(
int i = 0; i < sizeExtr; i++) {
156 : saddleId_{saddleId}, extremaId_{extremaId}, extremaRank_{rank} {
159 vpathFinished() : saddleId_{-1}, extremaId_{-1}, extremaRank_{0} {
160 for(
int i = 0; i < sizeExtr; i++) {
165 bool operator==(
const vpathFinished<sizeExtr> &vp) {
166 return this->saddleId_ == vp.saddleId_
167 && this->extremaId_ == vp.extremaId_;
171 void createVpathMPIType(MPI_Datatype &MPI_MessageType)
const {
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);
183 template <
int sizeExtr>
184 void createFinishedVpathMPIType(MPI_Datatype &MPI_MessageType)
const {
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);
209 template <
int sizeExtr,
int sizeSad>
226 char hasBeenModified_{0};
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];
259 for(
int i = 0; i < sizeSad; i++) {
260 sOrder_[i] = sOrder[i];
261 s1Order_[i] = s1Order[i];
262 s2Order_[i] = s2Order[i];
277 this->hasBeenModified_ = 0;
278 for(
int i = 0; i < sizeExtr; i++) {
282 for(
int i = 0; i < sizeSad; i++) {
297 this->sRank_ = sRank;
300 this->hasBeenModified_ = 0;
301 for(
int i = 0; i < sizeExtr; i++) {
305 for(
int i = 0; i < sizeSad; i++) {
306 sOrder_[i] = sOrder[i];
326 this->t1Rank_ = t1Rank;
328 this->sRank_ = sRank;
329 this->s1Rank_ = s1Rank;
331 this->hasBeenModified_ = mod;
332 for(
int i = 0; i < sizeExtr; i++) {
333 t1Order_[i] = t1Order[i];
336 for(
int i = 0; i < sizeSad; i++) {
337 sOrder_[i] = sOrder[i];
338 s1Order_[i] = s1Order[i];
341 ~messageType() =
default;
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];
355 template <
int extrSize,
int sadSize>
356 void createMPIMessageType(MPI_Datatype &MPI_MessageType)
const {
358 MPI_Datatype MPI_SimplexId = getMPIType(
id);
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);
418 : gid_{gid}, lid_{lid}, order_{order}, rank_{rank} {
420 vOrder_[i] = vOrder[i];
430 : gid_{gid}, lid_{lid}, order_{order}, rank_{rank} {
437 bool operator==(
const extremaNode<size> &t1)
const {
438 return this->gid_ == t1.gid_;
441 bool operator!=(
const extremaNode<size> &t1)
const {
442 return this->gid_ != t1.gid_;
444 bool operator<(
const extremaNode<size> &t1)
const {
445 if(this->gid_ == t1.gid_) {
448 if(this->order_ != -1 && t1.order_ != -1) {
449 return this->order_ < t1.order_;
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];
456 return this->gid_ < t1.gid_;
460 struct saddleIdPerProcess {
461 std::vector<ttk::SimplexId> saddleIds_;
479 std::array<ttk::SimplexId, 2> t_{-1, -1};
493 : gid_{gid}, order_{order}, rank_{rank} {
495 vOrder_[i] = vOrder[i];
501 : gid_{gid}, rank_{rank} {
503 vOrder_[i] = vOrder[i];
507 bool operator==(
const saddleEdge<size> &s1)
const {
508 return this->gid_ == s1.gid_;
511 bool operator<(
const saddleEdge<size> &s1)
const {
512 if(this->gid_ == s1.gid_) {
515 if(this->order_ != -1 && s1.order_ != -1) {
516 return this->order_ < s1.order_;
518 for(
int i = 0; i < size; i++) {
519 if(this->vOrder_[i] != s1.vOrder_[i]) {
520 return this->vOrder_[i] < s1.vOrder_[i];
523 return this->gid_ < s1.gid_;
546 bool operator==(
const saddle<size> &s1) {
547 return this->gid_ == s1.gid_;
550 bool operator<(
const saddle<size> s1)
const {
551 if(this->gid_ == s1.gid_) {
554 if(this->order_ != -1 && s1.order_ != -1) {
555 return this->order_ < s1.order_;
557 for(
int i = 0; i < size; i++) {
558 if(this->vOrder_[i] != s1.vOrder_[i]) {
559 return this->vOrder_[i] < s1.vOrder_[i];
562 return this->gid_ < s1.gid_;
565 bool operator>(
const saddle<size> s1)
const {
566 if(this->gid_ == s1.gid_) {
569 if(this->order_ != -1 && s1.order_ != -1) {
570 return this->order_ > s1.order_;
572 for(
int i = 0; i < size; i++) {
573 if(this->vOrder_[i] != s1.vOrder_[i]) {
574 return this->vOrder_[i] > s1.vOrder_[i];
577 return this->gid_ > s1.gid_;
585 struct maxPerProcess {
590 for(
int i = 0; i < 2; i++) {
596 for(
int i = 0; i < 2; i++) {
601 bool operator==(
const maxPerProcess m1)
const {
602 return this->proc_ == m1.proc_;
605 bool operator<(
const maxPerProcess b)
const {
606 if(this->proc_ == b.proc_) {
609 for(
int i = 0; i < 2; i++) {
610 if(this->max_[i] != b.max_[i]) {
611 return this->max_[i] > b.max_[i];
624 template <
typename GlobalBoundary>
625 void updateMax(maxPerProcess m, GlobalBoundary &maxBoundary)
const;
633 template <
typename GlobalBoundary>
636 GlobalBoundary &maxBoundary)
const;
645 template <
typename GlobalBoundary,
typename LocalBoundary>
646 bool isEmpty(LocalBoundary &localBoundary,
647 GlobalBoundary &globalBoundary)
const;
657 template <
typename LocalBoundary>
660 LocalBoundary &localBoundary)
const;
670 template <
typename GlobalBoundary>
671 void updateMaxBoundary(
const SimplexId s2,
673 GlobalBoundary &boundary,
687 template <
typename GlobalBoundary>
688 void updateLocalBoundary(
const saddle<3> &s2,
692 GlobalBoundary &globalBoundary,
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,
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;
767 template <
typename triangulationType,
768 typename GlobalBoundary,
769 typename LocalBoundary>
770 void addEdgeToBoundary(
773 std::vector<bool> &onBoundary,
774 GlobalBoundary &globalBoundaryIds,
775 LocalBoundary &localBoundaryIds,
776 const triangulationType &triangulation,
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(
798 GlobalBoundary &globalBoundaryIds,
799 const triangulationType &triangulation,
800 std::vector<std::pair<ttk::SimplexId, ttk::SimplexId>> &ghostEdges,
801 std::vector<bool> &hasChangedMax)
const;
803 inline void preconditionTriangulation(AbstractTriangulation *
const data) {
804 this->dg_.preconditionTriangulation(data);
807 inline void setInputOffsets(
const SimplexId *
const offsets) {
808 this->dg_.setInputOffsets(offsets);
811 inline void setComputeMinSad(
const bool data) {
812 this->ComputeMinSad = data;
814 inline void setComputeSadSad(
const bool data) {
815 this->ComputeSadSad = data;
817 inline void setComputeSadMax(
const bool data) {
818 this->ComputeSadMax = data;
821 template <
typename triangulationType>
822 inline int buildGradient(
const void *
const scalars,
823 const size_t scalarsMTime,
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);
846 this->dg_ = std::move(dg);
848 this->dg_.setLocalGradient();
851 return std::move(this->dg_);
854 void setUseTasks(
bool useTasks) {
855 this->UseTasks = useTasks;
858 template <
typename triangulationType>
860 getCellGreaterVertex(
const dcg::Cell &c,
861 const triangulationType &triangulation) {
862 return this->dg_.getCellGreaterVertex(c, triangulation);
865 inline const std::vector<std::vector<SimplexId>> &
866 get2SaddlesChildren()
const {
867 return this->s2Children_;
884 template <
typename triangulationType>
885 int computePersistencePairs(std::vector<PersistencePair> &pairs,
887 const triangulationType &triangulation,
888 const bool ignoreBoundary,
889 const bool compute2SaddlesChildren =
false);
897 struct GeneratorType {
899 std::vector<SimplexId> boundary;
904 std::array<SimplexId, 2> critVertsIds;
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,
924 std::vector<std::array<extremaNode<1>, 2>> &res,
925 std::vector<std::vector<char>> &ghostPresence,
927 &localGhostPresenceMap,
928 std::vector<std::vector<saddleIdPerProcess>> &ghostPresenceVector,
930 int localThreadNumber)
const;
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,
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,
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,
1097 template <
int sizeExtr,
typename GLI>
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;
1149 template <
int sizeExtr,
1151 typename triangulationType,
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,
1168 &localGhostPresenceMap,
1169 std::vector<std::vector<saddleIdPerProcess>> &ghostPresenceVector,
1170 const std::vector<SimplexId> &critMaxsOrder,
1172 int localThreadNumber)
const;
1187 template <
typename triangulationType>
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,
1195 const triangulationType &triangulation,
1197 int localThreadNumber)
const;
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,
1225 int localThreadNumber);
1227 template <
int sizeExtr,
1229 typename triangulationType,
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,
1278 int localThreadNumber);
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,
1309 template <
typename triangulationType>
1310 void extractCriticalCells(
1311 std::array<std::vector<SimplexId>, 4> &criticalCellsByDim,
1312 std::array<std::vector<SimplexId>, 4> &critCellsOrder,
1314 const triangulationType &triangulation,
1315 const bool sortEdges)
const;
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;
1343 using tripletType = std::array<ttk::SimplexId, 3>;
1369 template <
int sizeExtr,
int sizeSad>
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,
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> &)>>
1383 const std::function<
bool(
const messageType<sizeExtr, sizeSad> &,
1384 const messageType<sizeExtr, sizeSad> &)>
1386 std::vector<messageType<sizeExtr, sizeSad>> &recvBuffer,
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,
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,
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> &)>>
1528 const std::function<
bool(
const messageType<sizeExtr, sizeSad> &,
1529 const messageType<sizeExtr, sizeSad> &)>
1531 std::vector<messageType<sizeExtr, sizeSad>> &recvBuffer,
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>
1566 saddleEdge<sizeSad> sv,
1568 std::vector<extremaNode<sizeExtr>> &extremas,
1569 std::vector<saddleEdge<sizeSad>> &saddles)
const;
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;
1605 template <int sizeExtr, int sizeSad>
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;
1631 template <int sizeExtr, int sizeSad>
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;
1672 template <int sizeExtr>
1673 void addLocalExtrema(
1674 ttk::SimplexId &lid,
1675 const ttk::SimplexId gid,
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;
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,
1719 std::set<messageType<sizeExtr, sizeSad>,
1720 std::function<bool(const messageType<sizeExtr, sizeSad> &,
1721 const messageType<sizeExtr, sizeSad> &)>>
1723 const std::function<bool(const messageType<sizeExtr, sizeSad> &,
1724 const messageType<sizeExtr, sizeSad> &)>
1726 std::vector<messageType<sizeExtr, sizeSad>> &recvBuffer,
1727 ttk::SimplexId beginVect) const;
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,
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,
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>
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;
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];
1865 struct EdgeSimplex : Simplex<2> {
1866 template <
typename triangulationType>
1869 const triangulationType &triangulation) {
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]];
1876 std::sort(this->vertsOrder_, this->vertsOrder_ + 2,
1877 std::greater<ttk::SimplexId>());
1884 struct TriangleSimplex : Simplex<3> {
1885 template <
typename triangulationType>
1888 const triangulationType &triangulation) {
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]];
1897 std::sort(this->vertsOrder_, this->vertsOrder_ + 3,
1898 std::greater<ttk::SimplexId>());
1905 struct TetraSimplex : Simplex<4> {
1906 template <
typename triangulationType>
1909 const triangulationType &triangulation) {
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]];
1920 std::sort(this->vertsOrder_, this->vertsOrder_ + 4,
1921 std::greater<ttk::SimplexId>());
1926#ifdef TTK_ENABLE_MPI_TIME
1930 this->critCellsOrder_ = {};
1931#ifdef TTK_ENABLE_MPI_TIME
1935 printMsg(
"Memory cleanup performed using "
1937 +
" MPI processes lasted: " + std::to_string(elapsedTime));
1942 void minMaxClear()
const {
1943 this->saddleToPairedMin_ = {};
1944 this->saddleToPairedMax_ = {};
1945 this->minToPairedSaddle_ = {};
1946 this->maxToPairedSaddle_ = {};
1949 dcg::DiscreteGradient dg_{};
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_{};
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>
1971 mutable std::vector<Lock> sendComputeBufferLock_;
1972 mutable int currentBuffer_{0};
1979 bool ComputeMinSad{
true};
1980 bool ComputeSadSad{
true};
1981 bool ComputeSadMax{
true};
1982 bool Compute2SaddlesChildren{
false};
1983 bool UseTasks{
true};
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]};
1996 for(
size_t j = 0; j < ghost.size(); j++) {
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());
2005 = std::distance(ghost[j].saddleIds_.begin(), last);
2006 if(!(ghostSize % newSize == 0 && ghostSize / newSize == 2)) {
2007 ranks.emplace_back(ghost[j].rank_);
2010 ranks.emplace_back(ghost[j].rank_);
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)
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;
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();
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 {
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);
2067 sendMessageSize[2 * i] = finishedVPathToSend[i].size();
2068 sendMessageSize[2 * i + 1] = ghostPresenceToSend[i].size();
2071 MPI_Alltoall(sendMessageSize.data(), 2, MPI_SimplexId, recvMessageSize.data(),
2072 2, MPI_SimplexId, MPIcomm);
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]);
2083 if(sendMessageSize[2 * i] > 0) {
2084 MPI_Isend(finishedVPathToSend[i].data(), sendMessageSize[2 * i],
2085 MPI_FinishedVPathMPIType, i, 1, MPIcomm, &requests[count]);
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]);
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]);
2099 MPI_Waitall(count, requests.data(), MPI_STATUSES_IGNORE);
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,
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,
2113 int localThreadNumber)
const {
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(
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);
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](
2143 char saddleRank,
int threadNumber,
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) {
2150 int rank = triangulation.getVertexRank(lastCell.
id_);
2152 sendBufferThread[threadNumber][neighborsToId.find(rank)->second]
2153 .emplace_back(vpathToSend{.saddleId_ = saddleId,
2154 .extremaId_ = extremaId,
2155 .saddleRank_ = saddleRank});
2158 if(this->dg_.isCellCritical(lastCell)) {
2160 = localTriangToLocalVectExtrema.find(lastCell.
id_)->second;
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);
2171 if(r ==
static_cast<int>(ghost.size())) {
2172 ghost.emplace_back(saddleIdPerProcess{
2173 std::vector<ttk::SimplexId>{saddleId}, saddleRank});
2175 extremaLocks[id].unlock();
2178 extremaNode<1> n(extremaId, -1, offsets[lastCell.
id_], Rep{-1, -1},
2182#pragma omp atomic capture
2183 saddleLocalId = saddleAtomic[saddleId]++;
2184 res[saddleId][saddleLocalId] = n;
2187 sendFinishedVPathBufferThread[threadNumber][saddleRank]
2188 .emplace_back(vpathFinished<1>(saddleId, extremaId,
2190 offsets[lastCell.
id_]));
2200#pragma omp parallel shared(extremaLocks, saddleAtomic) reduction(+: elementNumber) \
2201 num_threads(localThreadNumber)
2203 int threadNumber = omp_get_thread_num();
2204#pragma omp for schedule(static)
2205 for(
size_t i = 0; i < criticalEdges.size(); ++i) {
2208 triangulation.getEdgeVertex(criticalEdges[i], 0, v0);
2209 triangulation.getEdgeVertex(criticalEdges[i], 1, v1);
2212 followVPath(v0, i,
ttk::MPIrank_, threadNumber, elementNumber);
2213 followVPath(v1, i,
ttk::MPIrank_, threadNumber, elementNumber);
2216 localElementNumber += elementNumber;
2219 MPI_Datatype MPI_MessageType;
2220 this->createVpathMPIType(MPI_MessageType);
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();
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++) {
2251 sendMessageSize[i] = sendBuffer[i].size();
2252 MPI_Isend(&sendMessageSize[i], 1, MPI_SimplexId, neighbors[i], 0, MPIcomm,
2254 MPI_Irecv(&recvMessageSize[i], 1, MPI_SimplexId, neighbors[i], 0, MPIcomm,
2257 std::vector<MPI_Request> sendRequestsData(neighborNumber);
2258 std::vector<MPI_Request> recvRequestsData(neighborNumber);
2259 std::vector<MPI_Status> recvStatusData(neighborNumber);
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]);
2279 sendPerformedCountTotal += sendPerformedCount;
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]);
2298 recvPerformedCountTotal += recvPerformedCount;
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)
2313 int threadNumber = omp_get_thread_num();
2314#pragma omp for schedule(static) reduction(+ : elementNumber)
2316 struct vpathToSend element = recvBuffer[rankId][j];
2318 = triangulation.getVertexLocalId(element.extremaId_);
2319 followVPath(v, element.saddleId_, element.saddleRank_,
2320 threadNumber, elementNumber);
2323 localElementNumber += elementNumber;
2326 recvPerformedCountTotal += recvPerformedCount;
2329 MPI_Waitall(sendCount, sendRequestsData.data(), MPI_STATUSES_IGNORE);
2332 &localElementNumber, &totalElement, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
2333 keepWorking = (totalElement != totalFinishedElement);
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(
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++) {
2349 this->extractGhost(ghostPresence, ghostPresenceVector, localThreadNumber);
2352 this->packageGhost<1>(
2353 finishedVPathToSendThread, ghostPresenceToSendThread, ghostCounterThread,
2354 sendFinishedVPathBufferThread, localTriangToLocalVectExtrema,
2356 return triangulation.getVertexLocalId(a);
2358 ghostPresence, localThreadNumber);
2360 this->mergeThreadVectors(finishedVPathToSend, finishedVPathToSendThread,
2361 ghostPresenceToSend, ghostPresenceToSendThread,
2362 ghostCounterThread, localThreadNumber);
2364 std::vector<std::vector<vpathFinished<1>>> recvVPathFinished(
ttk::MPIsize_);
2365 std::vector<std::vector<char>> recvGhostPresence(
ttk::MPIsize_);
2367 this->exchangeFinalVPathAndGhosts(ghostPresenceToSend, finishedVPathToSend,
2368 recvGhostPresence, recvVPathFinished,
2369 MPI_SimplexId, MPIcomm);
2372 this->unpackGhostPresence<1, 2>(
2373 recvGhostPresence, extremaLocks, ghostPresence, localGhostPresenceMap,
2374 localTriangToLocalVectExtrema, recvVPathFinished, saddleAtomic, res,
2376 return triangulation.getVertexLocalId(a);
2379 return triangulation.getVertexRank(a);
2386template <
int sizeExtr,
2388 typename triangulationType,
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,
2408 int localThreadNumber)
const {
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(
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);
2430#ifdef TTK_ENABLE_OPENMP
2431#pragma omp parallel for num_threads(localThreadNumber) reduction(+:totalFinishedElement)
2433 for(
size_t i = 0; i < criticalSaddles.size(); ++i) {
2434 totalFinishedElement += getFaceStarNumber(criticalSaddles[i]);
2436 localElementNumber = 0;
2439 MPI_IN_PLACE, &totalFinishedElement, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
2441 const auto followVPath = [
this, dim, &triangulation, &neighborsToId,
2442 &extremaLocks, &res, &saddleAtomic,
2443 &ghostPresenceVector, &sendBufferThread,
2444 &sendFinishedVPathBufferThread,
2445 &localTriangToLocalVectExtrema, &fillExtremaOrder,
2448 char saddleRank,
int threadNumber,
2450 std::vector<Cell> vpath{};
2451 this->dg_.getAscendingPath(Cell{dim, v}, vpath, triangulation);
2452 const Cell &lastCell = vpath.back();
2454 if(lastCell.
dim_ == dim) {
2456 int rank = triangulation.getCellRank(lastCell.
id_);
2458 sendBufferThread[threadNumber][neighborsToId.find(rank)->second]
2459 .emplace_back(vpathToSend{.saddleId_ = saddleId,
2460 .extremaId_ = extremaId,
2461 .saddleRank_ = saddleRank});
2464 if(this->dg_.isCellCritical(lastCell)) {
2466 = localTriangToLocalVectExtrema.find(lastCell.
id_)->second;
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);
2477 if(r == ghost.size()) {
2478 ghost.emplace_back(saddleIdPerProcess{
2479 std::vector<ttk::SimplexId>{saddleId}, saddleRank});
2481 extremaLocks[id].unlock();
2483 extremaNode<sizeExtr> n(extremaId, -1, critMaxsOrder[lastCell.
id_],
2486 fillExtremaOrder(lastCell.
id_, n.vOrder_);
2488#pragma omp atomic capture
2489 saddleLocalId = saddleAtomic[saddleId]++;
2490 res[saddleId][saddleLocalId] = n;
2492 auto vp{vpathFinished<sizeExtr>(
2494 fillExtremaOrder(lastCell.
id_, vp.vOrder_);
2496 sendFinishedVPathBufferThread[threadNumber][saddleRank]
2502 if(lastCell.
dim_ == dim - 1) {
2504 extremaNode<sizeExtr> n(
2507#pragma omp atomic capture
2508 saddleLocalId = saddleAtomic[saddleId]++;
2509 res[saddleId][saddleLocalId] = n;
2512 sendFinishedVPathBufferThread[threadNumber][saddleRank].emplace_back(
2513 vpathFinished<sizeExtr>(
2522#pragma omp parallel shared(extremaLocks, saddleAtomic) reduction(+: elementNumber) \
2523 num_threads(localThreadNumber)
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;
2532 const auto starNumber = getFaceStarNumber(sid);
2534 for(
SimplexId j = 0; j < starNumber; ++j) {
2536 getFaceStar(sid, j, cellId);
2537 followVPath(cellId, i,
ttk::MPIrank_, threadNumber, elementNumber);
2540 if(isOnBoundary(sid)) {
2542 extremaNode<sizeExtr> n(
2544#pragma omp atomic capture
2545 saddleLocalId = saddleAtomic[i]++;
2546 res[i][saddleLocalId] = n;
2550 localElementNumber += elementNumber;
2553 MPI_Datatype MPI_MessageType;
2554 this->createVpathMPIType(MPI_MessageType);
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();
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++) {
2585 sendMessageSize[i] = sendBuffer[i].size();
2586 MPI_Isend(&sendMessageSize[i], 1, MPI_SimplexId, neighbors[i], 0, MPIcomm,
2588 MPI_Irecv(&recvMessageSize[i], 1, MPI_SimplexId, neighbors[i], 0, MPIcomm,
2591 std::vector<MPI_Request> sendRequestsData(neighborNumber);
2592 std::vector<MPI_Request> recvRequestsData(neighborNumber);
2593 std::vector<MPI_Status> recvStatusData(neighborNumber);
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]);
2613 sendPerformedCountTotal += sendPerformedCount;
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]);
2632 recvPerformedCountTotal += recvPerformedCount;
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)
2647 int threadNumber = omp_get_thread_num();
2648#pragma omp for schedule(static) reduction(+ : elementNumber)
2650 struct vpathToSend element = recvBuffer[rankId][j];
2652 = triangulation.getCellLocalId(element.extremaId_);
2653 followVPath(v, element.saddleId_, element.saddleRank_,
2654 threadNumber, elementNumber);
2657 localElementNumber += elementNumber;
2660 recvPerformedCountTotal += recvPerformedCount;
2663 MPI_Waitall(sendCount, sendRequestsData.data(), MPI_STATUSES_IGNORE);
2666 &localElementNumber, &totalElement, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
2667 keepWorking = (totalElement != totalFinishedElement);
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(
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++) {
2684 this->extractGhost(ghostPresence, ghostPresenceVector, localThreadNumber);
2687 this->packageGhost<sizeExtr>(
2688 finishedVPathToSendThread, ghostPresenceToSendThread, ghostCounterThread,
2689 sendFinishedVPathBufferThread, localTriangToLocalVectExtrema,
2691 return triangulation.getCellLocalId(a);
2693 ghostPresence, localThreadNumber);
2696 this->mergeThreadVectors(finishedVPathToSend, finishedVPathToSendThread,
2697 ghostPresenceToSend, ghostPresenceToSendThread,
2698 ghostCounterThread, localThreadNumber);
2701 std::vector<std::vector<vpathFinished<sizeExtr>>> recvVPathFinished(
2703 std::vector<std::vector<char>> recvGhostPresence(
ttk::MPIsize_);
2706 this->exchangeFinalVPathAndGhosts<sizeExtr>(
2707 ghostPresenceToSend, finishedVPathToSend, recvGhostPresence,
2708 recvVPathFinished, MPI_SimplexId, MPIcomm);
2711 this->unpackGhostPresence<sizeExtr, sizeSad + 1>(
2712 recvGhostPresence, extremaLocks, ghostPresence, localGhostPresenceMap,
2713 localTriangToLocalVectExtrema, recvVPathFinished, saddleAtomic, res,
2715 return triangulation.getCellLocalId(a);
2718 return triangulation.getCellRank(a);
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 {
2738#pragma omp parallel for schedule(static) shared(localGhostPresenceMap) \
2739 num_threads(localThreadNumber)
2740 for(
size_t j = 0; j < recvVPathFinished[i].size(); j++) {
2742 auto &vp{recvVPathFinished[i][j]};
2744 = (j == 0) ? 0 : recvVPathFinished[i][j - 1].ghostPresenceSize_;
2747 order = vp.vOrder_[0];
2749 extremaNode<sizeExtr> n(
2750 vp.extremaId_, -1, order, Rep{-1, -1}, vp.extremaRank_, vp.vOrder_);
2753#pragma omp atomic capture
2754 saddleLocalId = saddleAtomic[vp.saddleId_]++;
2755 res[vp.saddleId_][saddleLocalId] = n;
2756 if(vp.ghostPresenceSize_ != 0) {
2760 if(vp.ghostPresenceSize_ - beginGhost > 1) {
2761 std::vector<char> ghost{};
2762 ghost.insert(ghost.end(), recvGhostPresence[i].begin() + beginGhost,
2763 recvGhostPresence[i].begin()
2770 { localGhostPresenceMap[vp.extremaId_] = ghost; }
2772 lid = localTriangToLocalVectExtrema.find(lid)->second;
2773 extremaLocks[lid].lock();
2774 ghostPresence[lid] = ghost;
2775 extremaLocks[lid].unlock();
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++) {
2800 for(
size_t k = 0; k < sendFinishedVPathBufferThread[j][i].size(); k++) {
2805 auto vp = sendFinishedVPathBufferThread[j][i][k];
2806 if(vp.extremaId_ > -1) {
2808 .find(getSimplexLocalId(vp.extremaId_))
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()) {
2817 vp.ghostPresenceSize_ = ghostCounterThread[j][i];
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();
2826 vp.ghostPresenceSize_ = ghostCounterThread[j][i];
2828 if(ghost.size() > 1) {
2829 ghostPresenceToSendThread[j][i].insert(
2830 ghostPresenceToSendThread[j][i].
end(), ghost.begin(),
2835 vp.ghostPresenceSize_ = ghostCounterThread[j][i];
2837 finishedVPathToSendThread[j][i].emplace_back(vp);
2839 sendFinishedVPathBufferThread[j][i].clear();
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,
2852 const triangulationType &triangulation,
2854 int localThreadNumber)
const {
2859 MPI_Datatype MPI_SimplexId = getMPIType(totalNumberOfVertices);
2860 ttk::SimplexId localNumberOfVertices = triangulation.getNumberOfVertices();
2862 MPI_Allreduce(&localNumberOfVertices, &totalNumberOfVertices, 1,
2863 MPI_SimplexId, MPI_SUM, MPIcomm);
2866 std::pair<ttk::SimplexId, ttk::SimplexId> localMin(-1, totalNumberOfVertices);
2868 if(criticalExtremasNumber > 0) {
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)
2874 if(offsets[criticalExtremas[i]] < localMin.second) {
2875 localMin.first = criticalExtremas[i];
2876 localMin.second = offsets[localMin.first];
2882 &localMin.second, &globalMinOffset, 1, MPI_SimplexId, MPI_MIN, MPIcomm);
2883 if(this->ComputeMinSad) {
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)
2899 localTriangToLocalVectExtrema[criticalExtremas[i]] = i;
2902 ghostPresenceVector.resize(
2903 criticalExtremasNumber, std::vector<saddleIdPerProcess>());
2905 saddle1ToMinima.resize(
2906 criticalEdges.size(), std::array<extremaNode<1>, 2>());
2908 localGhostPresenceVector.resize(
2909 criticalExtremasNumber, std::vector<char>());
2911 saddles.resize(criticalEdgesNumber);
2914 this->getSaddle1ToMinima(criticalEdges, localTriangToLocalVectExtrema,
2915 triangulation, offsets, saddle1ToMinima,
2916 localGhostPresenceVector, localGhostPresenceMap,
2917 ghostPresenceVector, MPIcomm, localThreadNumber);
2919 auto &saddleToPairedExtrema{this->saddleToPairedMin_};
2920 auto &extremaToPairedSaddle{this->minToPairedSaddle_};
2921 auto &globalToLocalSaddle{this->globalToLocalSaddle1_};
2922 std::vector<ttk::SimplexId> globalMinLid;
2925 MPI_IN_PLACE, &totalNumberOfPairs, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
2926 globalToLocalSaddle.reserve(criticalEdgesNumber);
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)
2933 auto &mins = saddle1ToMinima[i];
2934 const auto s1 = criticalEdges[i];
2936 std::sort(mins.begin(), mins.end());
2937 const auto last = std::unique(mins.begin(), mins.end());
2938 if(last != mins.end()) {
2941 saddles[i].lid_ = i;
2943 saddles[i].gid_ = gid;
2944 for(
int j = 0; j < 2; j++) {
2945 extremasGid.emplace_back(mins[j].gid_);
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)
2964 i < static_cast<ttk::SimplexId>(extremasGid.size()); i++) {
2965 globalToLocalExtrema.emplace(extremasGid[i], i);
2971 if(saddles[i].gid_ != -1) {
2972 globalToLocalSaddle.emplace(saddles[i].gid_, i);
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());
2984 if(last != mins.end()) {
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++) {
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{};
3000 mins[j].rep_.extremaId_ = lid;
3001 extremas[lid] = mins[j];
3002 if(globalMinOffset == mins[j].vOrder_[0]) {
3004 globalMinLid.emplace_back(lid);
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_];
3017 triangLid = localTriangToLocalVectExtrema.find(triangLid)->second;
3018 ghosts = localGhostPresenceVector[triangLid];
3020 ghostPresence[lid] = ghosts;
3026 const auto cmpSadMin
3027 = [=, &extremas, &saddles](
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_;
3035 for(
int i = 0; i < 2; i++) {
3036 if(s0.vOrder_[i] != s1.vOrder_[i]) {
3037 return s0.vOrder_[i] < s1.vOrder_[i];
3041 if(s0.gid_ == -1 && s1.gid_ == -1) {
3044 return extremas[s0.t_[0]].vOrder_[0] > extremas[s1.t_[0]].vOrder_[0];
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);
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);
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;
3070 MPI_Allreduce(MPI_IN_PLACE, &rerunNeeded, 1, MPI_CHAR, MPI_LOR, MPIcomm);
3072 MPI_IN_PLACE, &nMinSadPairs, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
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);
3085 for(
const auto lid : globalMinLid) {
3086 if(extremaToPairedSaddle[lid] != -1
3087 && saddles[extremaToPairedSaddle[lid]].rank_ ==
ttk::MPIrank_) {
3088 saddleToPairedExtrema[extremaToPairedSaddle[lid]] = -2;
3092 MPI_Allreduce(MPI_IN_PLACE, &rerunNeeded, 1, MPI_CHAR, MPI_LOR, MPIcomm);
3094 MPI_IN_PLACE, &nMinSadPairs, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
3096 extractPairs<1, 2>(pairs, extremas, saddles, saddleToPairedExtrema,
false,
3097 0, localThreadNumber);
3099 if(totalNumberOfPairs > 1) {
3100#pragma omp parallel for shared(pairs, nConnComp) num_threads(localThreadNumber)
3101 for(
const auto extr : extremas) {
3103 if(extremaToPairedSaddle[extr.lid_] < 0) {
3106 pairs.emplace_back(extr.gid_, -1, 0);
3113 if(globalMinOffset == localMin.second) {
3115 triangulation.getVertexGlobalId(localMin.first), -1, 0);
3121 "Computed " + std::to_string(nMinSadPairs) +
" min-saddle pairs", 1.0,
3122 tm.getElapsedTime(), localThreadNumber);
3124 if(globalMinOffset == localMin.second) {
3126 triangulation.getVertexGlobalId(localMin.first), -1, 0);
3132template <
int sizeExtr,
3134 typename triangulationType,
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,
3157 int localThreadNumber) {
3159 const auto dim = this->dg_.getDimensionality();
3160 std::vector<std::array<extremaNode<sizeExtr>, sizeSad + 1>> saddle2ToMaxima;
3161 std::vector<std::vector<saddleIdPerProcess>> ghostPresenceVector;
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)
3176 localTriangToLocalVectExtrema[criticalExtremas[i]] = i;
3179 ghostPresenceVector.resize(
3180 criticalExtremasNumber, std::vector<saddleIdPerProcess>());
3182 saddle2ToMaxima.resize(
3183 criticalSaddles.size(), std::array<extremaNode<sizeExtr>, sizeSad + 1>());
3185 localGhostPresenceVector.resize(
3186 criticalExtremasNumber, std::vector<char>());
3188 saddles.resize(criticalSaddlesNumber);
3190 this->getSaddle2ToMaxima<sizeExtr, sizeSad>(
3191 criticalSaddles, getFaceStar, getFaceStarNumber, isOnBoundary,
3192 fillExtremaOrder, triangulation, saddle2ToMaxima,
3193 localTriangToLocalVectExtrema, localGhostPresenceVector,
3194 localGhostPresenceMap, ghostPresenceVector, critMaxsOrder, MPIcomm,
3199 auto &saddleToPairedExtrema{this->saddleToPairedMax_};
3200 auto &extremaToPairedSaddle{this->maxToPairedSaddle_};
3202 globalToLocalExtrema.reserve(2 * saddle2ToMaxima.size());
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)
3212 auto &maxs = saddle2ToMaxima[i];
3213 const auto s2 = criticalSaddles[i];
3215 maxs.begin(), maxs.end(),
3216 [](
const extremaNode<sizeExtr> &a,
const extremaNode<sizeExtr> &b) {
3219 if(a.gid_ * b.gid_ >= 0) {
3220 return std::abs(a.gid_) < std::abs(b.gid_);
3222 return a.gid_ > b.gid_;
3225 auto last = std::unique(maxs.begin(), maxs.end());
3227 if(last->gid_ == -2) {
3231 maxs[sizeSad].gid_ = std::distance(maxs.begin(), last);
3235 if(maxs[sizeSad].gid_ != 2) {
3238 saddles[i].lid_ = i;
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_);
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)
3262 i < static_cast<ttk::SimplexId>(extremasGid.size()); i++) {
3263 globalToLocalExtrema.emplace(extremasGid[i], i);
3269 if(saddles[i].gid_ != -1) {
3270 globalToLocalSaddle.emplace(saddles[i].gid_, i);
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) {
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) {
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{};
3296 maxs[j].rep_.extremaId_ = lid;
3297 extremas[lid] = maxs[j];
3299 = triangulation.getCellLocalId(maxs[j].gid_);
3302 auto it = localGhostPresenceMap.find(maxs[j].gid_);
3303 if(it != localGhostPresenceMap.end()) {
3304 ghosts = localGhostPresenceMap[maxs[j].gid_];
3309 triangLid = localTriangToLocalVectExtrema.find(triangLid)->second;
3310 ghosts = localGhostPresenceVector[triangLid];
3312 ghostPresence[lid] = ghosts;
3320 const auto cmpSadMax
3321 = [&extremas, &saddles](
3323 const auto &s0{saddles[id1]};
3324 const auto &s1{saddles[id2]};
3326 if(s0.order_ != -1 && s1.order_ != -1) {
3327 return s0.order_ > s1.order_;
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];
3334 return s0.gid_ > s1.gid_;
3336 if(s0.gid_ == -1 && s1.gid_ == -1) {
3339 if(s0.t_[1] != s1.t_[1]) {
3340 if(s0.t_[1] == -1) {
3343 if(s1.t_[1] == -1) {
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_;
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];
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);
3364 extremaToPairedSaddle.resize(globalToLocalExtrema.size(), -1);
3365 saddleToPairedExtrema.resize(saddles.size(), -1);
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);
3374 extractPairs<sizeExtr, sizeSad>(pairs, extremas, saddles,
3375 saddleToPairedExtrema,
true, dim - 1,
3379 MPI_IN_PLACE, &nSadMaxPairs, 1, MPI_SimplexId, MPI_SUM, MPIcomm);
3383 "Computed " + std::to_string(nSadMaxPairs) +
" saddle-max pairs", 1.0,
3384 tm.getElapsedTime(), localThreadNumber);
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,
3397 int localThreadNumber) {
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{};
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;
3410 if(criticalExtremasNumber > 0) {
3412#pragma omp parallel for reduction(max \
3413 : globalMaxOffset) \
3414 num_threads(localThreadNumber)
3416 if(globalMaxOffset < offsets[i]) {
3417 globalMaxOffset = offsets[i];
3423 MPI_IN_PLACE, &globalMaxOffset, 1, MPI_SimplexId, MPI_MAX, MPIcomm);
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 \
3428 num_threads(localThreadNumber)
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]));
3439 int localMaxIdSize = localMaxId.size();
3443 &localMaxIdSize, 1, MPI_INTEGER, recvCount.data(), 1, MPI_INTEGER, MPIcomm);
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) {
3452 computeMaxSaddlePairs<4, 3>(
3453 pairs, criticalSaddles, critSaddlesOrder, criticalExtremas,
3454 critMaxsOrder, triangulation, globalToLocalSaddle, globalToLocalExtrema,
3456 return triangulation.getTriangleStar(a, i, r);
3459 return triangulation.getTriangleStarNumber(a);
3462 return triangulation.isTriangleOnBoundary(a);
3464 [
this, &triangulation, offsets](
3466 return fillTetraOrder(
id, offsets, triangulation, vertsOrder);
3469 return triangulation.getTriangleGlobalId(lid);
3471 [
this, &triangulation, offsets](
3473 return fillTriangleOrder(
id, offsets, triangulation, vOrd);
3475 MPIcomm, localThreadNumber);
3477 computeMaxSaddlePairs<3, 2>(
3478 pairs, criticalSaddles, critSaddlesOrder, criticalExtremas,
3479 critMaxsOrder, triangulation, globalToLocalSaddle, globalToLocalExtrema,
3481 return triangulation.getEdgeStar(a, i, r);
3484 return triangulation.getEdgeStarNumber(a);
3487 return triangulation.isEdgeOnBoundary(a);
3489 [
this, &triangulation, offsets](
3491 return fillTriangleOrder(
id, offsets, triangulation, vertsOrder);
3494 return triangulation.getEdgeGlobalId(lid);
3496 [
this, &triangulation, offsets](
3498 return fillEdgeOrder(
id, offsets, triangulation, vOrd);
3500 MPIcomm, localThreadNumber);
3503 if(ignoreBoundary) {
3504 std::vector<ttk::SimplexId> locatedId;
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());
3511 if(pairs[i].type < dim - 1) {
3515 = std::find(globalMaxId.begin(), globalMaxId.end(), pairs[i].death);
3516 if(it != globalMaxId.end()) {
3518 locatedId.emplace_back(i);
3521 for(
ttk::SimplexId i = 0; i < static_cast<ttk::SimplexId>(locatedId.size());
3524 this->saddleToPairedMax_[globalToLocalSaddle[pairs[id].death]] = -1;
3525 this->maxToPairedSaddle_[globalToLocalExtrema[pairs[id].birth]] = -1;
3526 pairs.erase(pairs.begin() +
id);
3532template <
int sizeExtr,
int sizeSad>
3534 extremaNode<sizeExtr> extr,
3535 saddleEdge<sizeSad> sv,
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_;
3544 auto rep = extremas[extr.rep_.extremaId_];
3545 saddleEdge<sizeSad> s;
3546 while(rep != currentNode && count < overflow_) {
3548 if(currentNode.rep_.extremaId_ == -1) {
3551 if(currentNode.rep_.saddleId_ > -1) {
3552 s = saddles[currentNode.rep_.saddleId_];
3553 if((s.gid_ == sv.gid_) || ((s < sv) == increasing)) {
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_) +
")");
3563 if(currentNode.rep_.extremaId_ == -1) {
3566 rep = extremas[currentNode.rep_.extremaId_];
3568 return currentNode.lid_;
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_;
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> &)>>
3588 const std::function<
bool(
const messageType<sizeExtr, sizeSad> &,
3589 const messageType<sizeExtr, sizeSad> &)>
3591 std::vector<messageType<sizeExtr, sizeSad>> &recvBuffer,
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);
3603 for(
int i = 0; i < sizeExtr; i++) {
3604 it->t1Order_[i] = 0;
3605 it->t2Order_[i] = 0;
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;
3620 saddleToPairedExtrema[sad.lid_] = -1;
3623template <
int sizeExtr,
int sizeSad>
3624void ttk::DiscreteMorseSandwichMPI::tripletsToPersistencePairs(
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,
3637 int localThreadNumber)
const {
3641 std::array<std::vector<std::vector<messageType<sizeExtr, sizeSad>>>, 2>
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> &)>
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];
3663 return elt0.t1Order_[0] < elt1.t1Order_[0];
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];
3675 return elt0.t1Order_[0] > elt1.t1Order_[0];
3678 std::set<messageType<sizeExtr, sizeSad>,
3679 std::function<bool(
const messageType<sizeExtr, sizeSad> &,
3680 const messageType<sizeExtr, sizeSad> &)>>
3681 recomputations(cmpMessages);
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,
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,
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_);
3712 MPI_Datatype MPI_SimplexId = getMPIType(hasSentMessages);
3713 char currentSendBuffer{0};
3714 while(hasSentMessages > 0) {
3720 std::vector<ttk::SimplexId> sendMessageSize(
ttk::MPIsize_, 0);
3721 std::vector<ttk::SimplexId> recvMessageSize(
ttk::MPIsize_, 0);
3724 int recvPerformedCount = 0;
3725 int recvPerformedCountTotal = 0;
3729 sendMessageSize[i] = sendBuffer[currentSendBuffer][i].size();
3730 localSentMessageNumber += sendMessageSize[i];
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);
3742 if((sendMessageSize[i] > 0)) {
3743 MPI_Isend(sendBuffer[currentSendBuffer][i].data(), sendMessageSize[i],
3744 MPI_MessageType, i, 1, MPIcomm, &sendRequestsData[sendCount]);
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]);
3754 recvPerformedCountTotal = 0;
3755 while(recvPerformedCountTotal < recvCount) {
3756 MPI_Waitsome(recvCount, recvRequestsData.data(), &recvPerformedCount,
3757 recvCompleted.data(), recvStatusData.data());
3759 if(recvPerformedCount > 0) {
3760 for(
int i = 0; i < recvPerformedCount; i++) {
3761 r = recvStatusData[i].MPI_SOURCE;
3763 recvBuffer[r].
end(), cmpMessages);
3765 recvPerformedCountTotal += recvPerformedCount;
3768 MPI_Waitall(sendCount, sendRequestsData.data(), MPI_STATUSES_IGNORE);
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());
3783 elt = recvBuffer[i][j];
3787 elt = recvBuffer[i][j];
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);
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());
3816 recomputations.clear();
3819 MPI_Allreduce(&localSentMessageNumber, &hasSentMessages, 1, MPI_SimplexId,
3821 if(hasSentMessages) {
3823 sendBuffer[currentSendBuffer][i].clear();
3824 recvBuffer[i].clear();
3826 currentSendBuffer = 1 - currentSendBuffer;
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,
3839 int localThreadNumber)
const {
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)
3849 if(saddleToPairedExtrema[i] > -1 && saddles[i].rank_ ==
ttk::MPIrank_) {
3853 saddles[i].gid_, extremas[saddleToPairedExtrema[i]].gid_, pairDim);
3858 extremas[saddleToPairedExtrema[i]].gid_, saddles[i].gid_, pairDim);
3865template <
int sizeExtr,
int sizeSad>
3867 std::vector<saddleEdge<sizeSad>> &saddles,
3868 std::vector<ttk::SimplexId> &saddleToPairedExtrema,
3869 int localThreadNumber)
const {
3872#ifdef TTK_ENABLE_OPENMP
3873#pragma omp parallel for reduction(+ : computedSaddleNumber) schedule(static) num_threads(localThreadNumber)
3876 if(saddleToPairedExtrema[i] > -1 && saddles[i].rank_ ==
ttk::MPIrank_) {
3877 computedSaddleNumber++;
3880 return computedSaddleNumber;
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);
3895 saddleToPairedExtrema.emplace_back(
static_cast<ttk::SimplexId>(-1));
3900template <
int sizeExtr,
int sizeSad>
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 {
3910 if(extremaGid > -1) {
3911 auto it = globalToLocalExtrema.find(extremaGid);
3912 if(it != globalToLocalExtrema.end()) {
3914 auto e{extremas[lid]};
3916 = getRep(extremas[lid], s, increasing, extremas, saddles);
3917 if(extremas[repLid].gid_ != e.gid_) {
3919 elt.t1_ = extremas[repLid].gid_;
3920 for(
int i = 0; i < sizeExtr; i++) {
3921 elt.t1Order_[i] = extremas[repLid].vOrder_[i];
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_];
3929 elt.s1Rank_ = s1.rank_;
3931 for(
int i = 0; i < sizeSad; i++) {
3932 elt.s1Order_[i] = s1.vOrder_[i];
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)
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];
3957template <
int sizeExtr,
int sizeSad>
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 {
3967 if(extremaGid > -1) {
3968 auto it = globalToLocalExtrema.find(extremaGid);
3969 if(it != globalToLocalExtrema.end()) {
3971 auto e{extremas[lid]};
3972 ttk::SimplexId repLid = getRep(e, s, increasing, extremas, saddles);
3973 if(extremas[repLid].gid_ != e.gid_) {
3975 elt.t2_ = extremas[repLid].gid_;
3976 for(
int i = 0; i < sizeExtr; i++) {
3977 elt.t2Order_[i] = extremas[repLid].vOrder_[i];
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_];
3985 elt.s2Rank_ = s2.rank_;
3987 for(
int i = 0; i < sizeSad; i++) {
3988 elt.s2Order_[i] = s2.vOrder_[i];
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)
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];
4014template <
int sizeExtr,
int sizeSad>
4015void ttk::DiscreteMorseSandwichMPI::swapT1T2(
4016 messageType<sizeExtr, sizeSad> &elt,
4019 bool increasing)
const {
4020 if(elt.t1_ != elt.t2_ && elt.t2_ != -1) {
4021 bool pairedR2 = elt.s2_ != -1 && elt.s2_ != elt.s_;
4024 && (compareArray(elt.s2Order_, elt.sOrder_, sizeSad) == increasing));
4027 bool pairedR1 = elt.s1_ != -1 && elt.s1_ != elt.s_;
4030 && (compareArray(elt.s1Order_, elt.sOrder_, sizeSad) == increasing));
4033 if((((compareArray(elt.t2Order_, elt.t1Order_, sizeExtr)) == increasing)
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;
4048template <
int sizeExtr>
4049void ttk::DiscreteMorseSandwichMPI::addLocalExtrema(
4054 std::vector<extremaNode<sizeExtr>> &extremas,
4055 std::unordered_map<ttk::SimplexId, ttk::SimplexId> &globalToLocalExtrema,
4056 std::vector<ttk::SimplexId> &extremaToPairedSaddle)
const {
4058 lid = extremas.size();
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;
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,
4080 std::set<messageType<sizeExtr, sizeSad>,
4081 std::function<
bool(
const messageType<sizeExtr, sizeSad> &,
4082 const messageType<sizeExtr, sizeSad> &)>>
4084 const std::function<
bool(
const messageType<sizeExtr, sizeSad> &,
4085 const messageType<sizeExtr, sizeSad> &)>
4087 std::vector<messageType<sizeExtr, sizeSad>> &recvBuffer,
4089 struct saddleEdge<sizeSad> s;
4090 auto it = globalToLocalSaddle.find(element.s_);
4091 if(it != globalToLocalSaddle.end()) {
4092 s = saddles[it->second];
4094 s = saddleEdge<sizeSad>(element.s_, -1, element.sOrder_, element.sRank_);
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;
4103 removePair(saddles[s.lid_], extremas[saddleToPairedExtrema[s.lid_]],
4104 saddleToPairedExtrema, extremaToPairedSaddle);
4106 processTriplet<sizeExtr>(
4107 saddles[s.lid_], saddleToPairedExtrema, extremaToPairedSaddle, saddles,
4108 extremas, increasing, ghostPresence, sendBuffer, recomputations,
4109 cmpMessages, recvBuffer, beginVect);
4113 = getUpdatedT1(element.t1_, element, s, globalToLocalExtrema, extremas,
4114 saddles, increasing);
4116 = getUpdatedT2(element.t2_, element, s, globalToLocalExtrema, extremas,
4117 saddles, increasing);
4119 swapT1T2(element, t1Lid, t2Lid, increasing);
4122 if(t1Lid == -1 || extremas[t1Lid].rep_.extremaId_ == -1) {
4123 element.hasBeenModified_ = 1;
4124 sendBuffer[element.t1Rank_].emplace_back(element);
4127 if(element.hasBeenModified_) {
4128 element.hasBeenModified_ = 0;
4129 sendBuffer[s.rank_].emplace_back(element);
4130 element.hasBeenModified_ = 1;
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;
4142 && (element.t1Rank_ != sender)) {
4143 sendBuffer[element.t1Rank_].emplace_back(element);
4145 if(extremas[saddleToPairedExtrema[s.lid_]].rank_ ==
ttk::MPIrank_
4146 && ghostPresence[extremas[saddleToPairedExtrema[s.lid_]].lid_]
4149 for(
const auto rank :
4150 ghostPresence[extremas[saddleToPairedExtrema[s.lid_]].lid_]) {
4152 && ((rank == sender && element.hasBeenModified_)
4153 || rank != sender)) {
4154 sendBuffer[rank].emplace_back(element);
4162 if(ls1Id != -1 && ls1Id != s.lid_) {
4164 auto ls1{saddles[ls1Id]};
4166 if((ls1.gid_ != s.gid_) && ((ls1 < s) == increasing)) {
4169 removePair(ls1, extremas[t1Lid], saddleToPairedExtrema,
4170 extremaToPairedSaddle);
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_
4177 extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_
4178 = extremas[saddleToPairedExtrema[s.lid_]].lid_;
4179 extremas[saddleToPairedExtrema[s.lid_]].rep_.saddleId_ = -1;
4181 removePair(s, extremas[saddleToPairedExtrema[s.lid_]],
4182 saddleToPairedExtrema, extremaToPairedSaddle);
4184 if(element.t2_ > -1) {
4185 addLocalExtrema(t2Lid, element.t2_, element.t2Rank_,
4186 element.t2Order_, extremas, globalToLocalExtrema,
4187 extremaToPairedSaddle);
4190 s, extremas[t1Lid], saddleToPairedExtrema, extremaToPairedSaddle);
4191 if(element.t2_ > -1) {
4192 extremas[t1Lid].rep_.extremaId_ = t2Lid;
4194 extremas[t1Lid].rep_.extremaId_ = t1Lid;
4196 extremas[t1Lid].rep_.saddleId_ = s.lid_;
4199 saddleEdge<sizeSad> lst1;
4200 if(extremas[t1Lid].rep_.saddleId_ > -1) {
4201 lst1 = saddles[extremas[t1Lid].rep_.saddleId_];
4203 if(element.s1_ != -1) {
4204 lst1 = saddleEdge<sizeSad>(
4205 element.s1_, element.s1Order_, element.s1Rank_);
4208 if(element.t2_ > -1) {
4209 saddleEdge<sizeSad> lst2;
4210 if(extremas[t2Lid].rep_.saddleId_ > -1) {
4211 lst2 = saddles[extremas[t2Lid].rep_.saddleId_];
4213 if(element.s2_ != -1) {
4214 lst2 = saddleEdge<sizeSad>(
4215 element.s2_, element.s2Order_, element.s2Rank_);
4218 storeMessageToSend<sizeExtr, sizeSad>(
4219 ghostPresence, sendBuffer, s, lst1, lst2, extremas[t1Lid],
4220 extremas[t2Lid], sender, element.hasBeenModified_);
4222 storeMessageToSend<sizeExtr, sizeSad>(
4223 ghostPresence, sendBuffer, s, lst1, extremas[t1Lid], sender,
4224 element.hasBeenModified_);
4229 ls1, recomputations, cmpMessages, recvBuffer, beginVect);
4232 storeRerunToSend<sizeExtr>(sendBuffer, saddles[oldSaddleId]);
4236 bool changesApplied{
true};
4238 s = addSaddle(s, globalToLocalSaddle, saddles, saddleToPairedExtrema);
4239 if(saddleToPairedExtrema[s.lid_] > -1) {
4241 && (extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_
4243 && (saddleToPairedExtrema[s.lid_] == t1Lid)) {
4244 changesApplied =
false;
4246 if(extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_ != -1
4247 && extremas[saddleToPairedExtrema[s.lid_]].rep_.saddleId_
4249 extremas[saddleToPairedExtrema[s.lid_]].rep_.extremaId_
4250 = extremas[saddleToPairedExtrema[s.lid_]].lid_;
4251 extremas[saddleToPairedExtrema[s.lid_]].rep_.saddleId_ = -1;
4253 removePair(s, extremas[saddleToPairedExtrema[s.lid_]],
4254 saddleToPairedExtrema, extremaToPairedSaddle);
4256 if(element.t2_ > -1) {
4257 addLocalExtrema(t2Lid, element.t2_, element.t2Rank_,
4258 element.t2Order_, extremas, globalToLocalExtrema,
4259 extremaToPairedSaddle);
4262 s, extremas[t1Lid], saddleToPairedExtrema, extremaToPairedSaddle);
4263 if(extremas[t1Lid].rep_.extremaId_ != -1) {
4264 if(element.t2_ > -1) {
4265 extremas[t1Lid].rep_.extremaId_ = t2Lid;
4267 extremas[t1Lid].rep_.extremaId_ = t1Lid;
4269 extremas[t1Lid].rep_.saddleId_ = s.lid_;
4271 if(changesApplied) {
4274 saddleEdge<sizeSad> lst1;
4275 if(extremas[t1Lid].rep_.saddleId_ > -1) {
4276 lst1 = saddles[extremas[t1Lid].rep_.saddleId_];
4278 if(element.s1_ != -1) {
4279 lst1 = saddleEdge<sizeSad>(
4280 element.s1_, element.s1Order_, element.s1Rank_);
4283 if(element.t2_ > -1) {
4284 saddleEdge<sizeSad> lst2;
4285 if(extremas[t2Lid].rep_.saddleId_ > -1) {
4286 lst2 = saddles[extremas[t2Lid].rep_.saddleId_];
4288 if(element.s2_ != -1) {
4289 lst2 = saddleEdge<sizeSad>(
4290 element.s2_, element.s2Order_, element.s2Rank_);
4293 storeMessageToSend<sizeExtr, sizeSad>(
4294 ghostPresence, sendBuffer, s, lst1, lst2, extremas[t1Lid],
4295 extremas[t2Lid], sender, element.hasBeenModified_);
4297 storeMessageToSend<sizeExtr, sizeSad>(
4298 ghostPresence, sendBuffer, s, lst1, extremas[t1Lid], sender,
4299 element.hasBeenModified_);
4306 if(((t1Lid != -1 && extremas[t1Lid].rep_.extremaId_ == -1) || t1Lid == -1)
4307 && element.hasBeenModified_ == 1) {
4308 sendBuffer[element.t1Rank_].emplace_back(element);
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;
4318 if(extremas[saddleToPairedExtrema[s.lid_]].rank_ ==
ttk::MPIrank_
4319 && ghostPresence[extremas[saddleToPairedExtrema[s.lid_]].lid_]
4322 for(
const auto rank :
4323 ghostPresence[extremas[saddleToPairedExtrema[s.lid_]].lid_]) {
4325 && ((rank == sender && element.hasBeenModified_)
4326 || rank != sender)) {
4327 sendBuffer[rank].emplace_back(element);
4334 if((element.s1_ != -1) && (element.s1_ != element.s_)
4335 && (!(compareArray(element.s1Order_, element.sOrder_, sizeSad)
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;
4347 addLocalExtrema(t1Lid, element.t1_, element.t1Rank_, element.t1Order_,
4348 extremas, globalToLocalExtrema,
4349 extremaToPairedSaddle);
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;
4366 extremas[t1Lid].rep_.extremaId_ = t1Lid;
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;
4376 auto extr{extremas[saddleToPairedExtrema[s.lid_]]};
4377 removePair(s, extremas[saddleToPairedExtrema[s.lid_]],
4378 saddleToPairedExtrema, extremaToPairedSaddle);
4380 && ghostPresence[extr.lid_].size() > 1) {
4381 for(
const auto rank : ghostPresence[extr.lid_]) {
4383 && ((rank == sender && element.hasBeenModified_)
4384 || rank != sender)) {
4385 sendBuffer[rank].emplace_back(element);
4391 if(extremaToPairedSaddle[t1Lid] < 0) {
4393 s, extremas[t1Lid], saddleToPairedExtrema, extremaToPairedSaddle);
4395 if(element.t2_ > -1) {
4396 addLocalExtrema(t2Lid, element.t2_, element.t2Rank_,
4397 element.t2Order_, extremas, globalToLocalExtrema,
4398 extremaToPairedSaddle);
4400 if(extremas[t1Lid].rep_.extremaId_ != -1) {
4401 if(element.t2_ > -1) {
4402 extremas[t1Lid].rep_.extremaId_ = t2Lid;
4404 extremas[t1Lid].rep_.extremaId_ = t1Lid;
4406 extremas[t1Lid].rep_.saddleId_ = s.lid_;
4409 || (ghostPresence[extremas[t1Lid].lid_].size() > 1)) {
4410 saddleEdge<sizeSad> ls1;
4411 saddleEdge<sizeSad> ls2;
4412 if(oldSaddle > -1) {
4413 ls1 = saddles[oldSaddle];
4415 if(element.t2_ > -1) {
4416 if(extremas[t2Lid].rep_.saddleId_ > -1) {
4417 ls2 = saddles[extremas[t2Lid].rep_.saddleId_];
4419 if(element.s2_ != -1) {
4420 ls2 = saddleEdge<sizeSad>(
4421 element.s2_, element.s2Order_, element.s2Rank_);
4424 storeMessageToSend<sizeExtr, sizeSad>(
4425 ghostPresence, sendBuffer, s, ls1, ls2, extremas[t1Lid],
4428 storeMessageToSend<sizeExtr, sizeSad>(
4429 ghostPresence, sendBuffer, s, ls1, extremas[t1Lid]);
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]];
4441 if(isPairedWithWrongSaddle && ((sCurrent < s) == increasing)) {
4444 removePair(sCurrent, extremas[t1Lid], saddleToPairedExtrema,
4445 extremaToPairedSaddle);
4447 if(isPairedWithWrongSaddle) {
4448 if((sCurrent < s) == increasing) {
4449 addPair(s, extremas[t1Lid], saddleToPairedExtrema,
4450 extremaToPairedSaddle);
4452 if(element.t2_ > -1) {
4453 addLocalExtrema(t2Lid, element.t2_, element.t2Rank_,
4454 element.t2Order_, extremas,
4455 globalToLocalExtrema, extremaToPairedSaddle);
4457 if(extremas[t1Lid].rep_.extremaId_ != -1) {
4458 if(element.t2_ > -1) {
4459 extremas[t1Lid].rep_.extremaId_ = t2Lid;
4461 extremas[t1Lid].rep_.extremaId_ = t1Lid;
4463 extremas[t1Lid].rep_.saddleId_ = s.lid_;
4466 || (ghostPresence[extremas[t1Lid].lid_].size() > 1)) {
4467 saddleEdge<sizeSad> ls1;
4468 saddleEdge<sizeSad> ls2;
4469 if(oldSaddle > -1) {
4470 ls1 = saddles[oldSaddle];
4472 if(element.t2_ > -1) {
4473 if(extremas[t2Lid].rep_.saddleId_ > -1) {
4474 ls2 = saddles[extremas[t2Lid].rep_.saddleId_];
4476 if(element.s2_ != -1) {
4477 ls2 = saddleEdge<sizeSad>(
4478 element.s2_, element.s2Order_, element.s2Rank_);
4481 storeMessageToSend<sizeExtr, sizeSad>(
4482 ghostPresence, sendBuffer, s, ls1, ls2, extremas[t1Lid],
4485 storeMessageToSend<sizeExtr, sizeSad>(
4486 ghostPresence, sendBuffer, s, ls1, extremas[t1Lid]);
4489 if(sCurrent.gid_ > -1 && sCurrent.gid_ != s.gid_) {
4491 addToRecvBuffer(sCurrent, recomputations, cmpMessages,
4492 recvBuffer, beginVect);
4494 storeRerunToSend<sizeExtr>(sendBuffer, sCurrent);
4500 s, recomputations, cmpMessages, recvBuffer, beginVect);
4502 storeRerunToSend<sizeExtr>(sendBuffer, s);
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,
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);
4527 for(
auto r : ghostPresence[rep1.lid_]) {
4528 sendBuffer[r].emplace_back(m);
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,
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);
4551 for(
auto r : ghostPresence[rep1.lid_]) {
4552 sendBuffer[r].emplace_back(m);
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_];
4569 if(rep2.rep_.saddleId_ > -1) {
4570 s2 = saddles[rep2.rep_.saddleId_];
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);
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_];
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);
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);
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,
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> &)>>
4618 const std::function<
bool(
const messageType<sizeExtr, sizeSad> &,
4619 const messageType<sizeExtr, sizeSad> &)>
4621 std::vector<messageType<sizeExtr, sizeSad>> &recvBuffer,
4625 = getRep(extremas[sv.t_[0]], sv, increasing, extremas, saddles);
4627 bool pairedR1 = extremaToPairedSaddle[r1Lid] > -1;
4630 && ((saddles[extremaToPairedSaddle[r1Lid]] < sv) == increasing));
4637 if(extremas[r1Lid].rep_.extremaId_ == -1) {
4639 storeMessageToSendToRepOwner(sendBuffer, sv, saddles, extremas[r1Lid]);
4649 oldSaddle = extremaToPairedSaddle[r1Lid];
4650 removePair(saddles[extremaToPairedSaddle[r1Lid]], extremas[r1Lid],
4651 saddleToPairedExtrema, extremaToPairedSaddle);
4654 sv, extremas[r1Lid], saddleToPairedExtrema, extremaToPairedSaddle);
4657 || (ghostPresence[r1Lid].size() > 1)) {
4658 saddleEdge<sizeSad> s1;
4659 if(oldSaddle > -1) {
4660 s1 = saddles[oldSaddle];
4662 storeMessageToSend<sizeExtr, sizeSad>(
4663 ghostPresence, sendBuffer, sv, s1, extremas[r1Lid]);
4666 if(extremas[r1Lid].rep_.extremaId_ != -1) {
4667 extremas[r1Lid].rep_.extremaId_ = r1Lid;
4668 extremas[r1Lid].rep_.saddleId_ = sv.lid_;
4672 addToRecvBuffer(saddles[oldSaddle], recomputations, cmpMessages,
4673 recvBuffer, beginVect);
4675 storeRerunToSend<sizeExtr>(sendBuffer, saddles[oldSaddle]);
4682 = getRep(extremas[sv.t_[1]], sv, increasing, extremas, saddles);
4683 bool pairedR2 = extremaToPairedSaddle[r2Lid] > -1;
4686 && ((saddles[extremaToPairedSaddle[r2Lid]] < sv) == increasing));
4689 if(extremas[r2Lid].rep_.extremaId_ == -1) {
4691 storeMessageToSendToRepOwner(
4692 sendBuffer, sv, saddles, extremas[r2Lid], extremas[r1Lid]);
4695 if(extremas[r1Lid].rep_.extremaId_ == -1) {
4697 storeMessageToSendToRepOwner(
4698 sendBuffer, sv, saddles, extremas[r1Lid], extremas[r2Lid]);
4702 if(extremas[r1Lid].gid_ != extremas[r2Lid].gid_) {
4703 if((((extremas[r2Lid] < extremas[r1Lid]) == increasing) || pairedR1)
4706 oldSaddle = extremaToPairedSaddle[r2Lid];
4707 removePair(saddles[extremaToPairedSaddle[r2Lid]], extremas[r2Lid],
4708 saddleToPairedExtrema, extremaToPairedSaddle);
4711 sv, extremas[r2Lid], saddleToPairedExtrema, extremaToPairedSaddle);
4712 extremas[r2Lid].rep_.extremaId_ = r1Lid;
4713 extremas[r2Lid].rep_.saddleId_ = sv.lid_;
4716 || (ghostPresence[r2Lid].size() > 1)) {
4717 saddleEdge<sizeSad> s1;
4718 saddleEdge<sizeSad> s2;
4719 if(extremaToPairedSaddle[r1Lid] > -1) {
4720 s1 = saddles[extremaToPairedSaddle[r1Lid]];
4722 if(oldSaddle > -1) {
4723 s2 = saddles[oldSaddle];
4725 storeMessageToSend<sizeExtr, sizeSad>(ghostPresence, sendBuffer, sv, s2,
4726 s1, extremas[r2Lid],
4731 && saddles[oldSaddle].gid_ != sv.gid_) {
4732 addToRecvBuffer(saddles[oldSaddle], recomputations, cmpMessages,
4733 recvBuffer, beginVect);
4735 storeRerunToSend<sizeExtr>(sendBuffer, saddles[oldSaddle]);
4741 oldSaddle = extremaToPairedSaddle[r1Lid];
4742 removePair(saddles[extremaToPairedSaddle[r1Lid]], extremas[r1Lid],
4743 saddleToPairedExtrema, extremaToPairedSaddle);
4746 sv, extremas[r1Lid], saddleToPairedExtrema, extremaToPairedSaddle);
4747 extremas[r1Lid].rep_.extremaId_ = r2Lid;
4748 extremas[r1Lid].rep_.saddleId_ = sv.lid_;
4750 || (ghostPresence[r1Lid].size() > 1)) {
4751 saddleEdge<sizeSad> s1;
4752 saddleEdge<sizeSad> s2;
4753 if(oldSaddle > -1) {
4754 s1 = saddles[oldSaddle];
4756 if(extremaToPairedSaddle[r2Lid] > -1) {
4757 s2 = saddles[extremaToPairedSaddle[r2Lid]];
4759 storeMessageToSend<sizeExtr, sizeSad>(ghostPresence, sendBuffer, sv,
4760 s1, s2, extremas[r1Lid],
4765 && saddles[oldSaddle].gid_ != sv.gid_) {
4766 addToRecvBuffer(saddles[oldSaddle], recomputations, cmpMessages,
4767 recvBuffer, beginVect);
4769 storeRerunToSend<sizeExtr>(sendBuffer, saddles[oldSaddle]);
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()
4783 saddleEdge<sizeSad> s1;
4784 if(extremaToPairedSaddle[saddleToPairedExtrema[sv.lid_]] > -1) {
4785 s1 = saddles[extremaToPairedSaddle[saddleToPairedExtrema[sv.lid_]]];
4787 storeMessageToSend<sizeExtr, sizeSad>(
4788 ghostPresence, sendBuffer, sv, s1, s1,
4789 extremas[saddleToPairedExtrema[sv.lid_]],
4790 extremas[saddleToPairedExtrema[sv.lid_]]);
4792 saddleToPairedExtrema[sv.lid_] =
static_cast<ttk::SimplexId>(-2);
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);
4806 maxBoundary.emplace(m);
4809template <
typename GlobalBoundary>
4810void ttk::DiscreteMorseSandwichMPI::getMaxOfProc(
4813 GlobalBoundary &maxBoundary)
const {
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];
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) {
4837template <
typename LocalBoundary>
4838bool ttk::DiscreteMorseSandwichMPI::addBoundary(
4839 const SimplexId e,
bool isOnBoundary, LocalBoundary &localBoundary)
const {
4842 localBoundary.emplace(e);
4843 isOnBoundary =
true;
4845 const auto it = localBoundary.find(e);
4846 localBoundary.erase(it);
4847 isOnBoundary =
false;
4849 return isOnBoundary;
4852template <
typename GlobalBoundary>
4853void ttk::DiscreteMorseSandwichMPI::updateMaxBoundary(
4856 GlobalBoundary &globalBoundary,
4859 if(!globalBoundary.empty()) {
4860 std::vector<ttk::SimplexId> vect(5);
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(),
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();
4885#pragma omp atomic update
4890template <
typename GlobalBoundary>
4891void ttk::DiscreteMorseSandwichMPI::updateLocalBoundary(
4892 const saddle<3> &s2,
4896 GlobalBoundary &globalBoundary,
4898 std::vector<ttk::SimplexId> vect(10);
4900 for(
int i = 0; i < 3; i++) {
4901 vect[2 + i] = s2.vOrder_[i];
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]);
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(),
4921 sendBoundaryBufferLock_[rank].unlock();
4922#pragma omp atomic update
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);
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)) {
4942 s2GlobalBoundary.erase(it);
4943 s2GlobalBoundary.emplace(m);
4946 s2GlobalBoundary.emplace(m);
4953template <
typename GlobalBoundary>
4954void ttk::DiscreteMorseSandwichMPI::updateMergedBoundary(
4955 const saddle<3> &s2,
4958 GlobalBoundary &globalBoundary)
const {
4959 if(!globalBoundary.empty()) {
4960 std::vector<ttk::SimplexId> vect(10);
4962 for(
int i = 0; i < 3; i++) {
4963 vect[2 + i] = s2.vOrder_[i];
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]);
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(),
4984 sendBoundaryBufferLock_[
max.proc_].unlock();
4986#pragma omp atomic update
4991template <
typename triangulationType,
4992 typename GlobalBoundary,
4993 typename LocalBoundary>
4994void ttk::DiscreteMorseSandwichMPI::addEdgeToBoundary(
4997 std::vector<bool> &onBoundary,
4998 GlobalBoundary &globalBoundaryIds,
4999 LocalBoundary &localBoundaryIds,
5000 const triangulationType &triangulation,
5002 std::vector<std::pair<ttk::SimplexId, ttk::SimplexId>> &ghostEdges,
5003 std::vector<bool> &hasChangedMax)
const {
5005 triangulation.getTriangleEdge(pTau, edgeId, e);
5008 onBoundary[e] = addBoundary(e, onBoundary[e], localBoundaryIds);
5010 ghostEdges.emplace_back(std::make_pair(e, rank));
5011 hasChangedMax.emplace_back(
false);
5013 fillEdgeOrder(e, offsets, triangulation, eOrder);
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;
5022 updateMax(maxPerProcess(rank, eOrder), globalBoundaryIds);
5023 hasChangedMax[hasChangedMax.size() - 1] =
true;
5028template <
typename triangulationType,
typename GlobalBoundary>
5029void ttk::DiscreteMorseSandwichMPI::packageLocalBoundaryUpdate(
5030 const saddle<3> &s2,
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()) {
5040 updateLocalBoundary(
5041 s2, triangulation.getEdgeGlobalId(ghostEdges[0].first), -1, tauOrder,
5042 globalBoundaryIds, ghostEdges[0].second);
5043 if(hasChangedMax[0]) {
5046 getMaxOfProc(ghostEdges[0].second, currentMax, globalBoundaryIds);
5048 s2.gid_, currentMax, globalBoundaryIds, ghostEdges[0].second);
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]) {
5059 getMaxOfProc(ghostEdges[0].second, currentMax, globalBoundaryIds);
5061 s2.gid_, currentMax, globalBoundaryIds, ghostEdges[0].second);
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]) {
5072 getMaxOfProc(ghostEdges[0].second, currentMax, globalBoundaryIds);
5074 s2.gid_, currentMax, globalBoundaryIds, ghostEdges[0].second);
5076 if(hasChangedMax[1]) {
5078 getMaxOfProc(ghostEdges[1].second, currentMax, globalBoundaryIds);
5080 s2.gid_, currentMax, globalBoundaryIds, ghostEdges[1].second);
5085 printErr(
"In packageLocalBoundaryUpdate:");
5086 printErr(
"This case is not supposed to be reached.");
5087 printErr(
"Something has gone wrong in the execution.");
5095 if(lid < firstBlockSize_) {
5099 lidElement = (lid - firstBlockSize_) % blockSize_;
5100 lidBlock = 1 + (lid - firstBlockSize_ - lidElement) / blockSize_;
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,
5119 int threadNumber = omp_get_thread_num();
5120 auto &onBoundary = onBoundaryThread[threadNumber];
5125#pragma omp atomic capture seq_cst
5127 lock = s2Locks[s2.lidBlock_][s2.lidElement_];
5128 s2Locks[s2.lidBlock_][s2.lidElement_] = 1;
5131 bool tooFar =
false;
5134 auto &localBoundaryIds{s2LocalBoundaries[s2.lidBlock_][s2.lidElement_]};
5135 auto &globalBoundaryIds{s2GlobalBoundaries[s2.lidBlock_][s2.lidElement_]};
5136 const auto clearOnBoundary = [&localBoundaryIds, &onBoundary]() {
5138 for(
const auto e : localBoundaryIds) {
5139 onBoundary[e] =
false;
5142 if(!isEmpty(localBoundaryIds, globalBoundaryIds)) {
5144 for(
const auto e : localBoundaryIds) {
5145 onBoundary[e] =
true;
5149 std::vector<std::pair<ttk::SimplexId, ttk::SimplexId>> ghostEdges;
5150 std::vector<bool> hasChangedMax;
5152 addEdgeToBoundary(triangulation.getTriangleLocalId(s2.gid_), i,
5153 onBoundary, globalBoundaryIds, localBoundaryIds,
5154 triangulation, offsets, ghostEdges, hasChangedMax);
5156 if(ghostEdges.size() > 0) {
5159 const auto tau{*localBoundaryIds.begin()};
5160 fillEdgeOrder(tau, offsets, triangulation, tauOrder);
5161 this->packageLocalBoundaryUpdate(s2, tauOrder, globalBoundaryIds,
5162 triangulation, ghostEdges,
5167 while(!isEmpty(localBoundaryIds, globalBoundaryIds)) {
5171 if(!localBoundaryIds.empty()) {
5172 auto it = localBoundaryIds.begin();
5175 while(i < localEdgeCounter && it != localBoundaryIds.end()) {
5179 if(it == localBoundaryIds.end()) {
5180 const auto globMax{*globalBoundaryIds.begin()};
5181 tau = *localBoundaryIds.begin();
5182 fillEdgeOrder(tau, offsets, triangulation, tauOrder);
5184 s2.gid_, tauOrder, globalBoundaryIds,
ttk::MPIrank_, globMax.proc_);
5186#pragma omp atomic write seq_cst
5187 s2Locks[s2.lidBlock_][s2.lidElement_] = 0;
5188#pragma omp atomic update
5193 fillEdgeOrder(tau, offsets, triangulation, tauOrder);
5195 if(!globalBoundaryIds.empty()) {
5196 const auto globMax{*globalBoundaryIds.begin()};
5197 if(localBoundaryIds.empty()) {
5199 s2.gid_, tauOrder, globalBoundaryIds,
ttk::MPIrank_, globMax.proc_);
5201 globalBoundaryIds.clear();
5202#pragma omp atomic write seq_cst
5203 s2Locks[s2.lidBlock_][s2.lidElement_] = 0;
5204#pragma omp atomic update
5208 if(tooFar || !compareArray(globMax.max_, tauOrder, 2)) {
5211 if(tooFar && tooFarCounter > this->sadSadLimit_) {
5212 updateMaxBoundary(s2.gid_, tauOrder, globalBoundaryIds,
5215#pragma omp atomic write seq_cst
5216 s2Locks[s2.lidBlock_][s2.lidElement_] = 0;
5217#pragma omp atomic update
5226 auto pTau{this->dg_.getPairedCell(Cell{1, tau}, triangulation)};
5230 std::vector<std::pair<ttk::SimplexId, ttk::SimplexId>> ghostEdges;
5231 std::vector<bool> hasChangedMax;
5232 std::vector<ttk::SimplexId> newMax;
5234 this->addEdgeToBoundary(pTau, i, onBoundary, globalBoundaryIds,
5235 localBoundaryIds, triangulation, offsets,
5236 ghostEdges, hasChangedMax);
5238 this->packageLocalBoundaryUpdate(s2, tauOrder, globalBoundaryIds,
5239 triangulation, ghostEdges,
5242 bool critical{
false};
5248 saddleTau = localEdgeToSaddle1[tau];
5249 if(saddleTau == -1) {
5255#ifdef TTK_ENABLE_OPENMP
5256#pragma omp atomic read seq_cst
5258 pTau = partners[saddleTau];
5260 getLid(pTau, pTauLidBlock, pTauLidElement);
5261 if(s2LocalBoundaries[pTauLidBlock][pTauLidElement].empty()) {
5267 }
while(*s2LocalBoundaries[pTauLidBlock][pTauLidElement].
begin() != tau);
5276#pragma omp atomic capture seq_cst
5278 lock = s1Locks[saddleTau];
5279 s1Locks[saddleTau] = 1;
5283 const auto cap = partners[saddleTau];
5284 if(partners[saddleTau] == -1) {
5286#pragma omp atomic write seq_cst
5287 s1Locks[saddleTau] = 0;
5288 const auto globMax{*globalBoundaryIds.begin()};
5290 s2.gid_, tauOrder, globalBoundaryIds,
ttk::MPIrank_, globMax.proc_);
5292#pragma omp atomic write seq_cst
5293 s2Locks[s2.lidBlock_][s2.lidElement_] = 0;
5294#pragma omp atomic update
5298 if(s2.lidBlock_ == 0) {
5299 partners[saddleTau] = s2.lidElement_;
5301 partners[saddleTau] = firstBlockSize_
5302 + blockSize_ * (s2.lidBlock_ - 1)
5305#pragma omp atomic update
5306 finishedPropagationCounter_++;
5309#pragma omp atomic write seq_cst
5310 s1Locks[saddleTau] = 0;
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
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,
5333 if(saddles2[pTauLidBlock][pTauLidElement] < s2) {
5340#pragma omp atomic capture seq_cst
5342 lock = s2Locks[pTauLidBlock][pTauLidElement];
5343 s2Locks[pTauLidBlock][pTauLidElement] = 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) {
5360#pragma omp atomic capture seq_cst
5362 lock = s1Locks[saddleTau];
5363 s1Locks[saddleTau] = 1;
5367 const auto cap = partners[saddleTau];
5368 if(partners[saddleTau] == pTau) {
5370#pragma omp atomic write seq_cst
5371 s1Locks[saddleTau] = 0;
5372 const auto globMax{*globalBoundaryIds.begin()};
5373 updateMaxBoundary(s2.gid_, tauOrder, globalBoundaryIds,
5376#pragma omp atomic write seq_cst
5377 s2Locks[s2.lidBlock_][s2.lidElement_] = 0;
5378#pragma omp atomic update
5382 if(s2.lidBlock_ == 0) {
5383 partners[saddleTau] = s2.lidElement_;
5385 partners[saddleTau] = firstBlockSize_
5386 + blockSize_ * (s2.lidBlock_ - 1)
5391#pragma omp atomic write seq_cst
5392 s1Locks[saddleTau] = 0;
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);
5412#pragma omp atomic write seq_cst
5413 s2Locks[s2.lidBlock_][s2.lidElement_] = 0;
5414#pragma omp atomic update
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,
5432 saddle<3> &s{saddles2[lidBlock][lidElement]};
5434#pragma omp atomic capture
5436 lock = s2Locks[lidBlock][lidElement];
5437 s2Locks[lidBlock][lidElement] = 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) {
5446 = {recvBoundaryBuffer[i + j + 1], recvBoundaryBuffer[i + j + 2]};
5448 maxPerProcess(recvBoundaryBuffer[i + j], newMax), globalBoundary);
5452 for(
int j = 7; j < size; j += 3) {
5455 = {recvBoundaryBuffer[i + j + 1], recvBoundaryBuffer[i + j + 2]};
5456 globalBoundary.emplace(
5457 maxPerProcess(recvBoundaryBuffer[i + j], newMax));
5463#pragma omp atomic capture
5465 lock = s2Locks[pTauLidBlock][pTauLidElement];
5466 s2Locks[pTauLidBlock][pTauLidElement] = 1;
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);
5478 s2LocalBoundary.erase(ite);
5482 localBoundaries[lidBlock][lidElement].insert(
5483 pTauLocalBoundary.begin(), pTauLocalBoundary.end());
5485#pragma omp atomic write
5486 s2Locks[pTauLidBlock][pTauLidElement] = 0;
5487#pragma omp atomic write
5488 s2Locks[lidBlock][lidElement] = 0;
5490 s.gid_ = recvBoundaryBuffer[i + 1];
5491 for(
int j = 0; j < 3; j++) {
5492 s.vOrder_[j] = recvBoundaryBuffer[i + 2 + j];
5494 s.lidBlock_ = lidBlock;
5495 s.lidElement_ = lidElement;
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,
5512 saddle<3> &s{saddles2[lidBlock][lidElement]};
5513 int size = -recvBoundaryBuffer[i];
5515#pragma omp atomic capture
5517 lock = s2Locks[lidBlock][lidElement];
5518 s2Locks[lidBlock][lidElement] = 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);
5527 localBoundary.emplace(leid1);
5530 it = localBoundary.find(leid2);
5531 if(it != localBoundary.end()) {
5532 localBoundary.erase(it);
5534 localBoundary.emplace(leid2);
5537 if(((s.lidBlock_ == -1) || isEmpty)) {
5538 GlobalBoundary &globalBoundary = globalBoundaries[lidBlock][lidElement];
5539 for(
int j = 7; j < size; j += 3) {
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);
5551#pragma omp atomic write
5552 s2Locks[lidBlock][lidElement] = 0;
5554 s.gid_ = recvBoundaryBuffer[i + 1];
5555 for(
int j = 0; j < 3; j++) {
5556 s.vOrder_[j] = recvBoundaryBuffer[i + 2 + j];
5558 s.lidBlock_ = lidBlock;
5559 s.lidElement_ = lidElement;
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 {
5576 i < static_cast<ttk::SimplexId>(recvBoundaryBuffer.size()); i++) {
5577 if(recvBoundaryBuffer[i] < -1) {
5580 auto it = globalToLocalSaddle2_.find(recvBoundaryBuffer[i + 1]);
5582 if(it != globalToLocalSaddle2_.end()) {
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;
5594 lid = firstBlockSize_ + currentLastElement_
5595 + blockSize_ * (currentLastBlock_ - 1);
5596 globalToLocalSaddle2_[recvBoundaryBuffer[i + 1]] = lid;
5597 currentLastElement_++;
5601 getLid(lid, lidBlock, lidElement);
5606 = {recvBoundaryBuffer[i + 3], recvBoundaryBuffer[i + 4]};
5609#pragma omp atomic capture
5611 lock = s2Locks[lidBlock][lidElement];
5612 s2Locks[lidBlock][lidElement] = 1;
5615 auto m = maxPerProcess(rank, newMax);
5616 auto itg = std::find(globalBoundaries[lidBlock][lidElement].
begin(),
5617 globalBoundaries[lidBlock][lidElement].
end(), m);
5619 if(itg != globalBoundaries[lidBlock][lidElement].
end()) {
5620 globalBoundaries[lidBlock][lidElement].erase(itg);
5622 if(!(newMax[0] == -1 && newMax[1] == -1)) {
5623 globalBoundaries[lidBlock][lidElement].emplace(
5624 maxPerProcess(rank, newMax));
5626#pragma omp atomic write
5627 s2Locks[lidBlock][lidElement] = 0;
5630 if(recvBoundaryBuffer[i + 5] == -1) {
5633 auto itgl = globalToLocalSaddle2_.find(pTau);
5634 if(itgl != globalToLocalSaddle2_.end()) {
5640 getLid(pTauLid, pTauLidBlock, pTauLidElement);
5642 this->mergeDistributedBoundary(
5643 recvBoundaryBuffer, s2Locks, globalBoundaries, localBoundaries,
5644 saddles2, i, lidBlock, lidElement, pTauLidBlock, pTauLidElement);
5648 saddle<3> &s{saddles2[lidBlock][lidElement]};
5650 s.gid_ = recvBoundaryBuffer[i + 1];
5651 for(
int j = 0; j < 3; j++) {
5652 s.vOrder_[j] = recvBoundaryBuffer[i + 2 + j];
5654 s.lidBlock_ = lidBlock;
5655 s.lidElement_ = lidElement;
5658#pragma omp atomic capture
5660 lock = s2Locks[lidBlock][lidElement];
5661 s2Locks[lidBlock][lidElement] = 1;
5664 GlobalBoundary &globalBoundary
5665 = globalBoundaries[lidBlock][lidElement];
5666 for(
int j = 7; j < size; j += 3) {
5669 recvBoundaryBuffer[i + j + 1], recvBoundaryBuffer[i + j + 2]};
5670 updateMax(maxPerProcess(recvBoundaryBuffer[i + j], newMax),
5674#pragma omp atomic write
5675 s2Locks[lidBlock][lidElement] = 0;
5680 = triangulation.getEdgeLocalId(recvBoundaryBuffer[i + 5]);
5682 = triangulation.getEdgeLocalId(recvBoundaryBuffer[i + 6]);
5684 this->addDistributedEdgeToLocalBoundary(
5685 recvBoundaryBuffer, s2Locks, globalBoundaries, localBoundaries,
5686 saddles2, i, lidBlock, lidElement, leid1, leid2);
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,
5704 const auto nSadExtrPairs = pairs.size();
5707 std::vector<SimplexId> saddles1Gid{}, saddles2Gid{};
5709#ifdef TTK_ENABLE_MPI_TIME
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];
5718 auto it = globalToLocalSaddle1_.find(triangulation.getEdgeGlobalId(s1));
5719 if(it == globalToLocalSaddle1_.end()) {
5720 saddles1Gid.emplace_back(gid);
5722 if(saddleToPairedMin_[it->second] < 0) {
5723 saddles1Gid.emplace_back(gid);
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];
5732 auto it = globalToLocalSaddle2_.find(triangulation.getTriangleGlobalId(s2));
5733 if(it == globalToLocalSaddle2_.end()) {
5734 saddles2Gid.emplace_back(gid);
5736 if(saddleToPairedMax_[it->second] < 0) {
5737 saddles2Gid.emplace_back(gid);
5743 firstBlockSize_ = saddle2Number;
5744 blockSize_ = std::max(
static_cast<ttk::SimplexId>(0.05 * saddle2Number),
5747 MPI_Datatype MPI_SimplexId = getMPIType(saddle1Number);
5748 MPI_Allreduce(&saddle2Number, &globalSaddle2Counter_, 1, MPI_SimplexId,
5749 MPI_SUM, ttk::MPIcomm_);
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));
5762 std::vector<int> s1Locks;
5764 std::vector<std::vector<int>> s2Locks(overallSize, std::vector<int>());
5765#pragma omp parallel master
5767#pragma omp task shared(globalToLocalSaddle2_, saddles2Gid)
5769 globalToLocalSaddle2_.emplace(saddles2Gid[i], i);
5772 this->minMaxClear();
5774 localEdgeToSaddle1_.resize(triangulation.getNumberOfEdges(), -1);
5775#pragma omp task shared(saddles2)
5776 saddles2[0].resize(saddle2Number);
5778 globalToLocalSaddle1_.clear();
5780 edgeTrianglePartner.resize(saddle1Number, -1);
5782 s1Locks.resize(saddle1Number, 0);
5784 s2Locks[0].resize(saddle2Number, 0);
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;
5792#pragma omp parallel for num_threads(threadNumber_) schedule(static)
5794 auto &s2{saddles2[0][i]};
5795 s2.gid_ = saddles2Gid[i];
5799 s2.order_ = crit2SaddlesOrder[lid];
5800 fillTriangleOrder(lid, offsets, triangulation, s2.vOrder_);
5803 const auto &edgesFiltrOrder{crit1SaddlesOrder};
5807 return edgesFiltrOrder[a] > edgesFiltrOrder[b];
5811 for(
int i = 0; i < 2; i++) {
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;
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
5831 printMsg(
"Total preprocessing performed using "
5833 +
" MPI processes lasted: " + std::to_string(elapsedTime));
5841#pragma omp parallel num_threads(threadNumber_) shared( \
5842 onBoundaryThread, s1Locks, s2Locks, s2GlobalBoundaries, s2LocalBoundaries, \
5843 localEdgeToSaddle1_, saddles2, edgeTrianglePartner)
5845#pragma omp single nowait
5848#pragma omp task firstprivate(i)
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);
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(
5876 while(totalFinishedPropagationCounter < globalSaddle2Counter_) {
5879#pragma omp atomic read
5880 messageCnt = messageCounter_;
5881 if(messageCnt > messageSize_) {
5884#pragma omp atomic read
5885 tempTask = taskCounter_;
5891#pragma omp atomic write
5892 messageCounter_ = 0;
5897 std::vector<std::array<ttk::SimplexId, 2>> sendMessageSize(
5899 std::vector<std::array<ttk::SimplexId, 2>> recvMessageSize(
5903 int recvPerformedCount = 0;
5904 int recvPerformedCountTotal = 0;
5905 int tempCurrentBuffer;
5906#pragma omp atomic capture
5908 tempCurrentBuffer = currentBuffer_;
5909 currentBuffer_ = 1 - currentBuffer_;
5911#pragma omp atomic read
5912 totalFinishedPropagationCounter = finishedPropagationCounter_;
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();
5926 MPI_Alltoall(sendMessageSize.data(), 2, MPI_SimplexId,
5927 recvMessageSize.data(), 2, MPI_SimplexId, ttk::MPIcomm_);
5929 MPI_Allreduce(MPI_IN_PLACE, &totalFinishedPropagationCounter, 1,
5930 MPI_SimplexId, MPI_SUM, ttk::MPIcomm_);
5931 messageSize_ = std::max(
5933 std::min(messageSize_,
5935 (globalSaddle2Counter_ - totalFinishedPropagationCounter)
5937 std::vector<MPI_Request> sendRequestsData(
ttk::MPIsize_ - 1);
5938 std::vector<MPI_Request> recvRequestsData(
ttk::MPIsize_ - 1);
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]);
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]);
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);
5970 recvPerformedCountTotal += recvPerformedCount;
5973 MPI_Waitall(sendCount, sendRequestsData.data(), MPI_STATUSES_IGNORE);
5977 recvBoundaryBuffer[i].clear();
5978 sendBoundaryBuffer_[tempCurrentBuffer][i].clear();
5982 recvPerformedCount = 0;
5983 recvPerformedCountTotal = 0;
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]);
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]);
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];
6013 = globalToLocalSaddle2_.find(recvComputeBuffer[r][j])
6015#pragma omp task firstprivate(lid) \
6016 shared(s2GlobalBoundaries, s2LocalBoundaries, edgeTrianglePartner, s1Locks, \
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);
6030 recvPerformedCountTotal += recvPerformedCount;
6033 MPI_Waitall(sendCount, sendRequestsData.data(), MPI_STATUSES_IGNORE);
6036 sendComputeBuffer_[tempCurrentBuffer][i].clear();
6037 recvComputeBuffer[i].clear();
6044#ifdef TTK_ENABLE_MPI_TIME
6048 +
" MPI processes lasted: " + std::to_string(elapsedTime));
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];
6061 getLid(edgeTrianglePartner[i], lidBlock, lidElement);
6062 const auto s2 = saddles2[lidBlock][lidElement].gid_;
6064 pairs.emplace_back(s1, s2, 1);
6067#ifdef TTK_ENABLE_MPI_TIME
6071 +
" MPI processes lasted: " + std::to_string(elapsedTime));
6077#pragma omp parallel for num_threads(threadNumber_)
6079 j < static_cast<ttk::SimplexId>(saddles2[i].size()); j++) {
6080 s2LocalBoundaries[i][j].clear();
6081 s2GlobalBoundaries[i][j].clear();
6084#pragma omp parallel master num_threads(threadNumber_)
6089 this->sendComputeBuffer_[0][i].clear();
6091 this->sendComputeBuffer_[1][i].clear();
6093 this->sendBoundaryBuffer_[0][i].clear();
6095 this->sendBoundaryBuffer_[1][i].clear();
6100 sendComputeBuffer_ = {};
6102 sendBoundaryBuffer_ = {};
6104 s2LocalBoundaries = {};
6106 s2GlobalBoundaries = {};
6118 globalToLocalSaddle1_ = {};
6120 globalToLocalSaddle2_ = {};
6122 localEdgeToSaddle1_ = {};
6124 edgeTrianglePartner = {};
6126 onBoundaryThread = {};
6128#ifdef TTK_ENABLE_MPI_TIME
6131 printMsg(
"D1 memory clean up performed using "
6133 +
" MPI processes lasted: " + std::to_string(elapsedTime));
6136 auto nSadSadPairs = pairs.size() - nSadExtrPairs;
6138 MPI_IN_PLACE, &nSadSadPairs, 1, MPI_SimplexId, MPI_SUM, ttk::MPIcomm_);
6141 "Computed " + std::to_string(nSadSadPairs) +
" saddle-saddle pairs", 1.0,
6142 tm2.getElapsedTime(), this->threadNumber_);
6145template <
typename triangulationType>
6146void ttk::DiscreteMorseSandwichMPI::extractCriticalCells(
6147 std::array<std::vector<SimplexId>, 4> &criticalCellsByDim,
6148 std::array<std::vector<SimplexId>, 4> &critCellsOrder,
6150 const triangulationType &triangulation,
6151 const bool sortEdges)
const {
6154 this->dg_.getCriticalPoints(criticalCellsByDim, triangulation);
6156 const auto dim{this->dg_.getDimensionality()};
6157 std::vector<EdgeSimplex> critEdges;
6158 std::vector<TriangleSimplex> critTriangles;
6159 std::vector<TetraSimplex> critTetras;
6161#ifdef TTK_ENABLE_OPENMP
6162#pragma omp parallel master num_threads(threadNumber_) \
6163 shared(criticalCellsByDim)
6168#ifdef TTK_ENABLE_OPENMP
6169#pragma omp task shared(critEdges)
6171 critEdges.resize(criticalCellsByDim[1].size());
6173#ifdef TTK_ENABLE_OPENMP
6174#pragma omp task shared(critEdges)
6176 critEdges.resize(triangulation.getNumberOfEdges());
6179#ifdef TTK_ENABLE_OPENMP
6180#pragma omp task shared(critTriangles)
6182 critTriangles.resize(criticalCellsByDim[2].size());
6183#ifdef TTK_ENABLE_OPENMP
6184#pragma omp task shared(critTetras)
6186 critTetras.resize(criticalCellsByDim[3].size());
6187#ifdef TTK_ENABLE_OPENMP
6188#pragma omp task shared(critCellsOrder_)
6190 this->critCellsOrder_[1].resize(
6191 this->dg_.getNumberOfCells(1, triangulation), -1);
6193#ifdef TTK_ENABLE_OPENMP
6194#pragma omp task shared(critCellsOrder_)
6196 this->critCellsOrder_[2].resize(
6197 this->dg_.getNumberOfCells(2, triangulation), -1);
6201#ifdef TTK_ENABLE_OPENMP
6202#pragma omp task shared(critCellsOrder_)
6204 this->critCellsOrder_[3].resize(
6205 this->dg_.getNumberOfCells(3, triangulation), -1);
6208#ifdef TTK_ENABLE_OPENMP
6209#pragma omp parallel num_threads(threadNumber_)
6213#ifdef TTK_ENABLE_OPENMP
6214#pragma omp for nowait
6216 for(
size_t i = 0; i < critEdges.size(); ++i) {
6217 critEdges[i].fillEdge(i, offsets, triangulation);
6220#ifdef TTK_ENABLE_OPENMP
6221#pragma omp for nowait
6223 for(
size_t i = 0; i < critEdges.size(); ++i) {
6224 critEdges[i].fillEdge(criticalCellsByDim[1][i], offsets, triangulation);
6228#ifdef TTK_ENABLE_OPENMP
6229#pragma omp for nowait
6231 for(
size_t i = 0; i < critTriangles.size(); ++i) {
6232 critTriangles[i].fillTriangle(
6233 criticalCellsByDim[2][i], offsets, triangulation);
6236#ifdef TTK_ENABLE_OPENMP
6239 for(
size_t i = 0; i < critTetras.size(); ++i) {
6240 critTetras[i].fillTetra(criticalCellsByDim[3][i], offsets, triangulation);
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());
6248#ifdef TTK_ENABLE_OPENMP
6249#pragma omp parallel num_threads(threadNumber_)
6252#ifdef TTK_ENABLE_OPENMP
6253#pragma omp for nowait
6255 for(
size_t i = 0; i < critEdges.size(); ++i) {
6256 critCellsOrder[1][critEdges[i].id_] = i;
6259#ifdef TTK_ENABLE_OPENMP
6260#pragma omp for nowait
6262 for(
size_t i = 0; i < critTriangles.size(); ++i) {
6263 criticalCellsByDim[2][i] = critTriangles[i].id_;
6264 critCellsOrder[2][critTriangles[i].id_] = i;
6267#ifdef 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;
6278 criticalCellsByDim[1].
end(),
6280 return critCellsOrder[1][a] < critCellsOrder[1][b];
6283#ifdef TTK_ENABLE_OPENMP
6284#pragma omp parallel for num_threads(threadNumber_)
6286 for(
size_t i = 0; i < critEdges.size(); ++i) {
6287 criticalCellsByDim[1][i] = critEdges[i].id_;
6292template <
typename triangulationType>
6293int ttk::DiscreteMorseSandwichMPI::computePersistencePairs(
6294 std::vector<PersistencePair> &pairs,
6296 const triangulationType &triangulation,
6297 const bool ignoreBoundary,
6298 const bool compute2SaddlesChildren) {
6300#ifdef TTK_ENABLE_MPI_TIME
6306 const auto dim = this->dg_.getDimensionality();
6307 this->Compute2SaddlesChildren = compute2SaddlesChildren;
6310 std::array<std::vector<SimplexId>, 4> criticalCellsByDim{};
6312 auto &critCellsOrder{this->critCellsOrder_};
6314 this->extractCriticalCells(
6315 criticalCellsByDim, critCellsOrder, offsets, triangulation, dim == 3);
6317#ifdef TTK_ENABLE_MPI_TIME
6320 printMsg(
"Extract critical cells performed using "
6322 +
" MPI processes lasted: " + std::to_string(elapsedTime));
6325#ifdef TTK_ENABLE_MPI_TIME
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)
6349 this->getMinSaddlePairs(pairs, criticalCellsByDim[1], critCellsOrder[1],
6350 criticalCellsByDim[0], offsets, nConnComp,
6351 triangulation, minSadComm, minSadThreadNumber);
6356 this->getMaxSaddlePairs(
6357 sadMaxPairs, criticalCellsByDim[dim - 1], critCellsOrder[dim - 1],
6358 criticalCellsByDim[dim], critCellsOrder[dim], triangulation,
6359 ignoreBoundary, offsets, sadMaxComm, maxSadThreadNumber);
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());
6368 this->getMinSaddlePairs(pairs, criticalCellsByDim[1], critCellsOrder[1],
6369 criticalCellsByDim[0], offsets, nConnComp,
6370 triangulation, ttk::MPIcomm_, threadNumber_);
6372 this->getMaxSaddlePairs(pairs, criticalCellsByDim[dim - 1],
6373 critCellsOrder[dim - 1], criticalCellsByDim[dim],
6374 critCellsOrder[dim], triangulation, ignoreBoundary,
6375 offsets, ttk::MPIcomm_, threadNumber_);
6378#ifdef TTK_ENABLE_MPI_TIME
6381 printMsg(
"Computation of D0 and D2 pairs performed using "
6383 +
" MPI processes lasted: " + std::to_string(elapsedTime));
6388 if(dim == 3 && this->ComputeSadSad) {
6389 char computeSaddleSaddles
6390 = !criticalCellsByDim[1].empty() && !criticalCellsByDim[2].empty();
6392 MPI_IN_PLACE, &computeSaddleSaddles, 1, MPI_CHAR, MPI_LOR, ttk::MPIcomm_);
6393 if(computeSaddleSaddles) {
6395 triangulation.getNumberOfTriangles() * 0.0001);
6396 this->getSaddleSaddlePairs(pairs, criticalCellsByDim[1],
6397 criticalCellsByDim[2], critCellsOrder[1],
6398 critCellsOrder[2], triangulation, offsets);
6401 this->minMaxClear();
6403#ifdef TTK_ENABLE_MPI_TIME
6406 printMsg(
"Computation of D1 pairs performed using "
6408 +
" MPI processes lasted: " + std::to_string(elapsedTime));
6475#ifdef TTK_ENABLE_MPI_TIME
6478 printMsg(
"Computation of persistence pairs performed using "
6480 +
" MPI processes lasted: " + std::to_string(elapsedTime));
#define TTK_FORCE_USE(x)
Force the compiler to use the function/method parameter.
int SimplexId
Identifier type for simplices of any dimension.
#define TTK_PSORT(NTHREADS,...)
Parallel sort macro.
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_
int SimplexId
Identifier type for simplices of any dimension.
COMMON_EXPORTS int MPIrank_
T end(std::pair< T, T > &p)
T begin(std::pair< T, T > &p)
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/| (_) |"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)