core 1.5.0
Shared MPI block decomposition, asynchronous ghost-layer exchange, SDF geometry, load balancing (header-only C++20)
Loading...
Searching...
No Matches
gather_by_global_id.hpp
Go to the documentation of this file.
1// core — the id-keyed replicated gather (amr/docs/amr_mg_core_boundary.md §3.1, §4): every rank
2// contributes (global id, value) pairs and every rank receives the whole global vector, indexed by
3// id. The one-liner that flow's GraphAMG bottom, amr's ReplicatedTailStage and voro's coming
4// GraphAMG bottom each wrote inline. Pure copies, bitwise.
5#ifndef PECLET_CORE_DECOMP_GATHER_BY_GLOBAL_ID_HPP
6#define PECLET_CORE_DECOMP_GATHER_BY_GLOBAL_ID_HPP
7
8#include <climits>
9#include <cstddef>
10#include <stdexcept>
11#include <type_traits>
12#include <vector>
13
15
16namespace peclet::core::decomp {
17
30template <class T>
31void gatherByGlobalId(const std::vector<long long>& ids, const std::vector<T>& vals,
32 long long nGlobal, std::vector<T>& out, MPI_Comm comm) {
33 static_assert(std::is_trivially_copyable_v<T>, "gatherByGlobalId moves T as raw bytes");
34 if (ids.size() != vals.size())
35 throw std::invalid_argument("gatherByGlobalId: ids and vals differ in length");
36 if (ids.size() > static_cast<std::size_t>(INT_MAX / sizeof(T)) ||
37 ids.size() > static_cast<std::size_t>(INT_MAX / sizeof(long long)))
38 throw std::overflow_error("gatherByGlobalId: a rank's contribution exceeds INT_MAX bytes");
39 int size = 1;
40 MPI_Comm_size(comm, &size);
41 const int n = static_cast<int>(ids.size());
42 std::vector<int> counts(static_cast<std::size_t>(size)), displs(static_cast<std::size_t>(size));
43 MPI_Allgather(&n, 1, MPI_INT, counts.data(), 1, MPI_INT, comm);
44 long long tot = 0;
45 for (int r = 0; r < size; ++r) {
46 displs[static_cast<std::size_t>(r)] = static_cast<int>(tot);
47 tot += counts[static_cast<std::size_t>(r)];
48 if (tot > INT_MAX)
49 throw std::overflow_error("gatherByGlobalId: the global vector exceeds INT_MAX entries");
50 }
51 if (tot != nGlobal)
52 throw std::invalid_argument("gatherByGlobalId: the ids do not cover [0, nGlobal) once each");
53 std::vector<long long> idAll(static_cast<std::size_t>(tot));
54 MPI_Allgatherv(ids.data(), n, MPI_LONG_LONG, idAll.data(), counts.data(), displs.data(),
55 MPI_LONG_LONG, comm);
56 std::vector<T> vAll(static_cast<std::size_t>(tot));
57 std::vector<int> bc(counts.size()), bd(displs.size());
58 for (std::size_t r = 0; r < counts.size(); ++r) {
59 if (static_cast<std::size_t>(counts[r]) > static_cast<std::size_t>(INT_MAX) / sizeof(T) ||
60 static_cast<std::size_t>(displs[r]) > static_cast<std::size_t>(INT_MAX) / sizeof(T))
61 throw std::overflow_error("gatherByGlobalId: the global vector exceeds INT_MAX bytes");
62 bc[r] = counts[r] * static_cast<int>(sizeof(T));
63 bd[r] = displs[r] * static_cast<int>(sizeof(T));
64 }
65 MPI_Allgatherv(vals.data(), n * static_cast<int>(sizeof(T)), MPI_BYTE, vAll.data(), bc.data(),
66 bd.data(), MPI_BYTE, comm);
67 out.assign(static_cast<std::size_t>(nGlobal), T{});
68 std::vector<char> seen(static_cast<std::size_t>(nGlobal), 0);
69 for (std::size_t k = 0; k < idAll.size(); ++k) {
70 const long long id = idAll[k];
71 if (id < 0 || id >= nGlobal || seen[static_cast<std::size_t>(id)])
72 throw std::invalid_argument("gatherByGlobalId: the ids do not cover [0, nGlobal) once each");
73 seen[static_cast<std::size_t>(id)] = 1;
74 out[static_cast<std::size_t>(id)] = vAll[k];
75 }
76}
77
78} // namespace peclet::core::decomp
79
80#endif // PECLET_CORE_DECOMP_GATHER_BY_GLOBAL_ID_HPP
int MPI_Comm_size(MPI_Comm, int *s)
Definition mpi_stub.hpp:70
int MPI_Comm
Definition mpi_stub.hpp:17
#define MPI_INT
Definition mpi_stub.hpp:39
#define MPI_BYTE
Definition mpi_stub.hpp:38
void gatherByGlobalId(const std::vector< long long > &ids, const std::vector< T > &vals, long long nGlobal, std::vector< T > &out, MPI_Comm comm)
ids[i] is the global id of vals[i] on this rank.
Kokkos::View< T *, MemSpace > View
1D device array.
Definition view.hpp:26