16#ifndef PECLET_DEM_MPI_HALO_HPP
17#define PECLET_DEM_MPI_HALO_HPP
26#include <Kokkos_Core.hpp>
29#include <unordered_map>
34#include "peclet/core/common/types.hpp"
35#include "peclet/core/common/view.hpp"
36#include "peclet/core/decomp/block_decomposer.hpp"
37#include "peclet/core/halo/particle_halo.hpp"
38#include "peclet/core/halo/particle_halo_topology.hpp"
39#include "peclet/core/halo/particle_migrator.hpp"
40#include "peclet/core/halo/particle_rebalance.hpp"
50 F3 vel, velPred, angVel, angVelPred, invInertia;
60 unsigned char material, grounded;
64 float orphan, orphanVPeak;
71 unsigned long long key;
80inline constexpr int kWarmCarryMax = 14;
85struct HertzPairEntry {
86 unsigned long long key;
98 F3 vel, angVel, invInertia;
99 float invMass, scale, targetScale;
101 float planeFric0, planeFric1;
103 unsigned char materialId, groundedLevel, numWarm, numHertz;
104 float orphan, orphanVPeak;
105 WarmPairEntry warm[kWarmCarryMax];
108 HertzPairEntry hertz[kWarmCarryMax];
109 float hertzXiWall[Particles::kHertzMaxWalls][3];
110 float hertzSnWall[Particles::kHertzMaxWalls];
116inline void haloPackF3(
V3 field, peclet::core::View<F3> owned,
int n) {
117 Kokkos::parallel_for(
118 "peclet::dem::halo::packF3", Kokkos::RangePolicy<CpExec>(0, n),
119 KOKKOS_LAMBDA(
int i) { owned(i) =
F3{field(i, 0), field(i, 1), field(i, 2)}; });
121inline void haloPackF4(
V4 field, peclet::core::View<F4> owned,
int n) {
122 Kokkos::parallel_for(
123 "peclet::dem::halo::packF4", Kokkos::RangePolicy<CpExec>(0, n),
124 KOKKOS_LAMBDA(
int i) { owned(i) = F4{field(i, 0), field(i, 1), field(i, 2), field(i, 3)}; });
127inline void haloUnpackF3(
V3 field, peclet::core::View<F3> ghost, peclet::core::View<F3> shift,
128 int no,
int ng,
bool doShift) {
129 Kokkos::parallel_for(
130 "peclet::dem::halo::unpackF3", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
137 field(no + g, 0) = v.x;
138 field(no + g, 1) = v.y;
139 field(no + g, 2) = v.z;
142inline void haloUnpackF4(
V4 field, peclet::core::View<F4> ghost,
int no,
int ng) {
143 Kokkos::parallel_for(
144 "peclet::dem::halo::unpackF4", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
145 field(no + g, 0) = ghost(g).x;
146 field(no + g, 1) = ghost(g).y;
147 field(no + g, 2) = ghost(g).z;
148 field(no + g, 3) = ghost(g).w;
152inline void haloPackGather(
V3 vel,
V3 velPred,
V3 angVel,
V3 angVelPred,
V3 invInertia,
V4 quat,
153 V4 quatPred,
Vf scale,
Vf invMass,
Vi shapeId,
Vi gid,
154 Kokkos::View<unsigned char*, CpMem> materialId,
155 Kokkos::View<unsigned char*, CpMem> grounded,
Vf orphan,
156 Vf orphanVPeak, peclet::core::View<MpiGatherPack> owned,
int n) {
157 Kokkos::parallel_for(
158 "peclet::dem::halo::packGather", Kokkos::RangePolicy<CpExec>(0, n), KOKKOS_LAMBDA(
int i) {
160 g.vel =
F3{vel(i, 0), vel(i, 1), vel(i, 2)};
161 g.velPred =
F3{velPred(i, 0), velPred(i, 1), velPred(i, 2)};
162 g.angVel =
F3{angVel(i, 0), angVel(i, 1), angVel(i, 2)};
163 g.angVelPred =
F3{angVelPred(i, 0), angVelPred(i, 1), angVelPred(i, 2)};
164 g.invInertia =
F3{invInertia(i, 0), invInertia(i, 1), invInertia(i, 2)};
165 g.quat = F4{quat(i, 0), quat(i, 1), quat(i, 2), quat(i, 3)};
166 g.quatPred = F4{quatPred(i, 0), quatPred(i, 1), quatPred(i, 2), quatPred(i, 3)};
168 g.invMass = invMass(i);
169 g.shape = shapeId(i);
171 g.material = materialId(i);
172 g.grounded = grounded(i);
173 g.orphan = orphan(i);
174 g.orphanVPeak = orphanVPeak(i);
181inline void haloUnpackGather(
V3 vel,
V3 velPred,
V3 angVel,
V3 angVelPred,
V3 invInertia,
V4 quat,
182 V4 quatPred,
Vf scale,
Vf invMass,
Vi shapeId,
Vi realIndices,
Vi gid,
183 Kokkos::View<unsigned char*, CpMem> materialId,
184 Kokkos::View<unsigned char*, CpMem> grounded,
Vf orphan,
185 Vf orphanVPeak, peclet::core::View<MpiGatherPack> ghost,
int no,
187 Kokkos::parallel_for(
188 "peclet::dem::halo::unpackGather", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
189 const MpiGatherPack p = ghost(g);
190 const int s = no + g;
194 velPred(s, 0) = p.velPred.x;
195 velPred(s, 1) = p.velPred.y;
196 velPred(s, 2) = p.velPred.z;
197 angVel(s, 0) = p.angVel.x;
198 angVel(s, 1) = p.angVel.y;
199 angVel(s, 2) = p.angVel.z;
200 angVelPred(s, 0) = p.angVelPred.x;
201 angVelPred(s, 1) = p.angVelPred.y;
202 angVelPred(s, 2) = p.angVelPred.z;
203 invInertia(s, 0) = p.invInertia.x;
204 invInertia(s, 1) = p.invInertia.y;
205 invInertia(s, 2) = p.invInertia.z;
206 quat(s, 0) = p.quat.x;
207 quat(s, 1) = p.quat.y;
208 quat(s, 2) = p.quat.z;
209 quat(s, 3) = p.quat.w;
210 quatPred(s, 0) = p.quatPred.x;
211 quatPred(s, 1) = p.quatPred.y;
212 quatPred(s, 2) = p.quatPred.z;
213 quatPred(s, 3) = p.quatPred.w;
215 invMass(s) = p.invMass;
216 shapeId(s) = p.shape;
219 materialId(s) = p.material;
220 grounded(s) = p.grounded;
221 orphan(s) = p.orphan;
222 orphanVPeak(s) = p.orphanVPeak;
232 void initMpi(std::array<double, 3> origin, std::array<double, 3> size, std::array<long, 3> gsize,
233 std::array<bool, 3> periodic, MPI_Comm comm) {
236 MPI_Comm_rank(comm_, &rank_);
237 MPI_Comm_size(comm_, &sz);
238 dec_.init(
static_cast<std::size_t
>(sz), peclet::core::IVec<3>{gsize[0], gsize[1], gsize[2]});
239 peclet::core::halo::DomainMap<3> map;
240 for (
int i = 0; i < 3; ++i) {
241 map.origin[i] = origin[i];
242 map.cellSize[i] = size[i] /
static_cast<double>(gsize[i]);
243 map.periodic[i] = periodic[i];
246 mig_.init(dec_, rank_, map, comm_);
253 void initMpi(
const peclet::core::decomp::BlockDecomposer<3>& dec, std::array<double, 3> origin,
254 std::array<double, 3> size, std::array<bool, 3> periodic, MPI_Comm comm) {
256 MPI_Comm_rank(comm_, &rank_);
258 const auto& gs = dec.globalSize();
259 peclet::core::halo::DomainMap<3> map;
260 for (
int i = 0; i < 3; ++i) {
261 map.origin[i] = origin[i];
262 map.cellSize[i] = size[i] /
static_cast<double>(gs[i]);
263 map.periodic[i] = periodic[i];
266 mig_.init(dec_, rank_, map, comm_);
270 const peclet::core::decomp::BlockDecomposer<3>& decomposer()
const {
return dec_; }
272 bool inited()
const {
return inited_; }
273 int rank()
const {
return rank_; }
274 int numGhost()
const {
return numGhost_; }
283 void setVerletSkin(
float skin) { verletSkin_ = skin < 0.0f ? 0.0f : skin; }
284 float verletSkin()
const {
return verletSkin_; }
288 void invalidateTopology() { haveTopo_ =
false; }
291 long numRebuilds()
const {
return nRebuild_; }
292 long numGathers()
const {
return nGather_; }
297 int gather(Particles& P,
double rcut) {
298 const int no = P.numReal;
306 bool rebuild = (verletSkin_ <= 0.0f) || !haveTopo_ || (no != lastNumReal_);
307 if (!rebuild && maxOwnedDisplacement(P.pos, no) >= verletSkin_)
313 const double band = rcut +
static_cast<double>(verletSkin_);
315 auto hpos = Kokkos::create_mirror_view(P.pos);
316 Kokkos::deep_copy(hpos, P.pos);
317 std::vector<peclet::core::Vec<3>> pv(
static_cast<std::size_t
>(no));
318 for (
int i = 0; i < no; ++i)
319 pv[i] = peclet::core::Vec<3>{hpos(i, 0), hpos(i, 1), hpos(i, 2)};
324 halo_.build(pv, band,
true);
326 const int ng =
static_cast<int>(halo_.numGhost());
333 if (no + ng > P.capacity)
334 throw std::runtime_error(
335 "ParticleHalo::gather: ghost overflow -- need capacity >= " + std::to_string(no + ng) +
336 " (numReal=" + std::to_string(no) +
" + numGhost=" + std::to_string(ng) +
"), have " +
337 std::to_string(P.capacity) +
"; increase the Simulation capacity.");
339 allocBuffers(no, ng);
342 if (verletSkin_ > 0.0f) {
343 if (refPos_.extent(0) <
static_cast<std::size_t
>(no))
344 refPos_ =
V3(
"peclet::dem::halo::refPos",
static_cast<std::size_t
>(P.capacity));
345 Kokkos::deep_copy(Kokkos::subview(refPos_, std::pair<int, int>(0, no), Kokkos::ALL),
346 Kokkos::subview(P.pos, std::pair<int, int>(0, no), Kokkos::ALL));
352 const int ng = numGhost_;
353 P.numParticles = no + ng;
356 selfMapReals(P.realIndices, no);
363 forwardPositions(P.pos);
364 forwardPositions(P.posPred);
368 haloPackGather(P.vel, P.velPred, P.angVel, P.angVelPred, P.invInertia, P.quat, P.quatPred,
369 P.scale, P.invMass, P.shapeId, P.gid, P.materialId, P.groundedLevel,
370 P.bodyOrphan, P.bodyOrphanVPeak, ownedPack_, no);
371 dev_.forward(ownedPack_, ghostPack_);
372 haloUnpackGather(P.vel, P.velPred, P.angVel, P.angVelPred, P.invInertia, P.quat, P.quatPred,
373 P.scale, P.invMass, P.shapeId, P.realIndices, P.gid, P.materialId,
374 P.groundedLevel, P.bodyOrphan, P.bodyOrphanVPeak, ghostPack_, no, ng);
378 MPI_Comm comm()
const {
return comm_; }
388 int rebalance(Particles& P) {
389 std::vector<peclet::core::Vec<3>> pos;
390 std::vector<char> payload;
391 packState(P, pos, payload);
392 const std::size_t newN = peclet::core::halo::rebalanceByParticleCount(
393 dec_, mig_, pos, payload,
sizeof(MigratePack), comm_);
394 if ((
int)newN > P.capacity)
395 throw std::runtime_error(
"ParticleHalo::rebalance: owned overflow -- rank received " +
396 std::to_string(newN) +
" particles, capacity " +
397 std::to_string(P.capacity));
398 unpackState(P, pos, payload, newN);
404 int migrateTo(Particles& P,
const peclet::core::decomp::BlockDecomposer<3>& newDec) {
405 std::vector<peclet::core::Vec<3>> pos;
406 std::vector<char> payload;
407 packState(P, pos, payload);
409 const std::size_t newN = mig_.migrate(pos, payload,
sizeof(MigratePack));
410 if ((
int)newN > P.capacity)
411 throw std::runtime_error(
"ParticleHalo::migrateTo: owned overflow -- rank received " +
412 std::to_string(newN) +
" particles, capacity " +
413 std::to_string(P.capacity));
414 unpackState(P, pos, payload, newN);
420 int migrateToWeights(Particles& P,
const std::vector<peclet::core::Real>& w) {
422 MPI_Comm_size(comm_, &size);
423 peclet::core::decomp::BlockDecomposer<3> newDec((std::size_t)size, dec_.globalSize(), w);
424 return migrateTo(P, newDec);
430 void packState(Particles& P, std::vector<peclet::core::Vec<3>>& pos, std::vector<char>& payload) {
431 const int no = P.numReal;
432 auto h_pos = Kokkos::create_mirror_view(P.pos);
433 auto h_quat = Kokkos::create_mirror_view(P.quat);
434 auto h_vel = Kokkos::create_mirror_view(P.vel);
435 auto h_angVel = Kokkos::create_mirror_view(P.angVel);
436 auto h_invI = Kokkos::create_mirror_view(P.invInertia);
437 auto h_invM = Kokkos::create_mirror_view(P.invMass);
438 auto h_scale = Kokkos::create_mirror_view(P.scale);
439 auto h_tScale = Kokkos::create_mirror_view(P.targetScale);
440 auto h_shape = Kokkos::create_mirror_view(P.shapeId);
441 auto h_pf = Kokkos::create_mirror_view(P.planeFriction);
442 auto h_gid = Kokkos::create_mirror_view(P.gid);
443 auto h_mat = Kokkos::create_mirror_view(P.materialId);
444 auto h_grd = Kokkos::create_mirror_view(P.groundedLevel);
445 Kokkos::deep_copy(h_pos, P.pos);
446 Kokkos::deep_copy(h_quat, P.quat);
447 Kokkos::deep_copy(h_vel, P.vel);
448 Kokkos::deep_copy(h_angVel, P.angVel);
449 Kokkos::deep_copy(h_invI, P.invInertia);
450 Kokkos::deep_copy(h_invM, P.invMass);
451 Kokkos::deep_copy(h_scale, P.scale);
452 Kokkos::deep_copy(h_tScale, P.targetScale);
453 Kokkos::deep_copy(h_shape, P.shapeId);
454 Kokkos::deep_copy(h_pf, P.planeFriction);
455 Kokkos::deep_copy(h_gid, P.gid);
456 Kokkos::deep_copy(h_mat, P.materialId);
457 Kokkos::deep_copy(h_grd, P.groundedLevel);
458 auto h_orp = Kokkos::create_mirror_view(P.bodyOrphan);
459 auto h_ovp = Kokkos::create_mirror_view(P.bodyOrphanVPeak);
460 Kokkos::deep_copy(h_orp, P.bodyOrphan);
461 Kokkos::deep_copy(h_ovp, P.bodyOrphanVPeak);
462 pos.assign((std::size_t)no, peclet::core::Vec<3>{});
463 payload.assign((std::size_t)no *
sizeof(MigratePack), 0);
464 std::vector<MigratePack> packs((std::size_t)no);
465 std::unordered_map<unsigned, int> gidToLocal;
466 gidToLocal.reserve((std::size_t)no * 2);
467 for (
int i = 0; i < no; ++i) {
468 pos[(std::size_t)i] = peclet::core::Vec<3>{h_pos(i, 0), h_pos(i, 1), h_pos(i, 2)};
469 MigratePack& m = packs[(std::size_t)i];
470 m.quat = F4{h_quat(i, 0), h_quat(i, 1), h_quat(i, 2), h_quat(i, 3)};
471 m.vel =
F3{h_vel(i, 0), h_vel(i, 1), h_vel(i, 2)};
472 m.angVel =
F3{h_angVel(i, 0), h_angVel(i, 1), h_angVel(i, 2)};
473 m.invInertia =
F3{h_invI(i, 0), h_invI(i, 1), h_invI(i, 2)};
474 m.invMass = h_invM(i);
475 m.scale = h_scale(i);
476 m.targetScale = h_tScale(i);
477 m.shapeId = h_shape(i);
478 m.planeFric0 = h_pf(i, 0);
479 m.planeFric1 = h_pf(i, 1);
481 m.materialId = h_mat(i);
482 m.groundedLevel = h_grd(i);
484 m.orphanVPeak = h_ovp(i);
486 gidToLocal.emplace(
static_cast<unsigned>(h_gid(i)), i);
492 if (P.prevPairCount > 0) {
493 const int pc = P.prevPairCount;
496 auto h_k = Kokkos::create_mirror_view(P.prevPairKeys);
497 auto h_l = Kokkos::create_mirror_view(P.prevLambda);
498 auto h_lt = Kokkos::create_mirror_view(P.prevLambdaT);
499 auto h_pi = Kokkos::create_mirror_view(P.prevPosImpulse);
500 auto h_rb = Kokkos::create_mirror_view(P.prevRestBank);
501 auto h_rv = Kokkos::create_mirror_view(P.prevRestVPeak);
502 Kokkos::deep_copy(h_k, P.prevPairKeys);
503 Kokkos::deep_copy(h_l, P.prevLambda);
504 Kokkos::deep_copy(h_lt, P.prevLambdaT);
505 Kokkos::deep_copy(h_pi, P.prevPosImpulse);
506 Kokkos::deep_copy(h_rb, P.prevRestBank);
507 Kokkos::deep_copy(h_rv, P.prevRestVPeak);
508 auto attach = [&](
int i,
const WarmPairEntry& e,
float w) {
509 MigratePack& m = packs[(std::size_t)i];
510 if (m.numWarm < kWarmCarryMax) {
511 m.warm[m.numWarm++] = e;
515 float worstW = 1e30f;
516 for (
int s = 0; s < kWarmCarryMax; ++s) {
517 const float ws = std::fabs(m.warm[s].lambda) + std::fabs(m.warm[s].posImpulse);
526 for (
int e = 0; e < pc; ++e) {
527 const unsigned long long k = h_k(e);
533 we.lambdaT[0] = h_lt(e, 0);
534 we.lambdaT[1] = h_lt(e, 1);
535 we.lambdaT[2] = h_lt(e, 2);
536 we.posImpulse = h_pi(e);
537 we.restBank = h_rb(e);
538 we.restVPeak = h_rv(e);
539 if (we.lambda == 0.0f && we.posImpulse == 0.0f && we.lambdaT[0] == 0.0f &&
540 we.lambdaT[1] == 0.0f && we.lambdaT[2] == 0.0f && we.restBank == 0.0f)
542 const float w = std::fabs(we.lambda) + std::fabs(we.posImpulse) + std::fabs(we.restBank);
543 const unsigned hi =
static_cast<unsigned>(k >> 32);
544 const unsigned lo =
static_cast<unsigned>(k & 0xFFFFFFFFu);
545 if (
auto it = gidToLocal.find(hi); it != gidToLocal.end())
546 attach(it->second, we, w);
547 if (lo != 0xFFFFFFFFu)
548 if (
auto it = gidToLocal.find(lo); it != gidToLocal.end())
549 attach(it->second, we, w);
555 if (P.hertzNumPairs > 0) {
556 const int hn = P.hertzNumPairs;
557 auto h_hk = Kokkos::create_mirror_view(P.hertzKeys);
558 auto h_hx = Kokkos::create_mirror_view(P.hertzXi);
559 Kokkos::deep_copy(h_hk, P.hertzKeys);
560 Kokkos::deep_copy(h_hx, P.hertzXi);
561 auto attachHertz = [&](
int i,
const HertzPairEntry& e,
float w) {
562 MigratePack& m = packs[(std::size_t)i];
563 if (m.numHertz < kWarmCarryMax) {
564 m.hertz[m.numHertz++] = e;
568 float worstW = 1e30f;
569 for (
int s = 0; s < kWarmCarryMax; ++s) {
570 const float ws = std::fabs(m.hertz[s].xi[0]) + std::fabs(m.hertz[s].xi[1]) +
571 std::fabs(m.hertz[s].xi[2]);
580 for (
int e = 0; e < hn; ++e) {
583 he.xi[0] = h_hx(e, 0);
584 he.xi[1] = h_hx(e, 1);
585 he.xi[2] = h_hx(e, 2);
586 if (he.xi[0] == 0.0f && he.xi[1] == 0.0f && he.xi[2] == 0.0f)
588 const float w = std::fabs(he.xi[0]) + std::fabs(he.xi[1]) + std::fabs(he.xi[2]);
589 const unsigned hi =
static_cast<unsigned>(he.key >> 32);
590 const unsigned lo =
static_cast<unsigned>(he.key & 0xFFFFFFFFu);
591 if (
auto it = gidToLocal.find(hi); it != gidToLocal.end())
592 attachHertz(it->second, he, w);
593 if (
auto it = gidToLocal.find(lo); it != gidToLocal.end())
594 attachHertz(it->second, he, w);
598 auto h_xw = Kokkos::create_mirror_view(P.hertzXiWall);
599 auto h_sw = Kokkos::create_mirror_view(P.hertzSnWall);
600 Kokkos::deep_copy(h_xw, P.hertzXiWall);
601 Kokkos::deep_copy(h_sw, P.hertzSnWall);
602 for (
int i = 0; i < no; ++i)
603 for (
int wi = 0; wi < Particles::kHertzMaxWalls; ++wi) {
604 const int slot = i * Particles::kHertzMaxWalls + wi;
605 MigratePack& m = packs[(std::size_t)i];
606 m.hertzXiWall[wi][0] = h_xw(slot, 0);
607 m.hertzXiWall[wi][1] = h_xw(slot, 1);
608 m.hertzXiWall[wi][2] = h_xw(slot, 2);
609 m.hertzSnWall[wi] = h_sw(slot);
612 for (
int i = 0; i < no; ++i)
613 std::memcpy(&payload[(std::size_t)i *
sizeof(MigratePack)], &packs[(std::size_t)i],
614 sizeof(MigratePack));
619 void unpackState(Particles& P,
const std::vector<peclet::core::Vec<3>>& pos,
620 const std::vector<char>& payload, std::size_t newN) {
621 auto h_pos = Kokkos::create_mirror_view(P.pos);
622 auto h_quat = Kokkos::create_mirror_view(P.quat);
623 auto h_vel = Kokkos::create_mirror_view(P.vel);
624 auto h_angVel = Kokkos::create_mirror_view(P.angVel);
625 auto h_invI = Kokkos::create_mirror_view(P.invInertia);
626 auto h_invM = Kokkos::create_mirror_view(P.invMass);
627 auto h_scale = Kokkos::create_mirror_view(P.scale);
628 auto h_tScale = Kokkos::create_mirror_view(P.targetScale);
629 auto h_shape = Kokkos::create_mirror_view(P.shapeId);
630 auto h_pf = Kokkos::create_mirror_view(P.planeFriction);
631 auto h_gid = Kokkos::create_mirror_view(P.gid);
632 auto h_mat = Kokkos::create_mirror_view(P.materialId);
633 auto h_grd = Kokkos::create_mirror_view(P.groundedLevel);
634 auto h_orp = Kokkos::create_mirror_view(P.bodyOrphan);
635 auto h_ovp = Kokkos::create_mirror_view(P.bodyOrphanVPeak);
636 Kokkos::deep_copy(h_orp, P.bodyOrphan);
637 Kokkos::deep_copy(h_ovp, P.bodyOrphanVPeak);
638 std::vector<WarmPairEntry> ledger;
639 ledger.reserve(newN * 4);
640 std::vector<HertzPairEntry> hertzLedger;
641 hertzLedger.reserve(newN * 4);
642 auto h_xw = Kokkos::create_mirror_view(P.hertzXiWall);
643 auto h_sw = Kokkos::create_mirror_view(P.hertzSnWall);
644 Kokkos::deep_copy(h_xw, P.hertzXiWall);
645 Kokkos::deep_copy(h_sw, P.hertzSnWall);
646 for (std::size_t i = 0; i < newN; ++i) {
647 h_pos((
int)i, 0) = pos[i][0];
648 h_pos((
int)i, 1) = pos[i][1];
649 h_pos((
int)i, 2) = pos[i][2];
651 std::memcpy(&m, &payload[i *
sizeof(MigratePack)],
sizeof(MigratePack));
652 h_quat((
int)i, 0) = m.quat.x;
653 h_quat((
int)i, 1) = m.quat.y;
654 h_quat((
int)i, 2) = m.quat.z;
655 h_quat((
int)i, 3) = m.quat.w;
656 h_vel((
int)i, 0) = m.vel.x;
657 h_vel((
int)i, 1) = m.vel.y;
658 h_vel((
int)i, 2) = m.vel.z;
659 h_angVel((
int)i, 0) = m.angVel.x;
660 h_angVel((
int)i, 1) = m.angVel.y;
661 h_angVel((
int)i, 2) = m.angVel.z;
662 h_invI((
int)i, 0) = m.invInertia.x;
663 h_invI((
int)i, 1) = m.invInertia.y;
664 h_invI((
int)i, 2) = m.invInertia.z;
665 h_invM((
int)i) = m.invMass;
666 h_scale((
int)i) = m.scale;
667 h_tScale((
int)i) = m.targetScale;
668 h_shape((
int)i) = m.shapeId;
669 h_pf((
int)i, 0) = m.planeFric0;
670 h_pf((
int)i, 1) = m.planeFric1;
671 h_gid((
int)i) = m.gid;
672 h_mat((
int)i) = m.materialId;
673 h_grd((
int)i) = m.groundedLevel;
674 h_orp((
int)i) = m.orphan;
675 h_ovp((
int)i) = m.orphanVPeak;
676 for (
int s = 0; s < (int)m.numWarm && s < kWarmCarryMax; ++s)
677 ledger.push_back(m.warm[s]);
678 for (
int s = 0; s < (int)m.numHertz && s < kWarmCarryMax; ++s)
679 hertzLedger.push_back(m.hertz[s]);
680 for (
int wi = 0; wi < Particles::kHertzMaxWalls; ++wi) {
681 const int slot = (int)i * Particles::kHertzMaxWalls + wi;
682 h_xw(slot, 0) = m.hertzXiWall[wi][0];
683 h_xw(slot, 1) = m.hertzXiWall[wi][1];
684 h_xw(slot, 2) = m.hertzXiWall[wi][2];
685 h_sw(slot) = m.hertzSnWall[wi];
688 Kokkos::deep_copy(P.pos, h_pos);
689 Kokkos::deep_copy(P.quat, h_quat);
690 Kokkos::deep_copy(P.vel, h_vel);
691 Kokkos::deep_copy(P.angVel, h_angVel);
692 Kokkos::deep_copy(P.invInertia, h_invI);
693 Kokkos::deep_copy(P.invMass, h_invM);
694 Kokkos::deep_copy(P.scale, h_scale);
695 Kokkos::deep_copy(P.targetScale, h_tScale);
696 Kokkos::deep_copy(P.shapeId, h_shape);
697 Kokkos::deep_copy(P.planeFriction, h_pf);
698 Kokkos::deep_copy(P.gid, h_gid);
699 Kokkos::deep_copy(P.materialId, h_mat);
700 Kokkos::deep_copy(P.groundedLevel, h_grd);
701 Kokkos::deep_copy(P.bodyOrphan, h_orp);
702 Kokkos::deep_copy(P.bodyOrphanVPeak, h_ovp);
707 std::sort(ledger.begin(), ledger.end(),
708 [](
const WarmPairEntry& a,
const WarmPairEntry& b) { return a.key < b.key; });
709 ledger.erase(std::unique(ledger.begin(), ledger.end(),
710 [](
const WarmPairEntry& a,
const WarmPairEntry& b) {
711 return a.key == b.key;
714 const int nl = std::min<int>((
int)ledger.size(), (
int)P.prevPairKeys.extent(0));
716 auto hk = Kokkos::create_mirror_view(P.prevPairKeys);
717 auto hl = Kokkos::create_mirror_view(P.prevLambda);
718 auto hlt = Kokkos::create_mirror_view(P.prevLambdaT);
719 auto hpi = Kokkos::create_mirror_view(P.prevPosImpulse);
720 auto hrb = Kokkos::create_mirror_view(P.prevRestBank);
721 auto hrv = Kokkos::create_mirror_view(P.prevRestVPeak);
722 Kokkos::deep_copy(hk, P.prevPairKeys);
723 Kokkos::deep_copy(hl, P.prevLambda);
724 Kokkos::deep_copy(hlt, P.prevLambdaT);
725 Kokkos::deep_copy(hpi, P.prevPosImpulse);
726 Kokkos::deep_copy(hrb, P.prevRestBank);
727 Kokkos::deep_copy(hrv, P.prevRestVPeak);
728 for (
int e = 0; e < nl; ++e) {
729 hk(e) = ledger[(std::size_t)e].key;
730 hl(e) = ledger[(std::size_t)e].lambda;
731 hlt(e, 0) = ledger[(std::size_t)e].lambdaT[0];
732 hlt(e, 1) = ledger[(std::size_t)e].lambdaT[1];
733 hlt(e, 2) = ledger[(std::size_t)e].lambdaT[2];
734 hpi(e) = ledger[(std::size_t)e].posImpulse;
735 hrb(e) = ledger[(std::size_t)e].restBank;
736 hrv(e) = ledger[(std::size_t)e].restVPeak;
738 Kokkos::deep_copy(P.prevPairKeys, hk);
739 Kokkos::deep_copy(P.prevLambda, hl);
740 Kokkos::deep_copy(P.prevLambdaT, hlt);
741 Kokkos::deep_copy(P.prevPosImpulse, hpi);
742 Kokkos::deep_copy(P.prevRestBank, hrb);
743 Kokkos::deep_copy(P.prevRestVPeak, hrv);
745 P.prevPairCount = nl;
752 Kokkos::deep_copy(P.hertzXiWall, h_xw);
753 Kokkos::deep_copy(P.hertzSnWall, h_sw);
754 std::sort(hertzLedger.begin(), hertzLedger.end(),
755 [](
const HertzPairEntry& a,
const HertzPairEntry& b) { return a.key < b.key; });
756 hertzLedger.erase(std::unique(hertzLedger.begin(), hertzLedger.end(),
757 [](
const HertzPairEntry& a,
const HertzPairEntry& b) {
758 return a.key == b.key;
761 const int nh = (int)hertzLedger.size();
762 if ((
int)P.hertzPrevKeys.extent(0) < nh) {
763 P.hertzPrevKeys = Kokkos::View<unsigned long long*, CpMem>(
"hertzPrevKeys", nh);
764 P.hertzPrevXi = Kokkos::View<float* [3], CpMem>(
"hertzPrevXi", nh);
767 auto hk = Kokkos::create_mirror_view(P.hertzPrevKeys);
768 auto hx = Kokkos::create_mirror_view(P.hertzPrevXi);
769 Kokkos::deep_copy(hk, P.hertzPrevKeys);
770 Kokkos::deep_copy(hx, P.hertzPrevXi);
771 for (
int e = 0; e < nh; ++e) {
772 hk(e) = hertzLedger[(std::size_t)e].key;
773 hx(e, 0) = hertzLedger[(std::size_t)e].xi[0];
774 hx(e, 1) = hertzLedger[(std::size_t)e].xi[1];
775 hx(e, 2) = hertzLedger[(std::size_t)e].xi[2];
777 Kokkos::deep_copy(P.hertzPrevKeys, hk);
778 Kokkos::deep_copy(P.hertzPrevXi, hx);
780 P.hertzPrevCount = nh;
781 P.hertzNumPairs = -1;
783 P.numReal = (int)newN;
784 P.numParticles = (int)newN;
789 void forward(V3 field) {
792 haloPackF3(field, ownedF3_, numReal_);
793 dev_.forward(ownedF3_, ghostF3_);
794 haloUnpackF3(field, ghostF3_, shiftDev_, numReal_, numGhost_,
false);
797 void forwardPositions(V3 field) {
800 haloPackF3(field, ownedF3_, numReal_);
801 dev_.forward(ownedF3_, ghostF3_);
802 haloUnpackF3(field, ghostF3_, shiftDev_, numReal_, numGhost_,
true);
805 void forward4(V4 field) {
808 haloPackF4(field, ownedF4_, numReal_);
809 dev_.forward(ownedF4_, ghostF4_);
810 haloUnpackF4(field, ghostF4_, numReal_, numGhost_);
813 void selfMapReals(Vi realIndices,
int no) {
814 Kokkos::parallel_for(
815 "peclet::dem::halo::selfMapReals", Kokkos::RangePolicy<CpExec>(0, no),
816 KOKKOS_LAMBDA(
int i) { realIndices(i) = i; });
820 void allocBuffers(
int no,
int ng) {
823 ownedF3_ = peclet::core::View<F3>(
"peclet::dem::halo::ownedF3", no);
824 ghostF3_ = peclet::core::View<F3>(
"peclet::dem::halo::ghostF3", ng);
825 ownedF4_ = peclet::core::View<F4>(
"peclet::dem::halo::ownedF4", no);
826 ghostF4_ = peclet::core::View<F4>(
"peclet::dem::halo::ghostF4", ng);
827 ownedPack_ = peclet::core::View<MpiGatherPack>(
"peclet::dem::halo::ownedPack", no);
828 ghostPack_ = peclet::core::View<MpiGatherPack>(
"peclet::dem::halo::ghostPack", ng);
835 float maxOwnedDisplacement(
const V3& pos,
int no)
const {
836 if (no <= 0 || refPos_.extent(0) <
static_cast<std::size_t
>(no))
839 V3 p = pos, r = refPos_;
840 Kokkos::parallel_reduce(
841 "peclet::dem::halo::maxdisp", Kokkos::RangePolicy<CpExec>(0, no),
842 KOKKOS_LAMBDA(
const int i,
float& m) {
843 const float dx = p(i, 0) - r(i, 0), dy = p(i, 1) - r(i, 1), dz = p(i, 2) - r(i, 2);
844 const float d = Kokkos::sqrt(dx * dx + dy * dy + dz * dz);
848 Kokkos::Max<float>(md));
854 auto t = halo_.flatten();
855 std::vector<F3> hs(t.shift.size());
856 for (std::size_t i = 0; i < t.shift.size(); ++i)
857 hs[i] =
F3{
static_cast<float>(t.shift[i][0]),
static_cast<float>(t.shift[i][1]),
858 static_cast<float>(t.shift[i][2])};
859 shiftDev_ = peclet::core::toDevice(hs,
"peclet::dem::halo::shift");
862 bool inited_ =
false;
863 int rank_ = 0, numReal_ = 0, numGhost_ = 0;
866 float verletSkin_ = 0.0f;
867 bool haveTopo_ =
false;
868 int lastNumReal_ = -1;
869 long nRebuild_ = 0, nGather_ = 0;
871 MPI_Comm comm_ = MPI_COMM_NULL;
872 peclet::core::decomp::BlockDecomposer<3> dec_;
873 peclet::core::halo::DomainMap<3> map_;
874 peclet::core::halo::ParticleMigrator<3> mig_;
875 peclet::core::halo::ParticleHaloTopology<3> halo_;
876 peclet::core::halo::ParticleHalo<3> dev_;
877 peclet::core::View<F3> ownedF3_, ghostF3_, shiftDev_;
878 peclet::core::View<F4> ownedF4_, ghostF4_;
879 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.