DNDSR 0.1.0.dev1+gcd065ad
Distributed Numeric Data Structure for CFV
Loading...
Searching...
No Matches
IndexMapping.hpp
Go to the documentation of this file.
1#pragma once
2/// @file IndexMapping.hpp
3/// @brief Global-to-local index mapping for distributed arrays.
4/// @par Unit Test Coverage (test_IndexMapping.cpp, MPI np=1,2,4)
5/// - GlobalOffsetsMapping: setMPIAlignBcast, globalSize, RLengths, ROffsets,
6/// operator() (local-to-global), search (global-to-local, both overloads)
7/// -- uniform and non-uniform distributions
8/// -- edge cases: out-of-range, negative index, rank boundary crossing
9/// - OffsetAscendIndexMapping (pull-based): searchInMain, searchInGhost,
10/// searchInAllGhost, search, search_indexAppend, operator() (reverse mapping)
11/// -- empty ghost set edge case
12/// @par Not Yet Tested
13/// - Push-based OffsetAscendIndexMapping constructor
14/// - sort(), ghostAt(), search_indexRank
15/// - Duplicate pull indices
16
17#include <unordered_map>
18#include <algorithm>
19#include <tuple>
20
21#include "Defines.hpp"
22#include "MPI.hpp"
23
24namespace DNDS
25{ // mapping from rank-main place to global indices
26 // should be global-identical, can broadcast
27
28 /**
29 * @brief Table mapping rank-local row indices to the global index space.
30 *
31 * @details Every rank owns a contiguous chunk in the global index space:
32 *
33 * ```
34 * rank: 0 1 2
35 * RankLengths = [ 3, 4, 5 ]
36 * RankOffsets = [ 0, 3, 7, 12 ] # prefix sum, size == nRanks + 1
37 * ```
38 *
39 * After #setMPIAlignBcast, every rank holds an identical copy of the table
40 * (it is globally replicated), so both local-to-global
41 * (`operator()`) and global-to-local (#search) queries are purely local.
42 * Used by @ref DNDS::ParArray "ParArray"::createGlobalMapping and friends.
43 */
45 {
46
47 ///@brief "dist" sizes of each rank
48 t_IndexVec RankLengths;
49 t_IndexVec RankOffsets;
50
51 public:
52 /// @brief Per-rank lengths vector (`size == nRanks`).
53 t_IndexVec &RLengths() { return RankLengths; }
54 /// @brief Prefix-sum offsets vector (`size == nRanks + 1`, last element == globalSize).
55 t_IndexVec &ROffsets() { return RankOffsets; }
56
57 /// @brief Total number of rows across all ranks.
58 [[nodiscard]] index globalSize() const
59 {
60 if (!RankOffsets.empty())
61 return RankOffsets[RankOffsets.size() - 1];
62 else
63 return 0;
64 }
65
66 /// @brief Broadcast each rank's length, then compute the global prefix sums.
67 /// @details Collective call. After it returns, every rank holds the full
68 /// #RLengths / #ROffsets tables. Called by
69 /// `ParArray::createGlobalMapping` during mesh/array setup.
70 /// @param mpi MPI context.
71 /// @param myLength Number of rows owned by the calling rank.
72 void setMPIAlignBcast(const MPIInfo &mpi, index myLength)
73 {
74 RankLengths.resize(mpi.size);
75 RankOffsets.resize(mpi.size + 1);
76 RankLengths[mpi.rank] = myLength;
77
78 // tMPI_reqVec bcastReqs(mpi.size); // for Ibcast
79
80 for (MPI_int r = 0; r < mpi.size; r++)
81 {
82 // std::cout << mpi.rank << '\t' << myLength << std::endl;
83 MPI::Bcast(RankLengths.data() + r, sizeof(index), MPI_BYTE, r, mpi.comm);
84 }
85 RankOffsets[0] = 0;
86 for (size_t i = 0; i < RankLengths.size(); i++)
87 {
88 RankOffsets[i + 1] = RankOffsets[i] + RankLengths[i];
89 DNDS_assert(RankOffsets[i + 1] >= 0);
90 }
91 }
92
93 /// @brief Convert a (rank, local) pair to a global index.
94 /// @details Equivalent to `RankOffsets[rank] + val`, with asserts.
95 [[nodiscard]] index operator()(MPI_int rank, index val) const
96 {
97 // if (!((rank >= 0 && rank <= RankLengths.size()) &&
98 // (val >= 0 && val <= RankOffsets[rank + 1] - RankOffsets[rank])))
99 // {
100 // PrintVec(RankOffsets, std::cout);
101 // std::cout << rank << " KK " << val << std::endl;
102 // }
103 DNDS_assert((rank >= 0 && rank <= RankLengths.size()) &&
104 (val >= 0 && val <= RankOffsets[rank + 1] - RankOffsets[rank]));
105 return RankOffsets[rank] + val;
106 }
107
108 /// @brief Convert a global index to `(rank, local)`. Returns `false` if out of range.
109 /// @details Uses `std::lower_bound` on the offsets table (O(log nRanks)).
110 /// @param globalQuery Global index.
111 /// @param[out] rank Owning rank on success.
112 /// @param[out] val Local offset within the owner.
113 /// @return true if `globalQuery` lies within `[0, globalSize())`.
114 [[nodiscard]] bool search(index globalQuery, MPI_int &rank, index &val) const
115 {
116 // find the first larger than query, for this is a [,) interval search, should find the ) position
117 // example: RankOffsets = {0,1,3,3,4,4,5,5}
118 // Rank = {0,1,2,3,4,5,6}
119 // query 5 should be rank 7, which is out-of bound, returns false
120 // query 4 should be rank 5, query 3 should be rank 3, query 2 should be rank 1
121 if (RankOffsets.empty()) // in case the communicator is of size 0 ??
122 return false;
123 auto place = std::lower_bound(RankOffsets.begin(), RankOffsets.end(), globalQuery, std::less_equal<index>());
124 rank = static_cast<MPI_int>(place - 1 - RankOffsets.begin()); // ! could overflow
125 if (rank < RankLengths.size() && rank >= 0)
126 {
127 val = globalQuery - RankOffsets[rank];
128 return true;
129 }
130 return false;
131 }
132
133 [[nodiscard]] std::tuple<bool, MPI_int, index> search(index globalQuery) const
134 {
135 MPI_int rank{-1};
136 index val{-1};
137 bool ret = this->search(globalQuery, rank, val);
138 return std::make_tuple(ret, rank, val);
139 }
140 };
141
142 /**
143 * @brief Mapping between a rank's *main* data and its *ghost* copies.
144 *
145 * @details `main` data (owned rows) is a contiguous range
146 * `[mainOffset, mainOffset + mainSize)` in the global index space. `ghost`
147 * data (rows needed from other ranks) is stored in *globally ascending*
148 * order so that binary search may be used for the reverse mapping.
149 *
150 * Two construction modes:
151 * - **Pull**: the caller provides the set of global indices this rank needs.
152 * - **Push**: the caller provides which local rows to send to which ranks.
153 * Both modes exchange the complementary information via `MPI_Alltoall(v)`
154 * so the resulting mapping is valid on both sides.
155 *
156 * Internal structure (all vectors are per-peer-rank layouts, sorted by rank):
157 * - `ghostSizes` / `ghostStart` / `ghostIndex`: what this rank *pulls* from
158 * each other rank.
159 * - `pushIndexSizes` / `pushIndexStarts` / `pushingIndexGlobal`: what this
160 * rank *sends* to each other rank.
161 *
162 * @warning Ghost indices carry only 32-bit payload in MPI calls; the total
163 * per-rank ghost count must fit in `int32_t`.
164 */
166 {
167 using mapIndex = MPI_int;
168 using t_MapIndexVec = std::vector<mapIndex>;
169 static_assert(std::numeric_limits<mapIndex>::max() >= std::numeric_limits<MPI_int>::max());
170 index mainOffset;
171 index mainSize;
172
173 public:
174 /// @brief Number of ghost rows pulled from each peer rank. (a.k.a. `pullIndexSizes`)
176
177 /// @brief Global indices of all ghost rows on this rank, ascending.
178 /// (a.k.a. `pullingIndexGlobal`)
180
181 /// @brief Per-peer prefix-sum offsets into `ghostIndex`
182 /// (size == `nRanks + 1`). (a.k.a. `pullIndexStarts`)
183 t_MapIndexVec ghostStart;
184
185 /// @brief Per-peer number of rows *this rank* will send out on a push.
187 /// @brief Per-peer prefix-sum offsets into `pushingIndexGlobal`.
188 t_MapIndexVec pushIndexStarts;
189 /// @brief Global indices this rank sends to others, grouped by receiver.
191
192 /// @brief Optional cached local-index form of ghostIndex (unused in current code).
193 /// @details Encoding: `>=0` means `array[i]`, `<0` means `ghost_array[-i-1]`.
194 /// Retained for potential future reconstruction paths.
196
197 /**
198 * @brief Construct the mapping from a pull specification.
199 *
200 * @tparam TpullSet Range of `index` (vector-like, supports sort/unique/erase).
201 * @param nmainOffset Global offset of this rank's main data block.
202 * @param nmainSize Number of main rows owned by this rank.
203 * @param pullingIndexGlobal Global indices this rank wants to see as ghosts.
204 * **Sorted and deduplicated in-place.**
205 * @param LGlobalMapping Globally-known offsets, used to attribute each
206 * global index to its owner rank.
207 * @param mpi MPI context; the constructor is collective.
208 *
209 * @warning `pullingIndexGlobal` is mutated (sorted / uniquified / shrunk).
210 * The resulting `ghostIndex` ends up in ascending global-index order
211 * regardless of the caller's original ordering.
212 */
213 template <class TpullSet>
214 OffsetAscendIndexMapping(index nmainOffset, index nmainSize,
215 TpullSet &&pullingIndexGlobal,
216 const GlobalOffsetsMapping &LGlobalMapping,
217 const MPIInfo &mpi)
218 : mainOffset(nmainOffset),
219 mainSize(nmainSize)
220 {
221 ///*make sorted and unique!
222 std::sort(pullingIndexGlobal.begin(), pullingIndexGlobal.end());
223 auto last = std::unique(pullingIndexGlobal.begin(), pullingIndexGlobal.end());
224 pullingIndexGlobal.erase(last, pullingIndexGlobal.end());
225 pullingIndexGlobal.shrink_to_fit();
226 ///
227
228 ghostSizes.assign(mpi.size, 0);
229
230 for (auto i : pullingIndexGlobal)
231 {
232 MPI_int rank = -1;
233 index loc = -1; // dummy here
234 bool search_result = LGlobalMapping.search(i, rank, loc);
235 DNDS_assert_info(search_result, "Search Failed");
236 // if (rank != mpi.rank) // must not exclude local ones for the sake of scatter/gather
237 ghostSizes[rank]++;
238 }
239
240 ghostStart.resize(ghostSizes.size() + 1);
241 ghostStart[0] = 0;
242 for (typename decltype(ghostSizes)::size_type i = 0; i < ghostSizes.size(); i++)
243 ghostStart[i + 1] = signedIntSafeAdd<mapIndex>(ghostStart[i], ghostSizes[i]);
244 ghostIndex.reserve(ghostStart[ghostSizes.size()]);
245 for (auto i : pullingIndexGlobal)
246 {
247 MPI_int rank;
248 index loc; // dummy here
249 bool search_result = LGlobalMapping.search(i, rank, loc);
250 DNDS_assert_info(search_result, "Search Failed");
251 // if (rank != mpi.rank)
252 ghostIndex.push_back(i);
253 }
254 // ghostIndex.shrink_to_fit(); // only for safety
255 this->sort();
256
257 // obtain pushIndexSizes and actual push indices
258 pushIndexSizes.resize(mpi.size);
259 MPI::Alltoall(ghostSizes.data(), 1, MPI_INT, pushIndexSizes.data(), 1, MPI_INT, mpi.comm);
264 mpi.comm);
265
266 // stores pullingRequest // ! now cancelled
267 // pullingRequestLocal = std::forward<TpullSet>(pullingIndexGlobal);
268 // for (auto &i : pullingRequestLocal)
269 // {
270 // MPI_int rank;
271 // index loc; // dummy here
272 // search(i, rank, loc);
273 // if (rank != -1)
274 // i = -(1 + ghostStart[rank] + loc);
275 // }
276 }
277
278 /**
279 * @brief Construct the mapping from a push specification.
280 *
281 * @details The caller supplies, for every peer rank, a list of *local*
282 * row indices that this rank should send. The constructor flips this
283 * into the pull view via `MPI_Alltoall(v)`.
284 *
285 * @tparam TpushSet Range-like of local indices.
286 * @tparam TpushStart Size-`nRanks+1` prefix-sum offsets.
287 * @param nmainOffset Global offset of this rank's main block.
288 * @param nmainSize Number of main rows owned by this rank.
289 * @param pushingIndexesLocal Local indices to push, grouped by receiver
290 * (flat array; `[pushingStarts[r], pushingStarts[r+1])`
291 * are the indices going to rank `r`).
292 * @param pushingStarts Prefix sums of per-receiver counts, size `nRanks+1`.
293 * @param LGlobalMapping Rank-offset table (to convert local -> global).
294 * @param mpi MPI context; collective.
295 */
296 template <class TpushSet, class TpushStart>
297 OffsetAscendIndexMapping(index nmainOffset, index nmainSize,
298 TpushSet &&pushingIndexesLocal, // which stores local index
299 TpushStart &&pushingStarts,
300 const GlobalOffsetsMapping &LGlobalMapping,
301 const MPIInfo &mpi)
302 : mainOffset(nmainOffset),
303 mainSize(nmainSize)
304 {
305 DNDS_assert(pushingStarts.size() == mpi.size + 1 && pushingIndexesLocal.size() == pushingStarts[mpi.size]);
306 pushIndexSizes.resize(mpi.size);
307 pushIndexStarts.resize(mpi.size + 1, 0);
308 for (int i = 0; i < mpi.size; i++)
309 pushIndexSizes[i] = pushingStarts[i + 1] - pushingStarts[i],
310 pushIndexStarts[i + 1] = pushingStarts[i + 1];
311 pushingIndexGlobal.resize(pushingIndexesLocal.size());
312 // std::forward<TpushStart>(pushingStarts); //! might delete
313 for (size_t i = 0; i < pushingIndexGlobal.size(); i++)
314 pushingIndexGlobal[i] = LGlobalMapping(mpi.rank, pushingIndexesLocal[i]); // convert from local to global
315 // std::forward<TpushSet>(pushingIndexesLocal); //! might delete
316
317 ghostSizes.assign(mpi.size, 0);
318 MPI::Alltoall(pushIndexSizes.data(), 1, MPI_INT, ghostSizes.data(), 1, MPI_INT, mpi.comm); // inverse to the normal pulling
319 ghostStart.resize(ghostSizes.size() + 1);
320 ghostStart[0] = 0;
321 for (size_t i = 0; i < ghostSizes.size(); i++)
322 ghostStart[i + 1] = ghostStart[i] + ghostSizes[i];
323 ghostIndex.resize(ghostStart[ghostSizes.size()]);
325 ghostIndex.data(), ghostSizes.data(), ghostStart.data(), DNDS_MPI_INDEX,
326 mpi.comm); // inverse to the normal pulling
327
328 // !doesn't store pullingRequest
329 }
330
331 // auto &ghost() { return ghostIndex; }
332
333 // auto &gStarts() { return ghostStart; }
334
335 /// @brief Sort `ghostIndex` into ascending order. Required invariant
336 /// maintained after pull construction.
337 // warning: using globally sorted condition
338 void sort()
339 {
340 std::sort(ghostIndex.begin(), ghostIndex.end());
341 }
342
343 /// @brief Direct mutable access to the `ighost`-th ghost global index for peer `rank`.
344 index &ghostAt(MPI_int rank, index ighost)
345 {
346 DNDS_assert(ighost >= 0 && ighost < (ghostStart[rank + 1] - ghostStart[rank]));
347 return ghostIndex[ghostStart[rank] + ighost];
348 }
349
350 // TtMapIndexVec is a std::vector of someint
351 // could overflow, accumulated to 32-bit
352 // template <class TtMapIndexVec>
353 // void allocateGhostIndex(const TtMapIndexVec &ghostSizes)
354 // {
355 // }
356
357 /// @brief Check whether a global index lies within the main block.
358 /// @param globalQuery Global index to look up.
359 /// @param[out] val On success, local offset within the main block.
360 /// @return true if `globalQuery` is owned by this rank.
361 [[nodiscard]] bool searchInMain(index globalQuery, index &val) const
362 {
363 // std::cout << mainOffset << mainSize << std::endl;
364 if (globalQuery >= mainOffset && globalQuery < mainSize + mainOffset)
365 {
366 val = globalQuery - mainOffset;
367 return true;
368 }
369 return false;
370 }
371
372 /// @brief Search a single peer rank's ghost slab for a global index.
373 /// @param[out] val Offset relative to `ghostStart[rank]` on success.
374 // returns place relative to ghostStart[rank]
375 [[nodiscard]] bool searchInGhost(index globalQuery, MPI_int rank, index &val) const
376 {
377 DNDS_assert((rank >= 0 && rank < ghostStart.size() - 1));
378 if ((ghostStart[rank + 1] - ghostStart[rank]) == 0)
379 return false; // size = 0 could result in seg error doing search
380 auto start = ghostIndex.begin() + ghostStart[rank];
381 auto end = ghostIndex.begin() + ghostStart[rank + 1];
382 auto place = std::lower_bound(start, end, globalQuery);
383 if (place != end && *place == globalQuery) // dereferencing end could result in seg error
384 {
385 val = place - (ghostIndex.begin() + ghostStart[rank]);
386 return true;
387 }
388 return false;
389 }
390
391 /// @brief Search the concatenated ghost array for a global index (O(log nGhost)).
392 /// @param[out] rank Peer rank that owns the found index.
393 /// @param[out] val Offset relative to `ghostStart[0]` (== 0).
394 // returns rank & place, place relative to ghostStart[0] (==0)
395 [[nodiscard]] bool searchInAllGhost(index globalQuery, MPI_int &rank, index &val) const
396 {
397 auto start = ghostIndex.begin();
398 auto end = ghostIndex.end();
399 auto place = std::lower_bound(start, end, globalQuery);
400 if (place != end && *place == globalQuery) // dereferencing end could result in seg error
401 {
402 val = place - start;
403 auto s_start = ghostStart.begin();
404 auto s_end = ghostStart.end();
405 auto s_place = std::lower_bound(s_start, s_end, val, std::less_equal<rowsize>());
406 DNDS_assert(s_place != s_end && s_place > s_start);
407 rank = static_cast<MPI_int>(s_place - s_start - 1);
408 return true;
409 }
410 return false;
411 }
412
413 /// @brief Search main + ghost in one call. `rank == -1` signals "hit in main".
414 /// @details Returned `val` is in the *ghost* address space (relative to
415 /// `ghostStart[0]`) when `rank != -1`, else it is the main-local index.
416 /// See also #search_indexAppend for the combined (father+son) index space.
417 /// \brief returns rank and place in ghost array, rank==-1 means main data
418 [[nodiscard]] bool search(index globalQuery, MPI_int &rank, index &val) const
419 {
420 if (searchInMain(globalQuery, val))
421 {
422 rank = -1;
423 return true;
424 }
425 if (searchInAllGhost(globalQuery, rank, val))
426 {
427 return true;
428 }
429 return false;
430 }
431 /// @brief Tuple-return overload of the main #search. `(success, rank, local)`.
432 [[nodiscard]] std::tuple<bool, MPI_int, index> search(index globalQuery) const
433 {
434 MPI_int rank{-1};
435 index val{-1};
436 bool ret = this->search(globalQuery, rank, val);
437 return std::make_tuple(ret, rank, val);
438 }
439
440 /// @brief Like #search, but the returned `val` is in the concatenated
441 /// *father+son* address space.
442 /// @details `val < mainSize` means main row; `val >= mainSize` means
443 /// ghost row at `val - mainSize`. Matches `ArrayPair::operator[]` layout.
444 /// \brief returns rank and place in ghost array, rank==-1 means main data;
445 /// returned val is used for pair indexing
446 [[nodiscard]] bool search_indexAppend(index globalQuery, MPI_int &rank, index &val) const
447 {
448 if (searchInMain(globalQuery, val))
449 {
450 rank = -1;
451 return true;
452 }
453 if (searchInAllGhost(globalQuery, rank, val))
454 {
455 // std::cout << mainSize << std::endl;
456 val += mainSize;
457 return true;
458 }
459 return false;
460 }
461 /// @brief Tuple-return overload of #search_indexAppend.
462 [[nodiscard]] std::tuple<bool, MPI_int, index> search_indexAppend(index globalQuery) const
463 {
464 MPI_int rank{-1};
465 index val{-1};
466 bool ret = this->search_indexAppend(globalQuery, rank, val);
467 return std::make_tuple(ret, rank, val);
468 }
469
470 /// @brief Like #search, but `val` is expressed relative to the owner
471 /// rank's ghost slab (`ghostStart[rank]`-relative).
472 /// @warning Do not confuse with #search, whose `val` is relative to the
473 /// concatenated ghost array.
474 /// \brief returns rank and place in ghost of rank, rank==-1 means main data
475 /// \warning search returns index that applies to local ghost array, this only goes for the ith of rank
476 [[nodiscard]] bool search_indexRank(index globalQuery, MPI_int &rank, index &val) const
477 {
478 if (searchInMain(globalQuery, val))
479 {
480 rank = -1;
481 return true;
482 }
483 if (searchInAllGhost(globalQuery, rank, val))
484 {
485 val -= ghostStart[rank];
486 return true;
487 }
488 return false;
489 }
490
491 /// @brief Tuple-return overload of #search_indexRank.
492 [[nodiscard]] std::tuple<bool, MPI_int, index> search_indexRank(index globalQuery) const
493 {
494 MPI_int rank{-1};
495 index val{-1};
496 bool ret = this->search_indexRank(globalQuery, rank, val);
497 return std::make_tuple(ret, rank, val);
498 }
499
500 /// @brief Reverse (local -> global) mapping.
501 /// @details
502 /// - `rank == -1`: `val` indexes the concatenated father+son address
503 /// space. `val < mainSize` returns the main global index;
504 /// `val >= mainSize` returns the corresponding ghost global index.
505 /// - `rank >= 0`: `val` indexes into that peer's ghost slab
506 /// (like #search_indexRank's output).
507 /// \brief if rank == -1, return the global index of local main data,
508 /// or else return the ghosted global index of local ghost data.
509 [[nodiscard]] index operator()(MPI_int rank, index val) const
510 {
511 if (rank == -1)
512 {
513 DNDS_assert(val >= 0);
514 if (val < mainSize)
515 return val + mainOffset;
516 else
517 {
518 DNDS_assert_info(val - mainSize < ghostStart.back(), fmt::format("{}, {}, ghostSize {}", val, mainSize, ghostStart.back()));
519 return ghostIndex.at(val - mainSize);
520 }
521 }
522 else
523 {
525 (rank >= 0 && rank < ghostStart.size() - 1) &&
526 (val >= 0 && val < ghostStart[rank + 1] - ghostStart[rank]));
527 return ghostIndex[ghostStart[rank] + val];
528 }
529 }
530 };
531
532}
Core type aliases, constants, and metaprogramming utilities for the DNDS framework.
#define DNDS_assert_info(expr, info)
Debug-only assertion with an extra std::string info message.
Definition Errors.hpp:113
#define DNDS_assert(expr)
Debug-only assertion (compiled out when DNDS_NDEBUG is defined). Prints the expression + file/line + ...
Definition Errors.hpp:108
MPI wrappers: MPIInfo, collective operations, type mapping, CommStrategy.
Table mapping rank-local row indices to the global index space.
std::tuple< bool, MPI_int, index > search(index globalQuery) const
index globalSize() const
Total number of rows across all ranks.
t_IndexVec & ROffsets()
Prefix-sum offsets vector (size == nRanks + 1, last element == globalSize).
void setMPIAlignBcast(const MPIInfo &mpi, index myLength)
Broadcast each rank's length, then compute the global prefix sums.
index operator()(MPI_int rank, index val) const
Convert a (rank, local) pair to a global index.
bool search(index globalQuery, MPI_int &rank, index &val) const
Convert a global index to (rank, local). Returns false if out of range.
t_IndexVec & RLengths()
Per-rank lengths vector (size == nRanks).
Mapping between a rank's main data and its ghost copies.
bool search_indexAppend(index globalQuery, MPI_int &rank, index &val) const
Like search, but the returned val is in the concatenated father+son address space.
bool searchInGhost(index globalQuery, MPI_int rank, index &val) const
Search a single peer rank's ghost slab for a global index.
OffsetAscendIndexMapping(index nmainOffset, index nmainSize, TpullSet &&pullingIndexGlobal, const GlobalOffsetsMapping &LGlobalMapping, const MPIInfo &mpi)
Construct the mapping from a pull specification.
t_IndexVec ghostIndex
Global indices of all ghost rows on this rank, ascending. (a.k.a. pullingIndexGlobal)
t_IndexVec pushingIndexGlobal
Global indices this rank sends to others, grouped by receiver.
OffsetAscendIndexMapping(index nmainOffset, index nmainSize, TpushSet &&pushingIndexesLocal, TpushStart &&pushingStarts, const GlobalOffsetsMapping &LGlobalMapping, const MPIInfo &mpi)
Construct the mapping from a push specification.
bool searchInMain(index globalQuery, index &val) const
Check whether a global index lies within the main block.
index operator()(MPI_int rank, index val) const
Reverse (local -> global) mapping.
bool search_indexRank(index globalQuery, MPI_int &rank, index &val) const
Like search, but val is expressed relative to the owner rank's ghost slab (ghostStart[rank]-relative)...
std::tuple< bool, MPI_int, index > search_indexRank(index globalQuery) const
Tuple-return overload of search_indexRank.
index & ghostAt(MPI_int rank, index ighost)
Direct mutable access to the ighost-th ghost global index for peer rank.
tMPI_intVec pushIndexSizes
Per-peer number of rows this rank will send out on a push.
t_MapIndexVec ghostStart
Per-peer prefix-sum offsets into ghostIndex (size == nRanks + 1). (a.k.a. pullIndexStarts)
tMPI_intVec ghostSizes
Number of ghost rows pulled from each peer rank. (a.k.a. pullIndexSizes)
std::tuple< bool, MPI_int, index > search(index globalQuery) const
Tuple-return overload of the main search. (success, rank, local).
std::tuple< bool, MPI_int, index > search_indexAppend(index globalQuery) const
Tuple-return overload of search_indexAppend.
t_IndexVec pullingRequestLocal
Optional cached local-index form of ghostIndex (unused in current code).
void sort()
Sort ghostIndex into ascending order. Required invariant maintained after pull construction.
t_MapIndexVec pushIndexStarts
Per-peer prefix-sum offsets into pushingIndexGlobal.
bool search(index globalQuery, MPI_int &rank, index &val) const
Search main + ghost in one call. rank == -1 signals "hit in main".
bool searchInAllGhost(index globalQuery, MPI_int &rank, index &val) const
Search the concatenated ghost array for a global index (O(log nGhost)).
MPI_int Bcast(void *buf, MPI_int num, MPI_Datatype type, MPI_int source_rank, MPI_Comm comm)
dumb wrapper
Definition MPI.cpp:150
MPI_int Alltoall(void *send, MPI_int sendNum, MPI_Datatype typeSend, void *recv, MPI_int recvNum, MPI_Datatype typeRecv, MPI_Comm comm)
Wrapper over MPI_Alltoall (fixed per-peer count).
Definition MPI.cpp:166
MPI_int Alltoallv(void *send, MPI_int *sendSizes, MPI_int *sendStarts, MPI_Datatype sendType, void *recv, MPI_int *recvSizes, MPI_int *recvStarts, MPI_Datatype recvType, MPI_Comm comm)
Wrapper over MPI_Alltoallv (variable per-peer counts + displacements).
Definition MPI.cpp:182
the host side operators are provided as implemented
const MPI_Datatype DNDS_MPI_INDEX
MPI datatype matching index (= MPI_INT64_T).
Definition MPI.hpp:90
void AccumulateRowSize(const TtRowsizeVec &rowsizes, TtIndexVec &rowstarts)
Build a prefix-sum table from a row-size vector.
Definition Defines.hpp:452
std::vector< index > t_IndexVec
Vector of index values (global offsets, local ids, etc.).
Definition Defines.hpp:161
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
Definition Defines.hpp:107
tMPI_sizeVec tMPI_intVec
Alias for tMPI_sizeVec; used where the name "int vec" reads better.
Definition MPI.hpp:54
int MPI_int
MPI counterpart type for MPI_int (= C int). Used for counts and ranks in MPI calls.
Definition MPI.hpp:43
Lightweight bundle of an MPI communicator and the calling rank's coordinates.
Definition MPI.hpp:215
tVec r(NCells)