16#ifndef PECLET_DEM_MPI_HALO_HPP
17#define PECLET_DEM_MPI_HALO_HPP
24#include <Kokkos_Core.hpp>
31#include "peclet/core/common/types.hpp"
32#include "peclet/core/common/view.hpp"
33#include "peclet/core/decomp/block_decomposer.hpp"
34#include "peclet/core/halo/particle_halo.hpp"
35#include "peclet/core/halo/particle_halo_topology.hpp"
36#include "peclet/core/halo/particle_migrator.hpp"
37#include "peclet/core/halo/particle_rebalance.hpp"
47 F3 vel, velPred, angVel, angVelPred, invInertia;
59 F3 vel, angVel, invInertia;
60 float invMass, scale, targetScale;
62 float planeFric0, planeFric1;
68inline void haloPackF3(
V3 field, peclet::core::View<F3> owned,
int n) {
70 "peclet::dem::halo::packF3", Kokkos::RangePolicy<CpExec>(0, n),
71 KOKKOS_LAMBDA(
int i) { owned(i) =
F3{field(i, 0), field(i, 1), field(i, 2)}; });
73inline void haloPackF4(
V4 field, peclet::core::View<F4> owned,
int n) {
75 "peclet::dem::halo::packF4", Kokkos::RangePolicy<CpExec>(0, n),
76 KOKKOS_LAMBDA(
int i) { owned(i) = F4{field(i, 0), field(i, 1), field(i, 2), field(i, 3)}; });
79inline void haloUnpackF3(
V3 field, peclet::core::View<F3> ghost, peclet::core::View<F3> shift,
80 int no,
int ng,
bool doShift) {
82 "peclet::dem::halo::unpackF3", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
89 field(no + g, 0) = v.x;
90 field(no + g, 1) = v.y;
91 field(no + g, 2) = v.z;
94inline void haloUnpackF4(
V4 field, peclet::core::View<F4> ghost,
int no,
int ng) {
96 "peclet::dem::halo::unpackF4", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
97 field(no + g, 0) = ghost(g).x;
98 field(no + g, 1) = ghost(g).y;
99 field(no + g, 2) = ghost(g).z;
100 field(no + g, 3) = ghost(g).w;
104inline void haloPackGather(
V3 vel,
V3 velPred,
V3 angVel,
V3 angVelPred,
V3 invInertia,
V4 quat,
105 V4 quatPred,
Vf scale,
Vf invMass,
Vi shapeId,
106 peclet::core::View<MpiGatherPack> owned,
int n) {
107 Kokkos::parallel_for(
108 "peclet::dem::halo::packGather", Kokkos::RangePolicy<CpExec>(0, n), KOKKOS_LAMBDA(
int i) {
110 g.vel =
F3{vel(i, 0), vel(i, 1), vel(i, 2)};
111 g.velPred =
F3{velPred(i, 0), velPred(i, 1), velPred(i, 2)};
112 g.angVel =
F3{angVel(i, 0), angVel(i, 1), angVel(i, 2)};
113 g.angVelPred =
F3{angVelPred(i, 0), angVelPred(i, 1), angVelPred(i, 2)};
114 g.invInertia =
F3{invInertia(i, 0), invInertia(i, 1), invInertia(i, 2)};
115 g.quat = F4{quat(i, 0), quat(i, 1), quat(i, 2), quat(i, 3)};
116 g.quatPred = F4{quatPred(i, 0), quatPred(i, 1), quatPred(i, 2), quatPred(i, 3)};
118 g.invMass = invMass(i);
119 g.shape = shapeId(i);
126inline void haloUnpackGather(
V3 vel,
V3 velPred,
V3 angVel,
V3 angVelPred,
V3 invInertia,
V4 quat,
127 V4 quatPred,
Vf scale,
Vf invMass,
Vi shapeId,
Vi realIndices,
128 peclet::core::View<MpiGatherPack> ghost,
int no,
int ng) {
129 Kokkos::parallel_for(
130 "peclet::dem::halo::unpackGather", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
131 const MpiGatherPack p = ghost(g);
132 const int s = no + g;
136 velPred(s, 0) = p.velPred.x;
137 velPred(s, 1) = p.velPred.y;
138 velPred(s, 2) = p.velPred.z;
139 angVel(s, 0) = p.angVel.x;
140 angVel(s, 1) = p.angVel.y;
141 angVel(s, 2) = p.angVel.z;
142 angVelPred(s, 0) = p.angVelPred.x;
143 angVelPred(s, 1) = p.angVelPred.y;
144 angVelPred(s, 2) = p.angVelPred.z;
145 invInertia(s, 0) = p.invInertia.x;
146 invInertia(s, 1) = p.invInertia.y;
147 invInertia(s, 2) = p.invInertia.z;
148 quat(s, 0) = p.quat.x;
149 quat(s, 1) = p.quat.y;
150 quat(s, 2) = p.quat.z;
151 quat(s, 3) = p.quat.w;
152 quatPred(s, 0) = p.quatPred.x;
153 quatPred(s, 1) = p.quatPred.y;
154 quatPred(s, 2) = p.quatPred.z;
155 quatPred(s, 3) = p.quatPred.w;
157 invMass(s) = p.invMass;
158 shapeId(s) = p.shape;
169 void initMpi(std::array<double, 3> origin, std::array<double, 3> size, std::array<long, 3> gsize,
170 std::array<bool, 3> periodic, MPI_Comm comm) {
173 MPI_Comm_rank(comm_, &rank_);
174 MPI_Comm_size(comm_, &sz);
175 dec_.init(
static_cast<std::size_t
>(sz), peclet::core::IVec<3>{gsize[0], gsize[1], gsize[2]});
176 peclet::core::halo::DomainMap<3> map;
177 for (
int i = 0; i < 3; ++i) {
178 map.origin[i] = origin[i];
179 map.cellSize[i] = size[i] /
static_cast<double>(gsize[i]);
180 map.periodic[i] = periodic[i];
183 mig_.init(dec_, rank_, map, comm_);
190 void initMpi(
const peclet::core::decomp::BlockDecomposer<3>& dec, std::array<double, 3> origin,
191 std::array<double, 3> size, std::array<bool, 3> periodic, MPI_Comm comm) {
193 MPI_Comm_rank(comm_, &rank_);
195 const auto& gs = dec.globalSize();
196 peclet::core::halo::DomainMap<3> map;
197 for (
int i = 0; i < 3; ++i) {
198 map.origin[i] = origin[i];
199 map.cellSize[i] = size[i] /
static_cast<double>(gs[i]);
200 map.periodic[i] = periodic[i];
203 mig_.init(dec_, rank_, map, comm_);
207 const peclet::core::decomp::BlockDecomposer<3>& decomposer()
const {
return dec_; }
209 bool inited()
const {
return inited_; }
210 int rank()
const {
return rank_; }
211 int numGhost()
const {
return numGhost_; }
220 void setVerletSkin(
float skin) { verletSkin_ = skin < 0.0f ? 0.0f : skin; }
221 float verletSkin()
const {
return verletSkin_; }
224 long numRebuilds()
const {
return nRebuild_; }
225 long numGathers()
const {
return nGather_; }
230 int gather(Particles& P,
double rcut) {
231 const int no = P.numReal;
239 bool rebuild = (verletSkin_ <= 0.0f) || !haveTopo_ || (no != lastNumReal_);
240 if (!rebuild && maxOwnedDisplacement(P.pos, no) >= verletSkin_)
246 const double band = rcut +
static_cast<double>(verletSkin_);
248 auto hpos = Kokkos::create_mirror_view(P.pos);
249 Kokkos::deep_copy(hpos, P.pos);
250 std::vector<peclet::core::Vec<3>> pv(
static_cast<std::size_t
>(no));
251 for (
int i = 0; i < no; ++i)
252 pv[i] = peclet::core::Vec<3>{hpos(i, 0), hpos(i, 1), hpos(i, 2)};
257 halo_.build(pv, band,
true);
259 const int ng =
static_cast<int>(halo_.numGhost());
266 if (no + ng > P.capacity)
267 throw std::runtime_error(
268 "ParticleHalo::gather: ghost overflow -- need capacity >= " + std::to_string(no + ng) +
269 " (numReal=" + std::to_string(no) +
" + numGhost=" + std::to_string(ng) +
"), have " +
270 std::to_string(P.capacity) +
"; increase the Simulation capacity.");
272 allocBuffers(no, ng);
275 if (verletSkin_ > 0.0f) {
276 if (refPos_.extent(0) <
static_cast<std::size_t
>(no))
277 refPos_ =
V3(
"peclet::dem::halo::refPos",
static_cast<std::size_t
>(P.capacity));
278 Kokkos::deep_copy(Kokkos::subview(refPos_, std::pair<int, int>(0, no), Kokkos::ALL),
279 Kokkos::subview(P.pos, std::pair<int, int>(0, no), Kokkos::ALL));
285 const int ng = numGhost_;
286 P.numParticles = no + ng;
289 selfMapReals(P.realIndices, no);
296 forwardPositions(P.pos);
297 forwardPositions(P.posPred);
301 haloPackGather(P.vel, P.velPred, P.angVel, P.angVelPred, P.invInertia, P.quat, P.quatPred,
302 P.scale, P.invMass, P.shapeId, ownedPack_, no);
303 dev_.forward(ownedPack_, ghostPack_);
304 haloUnpackGather(P.vel, P.velPred, P.angVel, P.angVelPred, P.invInertia, P.quat, P.quatPred,
305 P.scale, P.invMass, P.shapeId, P.realIndices, ghostPack_, no, ng);
317 int rebalance(Particles& P) {
318 std::vector<peclet::core::Vec<3>> pos;
319 std::vector<char> payload;
320 packState(P, pos, payload);
321 const std::size_t newN = peclet::core::halo::rebalanceByParticleCount(
322 dec_, mig_, pos, payload,
sizeof(MigratePack), comm_);
323 if ((
int)newN > P.capacity)
324 throw std::runtime_error(
"ParticleHalo::rebalance: owned overflow -- rank received " +
325 std::to_string(newN) +
" particles, capacity " +
326 std::to_string(P.capacity));
327 unpackState(P, pos, payload, newN);
333 int migrateTo(Particles& P,
const peclet::core::decomp::BlockDecomposer<3>& newDec) {
334 std::vector<peclet::core::Vec<3>> pos;
335 std::vector<char> payload;
336 packState(P, pos, payload);
338 const std::size_t newN = mig_.migrate(pos, payload,
sizeof(MigratePack));
339 if ((
int)newN > P.capacity)
340 throw std::runtime_error(
"ParticleHalo::migrateTo: owned overflow -- rank received " +
341 std::to_string(newN) +
" particles, capacity " +
342 std::to_string(P.capacity));
343 unpackState(P, pos, payload, newN);
349 int migrateToWeights(Particles& P,
const std::vector<peclet::core::Real>& w) {
351 MPI_Comm_size(comm_, &size);
352 peclet::core::decomp::BlockDecomposer<3> newDec((std::size_t)size, dec_.globalSize(), w);
353 return migrateTo(P, newDec);
358 void packState(Particles& P, std::vector<peclet::core::Vec<3>>& pos, std::vector<char>& payload) {
359 const int no = P.numReal;
360 auto h_pos = Kokkos::create_mirror_view(P.pos);
361 auto h_quat = Kokkos::create_mirror_view(P.quat);
362 auto h_vel = Kokkos::create_mirror_view(P.vel);
363 auto h_angVel = Kokkos::create_mirror_view(P.angVel);
364 auto h_invI = Kokkos::create_mirror_view(P.invInertia);
365 auto h_invM = Kokkos::create_mirror_view(P.invMass);
366 auto h_scale = Kokkos::create_mirror_view(P.scale);
367 auto h_tScale = Kokkos::create_mirror_view(P.targetScale);
368 auto h_shape = Kokkos::create_mirror_view(P.shapeId);
369 auto h_pf = Kokkos::create_mirror_view(P.planeFriction);
370 Kokkos::deep_copy(h_pos, P.pos);
371 Kokkos::deep_copy(h_quat, P.quat);
372 Kokkos::deep_copy(h_vel, P.vel);
373 Kokkos::deep_copy(h_angVel, P.angVel);
374 Kokkos::deep_copy(h_invI, P.invInertia);
375 Kokkos::deep_copy(h_invM, P.invMass);
376 Kokkos::deep_copy(h_scale, P.scale);
377 Kokkos::deep_copy(h_tScale, P.targetScale);
378 Kokkos::deep_copy(h_shape, P.shapeId);
379 Kokkos::deep_copy(h_pf, P.planeFriction);
380 pos.assign((std::size_t)no, peclet::core::Vec<3>{});
381 payload.assign((std::size_t)no *
sizeof(MigratePack), 0);
382 for (
int i = 0; i < no; ++i) {
383 pos[(std::size_t)i] = peclet::core::Vec<3>{h_pos(i, 0), h_pos(i, 1), h_pos(i, 2)};
385 m.quat = F4{h_quat(i, 0), h_quat(i, 1), h_quat(i, 2), h_quat(i, 3)};
386 m.vel =
F3{h_vel(i, 0), h_vel(i, 1), h_vel(i, 2)};
387 m.angVel =
F3{h_angVel(i, 0), h_angVel(i, 1), h_angVel(i, 2)};
388 m.invInertia =
F3{h_invI(i, 0), h_invI(i, 1), h_invI(i, 2)};
389 m.invMass = h_invM(i);
390 m.scale = h_scale(i);
391 m.targetScale = h_tScale(i);
392 m.shapeId = h_shape(i);
393 m.planeFric0 = h_pf(i, 0);
394 m.planeFric1 = h_pf(i, 1);
395 std::memcpy(&payload[(std::size_t)i *
sizeof(MigratePack)], &m,
sizeof(MigratePack));
399 void unpackState(Particles& P,
const std::vector<peclet::core::Vec<3>>& pos,
400 const std::vector<char>& payload, std::size_t newN) {
401 auto h_pos = Kokkos::create_mirror_view(P.pos);
402 auto h_quat = Kokkos::create_mirror_view(P.quat);
403 auto h_vel = Kokkos::create_mirror_view(P.vel);
404 auto h_angVel = Kokkos::create_mirror_view(P.angVel);
405 auto h_invI = Kokkos::create_mirror_view(P.invInertia);
406 auto h_invM = Kokkos::create_mirror_view(P.invMass);
407 auto h_scale = Kokkos::create_mirror_view(P.scale);
408 auto h_tScale = Kokkos::create_mirror_view(P.targetScale);
409 auto h_shape = Kokkos::create_mirror_view(P.shapeId);
410 auto h_pf = Kokkos::create_mirror_view(P.planeFriction);
411 for (std::size_t i = 0; i < newN; ++i) {
412 h_pos((
int)i, 0) = pos[i][0];
413 h_pos((
int)i, 1) = pos[i][1];
414 h_pos((
int)i, 2) = pos[i][2];
416 std::memcpy(&m, &payload[i *
sizeof(MigratePack)],
sizeof(MigratePack));
417 h_quat((
int)i, 0) = m.quat.x;
418 h_quat((
int)i, 1) = m.quat.y;
419 h_quat((
int)i, 2) = m.quat.z;
420 h_quat((
int)i, 3) = m.quat.w;
421 h_vel((
int)i, 0) = m.vel.x;
422 h_vel((
int)i, 1) = m.vel.y;
423 h_vel((
int)i, 2) = m.vel.z;
424 h_angVel((
int)i, 0) = m.angVel.x;
425 h_angVel((
int)i, 1) = m.angVel.y;
426 h_angVel((
int)i, 2) = m.angVel.z;
427 h_invI((
int)i, 0) = m.invInertia.x;
428 h_invI((
int)i, 1) = m.invInertia.y;
429 h_invI((
int)i, 2) = m.invInertia.z;
430 h_invM((
int)i) = m.invMass;
431 h_scale((
int)i) = m.scale;
432 h_tScale((
int)i) = m.targetScale;
433 h_shape((
int)i) = m.shapeId;
434 h_pf((
int)i, 0) = m.planeFric0;
435 h_pf((
int)i, 1) = m.planeFric1;
437 Kokkos::deep_copy(P.pos, h_pos);
438 Kokkos::deep_copy(P.quat, h_quat);
439 Kokkos::deep_copy(P.vel, h_vel);
440 Kokkos::deep_copy(P.angVel, h_angVel);
441 Kokkos::deep_copy(P.invInertia, h_invI);
442 Kokkos::deep_copy(P.invMass, h_invM);
443 Kokkos::deep_copy(P.scale, h_scale);
444 Kokkos::deep_copy(P.targetScale, h_tScale);
445 Kokkos::deep_copy(P.shapeId, h_shape);
446 Kokkos::deep_copy(P.planeFriction, h_pf);
448 P.numReal = (int)newN;
449 P.numParticles = (int)newN;
454 void forward(V3 field) {
457 haloPackF3(field, ownedF3_, numReal_);
458 dev_.forward(ownedF3_, ghostF3_);
459 haloUnpackF3(field, ghostF3_, shiftDev_, numReal_, numGhost_,
false);
462 void forwardPositions(V3 field) {
465 haloPackF3(field, ownedF3_, numReal_);
466 dev_.forward(ownedF3_, ghostF3_);
467 haloUnpackF3(field, ghostF3_, shiftDev_, numReal_, numGhost_,
true);
470 void forward4(V4 field) {
473 haloPackF4(field, ownedF4_, numReal_);
474 dev_.forward(ownedF4_, ghostF4_);
475 haloUnpackF4(field, ghostF4_, numReal_, numGhost_);
478 void selfMapReals(Vi realIndices,
int no) {
479 Kokkos::parallel_for(
480 "peclet::dem::halo::selfMapReals", Kokkos::RangePolicy<CpExec>(0, no),
481 KOKKOS_LAMBDA(
int i) { realIndices(i) = i; });
485 void allocBuffers(
int no,
int ng) {
488 ownedF3_ = peclet::core::View<F3>(
"peclet::dem::halo::ownedF3", no);
489 ghostF3_ = peclet::core::View<F3>(
"peclet::dem::halo::ghostF3", ng);
490 ownedF4_ = peclet::core::View<F4>(
"peclet::dem::halo::ownedF4", no);
491 ghostF4_ = peclet::core::View<F4>(
"peclet::dem::halo::ghostF4", ng);
492 ownedPack_ = peclet::core::View<MpiGatherPack>(
"peclet::dem::halo::ownedPack", no);
493 ghostPack_ = peclet::core::View<MpiGatherPack>(
"peclet::dem::halo::ghostPack", ng);
500 float maxOwnedDisplacement(
const V3& pos,
int no)
const {
501 if (no <= 0 || refPos_.extent(0) <
static_cast<std::size_t
>(no))
504 V3 p = pos, r = refPos_;
505 Kokkos::parallel_reduce(
506 "peclet::dem::halo::maxdisp", Kokkos::RangePolicy<CpExec>(0, no),
507 KOKKOS_LAMBDA(
const int i,
float& m) {
508 const float dx = p(i, 0) - r(i, 0), dy = p(i, 1) - r(i, 1), dz = p(i, 2) - r(i, 2);
509 const float d = Kokkos::sqrt(dx * dx + dy * dy + dz * dz);
513 Kokkos::Max<float>(md));
519 auto t = halo_.flatten();
520 std::vector<F3> hs(t.shift.size());
521 for (std::size_t i = 0; i < t.shift.size(); ++i)
522 hs[i] =
F3{
static_cast<float>(t.shift[i][0]),
static_cast<float>(t.shift[i][1]),
523 static_cast<float>(t.shift[i][2])};
524 shiftDev_ = peclet::core::toDevice(hs,
"peclet::dem::halo::shift");
527 bool inited_ =
false;
528 int rank_ = 0, numReal_ = 0, numGhost_ = 0;
531 float verletSkin_ = 0.0f;
532 bool haveTopo_ =
false;
533 int lastNumReal_ = -1;
534 long nRebuild_ = 0, nGather_ = 0;
536 MPI_Comm comm_ = MPI_COMM_NULL;
537 peclet::core::decomp::BlockDecomposer<3> dec_;
538 peclet::core::halo::DomainMap<3> map_;
539 peclet::core::halo::ParticleMigrator<3> mig_;
540 peclet::core::halo::ParticleHaloTopology<3> halo_;
541 peclet::core::halo::ParticleHalo<3> dev_;
542 peclet::core::View<F3> ownedF3_, ghostF3_, shiftDev_;
543 peclet::core::View<F4> ownedF4_, ghostF4_;
544 peclet::core::View<MpiGatherPack> ownedPack_, ghostPack_;
dem — portable POD types + math + analytic SDFs shared by the Kokkos kernel ports.
Kokkos::View< float *, CpMem > Vf
Kokkos::View< int *, CpMem > Vi
Kokkos::View< float *[3], CpMem > V3
Kokkos::View< float *[4], CpMem > V4
dem — portable (Kokkos) particle SoA container: the storage the dem flip pivots on.