13#ifndef PECLET_CORE_HALO_PARTICLE_MIGRATOR_HPP
14#define PECLET_CORE_HALO_PARTICLE_MIGRATOR_HPP
55 const IVec<Dim>& gsize = dec_->globalSize();
56 for (
int i = 0; i < Dim; ++i) {
57 double rel = (x[i] - map_.origin[i]) / map_.cellSize[i];
59 if (map_.periodic[i]) {
60 c =
wrap(c, gsize[i]);
62 c = std::clamp<Index>(c, 0, gsize[i] - 1);
74 const IVec<Dim>& gsize = dec_->globalSize();
75 for (
int i = 0; i < Dim; ++i) {
76 if (!map_.periodic[i])
78 double L = map_.cellSize[i] *
static_cast<double>(gsize[i]);
79 double rel = x[i] - map_.origin[i];
80 rel = std::fmod(std::fmod(rel, L) + L, L);
81 x[i] = map_.origin[i] + rel;
89 std::size_t
migrate(std::vector<
Vec<Dim>>& pos, std::vector<char>& payload, std::size_t stride) {
90 const std::size_t n = pos.size();
91 const std::size_t posBytes = Dim *
sizeof(double);
92 const std::size_t recBytes = posBytes + stride;
94 std::map<int, std::vector<char>> outbox;
97 for (std::size_t i = 0; i < n; ++i) {
100 if (owner == rank_) {
103 std::memmove(&payload[keep * stride], &payload[i * stride], stride);
106 auto& buf = outbox[owner];
107 std::size_t off = buf.size();
108 buf.resize(off + recBytes);
109 std::memcpy(buf.data() + off, wx.data(), posBytes);
111 std::memcpy(buf.data() + off + posBytes, &payload[i * stride], stride);
116 payload.resize(keep * stride);
119 std::vector<std::pair<int, const std::vector<char>*>> queue;
120 queue.reserve(outbox.size());
121 for (
auto& [r, buf] : outbox)
122 queue.emplace_back(r, &buf);
127 auto packNext = [&](std::vector<char>& out) ->
int {
128 if (q >= queue.size())
130 int dest = queue[q].first;
131 out = *queue[q].second;
135 auto onRecv = [&](
int , std::vector<char>& msg) {
136 std::size_t cnt = msg.size() / recBytes;
137 for (std::size_t i = 0; i < cnt; ++i) {
138 const char* rec = msg.data() + i * recBytes;
140 std::memcpy(x.data(), rec, posBytes);
143 std::size_t off = payload.size();
144 payload.resize(off + stride);
145 std::memcpy(&payload[off], rec + posBytes, stride);
150 nbx.
exchange(packNext, onRecv, 7401);
157 int rank()
const {
return rank_; }
164 const auto& o = dec_->origins()[r];
165 const auto& s = dec_->sizes()[r];
166 const IVec<Dim>& gsize = dec_->globalSize();
168 for (
int i = 0; i < Dim; ++i) {
169 double lo = map_.origin[i] + o[i] * map_.cellSize[i];
170 double hi = map_.origin[i] + (o[i] + s[i]) * map_.cellSize[i];
171 double L = map_.cellSize[i] *
static_cast<double>(gsize[i]);
175 if (map_.periodic[i]) {
176 cands[nc++] = x[i] - L;
177 cands[nc++] = x[i] + L;
179 double bestGap = std::numeric_limits<double>::infinity();
180 double bestImg = x[i];
181 for (
int c = 0; c < nc; ++c) {
183 double gap = (p < lo) ? (lo - p) : (p > hi) ? (p - hi) : 0.0;
190 d2 += bestGap * bestGap;
192 return d2 < rcut * rcut;
203 std::vector<
Vec<Dim>>& outShifts)
const {
204 const auto& o = dec_->origins()[r];
205 const auto& s = dec_->sizes()[r];
206 const IVec<Dim>& gsize = dec_->globalSize();
207 double lo[Dim], hi[Dim], cand[Dim][3];
209 for (
int d = 0; d < Dim; ++d) {
210 lo[d] = map_.origin[d] + o[d] * map_.cellSize[d];
211 hi[d] = map_.origin[d] + (o[d] + s[d]) * map_.cellSize[d];
212 const double L = map_.cellSize[d] *
static_cast<double>(gsize[d]);
215 if (map_.periodic[d]) {
222 for (
int d = 0; d < Dim; ++d)
224 const double rc2 = rcut * rcut;
225 for (
int idx = 0; idx < total; ++idx) {
228 bool identity =
true;
230 for (
int d = 0; d < Dim; ++d) {
231 const int k = t % nc[d];
233 shift[d] = cand[d][k];
236 const double p = x[d] + shift[d];
237 const double gap = (p < lo[d]) ? (lo[d] - p) : (p > hi[d]) ? (p - hi[d]) : 0.0;
240 if (d2 < rc2 && (allowIdentity || !identity))
241 outShifts.push_back(shift);
252 std::size_t stride,
double rcut, std::vector<
Vec<Dim>>& ghostPos,
253 std::vector<char>& ghostPayload) {
255 ghostPayload.clear();
256 const std::size_t posBytes = Dim *
sizeof(double);
257 const std::size_t recBytes = posBytes + stride;
261 std::map<int, std::vector<char>> outbox;
263 for (std::size_t i = 0; i < pos.size(); ++i) {
264 for (
int r = 0; r < nranks; ++r) {
269 auto& buf = outbox[r];
270 std::size_t off = buf.size();
271 buf.resize(off + recBytes);
272 std::memcpy(buf.data() + off, img.data(), posBytes);
274 std::memcpy(buf.data() + off + posBytes, &payload[i * stride], stride);
278 std::vector<std::pair<int, const std::vector<char>*>> queue;
279 queue.reserve(outbox.size());
280 for (
auto& kv : outbox)
281 queue.emplace_back(kv.first, &kv.second);
282 std::size_t q = 0, recv = 0;
284 auto packNext = [&](std::vector<char>& out) ->
int {
285 if (q >= queue.size())
287 int dest = queue[q].first;
288 out = *queue[q].second;
292 auto onRecv = [&](
int , std::vector<char>& msg) {
293 std::size_t cnt = msg.size() / recBytes;
294 for (std::size_t i = 0; i < cnt; ++i) {
295 const char* rec = msg.data() + i * recBytes;
297 std::memcpy(x.data(), rec, posBytes);
298 ghostPos.push_back(x);
300 std::size_t off = ghostPayload.size();
301 ghostPayload.resize(off + stride);
302 std::memcpy(&ghostPayload[off], rec + posBytes, stride);
307 nbx.
exchange(packNext, onRecv, 7402);
316 std::size_t sent_ = 0, received_ = 0;
void exchange(PackNext &&packNext, OnRecv &&onRecv, int tag=0)
IVec< Dim > cellOf(const Vec< Dim > &x) const
Global decomposition cell containing position x (after periodic wrap / boundary clamp).
std::size_t gatherGhosts(const std::vector< Vec< Dim > > &pos, const std::vector< char > &payload, std::size_t stride, double rcut, std::vector< Vec< Dim > > &ghostPos, std::vector< char > &ghostPayload)
Gather ghost copies of particles within rcut of this rank's block boundary.
const decomp::BlockDecomposer< Dim > & decomposer() const
std::size_t lastReceived() const
Vec< Dim > wrapPosition(Vec< Dim > x) const
Wrap a position into the periodic box (no-op on non-periodic axes).
std::size_t migrate(std::vector< Vec< Dim > > &pos, std::vector< char > &payload, std::size_t stride)
Reassign every particle to its owning rank.
void imagesWithinRcutOfBlock(const Vec< Dim > &x, int r, double rcut, bool allowIdentity, std::vector< Vec< Dim > > &outShifts) const
Append the shift (= image − x) of EVERY periodic image of x that comes within rcut of block r's AABB ...
bool withinRcutOfBlock(const Vec< Dim > &x, int r, double rcut, Vec< Dim > &img) const
True if x (via its closest periodic image) comes within rcut of block r's physical AABB.
void init(const decomp::BlockDecomposer< Dim > &dec, int rank, DomainMap< Dim > map, MPI_Comm comm=MPI_COMM_WORLD)
int ownerOf(const Vec< Dim > &x) const
Rank that owns the block containing position x (after periodic wrap / boundary clamp).
std::size_t lastSent() const
int MPI_Comm_size(MPI_Comm, int *s)
Index wrap(Index x, Index n)
Periodic wrap of a coordinate into [0, n): wrap(x, n) = (x % n + n) % n.
std::array< Real, Dim > Vec
Multi-dimensional real vector.
std::array< Index, Dim > IVec
Multi-dimensional integer index / coordinate (x-fastest order by convention).
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
Physical-space layout that maps a position to a global cell of the decomposition.
Vec< Dim > origin
physical coordinate of global cell (0,...)
std::array< bool, Dim > periodic
per-axis periodicity
Vec< Dim > cellSize
physical size of one global cell