29#ifndef PECLET_DEM_MPI_HALO_HPP
30#define PECLET_DEM_MPI_HALO_HPP
40#include <Kokkos_Core.hpp>
45#include <unordered_map>
50#include "peclet/core/common/types.hpp"
51#include "peclet/core/common/view.hpp"
52#include "peclet/core/decomp/block_decomposer.hpp"
53#include "peclet/core/halo/particle_halo.hpp"
54#include "peclet/core/halo/particle_halo_topology.hpp"
55#include "peclet/core/halo/particle_migrator.hpp"
56#include "peclet/core/halo/particle_rebalance.hpp"
66 F3 vel, velPred, angVel, angVelPred, invInertia;
76 unsigned char material, grounded;
79 float orphan, orphanVPeak;
86 unsigned long long key;
95inline constexpr int kWarmCarryMax = 14;
100struct HertzPairEntry {
101 unsigned long long key;
116 F3 vel, angVel, invInertia;
117 float invMass, scale, targetScale;
119 float planeFric0, planeFric1;
122 unsigned char materialId, groundedLevel, numWarm, numHertz;
123 float orphan, orphanVPeak;
126 F3 extForce, extTorque;
127 WarmPairEntry warm[kWarmCarryMax];
130 HertzPairEntry hertz[kWarmCarryMax];
131 float hertzXiWall[Particles::kHertzMaxWalls][3];
132 float hertzSnWall[Particles::kHertzMaxWalls];
137KOKKOS_INLINE_FUNCTION
int imagePack(
int x,
int y,
int z) {
138 return (x + 1) + 4 * (y + 1) + 16 * (z + 1);
140KOKKOS_INLINE_FUNCTION
int imageNegate(
int p) {
141 const int x = p % 4 - 1, y = (p / 4) % 4 - 1, z = (p / 16) % 4 - 1;
142 return imagePack(-x, -y, -z);
153struct ContactOwnership {
156 Kokkos::View<const int*, CpMem> gid, ghostSource, copyOffsets, copyRanks;
160 Kokkos::View<const int*, CpMem> ghostImage, copyImage;
163 Kokkos::View<const int*, CpMem> selfOffsets, selfImage;
164 KOKKOS_INLINE_FUNCTION
bool operator()(
const ContactC& c)
const {
165 const int a = c.bodyA, b = c.bodyB;
168 const bool ao = a < numReal, bo = b < numReal;
173#if defined(PECLET_DEM_TEST_MUTANT) && PECLET_DEM_TEST_MUTANT == 1
177 const int o = ao ? a : b, g = ao ? b : a;
178 if (gid(o) == gid(g))
180 const int s = ghostSource(g - numReal);
186 const int want = imageNegate(ghostImage(g - numReal));
188 for (
int k = selfOffsets(o); k < selfOffsets(o + 1); ++k)
189 if (selfImage(k) == want) {
196 bool partnerSees =
false;
197 const int want = imageNegate(ghostImage(g - numReal));
198 for (
int k = copyOffsets(o); k < copyOffsets(o + 1); ++k)
199 if (copyRanks(k) == s && copyImage(k) == want) {
206 return gid(o) < gid(g);
216struct VelocityIncrement {
222KOKKOS_INLINE_FUNCTION VelocityIncrement operator+(
const VelocityIncrement& a,
223 const VelocityIncrement& b) {
225 for (
int d = 0; d < 3; ++d) {
226 r.v[d] = a.v[d] + b.v[d];
227 r.w[d] = a.w[d] + b.w[d];
229 r.orphan = a.orphan + b.orphan;
230 r.orphanPeak = a.orphanPeak > b.orphanPeak ? a.orphanPeak : b.orphanPeak;
235struct PositionIncrement {
238KOKKOS_INLINE_FUNCTION PositionIncrement operator+(
const PositionIncrement& a,
239 const PositionIncrement& b) {
241 for (
int d = 0; d < 3; ++d)
242 r.x[d] = a.x[d] + b.x[d];
249inline void haloPackF3(
V3 field, peclet::core::View<F3> owned,
int n) {
250 Kokkos::parallel_for(
251 "peclet::dem::halo::packF3", Kokkos::RangePolicy<CpExec>(0, n),
252 KOKKOS_LAMBDA(
int i) { owned(i) = F3{field(i, 0), field(i, 1), field(i, 2)}; });
254inline void haloPackF4(
V4 field, peclet::core::View<F4> owned,
int n) {
255 Kokkos::parallel_for(
256 "peclet::dem::halo::packF4", Kokkos::RangePolicy<CpExec>(0, n),
257 KOKKOS_LAMBDA(
int i) { owned(i) = F4{field(i, 0), field(i, 1), field(i, 2), field(i, 3)}; });
262inline void haloUnpackF3(
V3 field, peclet::core::View<F3> ghost, peclet::core::View<F3> shift,
263 Vi slot,
int no,
int ng,
bool doShift) {
264 Kokkos::parallel_for(
265 "peclet::dem::halo::unpackF3", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
272 const int s = no + slot(g);
278inline void haloUnpackF4(
V4 field, peclet::core::View<F4> ghost,
Vi slot,
int no,
int ng) {
279 Kokkos::parallel_for(
280 "peclet::dem::halo::unpackF4", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
281 const int s = no + slot(g);
282 field(s, 0) = ghost(g).x;
283 field(s, 1) = ghost(g).y;
284 field(s, 2) = ghost(g).z;
285 field(s, 3) = ghost(g).w;
289inline void haloPackGather(
V3 vel,
V3 velPred,
V3 angVel,
V3 angVelPred,
V3 invInertia,
V4 quat,
290 V4 quatPred,
Vf scale,
Vf invMass,
Vi shapeId,
Vi gid,
291 Kokkos::View<unsigned char*, CpMem> materialId,
292 Kokkos::View<unsigned char*, CpMem> grounded,
Vf orphan,
Vf orphanVPeak,
293 peclet::core::View<MpiGatherPack> owned,
int n) {
294 Kokkos::parallel_for(
295 "peclet::dem::halo::packGather", Kokkos::RangePolicy<CpExec>(0, n), KOKKOS_LAMBDA(
int i) {
297 g.vel = F3{vel(i, 0), vel(i, 1), vel(i, 2)};
298 g.velPred = F3{velPred(i, 0), velPred(i, 1), velPred(i, 2)};
299 g.angVel = F3{angVel(i, 0), angVel(i, 1), angVel(i, 2)};
300 g.angVelPred = F3{angVelPred(i, 0), angVelPred(i, 1), angVelPred(i, 2)};
301 g.invInertia = F3{invInertia(i, 0), invInertia(i, 1), invInertia(i, 2)};
302 g.quat = F4{quat(i, 0), quat(i, 1), quat(i, 2), quat(i, 3)};
303 g.quatPred = F4{quatPred(i, 0), quatPred(i, 1), quatPred(i, 2), quatPred(i, 3)};
305 g.invMass = invMass(i);
306 g.shape = shapeId(i);
308 g.material = materialId(i);
309 g.grounded = grounded(i);
310 g.orphan = orphan(i);
311 g.orphanVPeak = orphanVPeak(i);
318inline void haloUnpackGather(
V3 vel,
V3 velPred,
V3 angVel,
V3 angVelPred,
V3 invInertia,
V4 quat,
319 V4 quatPred,
Vf scale,
Vf invMass,
Vi shapeId,
Vi realIndices,
Vi gid,
320 Kokkos::View<unsigned char*, CpMem> materialId,
321 Kokkos::View<unsigned char*, CpMem> grounded,
Vf orphan,
322 Vf orphanVPeak, peclet::core::View<MpiGatherPack> ghost,
Vi slot,
324 Kokkos::parallel_for(
325 "peclet::dem::halo::unpackGather", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
326 const MpiGatherPack p = ghost(g);
327 const int s = no + slot(g);
331 velPred(s, 0) = p.velPred.x;
332 velPred(s, 1) = p.velPred.y;
333 velPred(s, 2) = p.velPred.z;
334 angVel(s, 0) = p.angVel.x;
335 angVel(s, 1) = p.angVel.y;
336 angVel(s, 2) = p.angVel.z;
337 angVelPred(s, 0) = p.angVelPred.x;
338 angVelPred(s, 1) = p.angVelPred.y;
339 angVelPred(s, 2) = p.angVelPred.z;
340 invInertia(s, 0) = p.invInertia.x;
341 invInertia(s, 1) = p.invInertia.y;
342 invInertia(s, 2) = p.invInertia.z;
343 quat(s, 0) = p.quat.x;
344 quat(s, 1) = p.quat.y;
345 quat(s, 2) = p.quat.z;
346 quat(s, 3) = p.quat.w;
347 quatPred(s, 0) = p.quatPred.x;
348 quatPred(s, 1) = p.quatPred.y;
349 quatPred(s, 2) = p.quatPred.z;
350 quatPred(s, 3) = p.quatPred.w;
352 invMass(s) = p.invMass;
353 shapeId(s) = p.shape;
356 materialId(s) = p.material;
357 grounded(s) = p.grounded;
358 orphan(s) = p.orphan;
359 orphanVPeak(s) = p.orphanVPeak;
368inline void haloMarkVelocityBaseline(
V3 velPred,
V3 angVelPred,
Vf orphan,
Vf orphanPeak,
V3 baseV,
369 V3 baseW,
Vf baseO,
Vf basePk,
Vi slot,
int no,
int ng) {
370 Kokkos::parallel_for(
371 "peclet::dem::halo::markVelBase", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
372 const int s = no + slot(g);
373 for (
int d = 0; d < 3; ++d) {
374 baseV(g, d) = velPred(s, d);
375 baseW(g, d) = angVelPred(s, d);
377 baseO(g) = orphan(s);
378 basePk(g) = orphanPeak(s);
383inline void haloPackVelocityIncrement(
V3 velPred,
V3 angVelPred,
Vf orphan,
Vf orphanPeak,
V3 baseV,
384 V3 baseW,
Vf baseO,
Vf basePk,
385 peclet::core::View<VelocityIncrement> inc,
Vi slot,
int no,
387 Kokkos::parallel_for(
388 "peclet::dem::halo::packVelInc", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
389 const int s = no + slot(g);
391 for (
int d = 0; d < 3; ++d) {
392 r.v[d] = velPred(s, d) - baseV(g, d);
393 r.w[d] = angVelPred(s, d) - baseW(g, d);
395 r.orphan = orphan(s) - baseO(g);
396 r.orphanPeak = orphanPeak(s) > basePk(g) ? orphanPeak(s) : 0.0f;
401inline void haloApplyVelocityIncrement(
V3 velPred,
V3 angVelPred,
Vf orphan,
Vf orphanPeak,
402 peclet::core::View<VelocityIncrement> ownedInc,
int no) {
403 Kokkos::parallel_for(
404 "peclet::dem::halo::applyVelInc", Kokkos::RangePolicy<CpExec>(0, no), KOKKOS_LAMBDA(
int i) {
405 const VelocityIncrement r = ownedInc(i);
406 for (
int d = 0; d < 3; ++d) {
407 velPred(i, d) += r.v[d];
408 angVelPred(i, d) += r.w[d];
410 orphan(i) = Kokkos::fmax(0.0f, orphan(i) + r.orphan);
411 orphanPeak(i) = Kokkos::fmax(orphanPeak(i), r.orphanPeak);
414inline void haloMarkPositionBaseline(
V3 posPred,
V3 baseX,
Vi slot,
int no,
int ng) {
415 Kokkos::parallel_for(
416 "peclet::dem::halo::markPosBase", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
417 const int s = no + slot(g);
418 for (
int d = 0; d < 3; ++d)
419 baseX(g, d) = posPred(s, d);
424inline void haloPackPositionIncrement(
V3 posPred,
V3 baseX,
425 peclet::core::View<PositionIncrement> inc,
Vi slot,
int no,
427 Kokkos::parallel_for(
428 "peclet::dem::halo::packPosInc", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
429 const int s = no + slot(g);
431 for (
int d = 0; d < 3; ++d)
432 r.x[d] = posPred(s, d) - baseX(g, d);
436inline void haloApplyPositionIncrement(
V3 posPred, peclet::core::View<PositionIncrement> ownedInc,
438 Kokkos::parallel_for(
439 "peclet::dem::halo::applyPosInc", Kokkos::RangePolicy<CpExec>(0, no), KOKKOS_LAMBDA(
int i) {
440 const PositionIncrement r = ownedInc(i);
441 for (
int d = 0; d < 3; ++d)
442 posPred(i, d) += r.x[d];
446inline void haloPackGhostColumn(Kokkos::View<
float* [2],
CpMem> pf,
447 peclet::core::View<float> ghostVal,
Vi slot,
int no,
int ng) {
448 Kokkos::parallel_for(
449 "peclet::dem::halo::packGhostCol", Kokkos::RangePolicy<CpExec>(0, ng),
450 KOKKOS_LAMBDA(
int g) { ghostVal(g) = pf(no + slot(g), 1); });
452inline void haloAddOwnedColumn(Kokkos::View<
float* [2],
CpMem> pf,
453 peclet::core::View<float> ownedVal,
int no) {
454 Kokkos::parallel_for(
455 "peclet::dem::halo::addOwnedCol", Kokkos::RangePolicy<CpExec>(0, no),
456 KOKKOS_LAMBDA(
int i) { pf(i, 1) += ownedVal(i); });
458inline void haloPackOwnedColumn(Kokkos::View<
float* [2],
CpMem> pf,
459 peclet::core::View<float> ownedVal,
int no) {
460 Kokkos::parallel_for(
461 "peclet::dem::halo::packOwnedCol", Kokkos::RangePolicy<CpExec>(0, no),
462 KOKKOS_LAMBDA(
int i) { ownedVal(i) = pf(i, 1); });
464inline void haloUnpackGhostColumn(Kokkos::View<
float* [2],
CpMem> pf,
465 peclet::core::View<float> ghostVal,
Vi slot,
int no,
int ng) {
466 Kokkos::parallel_for(
467 "peclet::dem::halo::unpackGhostCol", Kokkos::RangePolicy<CpExec>(0, ng),
468 KOKKOS_LAMBDA(
int g) { pf(no + slot(g), 1) = ghostVal(g); });
472inline void haloPackGhostCount(
Vi counts, peclet::core::View<int> ghostCnt,
Vi slot,
int no,
474 Kokkos::parallel_for(
475 "peclet::dem::halo::packGhostCount", Kokkos::RangePolicy<CpExec>(0, ng),
476 KOKKOS_LAMBDA(
int g) { ghostCnt(g) = counts(no + slot(g)); });
478inline void haloAddPackOwnedCount(
Vi counts, peclet::core::View<int> ownedCnt,
int no) {
479 Kokkos::parallel_for(
480 "peclet::dem::halo::addPackOwnedCount", Kokkos::RangePolicy<CpExec>(0, no),
481 KOKKOS_LAMBDA(
int i) {
482 counts(i) += ownedCnt(i);
483 ownedCnt(i) = counts(i);
486inline void haloUnpackGhostCount(
Vi counts, peclet::core::View<int> ghostCnt,
Vi slot,
int no,
488 Kokkos::parallel_for(
489 "peclet::dem::halo::unpackGhostCount", Kokkos::RangePolicy<CpExec>(0, ng),
490 KOKKOS_LAMBDA(
int g) { counts(no + slot(g)) = ghostCnt(g); });
496struct VelocityState {
500struct PositionState {
507inline void haloPackVelocityState(
V3 velPred,
V3 angVelPred,
508 peclet::core::View<VelocityState> owned,
int n,
Vi rows = {}) {
509 const bool list = rows.extent(0) > 0;
510 Kokkos::parallel_for(
511 "peclet::dem::halo::packVelState", Kokkos::RangePolicy<CpExec>(0, n), KOKKOS_LAMBDA(
int j) {
512 const int i = list ? rows(j) : j;
514 for (
int d = 0; d < 3; ++d) {
515 r.v[d] = velPred(i, d);
516 r.w[d] = angVelPred(i, d);
521inline void haloUnpackVelocityState(
V3 velPred,
V3 angVelPred,
522 peclet::core::View<VelocityState> ghost,
Vi slot,
int no,
524 Kokkos::parallel_for(
525 "peclet::dem::halo::unpackVelState", Kokkos::RangePolicy<CpExec>(0, ng),
526 KOKKOS_LAMBDA(
int g) {
527 const VelocityState r = ghost(g);
528 const int s = no + slot(g);
529 for (
int d = 0; d < 3; ++d) {
530 velPred(s, d) = r.v[d];
531 angVelPred(s, d) = r.w[d];
535inline void haloPackPositionState(
V3 posPred,
V4 quatPred, peclet::core::View<PositionState> owned,
536 int n,
Vi rows = {}) {
537 const bool list = rows.extent(0) > 0;
538 Kokkos::parallel_for(
539 "peclet::dem::halo::packPosState", Kokkos::RangePolicy<CpExec>(0, n), KOKKOS_LAMBDA(
int j) {
540 const int i = list ? rows(j) : j;
542 for (
int d = 0; d < 3; ++d)
543 r.x[d] = posPred(i, d);
544 for (
int d = 0; d < 4; ++d)
545 r.q[d] = quatPred(i, d);
550inline void haloUnpackPositionState(
V3 posPred,
V4 quatPred,
551 peclet::core::View<PositionState> ghost,
552 peclet::core::View<F3> shift,
Vi slot,
int no,
int ng) {
553 Kokkos::parallel_for(
554 "peclet::dem::halo::unpackPosState", Kokkos::RangePolicy<CpExec>(0, ng),
555 KOKKOS_LAMBDA(
int g) {
556 const PositionState r = ghost(g);
557 const F3 sh = shift(g);
558 const int s = no + slot(g);
559 posPred(s, 0) = r.x[0] + sh.x;
560 posPred(s, 1) = r.x[1] + sh.y;
561 posPred(s, 2) = r.x[2] + sh.z;
562 for (
int d = 0; d < 4; ++d)
563 quatPred(s, d) = r.q[d];
575struct OpeningIncrement {
583KOKKOS_INLINE_FUNCTION OpeningIncrement operator+(
const OpeningIncrement& a,
584 const OpeningIncrement& b) {
586 for (
int d = 0; d < 3; ++d) {
587 r.v[d] = a.v[d] + b.v[d];
588 r.w[d] = a.w[d] + b.w[d];
590 r.orphan = a.orphan + b.orphan;
591 r.orphanPeak = a.orphanPeak > b.orphanPeak ? a.orphanPeak : b.orphanPeak;
592 r.aVel = a.aVel + b.aVel;
593 r.aPos = a.aPos + b.aPos;
606struct OpeningStateNoRot {
615struct VelocityStateB {
621struct VelocityStateBNoRot {
627inline void haloPackOpeningIncrement(
V3 velPred,
V3 angVelPred,
Vf orphan,
Vf orphanPeak,
V3 baseV,
628 V3 baseW,
Vf baseO,
Vf basePk,
Vi aVel,
Vi aPos,
629 peclet::core::View<OpeningIncrement> inc,
Vi slot,
int no,
631 Kokkos::parallel_for(
632 "peclet::dem::halo::packOpenInc", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
633 const int s = no + slot(g);
635 for (
int d = 0; d < 3; ++d) {
636 r.v[d] = velPred(s, d) - baseV(g, d);
637 r.w[d] = angVelPred(s, d) - baseW(g, d);
639 r.orphan = orphan(s) - baseO(g);
640 r.orphanPeak = orphanPeak(s) > basePk(g) ? orphanPeak(s) : 0.0f;
647inline void haloApplyOpening(
V3 velPred,
V3 angVelPred,
Vf orphan,
Vf orphanPeak,
Vi aVel,
Vi aPos,
648 Vi kVel,
Vi kPos, peclet::core::View<OpeningIncrement> ownedInc,
650 Kokkos::parallel_for(
651 "peclet::dem::halo::applyOpen", Kokkos::RangePolicy<CpExec>(0, no), KOKKOS_LAMBDA(
int i) {
652 const OpeningIncrement r = ownedInc(i);
653 for (
int d = 0; d < 3; ++d) {
654 velPred(i, d) += r.v[d];
655 angVelPred(i, d) += r.w[d];
657 orphan(i) = Kokkos::fmax(0.0f, orphan(i) + r.orphan);
658 orphanPeak(i) = Kokkos::fmax(orphanPeak(i), r.orphanPeak);
659 const int kv = aVel(i) + r.aVel, kp = aPos(i) + r.aPos;
660 kVel(i) = kv > 1 ? kv : 1;
661 kPos(i) = kp > 1 ? kp : 1;
665inline void haloPackOpeningState(
V3 velPred,
V3 angVelPred,
Vf orphan,
Vf orphanPeak,
Vi kVel,
666 Vi kPos, peclet::core::View<S> owned,
int no) {
667 Kokkos::parallel_for(
668 "peclet::dem::halo::packOpenState", Kokkos::RangePolicy<CpExec>(0, no), KOKKOS_LAMBDA(
int i) {
669 const V3& wv = angVelPred;
671 for (
int d = 0; d < 3; ++d)
672 r.v[d] = velPred(i, d);
673 if constexpr (std::is_same_v<S, OpeningState>) {
674 for (
int d = 0; d < 3; ++d)
678 r.peak = orphanPeak(i);
685inline void haloUnpackOpeningState(
V3 velPred,
V3 angVelPred,
Vf orphan,
Vf orphanPeak,
Vi kVel,
686 Vi kPos, peclet::core::View<S> ghost,
Vi slot,
int no,
int ng) {
687 Kokkos::parallel_for(
688 "peclet::dem::halo::unpackOpenState", Kokkos::RangePolicy<CpExec>(0, ng),
689 KOKKOS_LAMBDA(
int g) {
690 const V3& wv = angVelPred;
691 const S r = ghost(g);
692 const int s = no + slot(g);
693 for (
int d = 0; d < 3; ++d)
694 velPred(s, d) = r.v[d];
695 if constexpr (std::is_same_v<S, OpeningState>) {
696 for (
int d = 0; d < 3; ++d)
700 orphanPeak(s) = r.peak;
706inline void haloPackVelocityStateB(
V3 velPred,
V3 angVelPred,
Vf orphan,
Vf orphanPeak,
707 peclet::core::View<S> owned,
int no,
Vi rows = {}) {
708 const bool list = rows.extent(0) > 0;
709 Kokkos::parallel_for(
710 "peclet::dem::halo::packVelStateB", Kokkos::RangePolicy<CpExec>(0, no), KOKKOS_LAMBDA(
int j) {
711 const int i = list ? rows(j) : j;
712 const V3& wv = angVelPred;
714 for (
int d = 0; d < 3; ++d)
715 r.v[d] = velPred(i, d);
716 if constexpr (std::is_same_v<S, VelocityStateB>) {
717 for (
int d = 0; d < 3; ++d)
721 r.peak = orphanPeak(i);
726inline void haloUnpackVelocityStateB(
V3 velPred,
V3 angVelPred,
Vf orphan,
Vf orphanPeak,
727 peclet::core::View<S> ghost,
Vi slot,
int no,
int ng) {
728 Kokkos::parallel_for(
729 "peclet::dem::halo::unpackVelStateB", Kokkos::RangePolicy<CpExec>(0, ng),
730 KOKKOS_LAMBDA(
int g) {
731 const V3& wv = angVelPred;
732 const S r = ghost(g);
733 const int s = no + slot(g);
734 for (
int d = 0; d < 3; ++d)
735 velPred(s, d) = r.v[d];
736 if constexpr (std::is_same_v<S, VelocityStateB>) {
737 for (
int d = 0; d < 3; ++d)
741 orphanPeak(s) = r.peak;
748inline void haloMarkOwnerVelocitySeeds(
V3 velPred,
V3 angVelPred,
Vf orphan,
V3 seedV,
V3 seedW,
749 Vf seedO,
int no,
Vi rows = {}) {
750 const bool list = rows.extent(0) > 0;
751 Kokkos::parallel_for(
752 "peclet::dem::halo::markOwnerVelSeed", Kokkos::RangePolicy<CpExec>(0, no),
753 KOKKOS_LAMBDA(
int j) {
754 const int i = list ? rows(j) : j;
755 for (
int d = 0; d < 3; ++d) {
756 seedV(i, d) = velPred(i, d);
757 seedW(i, d) = angVelPred(i, d);
759 seedO(i) = orphan(i);
762inline void haloMarkOwnerPositionSeeds(
V3 posPred,
V3 seedX,
int no,
Vi rows = {}) {
763 const bool list = rows.extent(0) > 0;
764 Kokkos::parallel_for(
765 "peclet::dem::halo::markOwnerPosSeed", Kokkos::RangePolicy<CpExec>(0, no),
766 KOKKOS_LAMBDA(
int j) {
767 const int i = list ? rows(j) : j;
768 for (
int d = 0; d < 3; ++d)
769 seedX(i, d) = posPred(i, d);
774inline void haloSetOrphanShares(
Vf orphan,
Vi kVel,
int n) {
775 Kokkos::parallel_for(
776 "peclet::dem::halo::orphanShares", Kokkos::RangePolicy<CpExec>(0, n), KOKKOS_LAMBDA(
int q) {
777 const int k = kVel(q);
779 orphan(q) = orphan(q) /
static_cast<float>(k);
784inline int haloBuildMRows(
Vi k, Kokkos::View<const unsigned char*, CpMem> mark,
Vi rows,
int no) {
786 Kokkos::parallel_scan(
787 "peclet::dem::halo::mRows", Kokkos::RangePolicy<CpExec>(0, no),
788 KOKKOS_LAMBDA(
int i,
int& run,
const bool final) {
789 if (mark(i) != 0 || k(i) > 1) {
812KOKKOS_INLINE_FUNCTION
float payloadLin(
const F3& r,
int d) {
813 return d == 0 ? r.x : (d == 1 ? r.y : r.z);
815KOKKOS_INLINE_FUNCTION
void setPayloadLin(F3& r,
int d,
float x) {
816 (d == 0 ? r.x : (d == 1 ? r.y : r.z)) = x;
819KOKKOS_INLINE_FUNCTION
float payloadLin(
const S& r,
int d) {
820 if constexpr (std::is_same_v<S, PositionState>)
826KOKKOS_INLINE_FUNCTION
void setPayloadLin(S& r,
int d,
float x) {
827 if constexpr (std::is_same_v<S, PositionState>)
833inline constexpr bool kPayloadW =
834 std::is_same_v<S, VelocityState> || std::is_same_v<S, VelocityStateB>;
836inline constexpr bool kPayloadB =
837 std::is_same_v<S, VelocityStateB> || std::is_same_v<S, VelocityStateBNoRot>;
842inline void haloVelocityMPackZero(
V3 velPred,
V3 angVelPred,
Vf orphan,
Vf orphanPeak,
V3 baseV,
843 V3 baseW,
Vf baseO,
Vf basePk,
Vi aVel,
844 peclet::core::View<VelocityIncrement> inc,
Vi slot,
int no,
845 int ng, peclet::core::View<VelocityIncrement> ownedInc,
Vi rows,
847 Kokkos::parallel_for(
848 "peclet::dem::halo::velMPackZero", Kokkos::RangePolicy<CpExec>(0, ng + nRows),
849 KOKKOS_LAMBDA(
int j) {
851 ownedInc(rows(j - ng)) = VelocityIncrement{};
855 const int s = no + slot(g);
856 const float f =
static_cast<float>(aVel(s));
858 for (
int d = 0; d < 3; ++d) {
859 r.v[d] = f * (velPred(s, d) - baseV(g, d));
860 r.w[d] = f * (angVelPred(s, d) - baseW(g, d));
862 r.orphan = f * (orphan(s) - baseO(g));
863 r.orphanPeak = (f > 0.0f && orphanPeak(s) > basePk(g)) ? orphanPeak(s) : 0.0f;
866 bool moved = orphan(s) != baseO(g);
867 for (
int d = 0; d < 3; ++d)
868 moved = moved || velPred(s, d) != baseV(g, d) || angVelPred(s, d) != baseW(g, d);
871 "peclet::dem rank-level M: an inactive velocity slot (a = 0) was written");
884inline void haloVelocityMApply(
V3 velPred,
V3 angVelPred,
Vf orphan,
Vf orphanPeak,
V3 seedV,
885 V3 seedW,
Vf seedO,
Vi aVel,
Vi kVel,
886 peclet::core::View<VelocityIncrement> ownedInc,
887 Kokkos::View<int, CpMem> clamps,
888 Kokkos::View<float, CpMem> consensus,
Vi rows,
int nRows,
889 Kokkos::View<const unsigned char*, CpMem> iface,
890 peclet::core::View<S> out,
bool shares) {
891 Kokkos::parallel_for(
892 "peclet::dem::halo::velMApply", Kokkos::RangePolicy<CpExec>(0, nRows), KOKKOS_LAMBDA(
int j) {
893 const V3& wv = angVelPred;
894 const Vf& ov = orphan;
895 const Vf& pv = orphanPeak;
896 const int i = rows(j);
897 const VelocityIncrement r = ownedInc(i);
898 const int k = kVel(i);
901 for (
int d = 0; d < 3; ++d) {
902 velPred(i, d) += r.v[d];
903 angVelPred(i, d) += r.w[d];
905 pre = orphan(i) + r.orphan;
907 const float kf =
static_cast<float>(k), af =
static_cast<float>(aVel(i));
909 for (
int d = 0; d < 3; ++d) {
910#if defined(PECLET_DEM_TEST_MUTANT) && PECLET_DEM_TEST_MUTANT == 2
911 const float own = velPred(i, d) - seedV(i, d),
912 mean = af * own + r.v[d];
914 const float own = velPred(i, d) - seedV(i, d), mean = (af * own + r.v[d]) / kf;
916 c2 += (mean - own) * (mean - own);
917 velPred(i, d) = seedV(i, d) + mean;
918 angVelPred(i, d) = seedW(i, d) + (af * (angVelPred(i, d) - seedW(i, d)) + r.w[d]) / kf;
920 if (af > 0.0f && c2 > 0.0f)
921 Kokkos::atomic_max(&consensus(), Kokkos::sqrt(c2));
922 const float B = seedO(i);
923 pre = B + af * (orphan(i) - B / kf) + r.orphan;
925 if (!(pre >= -1e-6f * Kokkos::fmax(B, 1e-30f)))
926 Kokkos::abort(
"peclet::dem rank-level M: the orphan balance was overdrawn");
930 Kokkos::atomic_add(&clamps(), 1);
931 orphan(i) = Kokkos::fmax(0.0f, pre);
932 orphanPeak(i) = Kokkos::fmax(orphanPeak(i), r.orphanPeak);
935 for (
int d = 0; d < 3; ++d)
936 setPayloadLin(p, d, velPred(i, d));
937 if constexpr (kPayloadW<S>) {
938 for (
int d = 0; d < 3; ++d)
941 if constexpr (kPayloadB<S>) {
947 for (
int d = 0; d < 3; ++d) {
948 seedV(i, d) = velPred(i, d);
949 seedW(i, d) = angVelPred(i, d);
951 seedO(i) = orphan(i);
953 orphan(i) = orphan(i) /
static_cast<float>(k);
960inline void haloVelocityMUnpack(
V3 velPred,
V3 angVelPred,
Vf orphan,
Vf orphanPeak,
961 peclet::core::View<S> ghost,
V3 baseV,
V3 baseW,
Vf baseO,
962 Vf basePk,
Vi aVel,
Vi kVel,
963 peclet::core::View<VelocityIncrement> inc,
964 Kokkos::View<float, CpMem> consensus,
Vi slot,
int no,
int ng,
966 Kokkos::parallel_for(
967 "peclet::dem::halo::velMUnpack", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
968 const V3& wv = angVelPred;
969 const Vf& ov = orphan;
970 const Vf& pv = orphanPeak;
971 const S p = ghost(g);
972 const int s = no + slot(g);
973 for (
int d = 0; d < 3; ++d)
974 velPred(s, d) = payloadLin(p, d);
975 if constexpr (kPayloadW<S>) {
976 for (
int d = 0; d < 3; ++d)
979 if constexpr (kPayloadB<S>) {
983 const int k = kVel(s);
984 if (aVel(s) > 0 && k > 1) {
985 const float af =
static_cast<float>(aVel(s));
986 const VelocityIncrement r = inc(g);
988 for (
int d = 0; d < 3; ++d) {
989 const float e = (velPred(s, d) - baseV(g, d)) - r.v[d] / af;
993 Kokkos::atomic_max(&consensus(), Kokkos::sqrt(c2));
996 orphan(s) = orphan(s) /
static_cast<float>(k);
997 for (
int d = 0; d < 3; ++d) {
998 baseV(g, d) = velPred(s, d);
999 baseW(g, d) = angVelPred(s, d);
1001 baseO(g) = orphan(s);
1002 basePk(g) = orphanPeak(s);
1007inline void haloRestoreOrphanBalance(
Vf orphan,
Vf seedO,
Vi aVel,
Vi kVel,
int no,
Vi rows = {}) {
1008 const bool list = rows.extent(0) > 0;
1009 Kokkos::parallel_for(
1010 "peclet::dem::halo::restoreOrphan", Kokkos::RangePolicy<CpExec>(0, no), KOKKOS_LAMBDA(
int j) {
1011 const int i = list ? rows(j) : j;
1012 const int k = kVel(i);
1015 const float kf =
static_cast<float>(k), B = seedO(i);
1016 orphan(i) = B +
static_cast<float>(aVel(i)) * (orphan(i) - B / kf);
1020inline void haloPositionMPackZero(
V3 posPred,
V3 baseX,
Vi aPos,
1021 peclet::core::View<PositionIncrement> inc,
Vi slot,
int no,
1022 int ng, peclet::core::View<PositionIncrement> ownedInc,
Vi rows,
1024 Kokkos::parallel_for(
1025 "peclet::dem::halo::posMPackZero", Kokkos::RangePolicy<CpExec>(0, ng + nRows),
1026 KOKKOS_LAMBDA(
int j) {
1028 ownedInc(rows(j - ng)) = PositionIncrement{};
1032 const int s = no + slot(g);
1033 const float f =
static_cast<float>(aPos(s));
1034 PositionIncrement r;
1035 for (
int d = 0; d < 3; ++d)
1036 r.x[d] = f * (posPred(s, d) - baseX(g, d));
1038 if (aPos(s) == 0 && (posPred(s, 0) != baseX(g, 0) || posPred(s, 1) != baseX(g, 1) ||
1039 posPred(s, 2) != baseX(g, 2)))
1040 Kokkos::abort(
"peclet::dem rank-level M: an inactive position slot (a = 0) was written");
1047inline void haloPositionMApply(
V3 posPred,
V4 quatPred,
V3 seedX,
Vi aPos,
Vi kPos,
1048 peclet::core::View<PositionIncrement> ownedInc,
1049 Kokkos::View<float, CpMem> consensus,
Vi rows,
int nRows,
1050 Kokkos::View<const unsigned char*, CpMem> iface,
1051 peclet::core::View<S> out) {
1052 Kokkos::parallel_for(
1053 "peclet::dem::halo::posMApply", Kokkos::RangePolicy<CpExec>(0, nRows), KOKKOS_LAMBDA(
int j) {
1054 const V4& qv = quatPred;
1055 const int i = rows(j);
1056 const PositionIncrement r = ownedInc(i);
1057 const int k = kPos(i);
1059 for (
int d = 0; d < 3; ++d)
1060 posPred(i, d) += r.x[d];
1062 const float kf =
static_cast<float>(k), af =
static_cast<float>(aPos(i));
1064 for (
int d = 0; d < 3; ++d) {
1065 const float own = posPred(i, d) - seedX(i, d), mean = (af * own + r.x[d]) / kf;
1066 c2 += (mean - own) * (mean - own);
1067 posPred(i, d) = seedX(i, d) + mean;
1069 if (af > 0.0f && c2 > 0.0f)
1070 Kokkos::atomic_max(&consensus(), Kokkos::sqrt(c2));
1072 if (iface(i) != 0) {
1074 for (
int d = 0; d < 3; ++d)
1075 setPayloadLin(p, d, posPred(i, d));
1076 if constexpr (std::is_same_v<S, PositionState>) {
1077 for (
int d = 0; d < 4; ++d)
1082 for (
int d = 0; d < 3; ++d)
1083 seedX(i, d) = posPred(i, d);
1089inline void haloPositionMUnpack(
V3 posPred,
V4 quatPred, peclet::core::View<S> ghost,
1090 peclet::core::View<F3> shift,
V3 baseX,
Vi aPos,
Vi kPos,
1091 peclet::core::View<PositionIncrement> inc,
1092 Kokkos::View<float, CpMem> consensus,
Vi slot,
int no,
int ng) {
1093 Kokkos::parallel_for(
1094 "peclet::dem::halo::posMUnpack", Kokkos::RangePolicy<CpExec>(0, ng), KOKKOS_LAMBDA(
int g) {
1095 const V4& qv = quatPred;
1096 const S p = ghost(g);
1097 const F3 sh = shift(g);
1098 const int s = no + slot(g);
1099 posPred(s, 0) = payloadLin(p, 0) + sh.x;
1100 posPred(s, 1) = payloadLin(p, 1) + sh.y;
1101 posPred(s, 2) = payloadLin(p, 2) + sh.z;
1102 if constexpr (std::is_same_v<S, PositionState>) {
1103 for (
int d = 0; d < 4; ++d)
1106 if (aPos(s) > 0 && kPos(s) > 1) {
1107 const float af =
static_cast<float>(aPos(s));
1108 const PositionIncrement r = inc(g);
1110 for (
int d = 0; d < 3; ++d) {
1111 const float e = (posPred(s, d) - baseX(g, d)) - r.x[d] / af;
1115 Kokkos::atomic_max(&consensus(), Kokkos::sqrt(c2));
1117 for (
int d = 0; d < 3; ++d)
1118 baseX(g, d) = posPred(s, d);
1122inline void haloPackGhostInt(
Vi src, peclet::core::View<int> ghost,
Vi slot,
int no,
int ng) {
1123 Kokkos::parallel_for(
1124 "peclet::dem::halo::packGhostInt", Kokkos::RangePolicy<CpExec>(0, ng),
1125 KOKKOS_LAMBDA(
int g) { ghost(g) = src(no + slot(g)); });
1127inline void haloOwnerCountK(
Vi a,
Vi k, peclet::core::View<int> owned,
int no) {
1128 Kokkos::parallel_for(
1129 "peclet::dem::halo::ownerCountK", Kokkos::RangePolicy<CpExec>(0, no), KOKKOS_LAMBDA(
int i) {
1130 const int kk = a(i) + owned(i);
1131 k(i) = kk > 1 ? kk : 1;
1135inline void haloUnpackGhostInt(
Vi dst, peclet::core::View<int> ghost,
Vi slot,
int no,
int ng) {
1136 Kokkos::parallel_for(
1137 "peclet::dem::halo::unpackGhostInt", Kokkos::RangePolicy<CpExec>(0, ng),
1138 KOKKOS_LAMBDA(
int g) { dst(no + slot(g)) = ghost(g); });
1144struct PositionCountsMask {
1145 unsigned long long mask;
1149KOKKOS_INLINE_FUNCTION PositionCountsMask operator+(
const PositionCountsMask& a,
1150 const PositionCountsMask& b) {
1151 return PositionCountsMask{a.mask | b.mask, a.aPos + b.aPos, 0};
1153inline void haloPackGhostCountsMask(
Vi aVel,
Vi aPos, peclet::core::View<PositionCountsMask> ghost,
1154 Vi slot,
unsigned long long bit,
int no,
int ng) {
1155 Kokkos::parallel_for(
1156 "peclet::dem::halo::packCountsMask", Kokkos::RangePolicy<CpExec>(0, ng),
1157 KOKKOS_LAMBDA(
int g) {
1158 const int s = no + slot(g);
1159 ghost(g) = PositionCountsMask{aVel(s) > 0 ? bit : 0ull, aPos(s), 0};
1162inline void haloOwnerCountsMask(
Vi aVel,
Vi aPos,
Vi kPos,
1163 Kokkos::View<unsigned long long*, CpMem> velMask,
1164 peclet::core::View<PositionCountsMask> owned,
1165 unsigned long long bit,
int no) {
1166 Kokkos::parallel_for(
1167 "peclet::dem::halo::ownerCountsMask", Kokkos::RangePolicy<CpExec>(0, no),
1168 KOKKOS_LAMBDA(
int i) {
1169 const PositionCountsMask r = owned(i);
1170 const int kk = aPos(i) + r.aPos;
1171 kPos(i) = kk > 1 ? kk : 1;
1172 const unsigned long long m = r.mask | (aVel(i) > 0 ? bit : 0ull);
1174 owned(i) = PositionCountsMask{m, kPos(i), 0};
1177inline void haloUnpackCountsMask(
Vi kPos, Kokkos::View<unsigned long long*, CpMem> velMask,
1178 peclet::core::View<PositionCountsMask> ghost,
Vi slot,
int no,
1180 Kokkos::parallel_for(
1181 "peclet::dem::halo::unpackCountsMask", Kokkos::RangePolicy<CpExec>(0, ng),
1182 KOKKOS_LAMBDA(
int g) {
1183 const int s = no + slot(g);
1184 kPos(s) = ghost(g).aPos;
1185 velMask(s) = ghost(g).mask;
1191inline void haloMapVelocitySlots(
Vi realIndices,
Vi canon,
int no,
int ng) {
1192 Kokkos::parallel_for(
1193 "peclet::dem::halo::mapVelSlots", Kokkos::RangePolicy<CpExec>(0, ng),
1194 KOKKOS_LAMBDA(
int j) { realIndices(no + j) = canon(j); });
1210 void initMpi(std::array<double, 3> origin, std::array<double, 3> size, std::array<long, 3> gsize,
1211 std::array<bool, 3> periodic, MPI_Comm comm) {
1214 MPI_Comm_rank(comm_, &rank_);
1215 MPI_Comm_size(comm_, &sz);
1216 dec_.init(
static_cast<std::size_t
>(sz), peclet::core::IVec<3>{gsize[0], gsize[1], gsize[2]});
1217 peclet::core::halo::DomainMap<3> map;
1218 for (
int i = 0; i < 3; ++i) {
1219 map.origin[i] = origin[i];
1220 map.cellSize[i] = size[i] /
static_cast<double>(gsize[i]);
1221 map.periodic[i] = periodic[i];
1224 mig_.init(dec_, rank_, map, comm_);
1231 void initMpi(
const peclet::core::decomp::BlockDecomposer<3>& dec, std::array<double, 3> origin,
1232 std::array<double, 3> size, std::array<bool, 3> periodic, MPI_Comm comm) {
1234 MPI_Comm_rank(comm_, &rank_);
1236 const auto& gs = dec.globalSize();
1237 peclet::core::halo::DomainMap<3> map;
1238 for (
int i = 0; i < 3; ++i) {
1239 map.origin[i] = origin[i];
1240 map.cellSize[i] = size[i] /
static_cast<double>(gs[i]);
1241 map.periodic[i] = periodic[i];
1244 mig_.init(dec_, rank_, map, comm_);
1248 const peclet::core::decomp::BlockDecomposer<3>& decomposer()
const {
return dec_; }
1253 int rankColor()
const {
return rankColor_; }
1254 int numRankColors()
const {
return numRankColors_; }
1256 bool inited()
const {
return inited_; }
1257 int rank()
const {
return rank_; }
1258 int numGhost()
const {
return numGhost_; }
1267 void setVerletSkin(
float skin) { verletSkin_ = skin < 0.0f ? 0.0f : skin; }
1268 float verletSkin()
const {
return verletSkin_; }
1272 void invalidateTopology() { haveTopo_ =
false; }
1275 long numRebuilds()
const {
return nRebuild_; }
1276 long numGathers()
const {
return nGather_; }
1281 int gather(Particles& P,
double rcut) {
1282 const int no = P.numReal;
1292 const double band = rcut +
static_cast<double>(verletSkin_);
1293 ensureRankColoring(band);
1295 velM_ = posM_ = orphanShares_ =
false;
1296 bool rebuild = (verletSkin_ <= 0.0f) || !haveTopo_ || (no != lastNumReal_) || band != lastBand_;
1297 if (!rebuild && maxOwnedDisplacement(P.pos, no) >= verletSkin_)
1304 if (verletSkin_ > 0.0f) {
1305 int mine = rebuild ? 1 : 0, any = 0;
1314 auto hpos = Kokkos::create_mirror_view(P.pos);
1315 Kokkos::deep_copy(hpos, P.pos);
1316 std::vector<peclet::core::Vec<3>> pv(
static_cast<std::size_t
>(no));
1323 const auto& gsz = dec_.globalSize();
1324 for (
int i = 0; i < no; ++i) {
1325 double q[3] = {hpos(i, 0), hpos(i, 1), hpos(i, 2)};
1326 for (
int d = 0; d < 3; ++d)
1327 if (!map_.periodic[d])
1328 q[d] = std::clamp(q[d], map_.origin[d],
1329 map_.origin[d] +
static_cast<double>(gsz[d]) * map_.cellSize[d]);
1330 pv[i] = peclet::core::Vec<3>{q[0], q[1], q[2]};
1340 halo_.build(pv, band,
true,
true);
1342 const int ng =
static_cast<int>(halo_.numGhost());
1349 if (no + ng > P.capacity)
1350 throw std::runtime_error(
1351 "ParticleHalo::gather: ghost overflow -- need capacity >= " + std::to_string(no + ng) +
1352 " (numReal=" + std::to_string(no) +
" + numGhost=" + std::to_string(ng) +
"), have " +
1353 std::to_string(P.capacity) +
"; increase the Simulation capacity.");
1354 const auto topo = halo_.flatten();
1356 numSend_ =
static_cast<int>(topo.sendIdx.size());
1357 allocBuffers(no, ng);
1359 const std::vector<int> hSlot = uploadGhostSlots(topo, ng);
1360 buildOwnershipMaps(topo, no, ng, hSlot);
1361 numReceived_ =
static_cast<int>(topo.numReceived);
1362 selfIdx_.assign(topo.selfIdx.begin(), topo.selfIdx.end());
1363 buildInterfaceRows(topo, no);
1365 if (verletSkin_ > 0.0f) {
1366 if (refPos_.extent(0) <
static_cast<std::size_t
>(no))
1367 refPos_ =
V3(
"peclet::dem::halo::refPos",
static_cast<std::size_t
>(P.capacity));
1368 Kokkos::deep_copy(Kokkos::subview(refPos_, std::pair<int, int>(0, no), Kokkos::ALL),
1369 Kokkos::subview(P.pos, std::pair<int, int>(0, no), Kokkos::ALL));
1377 const int ng = numGhost_;
1378 P.numParticles = no + ng;
1381 selfMapReals(P.realIndices, no);
1388 forwardPositions(P.pos);
1389 forwardPositions(P.posPred);
1393 haloPackGather(P.vel, P.velPred, P.angVel, P.angVelPred, P.invInertia, P.quat, P.quatPred,
1394 P.scale, P.invMass, P.shapeId, P.gid, P.materialId, P.groundedLevel,
1395 P.bodyOrphan, P.bodyOrphanVPeak, ownedPack_, no);
1396 dev_.forward(ownedPack_, ghostPack_);
1397 haloUnpackGather(P.vel, P.velPred, P.angVel, P.angVelPred, P.invInertia, P.quat, P.quatPred,
1398 P.scale, P.invMass, P.shapeId, P.realIndices, P.gid, P.materialId,
1399 P.groundedLevel, P.bodyOrphan, P.bodyOrphanVPeak, ghostPack_, ghostSlot_, no,
1404 MPI_Comm comm()
const {
return comm_; }
1408 bool exchanges()
const {
return numGhost_ > 0 || numSend_ > 0; }
1418 int rebalance(Particles& P) {
1419 std::vector<peclet::core::Vec<3>> pos;
1420 std::vector<char> payload;
1421 packState(P, pos, payload);
1422 const std::size_t newN = peclet::core::halo::rebalanceByParticleCount(
1423 dec_, mig_, pos, payload,
sizeof(MigratePack), comm_);
1424 orderArrivalsBySource(pos, payload, newN);
1425 if ((
int)newN > P.capacity)
1426 throw std::runtime_error(
"ParticleHalo::rebalance: owned overflow -- rank received " +
1427 std::to_string(newN) +
" particles, capacity " +
1428 std::to_string(P.capacity));
1429 unpackState(P, pos, payload, newN);
1438 int migrateToBlocks(Particles& P) {
1439 std::vector<peclet::core::Vec<3>> pos;
1440 std::vector<char> payload;
1441 packState(P, pos, payload);
1442 const std::size_t newN = mig_.migrate(pos, payload,
sizeof(MigratePack));
1443 orderArrivalsBySource(pos, payload, newN);
1444 if ((
int)newN > P.capacity)
1445 throw std::runtime_error(
"ParticleHalo::migrateToBlocks: owned overflow -- rank received " +
1446 std::to_string(newN) +
" particles, capacity " +
1447 std::to_string(P.capacity));
1448 unpackState(P, pos, payload, newN);
1457 BlockBox blockBox()
const {
1458 const auto blk = dec_.block(
static_cast<std::size_t
>(rank_));
1459 const auto& gs = dec_.globalSize();
1460 float lo[3], hi[3], L[3];
1461 for (
int d = 0; d < 3; ++d) {
1462 lo[d] =
static_cast<float>(map_.origin[d] +
1463 static_cast<double>(blk.origin[d]) * map_.cellSize[d]);
1464 hi[d] =
static_cast<float>(map_.origin[d] +
static_cast<double>(blk.origin[d] + blk.size[d]) *
1466 L[d] = map_.periodic[d] ?
static_cast<float>(
static_cast<double>(gs[d]) * map_.cellSize[d])
1470 if (!map_.periodic[d]) {
1471 if (blk.origin[d] == 0)
1472 lo[d] = -std::numeric_limits<float>::infinity();
1473 if (blk.origin[d] + blk.size[d] == gs[d])
1474 hi[d] = std::numeric_limits<float>::infinity();
1477 return BlockBox{F3{lo[0], lo[1], lo[2]}, F3{hi[0], hi[1], hi[2]}, F3{L[0], L[1], L[2]}};
1482 int migrateTo(Particles& P,
const peclet::core::decomp::BlockDecomposer<3>& newDec) {
1483 std::vector<peclet::core::Vec<3>> pos;
1484 std::vector<char> payload;
1485 packState(P, pos, payload);
1489 const std::size_t newN = mig_.migrate(pos, payload,
sizeof(MigratePack));
1490 orderArrivalsBySource(pos, payload, newN);
1491 if ((
int)newN > P.capacity)
1492 throw std::runtime_error(
"ParticleHalo::migrateTo: owned overflow -- rank received " +
1493 std::to_string(newN) +
" particles, capacity " +
1494 std::to_string(P.capacity));
1495 unpackState(P, pos, payload, newN);
1506 int migrateToWeights(Particles& P,
const std::vector<peclet::core::Real>& w,
int align = 1) {
1508 MPI_Comm_size(comm_, &size);
1510 peclet::core::decomp::BlockDecomposer<3> newDec((std::size_t)size, dec_.globalSize(), w);
1511 return migrateTo(P, newDec);
1513 peclet::core::decomp::BlockDecomposer<3> newDec;
1514 newDec.init((std::size_t)size, dec_.globalSize(), w,
1515 peclet::core::IVec<3>{align, align, align});
1516 return migrateTo(P, newDec);
1522 void packState(Particles& P, std::vector<peclet::core::Vec<3>>& pos, std::vector<char>& payload) {
1523 const int no = P.numReal;
1524 auto h_pos = Kokkos::create_mirror_view(P.pos);
1525 auto h_quat = Kokkos::create_mirror_view(P.quat);
1526 auto h_vel = Kokkos::create_mirror_view(P.vel);
1527 auto h_angVel = Kokkos::create_mirror_view(P.angVel);
1528 auto h_invI = Kokkos::create_mirror_view(P.invInertia);
1529 auto h_invM = Kokkos::create_mirror_view(P.invMass);
1530 auto h_scale = Kokkos::create_mirror_view(P.scale);
1531 auto h_tScale = Kokkos::create_mirror_view(P.targetScale);
1532 auto h_shape = Kokkos::create_mirror_view(P.shapeId);
1533 auto h_pf = Kokkos::create_mirror_view(P.planeFriction);
1534 auto h_gid = Kokkos::create_mirror_view(P.gid);
1535 auto h_mat = Kokkos::create_mirror_view(P.materialId);
1536 auto h_grd = Kokkos::create_mirror_view(P.groundedLevel);
1537 Kokkos::deep_copy(h_pos, P.pos);
1538 Kokkos::deep_copy(h_quat, P.quat);
1539 Kokkos::deep_copy(h_vel, P.vel);
1540 Kokkos::deep_copy(h_angVel, P.angVel);
1541 Kokkos::deep_copy(h_invI, P.invInertia);
1542 Kokkos::deep_copy(h_invM, P.invMass);
1543 Kokkos::deep_copy(h_scale, P.scale);
1544 Kokkos::deep_copy(h_tScale, P.targetScale);
1545 Kokkos::deep_copy(h_shape, P.shapeId);
1546 Kokkos::deep_copy(h_pf, P.planeFriction);
1547 Kokkos::deep_copy(h_gid, P.gid);
1548 Kokkos::deep_copy(h_mat, P.materialId);
1549 Kokkos::deep_copy(h_grd, P.groundedLevel);
1550 auto h_orp = Kokkos::create_mirror_view(P.bodyOrphan);
1551 auto h_ovp = Kokkos::create_mirror_view(P.bodyOrphanVPeak);
1552 Kokkos::deep_copy(h_orp, P.bodyOrphan);
1553 Kokkos::deep_copy(h_ovp, P.bodyOrphanVPeak);
1554 auto h_ef = Kokkos::create_mirror_view(P.extForce);
1555 auto h_et = Kokkos::create_mirror_view(P.extTorque);
1556 Kokkos::deep_copy(h_ef, P.extForce);
1557 Kokkos::deep_copy(h_et, P.extTorque);
1558 pos.assign((std::size_t)no, peclet::core::Vec<3>{});
1559 payload.assign((std::size_t)no *
sizeof(MigratePack), 0);
1560 std::vector<MigratePack> packs((std::size_t)no);
1561 std::unordered_map<unsigned, int> gidToLocal;
1562 gidToLocal.reserve((std::size_t)no * 2);
1563 for (
int i = 0; i < no; ++i) {
1564 pos[(std::size_t)i] = peclet::core::Vec<3>{h_pos(i, 0), h_pos(i, 1), h_pos(i, 2)};
1565 MigratePack& m = packs[(std::size_t)i];
1566 m.quat = F4{h_quat(i, 0), h_quat(i, 1), h_quat(i, 2), h_quat(i, 3)};
1567 m.vel = F3{h_vel(i, 0), h_vel(i, 1), h_vel(i, 2)};
1568 m.angVel = F3{h_angVel(i, 0), h_angVel(i, 1), h_angVel(i, 2)};
1569 m.invInertia = F3{h_invI(i, 0), h_invI(i, 1), h_invI(i, 2)};
1570 m.invMass = h_invM(i);
1571 m.scale = h_scale(i);
1572 m.targetScale = h_tScale(i);
1573 m.shapeId = h_shape(i);
1574 m.planeFric0 = h_pf(i, 0);
1575 m.planeFric1 = h_pf(i, 1);
1578 m.materialId = h_mat(i);
1579 m.groundedLevel = h_grd(i);
1580 m.orphan = h_orp(i);
1581 m.orphanVPeak = h_ovp(i);
1582 m.extForce = F3{h_ef(i, 0), h_ef(i, 1), h_ef(i, 2)};
1583 m.extTorque = F3{h_et(i, 0), h_et(i, 1), h_et(i, 2)};
1585 gidToLocal.emplace(
static_cast<unsigned>(h_gid(i)), i);
1591 if (P.prevPairCount > 0) {
1592 const int pc = P.prevPairCount;
1595 auto h_k = Kokkos::create_mirror_view(P.prevPairKeys);
1596 auto h_l = Kokkos::create_mirror_view(P.prevLambda);
1597 auto h_lt = Kokkos::create_mirror_view(P.prevLambdaT);
1598 auto h_pi = Kokkos::create_mirror_view(P.prevPosImpulse);
1599 auto h_rb = Kokkos::create_mirror_view(P.prevRestBank);
1600 auto h_rv = Kokkos::create_mirror_view(P.prevRestVPeak);
1601 Kokkos::deep_copy(h_k, P.prevPairKeys);
1602 Kokkos::deep_copy(h_l, P.prevLambda);
1603 Kokkos::deep_copy(h_lt, P.prevLambdaT);
1604 Kokkos::deep_copy(h_pi, P.prevPosImpulse);
1605 Kokkos::deep_copy(h_rb, P.prevRestBank);
1606 Kokkos::deep_copy(h_rv, P.prevRestVPeak);
1607 auto attach = [&](
int i,
const WarmPairEntry& e,
float w) {
1608 MigratePack& m = packs[(std::size_t)i];
1609 if (m.numWarm < kWarmCarryMax) {
1610 m.warm[m.numWarm++] = e;
1614 float worstW = 1e30f;
1615 for (
int s = 0; s < kWarmCarryMax; ++s) {
1616 const float ws = std::fabs(m.warm[s].lambda) + std::fabs(m.warm[s].posImpulse);
1625 for (
int e = 0; e < pc; ++e) {
1626 const unsigned long long k = h_k(e);
1632 we.lambdaT[0] = h_lt(e, 0);
1633 we.lambdaT[1] = h_lt(e, 1);
1634 we.lambdaT[2] = h_lt(e, 2);
1635 we.posImpulse = h_pi(e);
1636 we.restBank = h_rb(e);
1637 we.restVPeak = h_rv(e);
1638 if (we.lambda == 0.0f && we.posImpulse == 0.0f && we.lambdaT[0] == 0.0f &&
1639 we.lambdaT[1] == 0.0f && we.lambdaT[2] == 0.0f && we.restBank == 0.0f)
1641 const float w = std::fabs(we.lambda) + std::fabs(we.posImpulse) + std::fabs(we.restBank);
1642 const unsigned hi =
static_cast<unsigned>(k >> 32);
1643 const unsigned lo =
static_cast<unsigned>(k & 0xFFFFFFFFu);
1644 if (
auto it = gidToLocal.find(hi); it != gidToLocal.end())
1645 attach(it->second, we, w);
1646 if (lo != 0xFFFFFFFFu)
1647 if (
auto it = gidToLocal.find(lo); it != gidToLocal.end())
1648 attach(it->second, we, w);
1654 if (P.hertzNumPairs > 0) {
1655 const int hn = P.hertzNumPairs;
1656 auto h_hk = Kokkos::create_mirror_view(P.hertzKeys);
1657 auto h_hx = Kokkos::create_mirror_view(P.hertzXi);
1658 Kokkos::deep_copy(h_hk, P.hertzKeys);
1659 Kokkos::deep_copy(h_hx, P.hertzXi);
1660 auto attachHertz = [&](
int i,
const HertzPairEntry& e,
float w) {
1661 MigratePack& m = packs[(std::size_t)i];
1662 if (m.numHertz < kWarmCarryMax) {
1663 m.hertz[m.numHertz++] = e;
1667 float worstW = 1e30f;
1668 for (
int s = 0; s < kWarmCarryMax; ++s) {
1669 const float ws = std::fabs(m.hertz[s].xi[0]) + std::fabs(m.hertz[s].xi[1]) +
1670 std::fabs(m.hertz[s].xi[2]);
1679 for (
int e = 0; e < hn; ++e) {
1682 he.xi[0] = h_hx(e, 0);
1683 he.xi[1] = h_hx(e, 1);
1684 he.xi[2] = h_hx(e, 2);
1685 if (he.xi[0] == 0.0f && he.xi[1] == 0.0f && he.xi[2] == 0.0f)
1687 const float w = std::fabs(he.xi[0]) + std::fabs(he.xi[1]) + std::fabs(he.xi[2]);
1688 const unsigned hi =
static_cast<unsigned>(he.key >> 32);
1689 const unsigned lo =
static_cast<unsigned>(he.key & 0xFFFFFFFFu);
1690 if (
auto it = gidToLocal.find(hi); it != gidToLocal.end())
1691 attachHertz(it->second, he, w);
1692 if (
auto it = gidToLocal.find(lo); it != gidToLocal.end())
1693 attachHertz(it->second, he, w);
1697 auto h_xw = Kokkos::create_mirror_view(P.hertzXiWall);
1698 auto h_sw = Kokkos::create_mirror_view(P.hertzSnWall);
1699 Kokkos::deep_copy(h_xw, P.hertzXiWall);
1700 Kokkos::deep_copy(h_sw, P.hertzSnWall);
1701 for (
int i = 0; i < no; ++i)
1702 for (
int wi = 0; wi < Particles::kHertzMaxWalls; ++wi) {
1703 const int slot = i * Particles::kHertzMaxWalls + wi;
1704 MigratePack& m = packs[(std::size_t)i];
1705 m.hertzXiWall[wi][0] = h_xw(slot, 0);
1706 m.hertzXiWall[wi][1] = h_xw(slot, 1);
1707 m.hertzXiWall[wi][2] = h_xw(slot, 2);
1708 m.hertzSnWall[wi] = h_sw(slot);
1711 for (
int i = 0; i < no; ++i)
1712 std::memcpy(&payload[(std::size_t)i *
sizeof(MigratePack)], &packs[(std::size_t)i],
1713 sizeof(MigratePack));
1718 void unpackState(Particles& P,
const std::vector<peclet::core::Vec<3>>& pos,
1719 const std::vector<char>& payload, std::size_t newN) {
1720 auto h_pos = Kokkos::create_mirror_view(P.pos);
1721 auto h_quat = Kokkos::create_mirror_view(P.quat);
1722 auto h_vel = Kokkos::create_mirror_view(P.vel);
1723 auto h_angVel = Kokkos::create_mirror_view(P.angVel);
1724 auto h_invI = Kokkos::create_mirror_view(P.invInertia);
1725 auto h_invM = Kokkos::create_mirror_view(P.invMass);
1726 auto h_scale = Kokkos::create_mirror_view(P.scale);
1727 auto h_tScale = Kokkos::create_mirror_view(P.targetScale);
1728 auto h_shape = Kokkos::create_mirror_view(P.shapeId);
1729 auto h_pf = Kokkos::create_mirror_view(P.planeFriction);
1730 auto h_gid = Kokkos::create_mirror_view(P.gid);
1731 auto h_mat = Kokkos::create_mirror_view(P.materialId);
1732 auto h_grd = Kokkos::create_mirror_view(P.groundedLevel);
1733 auto h_orp = Kokkos::create_mirror_view(P.bodyOrphan);
1734 auto h_ovp = Kokkos::create_mirror_view(P.bodyOrphanVPeak);
1735 Kokkos::deep_copy(h_orp, P.bodyOrphan);
1736 Kokkos::deep_copy(h_ovp, P.bodyOrphanVPeak);
1737 auto h_ef = Kokkos::create_mirror_view(P.extForce);
1738 auto h_et = Kokkos::create_mirror_view(P.extTorque);
1739 Kokkos::deep_copy(h_ef, P.extForce);
1740 Kokkos::deep_copy(h_et, P.extTorque);
1741 std::vector<WarmPairEntry> ledger;
1742 ledger.reserve(newN * 4);
1743 std::vector<HertzPairEntry> hertzLedger;
1744 hertzLedger.reserve(newN * 4);
1745 auto h_xw = Kokkos::create_mirror_view(P.hertzXiWall);
1746 auto h_sw = Kokkos::create_mirror_view(P.hertzSnWall);
1747 Kokkos::deep_copy(h_xw, P.hertzXiWall);
1748 Kokkos::deep_copy(h_sw, P.hertzSnWall);
1749 for (std::size_t i = 0; i < newN; ++i) {
1750 h_pos((
int)i, 0) = pos[i][0];
1751 h_pos((
int)i, 1) = pos[i][1];
1752 h_pos((
int)i, 2) = pos[i][2];
1754 std::memcpy(&m, &payload[i *
sizeof(MigratePack)],
sizeof(MigratePack));
1755 h_quat((
int)i, 0) = m.quat.x;
1756 h_quat((
int)i, 1) = m.quat.y;
1757 h_quat((
int)i, 2) = m.quat.z;
1758 h_quat((
int)i, 3) = m.quat.w;
1759 h_vel((
int)i, 0) = m.vel.x;
1760 h_vel((
int)i, 1) = m.vel.y;
1761 h_vel((
int)i, 2) = m.vel.z;
1762 h_angVel((
int)i, 0) = m.angVel.x;
1763 h_angVel((
int)i, 1) = m.angVel.y;
1764 h_angVel((
int)i, 2) = m.angVel.z;
1765 h_invI((
int)i, 0) = m.invInertia.x;
1766 h_invI((
int)i, 1) = m.invInertia.y;
1767 h_invI((
int)i, 2) = m.invInertia.z;
1768 h_invM((
int)i) = m.invMass;
1769 h_scale((
int)i) = m.scale;
1770 h_tScale((
int)i) = m.targetScale;
1771 h_shape((
int)i) = m.shapeId;
1772 h_pf((
int)i, 0) = m.planeFric0;
1773 h_pf((
int)i, 1) = m.planeFric1;
1774 h_gid((
int)i) = m.gid;
1775 h_mat((
int)i) = m.materialId;
1776 h_grd((
int)i) = m.groundedLevel;
1777 h_orp((
int)i) = m.orphan;
1778 h_ovp((
int)i) = m.orphanVPeak;
1779 h_ef((
int)i, 0) = m.extForce.x;
1780 h_ef((
int)i, 1) = m.extForce.y;
1781 h_ef((
int)i, 2) = m.extForce.z;
1782 h_et((
int)i, 0) = m.extTorque.x;
1783 h_et((
int)i, 1) = m.extTorque.y;
1784 h_et((
int)i, 2) = m.extTorque.z;
1785 for (
int s = 0; s < (int)m.numWarm && s < kWarmCarryMax; ++s)
1786 ledger.push_back(m.warm[s]);
1787 for (
int s = 0; s < (int)m.numHertz && s < kWarmCarryMax; ++s)
1788 hertzLedger.push_back(m.hertz[s]);
1789 for (
int wi = 0; wi < Particles::kHertzMaxWalls; ++wi) {
1790 const int slot = (int)i * Particles::kHertzMaxWalls + wi;
1791 h_xw(slot, 0) = m.hertzXiWall[wi][0];
1792 h_xw(slot, 1) = m.hertzXiWall[wi][1];
1793 h_xw(slot, 2) = m.hertzXiWall[wi][2];
1794 h_sw(slot) = m.hertzSnWall[wi];
1797 Kokkos::deep_copy(P.pos, h_pos);
1798 Kokkos::deep_copy(P.quat, h_quat);
1799 Kokkos::deep_copy(P.vel, h_vel);
1800 Kokkos::deep_copy(P.angVel, h_angVel);
1801 Kokkos::deep_copy(P.invInertia, h_invI);
1802 Kokkos::deep_copy(P.invMass, h_invM);
1803 Kokkos::deep_copy(P.scale, h_scale);
1804 Kokkos::deep_copy(P.targetScale, h_tScale);
1805 Kokkos::deep_copy(P.shapeId, h_shape);
1806 Kokkos::deep_copy(P.planeFriction, h_pf);
1807 Kokkos::deep_copy(P.gid, h_gid);
1808 Kokkos::deep_copy(P.materialId, h_mat);
1809 Kokkos::deep_copy(P.groundedLevel, h_grd);
1810 Kokkos::deep_copy(P.bodyOrphan, h_orp);
1811 Kokkos::deep_copy(P.bodyOrphanVPeak, h_ovp);
1812 Kokkos::deep_copy(P.extForce, h_ef);
1813 Kokkos::deep_copy(P.extTorque, h_et);
1818 std::sort(ledger.begin(), ledger.end(),
1819 [](
const WarmPairEntry& a,
const WarmPairEntry& b) { return a.key < b.key; });
1821 std::unique(ledger.begin(), ledger.end(),
1822 [](
const WarmPairEntry& a,
const WarmPairEntry& b) { return a.key == b.key; }),
1824 const int nl = std::min<int>((
int)ledger.size(), (
int)P.prevPairKeys.extent(0));
1826 auto hk = Kokkos::create_mirror_view(P.prevPairKeys);
1827 auto hl = Kokkos::create_mirror_view(P.prevLambda);
1828 auto hlt = Kokkos::create_mirror_view(P.prevLambdaT);
1829 auto hpi = Kokkos::create_mirror_view(P.prevPosImpulse);
1830 auto hrb = Kokkos::create_mirror_view(P.prevRestBank);
1831 auto hrv = Kokkos::create_mirror_view(P.prevRestVPeak);
1832 Kokkos::deep_copy(hk, P.prevPairKeys);
1833 Kokkos::deep_copy(hl, P.prevLambda);
1834 Kokkos::deep_copy(hlt, P.prevLambdaT);
1835 Kokkos::deep_copy(hpi, P.prevPosImpulse);
1836 Kokkos::deep_copy(hrb, P.prevRestBank);
1837 Kokkos::deep_copy(hrv, P.prevRestVPeak);
1838 for (
int e = 0; e < nl; ++e) {
1839 hk(e) = ledger[(std::size_t)e].key;
1840 hl(e) = ledger[(std::size_t)e].lambda;
1841 hlt(e, 0) = ledger[(std::size_t)e].lambdaT[0];
1842 hlt(e, 1) = ledger[(std::size_t)e].lambdaT[1];
1843 hlt(e, 2) = ledger[(std::size_t)e].lambdaT[2];
1844 hpi(e) = ledger[(std::size_t)e].posImpulse;
1845 hrb(e) = ledger[(std::size_t)e].restBank;
1846 hrv(e) = ledger[(std::size_t)e].restVPeak;
1848 Kokkos::deep_copy(P.prevPairKeys, hk);
1849 Kokkos::deep_copy(P.prevLambda, hl);
1850 Kokkos::deep_copy(P.prevLambdaT, hlt);
1851 Kokkos::deep_copy(P.prevPosImpulse, hpi);
1852 Kokkos::deep_copy(P.prevRestBank, hrb);
1853 Kokkos::deep_copy(P.prevRestVPeak, hrv);
1855 P.prevPairCount = nl;
1862 Kokkos::deep_copy(P.hertzXiWall, h_xw);
1863 Kokkos::deep_copy(P.hertzSnWall, h_sw);
1864 std::sort(hertzLedger.begin(), hertzLedger.end(),
1865 [](
const HertzPairEntry& a,
const HertzPairEntry& b) { return a.key < b.key; });
1866 hertzLedger.erase(std::unique(hertzLedger.begin(), hertzLedger.end(),
1867 [](
const HertzPairEntry& a,
const HertzPairEntry& b) {
1868 return a.key == b.key;
1871 const int nh = (int)hertzLedger.size();
1872 if ((
int)P.hertzPrevKeys.extent(0) < nh) {
1873 P.hertzPrevKeys = Kokkos::View<unsigned long long*, CpMem>(
"hertzPrevKeys", nh);
1874 P.hertzPrevXi = Kokkos::View<float* [3], CpMem>(
"hertzPrevXi", nh);
1877 auto hk = Kokkos::create_mirror_view(P.hertzPrevKeys);
1878 auto hx = Kokkos::create_mirror_view(P.hertzPrevXi);
1879 Kokkos::deep_copy(hk, P.hertzPrevKeys);
1880 Kokkos::deep_copy(hx, P.hertzPrevXi);
1881 for (
int e = 0; e < nh; ++e) {
1882 hk(e) = hertzLedger[(std::size_t)e].key;
1883 hx(e, 0) = hertzLedger[(std::size_t)e].xi[0];
1884 hx(e, 1) = hertzLedger[(std::size_t)e].xi[1];
1885 hx(e, 2) = hertzLedger[(std::size_t)e].xi[2];
1887 Kokkos::deep_copy(P.hertzPrevKeys, hk);
1888 Kokkos::deep_copy(P.hertzPrevXi, hx);
1890 P.hertzPrevCount = nh;
1891 P.hertzNumPairs = -1;
1893 P.numReal = (int)newN;
1894 P.numParticles = (int)newN;
1906 void forward(V3 field) {
1909 haloPackF3(field, ownedF3_, numReal_);
1910 dev_.forward(ownedF3_, ghostF3_);
1911 haloUnpackF3(field, ghostF3_, shiftDev_, ghostSlot_, numReal_, numGhost_,
false);
1914 void forwardPositions(V3 field) {
1917 haloPackF3(field, ownedF3_, numReal_);
1918 dev_.forward(ownedF3_, ghostF3_);
1919 haloUnpackF3(field, ghostF3_, shiftDev_, ghostSlot_, numReal_, numGhost_,
true);
1922 void forward4(V4 field) {
1925 haloPackF4(field, ownedF4_, numReal_);
1926 dev_.forward(ownedF4_, ghostF4_);
1927 haloUnpackF4(field, ghostF4_, ghostSlot_, numReal_, numGhost_);
1931 ContactOwnership contactOwnership(
const Particles& P)
const {
1932 return ContactOwnership{rank_, numReal_, P.gid, ghostSource_, copyOffsets_,
1933 copyRanks_, ghostImage_, copyImage_, selfOffsets_, selfImage_};
1939 std::vector<float> debugGhostShiftsBySlot()
const {
1940 std::vector<float> out(3 *
static_cast<std::size_t
>(numGhost_ > 0 ? numGhost_ : 0), 0.0f);
1943 auto hs = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), shiftDev_);
1944 auto hk = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), ghostSlot_);
1945 for (
int g = 0; g < numGhost_; ++g) {
1946 const int s = hk(g);
1947 out[3 * s] = hs(g).x;
1948 out[3 * s + 1] = hs(g).y;
1949 out[3 * s + 2] = hs(g).z;
1960 void markVelocityBaseline(Particles& P) {
1963 haloMarkVelocityBaseline(P.velPred, P.angVelPred, P.bodyOrphan, P.bodyOrphanVPeak, baseVel_,
1964 baseAngVel_, baseOrphan_, baseOrphanPeak_, ghostSlot_, numReal_,
1974 void syncVelocities(Particles& P,
bool rotation) {
1978 syncVelocitiesM(P, rotation);
1981 haloPackVelocityIncrement(P.velPred, P.angVelPred, P.bodyOrphan, P.bodyOrphanVPeak, baseVel_,
1982 baseAngVel_, baseOrphan_, baseOrphanPeak_, ghostVelInc_, ghostSlot_,
1983 numReal_, numGhost_);
1985 Kokkos::subview(ownedVelInc_, std::pair<std::size_t, std::size_t>(0, numReal_)),
1986 VelocityIncrement{});
1987 dev_.reverse(ghostVelInc_, ownedVelInc_);
1988 haloApplyVelocityIncrement(P.velPred, P.angVelPred, P.bodyOrphan, P.bodyOrphanVPeak,
1989 ownedVelInc_, numReal_);
1991 haloPackVelocityState(P.velPred, P.angVelPred, ownedVelState_, nIface_, iface_);
1992 dev_.forward(ownedVelState_, ghostVelState_);
1993 haloUnpackVelocityState(P.velPred, P.angVelPred, ghostVelState_, ghostSlot_, numReal_,
1998 markVelocityBaseline(P);
2002 void publishPositions(Particles& P,
bool rotation) {
2005 forwardPositionState(P, rotation);
2006 markPositionPublished(P);
2009 void forwardPositionState(Particles& P,
bool rotation) {
2011 haloPackPositionState(P.posPred, P.quatPred, ownedPosState_, nIface_, iface_);
2012 dev_.forward(ownedPosState_, ghostPosState_);
2013 haloUnpackPositionState(P.posPred, P.quatPred, ghostPosState_, shiftDev_, ghostSlot_,
2014 numReal_, numGhost_);
2016 forwardPositions(P.posPred);
2020 void markPositionPublished(Particles& P) {
2021 haloMarkPositionBaseline(P.posPred, basePos_, ghostSlot_, numReal_, numGhost_);
2023 haloMarkOwnerPositionSeeds(P.posPred, ownerSeedPos_, nPosRows_, posRows_);
2030 void syncPositions(Particles& P,
bool rotation) {
2034 haloPositionMPackZero(P.posPred, basePos_, P.aPos, ghostPosInc_, ghostSlot_, numReal_,
2035 numGhost_, ownedPosInc_, posRows_, nPosRows_);
2036 dev_.reverse(ghostPosInc_, ownedPosInc_);
2038 positionMRound(P, ownedPosState_, ghostPosState_);
2040 positionMRound(P, ownedF3_, ghostF3_);
2043 haloPackPositionIncrement(P.posPred, basePos_, ghostPosInc_, ghostSlot_, numReal_, numGhost_);
2045 Kokkos::subview(ownedPosInc_, std::pair<std::size_t, std::size_t>(0, numReal_)),
2046 PositionIncrement{});
2047 dev_.reverse(ghostPosInc_, ownedPosInc_);
2048 haloApplyPositionIncrement(P.posPred, ownedPosInc_, numReal_);
2049 publishPositions(P, rotation);
2054 bool velocityMassSplit()
const {
return velM_; }
2055 bool positionMassSplit()
const {
return posM_; }
2064 void openVelocityPhase(Particles& P,
bool rotation,
bool shares) {
2067 ensureClampCounter(P);
2068 const int no = numReal_, ng = numGhost_;
2069 haloPackOpeningIncrement(P.velPred, P.angVelPred, P.bodyOrphan, P.bodyOrphanVPeak, baseVel_,
2070 baseAngVel_, baseOrphan_, baseOrphanPeak_, P.aVel, P.aPos,
2071 ghostOpenInc_, ghostSlot_, no, ng);
2072 Kokkos::deep_copy(Kokkos::subview(ownedOpenInc_, std::pair<std::size_t, std::size_t>(0, no)),
2073 OpeningIncrement{});
2074 dev_.reverse(ghostOpenInc_, ownedOpenInc_);
2075 haloApplyOpening(P.velPred, P.angVelPred, P.bodyOrphan, P.bodyOrphanVPeak, P.aVel, P.aPos,
2076 P.kVel, P.kPos, ownedOpenInc_, no);
2078 haloPackOpeningState(P.velPred, P.angVelPred, P.bodyOrphan, P.bodyOrphanVPeak, P.kVel, P.kPos,
2079 ownedOpenState_, no);
2080 dev_.forward(ownedOpenState_, ghostOpenState_);
2081 haloUnpackOpeningState(P.velPred, P.angVelPred, P.bodyOrphan, P.bodyOrphanVPeak, P.kVel,
2082 P.kPos, ghostOpenState_, ghostSlot_, no, ng);
2084 haloPackOpeningState(P.velPred, P.angVelPred, P.bodyOrphan, P.bodyOrphanVPeak, P.kVel, P.kPos,
2085 ownedOpenStateNR_, no);
2086 dev_.forward(ownedOpenStateNR_, ghostOpenStateNR_);
2087 haloUnpackOpeningState(P.velPred, P.angVelPred, P.bodyOrphan, P.bodyOrphanVPeak, P.kVel,
2088 P.kPos, ghostOpenStateNR_, ghostSlot_, no, ng);
2090 buildMRows(P.kVel, velRows_, nVelRows_);
2091 buildMRows(P.kPos, posRows_, nPosRows_);
2092 velM_ = posM_ =
true;
2093 orphanShares_ = shares;
2094 markVelocityReconciled(P);
2103 void openPositionCounts(Particles& P,
bool velocityMask) {
2106 const int no = numReal_, ng = numGhost_;
2108 const unsigned long long bit = 1ull << rankColor_;
2109 haloPackGhostCountsMask(P.aVel, P.aPos, ghostCM_, ghostSlot_, bit, no, ng);
2110 Kokkos::deep_copy(Kokkos::subview(ownedCM_, std::pair<std::size_t, std::size_t>(0, no)),
2111 PositionCountsMask{0ull, 0, 0});
2112 dev_.reverse(ghostCM_, ownedCM_);
2113 haloOwnerCountsMask(P.aVel, P.aPos, P.kPos, P.velMask, ownedCM_, bit, no);
2114 dev_.forward(ownedCM_, ghostCM_);
2115 haloUnpackCountsMask(P.kPos, P.velMask, ghostCM_, ghostSlot_, no, ng);
2116 buildMRows(P.kPos, posRows_, nPosRows_);
2120 haloPackGhostInt(P.aPos, ghostCnt_, ghostSlot_, no, ng);
2121 Kokkos::deep_copy(Kokkos::subview(ownedCnt_, std::pair<std::size_t, std::size_t>(0, no)), 0);
2122 dev_.reverse(ghostCnt_, ownedCnt_);
2123 haloOwnerCountK(P.aPos, P.kPos, ownedCnt_, no);
2124 dev_.forward(ownedCnt_, ghostCnt_);
2125 haloUnpackGhostInt(P.kPos, ghostCnt_, ghostSlot_, no, ng);
2126 buildMRows(P.kPos, posRows_, nPosRows_);
2130 void restoreOrphanBalance(Particles& P) {
2131 if (!velM_ || !orphanShares_)
2133 haloRestoreOrphanBalance(P.bodyOrphan, ownerSeedOrphan_, P.aVel, P.kVel, nVelRows_, velRows_);
2144 void mapVelocitySlots(Particles& P) {
2145 const int no = numReal_, ng = numGhost_;
2148 if (canonStale_ || ghostCanon_.extent(0) <
static_cast<std::size_t
>(ng)) {
2149 const int nr = numReceived_;
2151 Kokkos::create_mirror_view(Kokkos::subview(P.gid, std::pair<int, int>(no, no + nr)));
2152 Kokkos::deep_copy(hg, Kokkos::subview(P.gid, std::pair<int, int>(no, no + nr)));
2153 std::vector<std::pair<int, int>> key(
static_cast<std::size_t
>(nr));
2154 for (
int g = 0; g < nr; ++g)
2155 key[
static_cast<std::size_t
>(g)] = {hg(g), g};
2156 std::sort(key.begin(), key.end());
2157 std::vector<int> canon(
static_cast<std::size_t
>(ng));
2158 for (std::size_t p = 0; p < key.size();) {
2160 while (e < key.size() && key[e].first == key[p].first)
2162 const int c = no + key[p].second;
2163 for (std::size_t t = p; t < e; ++t)
2164 canon[
static_cast<std::size_t
>(key[t].second)] = c;
2167 for (
int g = nr; g < ng; ++g)
2168 canon[
static_cast<std::size_t
>(g)] = selfIdx_[
static_cast<std::size_t
>(g - nr)];
2169 ghostCanon_ = peclet::core::toDevice(canon,
"peclet::dem::halo::ghostCanon");
2170 canonStale_ =
false;
2172 haloMapVelocitySlots(P.realIndices, ghostCanon_, no, ng);
2184 void ensureRankColoring(
double band) {
2187 const int nb =
static_cast<int>(dec_.numBlocks());
2188 unsigned long long fp = 1469598103934665603ull;
2189 for (
int b = 0; b < nb; ++b) {
2190 const auto blk = dec_.block(
static_cast<std::size_t
>(b));
2191 for (
int d = 0; d < 3; ++d) {
2192 fp = (fp ^
static_cast<unsigned long long>(blk.origin[d])) * 1099511628211ull;
2193 fp = (fp ^
static_cast<unsigned long long>(blk.size[d])) * 1099511628211ull;
2196 if (band == colorBand_ && fp == colorFingerprint_)
2199 colorFingerprint_ = fp;
2200 const auto& gs = dec_.globalSize();
2201 std::vector<std::array<double, 6>> box(
static_cast<std::size_t
>(nb));
2202 for (
int b = 0; b < nb; ++b) {
2203 const auto blk = dec_.block(
static_cast<std::size_t
>(b));
2204 for (
int d = 0; d < 3; ++d) {
2205 box[b][d] = map_.origin[d] +
static_cast<double>(blk.origin[d]) * map_.cellSize[d];
2206 box[b][3 + d] = box[b][d] +
static_cast<double>(blk.size[d]) * map_.cellSize[d];
2209 const double lim2 = 4.0 * band * band;
2210 auto adjacent = [&](
int a,
int b) {
2212 for (
int d = 0; d < 3; ++d) {
2213 const double L =
static_cast<double>(gs[d]) * map_.cellSize[d];
2214 auto gap = [&](
double shift) {
2215 const double loB = box[b][d] + shift, hiB = box[b][3 + d] + shift;
2216 return std::max(0.0, std::max(box[a][d] - hiB, loB - box[a][3 + d]));
2218 double g = gap(0.0);
2219 if (map_.periodic[d])
2220 g = std::min(g, std::min(gap(L), gap(-L)));
2225 std::vector<int> col(
static_cast<std::size_t
>(nb), 0);
2227 for (
int r = 0; r < nb; ++r) {
2228 unsigned long long used = 0ull;
2229 for (
int q = 0; q < r; ++q)
2231 used |= 1ull << col[q];
2233 while (c < 64 && ((used >> c) & 1ull))
2236 throw std::runtime_error(
2237 "ParticleHalo: the rank graph needs more than 64 colours for rank-level X "
2238 "(docs/contact_solve_framework.md §1.4); the blocks are too small for the band");
2240 maxc = std::max(maxc, c);
2242 rankColor_ = col[
static_cast<std::size_t
>(rank_)];
2243 numRankColors_ = maxc + 1;
2247 void buildInterfaceRows(
const peclet::core::halo::ParticleHaloTopology<3>::FlatTopo& t,
int no) {
2248 std::vector<unsigned char> mark(
static_cast<std::size_t
>(std::max(no, 1)), 0);
2249 for (
const auto i : t.sendIdx)
2250 mark[static_cast<std::size_t>(i)] = 1;
2251 for (
const auto i : t.selfIdx)
2252 mark[static_cast<std::size_t>(i)] = 1;
2253 std::vector<int> rows;
2254 for (
int i = 0; i < no; ++i)
2255 if (mark[
static_cast<std::size_t
>(i)])
2257 nIface_ =
static_cast<int>(rows.size());
2260 iface_ = peclet::core::toDevice(rows,
"peclet::dem::halo::ifaceRows");
2261 ifaceMark_ = peclet::core::toDevice(mark,
"peclet::dem::halo::ifaceMark");
2267 void buildMRows(Vi k, Vi& rows,
int& nRows) {
2268 const int no = numReal_;
2269 if (rows.extent(0) <
static_cast<std::size_t
>(std::max(no, 1)))
2270 rows =
Vi(
"peclet::dem::halo::mRows",
static_cast<std::size_t
>(std::max(no, 1)));
2271 nRows = haloBuildMRows(k, ifaceMark_, rows, no);
2273 void ensureClampCounter(Particles& P) {
2274 if (P.orphanClampCount.data() ==
nullptr)
2275 P.orphanClampCount = Kokkos::View<int, CpMem>(
"peclet::dem::orphanClampCount");
2279 void markVelocityReconciled(Particles& P) {
2280 haloMarkOwnerVelocitySeeds(P.velPred, P.angVelPred, P.bodyOrphan, ownerSeedVel_,
2281 ownerSeedAngVel_, ownerSeedOrphan_, nVelRows_, velRows_);
2283 haloSetOrphanShares(P.bodyOrphan, P.kVel, numReal_ + numGhost_);
2284 markVelocityBaseline(P);
2288 void syncVelocitiesM(Particles& P,
bool rotation) {
2289 haloVelocityMPackZero(P.velPred, P.angVelPred, P.bodyOrphan, P.bodyOrphanVPeak, baseVel_,
2290 baseAngVel_, baseOrphan_, baseOrphanPeak_, P.aVel, ghostVelInc_,
2291 ghostSlot_, numReal_, numGhost_, ownedVelInc_, velRows_, nVelRows_);
2292 dev_.reverse(ghostVelInc_, ownedVelInc_);
2293 if (orphanShares_) {
2295 velocityMRound(P, ownedVelStateB_, ghostVelStateB_);
2297 velocityMRound(P, ownedVelStateBNR_, ghostVelStateBNR_);
2298 }
else if (rotation) {
2299 velocityMRound(P, ownedVelState_, ghostVelState_);
2301 velocityMRound(P, ownedF3_, ghostF3_);
2306 void velocityMRound(Particles& P, peclet::core::View<S> owned, peclet::core::View<S> ghost) {
2307 haloVelocityMApply(P.velPred, P.angVelPred, P.bodyOrphan, P.bodyOrphanVPeak, ownerSeedVel_,
2308 ownerSeedAngVel_, ownerSeedOrphan_, P.aVel, P.kVel, ownedVelInc_,
2309 P.orphanClampCount, P.maxConsensus, velRows_, nVelRows_, ifaceMark_, owned,
2311 dev_.forward(owned, ghost);
2312 haloVelocityMUnpack(P.velPred, P.angVelPred, P.bodyOrphan, P.bodyOrphanVPeak, ghost, baseVel_,
2313 baseAngVel_, baseOrphan_, baseOrphanPeak_, P.aVel, P.kVel, ghostVelInc_,
2314 P.maxConsensus, ghostSlot_, numReal_, numGhost_, orphanShares_);
2318 void positionMRound(Particles& P, peclet::core::View<S> owned, peclet::core::View<S> ghost) {
2319 haloPositionMApply(P.posPred, P.quatPred, ownerSeedPos_, P.aPos, P.kPos, ownedPosInc_,
2320 P.maxConsensus, posRows_, nPosRows_, ifaceMark_, owned);
2321 dev_.forward(owned, ghost);
2322 haloPositionMUnpack(P.posPred, P.quatPred, ghost, shiftDev_, basePos_, P.aPos, P.kPos,
2323 ghostPosInc_, P.maxConsensus, ghostSlot_, numReal_, numGhost_);
2330 void syncFrictionCounts(Particles& P) {
2333 haloPackGhostColumn(P.planeFriction, ghostVal_, ghostSlot_, numReal_, numGhost_);
2334 Kokkos::deep_copy(Kokkos::subview(ownedVal_, std::pair<std::size_t, std::size_t>(0, numReal_)),
2336 dev_.reverse(ghostVal_, ownedVal_);
2337 haloAddOwnedColumn(P.planeFriction, ownedVal_, numReal_);
2338 haloPackOwnedColumn(P.planeFriction, ownedVal_, numReal_);
2339 dev_.forward(ownedVal_, ghostVal_);
2340 haloUnpackGhostColumn(P.planeFriction, ghostVal_, ghostSlot_, numReal_, numGhost_);
2347 void syncContactCounts(Particles& P) {
2350 haloPackGhostCount(P.constraintCounts, ghostCnt_, ghostSlot_, numReal_, numGhost_);
2351 Kokkos::deep_copy(Kokkos::subview(ownedCnt_, std::pair<std::size_t, std::size_t>(0, numReal_)),
2353 dev_.reverse(ghostCnt_, ownedCnt_);
2354 haloAddPackOwnedCount(P.constraintCounts, ownedCnt_, numReal_);
2355 dev_.forward(ownedCnt_, ghostCnt_);
2356 haloUnpackGhostCount(P.constraintCounts, ghostCnt_, ghostSlot_, numReal_, numGhost_);
2359 void selfMapReals(Vi realIndices,
int no) {
2360 Kokkos::parallel_for(
2361 "peclet::dem::halo::selfMapReals", Kokkos::RangePolicy<CpExec>(0, no),
2362 KOKKOS_LAMBDA(
int i) { realIndices(i) = i; });
2366 void allocBuffers(
int no,
int ng) {
2369 ownedF3_ = peclet::core::View<F3>(
"peclet::dem::halo::ownedF3", no);
2370 ghostF3_ = peclet::core::View<F3>(
"peclet::dem::halo::ghostF3", ng);
2371 ownedF4_ = peclet::core::View<F4>(
"peclet::dem::halo::ownedF4", no);
2372 ghostF4_ = peclet::core::View<F4>(
"peclet::dem::halo::ghostF4", ng);
2373 ownedPack_ = peclet::core::View<MpiGatherPack>(
"peclet::dem::halo::ownedPack", no);
2374 ghostPack_ = peclet::core::View<MpiGatherPack>(
"peclet::dem::halo::ghostPack", ng);
2377 const std::size_t g1 =
static_cast<std::size_t
>(std::max(ng, 1)),
2378 o1 =
static_cast<std::size_t
>(std::max(no, 1));
2379 if (baseVel_.extent(0) < g1) {
2380 baseVel_ =
V3(
"peclet::dem::halo::baseVel", g1);
2381 baseAngVel_ =
V3(
"peclet::dem::halo::baseAngVel", g1);
2382 basePos_ =
V3(
"peclet::dem::halo::basePos", g1);
2383 baseOrphan_ =
Vf(
"peclet::dem::halo::baseOrphan", g1);
2384 baseOrphanPeak_ =
Vf(
"peclet::dem::halo::baseOrphanPeak", g1);
2385 ghostVelInc_ = peclet::core::View<VelocityIncrement>(
"peclet::dem::halo::ghostVelInc", g1);
2386 ghostPosInc_ = peclet::core::View<PositionIncrement>(
"peclet::dem::halo::ghostPosInc", g1);
2387 ghostVal_ = peclet::core::View<float>(
"peclet::dem::halo::ghostVal", g1);
2388 ghostCnt_ = peclet::core::View<int>(
"peclet::dem::halo::ghostCnt", g1);
2389 ghostCM_ = peclet::core::View<PositionCountsMask>(
"peclet::dem::halo::ghostCM", g1);
2390 ghostVelState_ = peclet::core::View<VelocityState>(
"peclet::dem::halo::ghostVelState", g1);
2391 ghostPosState_ = peclet::core::View<PositionState>(
"peclet::dem::halo::ghostPosState", g1);
2392 ghostOpenInc_ = peclet::core::View<OpeningIncrement>(
"peclet::dem::halo::ghostOpenInc", g1);
2393 ghostOpenState_ = peclet::core::View<OpeningState>(
"peclet::dem::halo::ghostOpenState", g1);
2395 peclet::core::View<OpeningStateNoRot>(
"peclet::dem::halo::ghostOpenStateNR", g1);
2396 ghostVelStateB_ = peclet::core::View<VelocityStateB>(
"peclet::dem::halo::ghostVelStateB", g1);
2398 peclet::core::View<VelocityStateBNoRot>(
"peclet::dem::halo::ghostVelStateBNR", g1);
2400 if (ownedVelInc_.extent(0) < o1) {
2401 ownedVelInc_ = peclet::core::View<VelocityIncrement>(
"peclet::dem::halo::ownedVelInc", o1);
2402 ownedPosInc_ = peclet::core::View<PositionIncrement>(
"peclet::dem::halo::ownedPosInc", o1);
2403 ownedVal_ = peclet::core::View<float>(
"peclet::dem::halo::ownedVal", o1);
2404 ownedCnt_ = peclet::core::View<int>(
"peclet::dem::halo::ownedCnt", o1);
2405 ownedCM_ = peclet::core::View<PositionCountsMask>(
"peclet::dem::halo::ownedCM", o1);
2406 ownedVelState_ = peclet::core::View<VelocityState>(
"peclet::dem::halo::ownedVelState", o1);
2407 ownedPosState_ = peclet::core::View<PositionState>(
"peclet::dem::halo::ownedPosState", o1);
2408 ownedOpenInc_ = peclet::core::View<OpeningIncrement>(
"peclet::dem::halo::ownedOpenInc", o1);
2409 ownedOpenState_ = peclet::core::View<OpeningState>(
"peclet::dem::halo::ownedOpenState", o1);
2411 peclet::core::View<OpeningStateNoRot>(
"peclet::dem::halo::ownedOpenStateNR", o1);
2412 ownedVelStateB_ = peclet::core::View<VelocityStateB>(
"peclet::dem::halo::ownedVelStateB", o1);
2414 peclet::core::View<VelocityStateBNoRot>(
"peclet::dem::halo::ownedVelStateBNR", o1);
2415 ownerSeedVel_ =
V3(
"peclet::dem::halo::ownerSeedVel", o1);
2416 ownerSeedAngVel_ =
V3(
"peclet::dem::halo::ownerSeedAngVel", o1);
2417 ownerSeedOrphan_ =
Vf(
"peclet::dem::halo::ownerSeedOrphan", o1);
2418 ownerSeedPos_ =
V3(
"peclet::dem::halo::ownerSeedPos", o1);
2427 void buildOwnershipMaps(
const peclet::core::halo::ParticleHaloTopology<3>::FlatTopo& t,
int no,
2428 int ng,
const std::vector<int>& hSlot) {
2429 const std::size_t g1 =
static_cast<std::size_t
>(std::max(ng, 1));
2430 std::vector<int> src(g1, rank_);
2431 for (std::size_t k = 0; k < t.recvRanks.size(); ++k)
2432 for (
int j = 0; j < t.recvCounts[k]; ++j)
2433 src[
static_cast<std::size_t
>(hSlot[
static_cast<std::size_t
>(t.recvOffsets[k]) + j])] =
2435 std::vector<int> off(
static_cast<std::size_t
>(no) + 1, 0);
2436 for (
const auto i : t.sendIdx)
2437 ++off[static_cast<std::size_t>(i) + 1];
2438 for (
int i = 0; i < no; ++i)
2439 off[
static_cast<std::size_t
>(i) + 1] += off[
static_cast<std::size_t
>(i)];
2441 const auto& gsz = dec_.globalSize();
2442 auto imageOf = [&](
const peclet::core::Vec<3>& sh) {
2443 int c[3] = {0, 0, 0};
2444 for (
int d = 0; d < 3; ++d)
2445 if (map_.periodic[d]) {
2446 const double L =
static_cast<double>(gsz[d]) * map_.cellSize[d];
2447 c[d] =
static_cast<int>(std::lround(sh[d] / L));
2449 return imagePack(c[0], c[1], c[2]);
2451 std::vector<int> gimg(g1, imagePack(0, 0, 0));
2452 for (std::size_t j = 0; j < t.shift.size(); ++j)
2453 gimg[
static_cast<std::size_t
>(hSlot[j])] = imageOf(t.shift[j]);
2454 std::vector<int> ranks(std::max<std::size_t>(t.sendIdx.size(), 1), -1);
2455 std::vector<int> imgs(std::max<std::size_t>(t.sendIdx.size(), 1), imagePack(0, 0, 0));
2456 std::vector<int> cur(off.begin(), off.end() - 1);
2457 for (std::size_t k = 0; k < t.sendRanks.size(); ++k)
2458 for (
int j = t.sendOffsets[k]; j < t.sendOffsets[k + 1]; ++j) {
2459 const std::size_t at =
2460 static_cast<std::size_t
>(cur[
static_cast<std::size_t
>(t.sendIdx[j])]++);
2461 ranks[at] = t.sendRanks[k];
2462 imgs[at] = imageOf(t.sendShift[
static_cast<std::size_t
>(j)]);
2465 std::vector<int> soff(
static_cast<std::size_t
>(no) + 1, 0);
2466 for (
const auto i : t.selfIdx)
2467 ++soff[static_cast<std::size_t>(i) + 1];
2468 for (
int i = 0; i < no; ++i)
2469 soff[
static_cast<std::size_t
>(i) + 1] += soff[
static_cast<std::size_t
>(i)];
2470 std::vector<int> simg(std::max<std::size_t>(t.selfIdx.size(), 1), imagePack(0, 0, 0));
2472 std::vector<int> scur(soff.begin(), soff.end() - 1);
2473 for (std::size_t j = 0; j < t.selfIdx.size(); ++j)
2474 simg[
static_cast<std::size_t
>(scur[
static_cast<std::size_t
>(t.selfIdx[j])]++)] =
2475 imageOf(t.shift[
static_cast<std::size_t
>(t.numReceived) + j]);
2477 selfOffsets_ = peclet::core::toDevice(soff,
"peclet::dem::halo::selfOffsets");
2478 selfImage_ = peclet::core::toDevice(simg,
"peclet::dem::halo::selfImage");
2479 ghostSource_ = peclet::core::toDevice(src,
"peclet::dem::halo::ghostSource");
2480 ghostImage_ = peclet::core::toDevice(gimg,
"peclet::dem::halo::ghostImage");
2481 copyOffsets_ = peclet::core::toDevice(off,
"peclet::dem::halo::copyOffsets");
2482 copyRanks_ = peclet::core::toDevice(ranks,
"peclet::dem::halo::copyRanks");
2483 copyImage_ = peclet::core::toDevice(imgs,
"peclet::dem::halo::copyImage");
2490 float maxOwnedDisplacement(
const V3& pos,
int no)
const {
2496 if (refPos_.extent(0) <
static_cast<std::size_t
>(no))
2499 V3 p = pos, r = refPos_;
2500 Kokkos::parallel_reduce(
2501 "peclet::dem::halo::maxdisp", Kokkos::RangePolicy<CpExec>(0, no),
2502 KOKKOS_LAMBDA(
const int i,
float& m) {
2503 const float dx = p(i, 0) - r(i, 0), dy = p(i, 1) - r(i, 1), dz = p(i, 2) - r(i, 2);
2504 const float d = Kokkos::sqrt(dx * dx + dy * dy + dz * dz);
2508 Kokkos::Max<float>(md));
2516 std::vector<int> uploadGhostSlots(
const peclet::core::halo::ParticleHaloTopology<3>::FlatTopo& t,
2518 std::vector<std::size_t> blocks(t.recvRanks.size());
2519 for (std::size_t k = 0; k < blocks.size(); ++k)
2521 std::sort(blocks.begin(), blocks.end(),
2522 [&](std::size_t a, std::size_t b) { return t.recvRanks[a] < t.recvRanks[b]; });
2523 if (ghostSlot_.extent(0) <
static_cast<std::size_t
>(std::max(ng, 1)))
2524 ghostSlot_ =
Vi(
"peclet::dem::halo::ghostSlot",
static_cast<std::size_t
>(std::max(ng, 1)));
2525 auto h = Kokkos::create_mirror_view(ghostSlot_);
2527 for (std::size_t k : blocks)
2528 for (int j = 0; j < t.recvCounts[k]; ++j)
2529 h(
static_cast<int>(t.recvOffsets[k]) + j) = next++;
2530 for (
int g = next; g < ng; ++g)
2532 Kokkos::deep_copy(ghostSlot_, h);
2533 std::vector<int> hs(
static_cast<std::size_t
>(std::max(ng, 0)));
2534 for (
int g = 0; g < ng; ++g)
2535 hs[
static_cast<std::size_t
>(g)] = h(g);
2541 void orderArrivalsBySource(std::vector<peclet::core::Vec<3>>& pos, std::vector<char>& payload,
2543 const std::size_t nArr = mig_.lastReceived();
2544 if (nArr < 2 || nArr > newN)
2546 const std::size_t first = newN - nArr;
2547 std::vector<int> src(nArr);
2548 for (std::size_t k = 0; k < nArr; ++k)
2549 std::memcpy(&src[k],
2550 &payload[(first + k) *
sizeof(MigratePack) + offsetof(MigratePack, srcRank)],
2552 if (std::is_sorted(src.begin(), src.end()))
2554 std::vector<std::size_t> perm(nArr);
2555 for (std::size_t k = 0; k < nArr; ++k)
2557 std::stable_sort(perm.begin(), perm.end(),
2558 [&](std::size_t a, std::size_t b) { return src[a] < src[b]; });
2559 std::vector<peclet::core::Vec<3>> p2(nArr);
2560 std::vector<char> b2(nArr *
sizeof(MigratePack));
2561 for (std::size_t k = 0; k < nArr; ++k) {
2562 p2[k] = pos[first + perm[k]];
2563 std::memcpy(&b2[k *
sizeof(MigratePack)], &payload[(first + perm[k]) *
sizeof(MigratePack)],
2564 sizeof(MigratePack));
2566 std::copy(p2.begin(), p2.end(), pos.begin() +
static_cast<std::ptrdiff_t
>(first));
2567 std::memcpy(&payload[first *
sizeof(MigratePack)], b2.data(), b2.size());
2570 void uploadShift(
const peclet::core::halo::ParticleHaloTopology<3>::FlatTopo& t) {
2571 std::vector<F3> hs(t.shift.size());
2572 for (std::size_t i = 0; i < t.shift.size(); ++i)
2573 hs[i] = F3{
static_cast<float>(t.shift[i][0]),
static_cast<float>(t.shift[i][1]),
2574 static_cast<float>(t.shift[i][2])};
2575 shiftDev_ = peclet::core::toDevice(hs,
"peclet::dem::halo::shift");
2578 bool inited_ =
false;
2579 int rank_ = 0, numReal_ = 0, numGhost_ = 0;
2583 float verletSkin_ = 0.0f;
2584 bool haveTopo_ =
false;
2585 int lastNumReal_ = -1;
2586 double lastBand_ = -1.0;
2587 long nRebuild_ = 0, nGather_ = 0;
2589 MPI_Comm comm_ = MPI_COMM_NULL;
2590 peclet::core::decomp::BlockDecomposer<3> dec_;
2591 peclet::core::halo::DomainMap<3> map_;
2592 peclet::core::halo::ParticleMigrator<3> mig_;
2593 peclet::core::halo::ParticleHaloTopology<3> halo_;
2594 peclet::core::halo::ParticleHalo<3> dev_;
2595 peclet::core::View<F3> ownedF3_, ghostF3_, shiftDev_;
2597 peclet::core::View<F4> ownedF4_, ghostF4_;
2598 peclet::core::View<MpiGatherPack> ownedPack_, ghostPack_;
2601 V3 baseVel_, baseAngVel_, basePos_;
2602 Vf baseOrphan_, baseOrphanPeak_;
2603 peclet::core::View<VelocityIncrement> ghostVelInc_, ownedVelInc_;
2604 peclet::core::View<PositionIncrement> ghostPosInc_, ownedPosInc_;
2605 peclet::core::View<float> ghostVal_, ownedVal_;
2606 peclet::core::View<int> ghostCnt_, ownedCnt_;
2607 peclet::core::View<PositionCountsMask> ghostCM_, ownedCM_;
2610 int rankColor_ = 0, numRankColors_ = 1;
2611 double colorBand_ = -1.0;
2612 unsigned long long colorFingerprint_ = 0ull;
2613 peclet::core::View<VelocityState> ghostVelState_, ownedVelState_;
2614 peclet::core::View<PositionState> ghostPosState_, ownedPosState_;
2616 Kokkos::View<int*, CpMem> ghostSource_, copyOffsets_, copyRanks_, ghostImage_, copyImage_,
2617 selfOffsets_, selfImage_;
2621 peclet::core::View<OpeningIncrement> ghostOpenInc_, ownedOpenInc_;
2622 peclet::core::View<OpeningState> ghostOpenState_, ownedOpenState_;
2623 peclet::core::View<OpeningStateNoRot> ghostOpenStateNR_, ownedOpenStateNR_;
2624 peclet::core::View<VelocityStateB> ghostVelStateB_, ownedVelStateB_;
2625 peclet::core::View<VelocityStateBNoRot> ghostVelStateBNR_, ownedVelStateBNR_;
2626 V3 ownerSeedVel_, ownerSeedAngVel_, ownerSeedPos_;
2627 Vf ownerSeedOrphan_;
2628 bool velM_ =
false, posM_ =
false, orphanShares_ =
false;
2629 bool canonStale_ =
true;
2630 int numReceived_ = 0;
2632 Kokkos::View<unsigned char*, CpMem> ifaceMark_;
2634 Vi velRows_, posRows_;
2635 int nVelRows_ = 0, nPosRows_ = 0;
2636 std::vector<int> selfIdx_;
2637 Kokkos::View<int*, CpMem> ghostCanon_;
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
CpExec::memory_space CpMem
Kokkos::View< float *[4], CpMem > V4
dem — portable (Kokkos) particle SoA container: the storage the dem flip pivots on.
int MPI_Allreduce(const void *a, void *b, int c, MPI_Datatype d, MPI_Op e, MPI_Comm f)