16#ifndef DEM_CONTACT_PREPROCESSING_HPP
17#define DEM_CONTACT_PREPROCESSING_HPP
21#include <Kokkos_Core.hpp>
22#include <Kokkos_Sort.hpp>
29using CpExec = Kokkos::DefaultExecutionSpace;
30using CpMem = CpExec::memory_space;
64 return -1 - wallIndex;
92KOKKOS_INLINE_FUNCTION
unsigned long long pairKeyFromGids(
unsigned a,
unsigned b) {
93 const unsigned hi = a < b ? a : b, lo = a < b ? b : a;
94 return (
static_cast<unsigned long long>(hi) << 32) | lo;
100template <
class KeysView>
101KOKKOS_INLINE_FUNCTION
int lowerBoundKey(
const KeysView& keys,
int count,
unsigned long long key) {
102 int lo = 0, hi = count;
104 const int mid = (lo + hi) >> 1;
119 Kokkos::View<const int*, CpMem> realIdx) {
120 const unsigned a =
static_cast<unsigned>(realIdx(m.
bodyA));
121 const unsigned b = (m.
bodyB >= 0) ?
static_cast<unsigned>(realIdx(m.
bodyB)) : 0xFFFFFFFFu;
129 int numManifolds, Kokkos::View<const int*, CpMem> realIdx,
130 Kokkos::View<const int*, CpMem> keyIdx,
131 Kokkos::View<const unsigned long long*, CpMem> prevKeys,
133 Kokkos::View<unsigned long long*, CpMem> outKeys,
134 Kokkos::View<unsigned char*, CpMem> outFlags) {
137 Kokkos::parallel_for(
138 "peclet::dem::mark_persistent", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
139 KOKKOS_LAMBDA(
int idx) {
142 outKeys(idx) = ~0ull;
146 const unsigned long long k =
pairKeyOf(m, keyIdx);
149 outFlags(idx) = (lo < prevCount && prevKeys(lo) == k) ? 1 : 0;
159 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
160 Kokkos::View<const int*, CpMem> realIdx, Kokkos::View<const int*, CpMem> keyIdx,
161 Kokkos::View<const unsigned long long*, CpMem> prevKeys,
162 Kokkos::View<const float*, CpMem> prevLambda, Kokkos::View<
const float* [3],
CpMem> prevLambdaT,
163 Kokkos::View<const float*, CpMem> prevPosImpulse,
164 Kokkos::View<const float*, CpMem> prevRestBank, Kokkos::View<const float*, CpMem> prevRestVPeak,
165 int prevCount, Kokkos::View<unsigned long long*, CpMem> outKeys,
166 Kokkos::View<float*, CpMem> outWarm, Kokkos::View<
float* [3],
CpMem> outWarmT,
167 Kokkos::View<float*, CpMem> outPosImpulse, Kokkos::View<float*, CpMem> outRestBank,
168 Kokkos::View<float*, CpMem> outRestVPeak, Kokkos::View<unsigned char*, CpMem> outMatched = {},
169 bool dedupTwins =
true) {
171 Kokkos::parallel_for(
172 "peclet::dem::gather_warm", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
173 KOKKOS_LAMBDA(
int idx) {
174 const ManifoldC m = manifolds(idx);
176 if (dedupTwins && m.num_points > 0 && m.bodyB >= 0 && realIdx(m.bodyA) > realIdx(m.bodyB))
178 if (m.num_points <= 0 || dup) {
179 outKeys(idx) = ~0ull;
181 outWarmT(idx, 0) = outWarmT(idx, 1) = outWarmT(idx, 2) = 0.0f;
182 outPosImpulse(idx) = 0.0f;
183 outRestBank(idx) = 0.0f;
184 outRestVPeak(idx) = 0.0f;
187 const unsigned long long k =
pairKeyOf(m, keyIdx);
190 const bool hit = (lo < prevCount && prevKeys(lo) == k);
191 if (hit && outMatched.extent(0) > 0)
193 outWarm(idx) = hit ? prevLambda(lo) : 0.0f;
194 outWarmT(idx, 0) = hit ? prevLambdaT(lo, 0) : 0.0f;
195 outWarmT(idx, 1) = hit ? prevLambdaT(lo, 1) : 0.0f;
196 outWarmT(idx, 2) = hit ? prevLambdaT(lo, 2) : 0.0f;
197 outPosImpulse(idx) = hit ? prevPosImpulse(lo) : 0.0f;
198 outRestBank(idx) = hit ? prevRestBank(lo) : 0.0f;
199 outRestVPeak(idx) = hit ? prevRestVPeak(lo) : 0.0f;
206 Kokkos::View<const unsigned long long*, CpMem> keys, Kokkos::View<const float*, CpMem> lambda,
207 Kokkos::View<
const float* [3],
CpMem> lambdaT, Kokkos::View<const float*, CpMem> restBank,
208 Kokkos::View<const float*, CpMem> restVPeak, Kokkos::View<unsigned long long*, CpMem> prevKeys,
209 Kokkos::View<float*, CpMem> prevLambda, Kokkos::View<
float* [3],
CpMem> prevLambdaT,
210 Kokkos::View<float*, CpMem> prevRestBank, Kokkos::View<float*, CpMem> prevRestVPeak,
211 Kokkos::View<int*, CpMem> perm,
215 Kokkos::View<const int*, CpMem> color = {}, Kokkos::View<int*, CpMem> prevColor = {}) {
216 if (numManifolds <= 0)
219 const int n = numManifolds;
220 const auto rng = Kokkos::pair<int, int>(0, n);
221 auto kd = Kokkos::subview(prevKeys, rng);
222 Kokkos::deep_copy(space, kd, Kokkos::subview(keys, rng));
223 Kokkos::parallel_for(
224 "peclet::dem::commit_iota", Kokkos::RangePolicy<CpExec>(space, 0, n),
225 KOKKOS_LAMBDA(
int i) { perm(i) = i; });
227 auto pd = Kokkos::subview(perm, rng);
228 Kokkos::Experimental::sort_by_key(space, kd, pd);
230 Kokkos::View<float*, CpMem> pl = prevLambda;
231 Kokkos::View<float* [3], CpMem> plt = prevLambdaT;
232 Kokkos::View<float*, CpMem> prb = prevRestBank;
233 Kokkos::View<float*, CpMem> prv = prevRestVPeak;
234 const bool carryColor = color.extent(0) > 0 && prevColor.extent(0) > 0;
235 Kokkos::View<const int*, CpMem> col = color;
236 Kokkos::View<int*, CpMem> pcol = prevColor;
237 Kokkos::parallel_for(
238 "peclet::dem::commit_gather", Kokkos::RangePolicy<CpExec>(space, 0, n), KOKKOS_LAMBDA(
int i) {
239 const int j = perm(i);
241 plt(i, 0) = lambdaT(j, 0);
242 plt(i, 1) = lambdaT(j, 1);
243 plt(i, 2) = lambdaT(j, 2);
244 prb(i) = restBank(j);
245 prv(i) = restVPeak(j);
254 Kokkos::View<unsigned long long*, CpMem> prevKeys,
256 if (numManifolds <= 0)
259 auto src = Kokkos::subview(keys, Kokkos::pair<int, int>(0, numManifolds));
260 auto dst = Kokkos::subview(prevKeys, Kokkos::pair<int, int>(0, numManifolds));
261 Kokkos::deep_copy(space, dst, src);
262 Kokkos::sort(space, dst);
272 int numManifolds, Kokkos::View<const int*, CpMem> realIdx,
273 Kokkos::View<
const float* [3],
CpMem> posPred,
F3 gHat,
274 Kokkos::View<unsigned char*, CpMem> grounded,
int numReal,
275 int sweeps,
int decay) {
277 Kokkos::parallel_for(
278 "peclet::dem::grounded_decay", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
279 KOKKOS_LAMBDA(
int i) {
280 const int g =
static_cast<int>(grounded(i)) - decay;
281 grounded(i) =
static_cast<unsigned char>(g > 0 ? g : 0);
283 for (
int s = 0; s < sweeps; ++s) {
284 Kokkos::parallel_for(
285 "peclet::dem::grounded_sweep", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
286 KOKKOS_LAMBDA(
int idx) {
290 const int realA = realIdx(m.
bodyA);
292 Kokkos::atomic_max(&grounded(realA),
static_cast<unsigned char>(255));
295 const int realB = realIdx(m.
bodyB);
297 const float up = -(dx.
x * gHat.
x + dx.
y * gHat.
y + dx.
z * gHat.
z);
298 const float thr = 0.3f * Kokkos::sqrt(
dot3(dx, dx));
300 const int lvl =
static_cast<int>(grounded(realB)) - 1;
302 Kokkos::atomic_max(&grounded(realA),
static_cast<unsigned char>(lvl));
303 }
else if (up < -thr) {
304 const int lvl =
static_cast<int>(grounded(realA)) - 1;
306 Kokkos::atomic_max(&grounded(realB),
static_cast<unsigned char>(lvl));
321 int numManifolds, Kokkos::View<const int*, CpMem> realIdx,
322 Kokkos::View<
const float* [3],
CpMem> posPred,
F3 gHat,
323 Kokkos::View<int*, CpMem> heights,
int numReal) {
325 Kokkos::parallel_for(
326 "peclet::dem::height_init", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
327 KOKKOS_LAMBDA(
int i) { heights(i) =
kLevelInf; });
328 const int maxSweeps = 1024;
329 for (
int s = 0; s < maxSweeps; ++s) {
331 Kokkos::parallel_reduce(
332 "peclet::dem::height_sweep", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
333 KOKKOS_LAMBDA(
int idx,
int& acc) {
337 const int realA = realIdx(m.
bodyA);
339 if (Kokkos::atomic_fetch_min(&heights(realA), 0) > 0)
343 const int realB = realIdx(m.
bodyB);
345 const float up = -(dx.
x * gHat.
x + dx.
y * gHat.
y + dx.
z * gHat.
z);
346 const float thr = 0.3f * Kokkos::sqrt(
dot3(dx, dx));
348 const int cand = heights(realB);
349 if (cand <
kLevelInf && Kokkos::atomic_fetch_min(&heights(realA), cand + 1) > cand + 1)
351 }
else if (up < -thr) {
352 const int cand = heights(realA);
353 if (cand <
kLevelInf && Kokkos::atomic_fetch_min(&heights(realB), cand + 1) > cand + 1)
372 Kokkos::View<int*, CpMem> perm,
373 Kokkos::View<int*, CpMem> cursor,
374 std::vector<int>& offs) {
375 offs.assign(
static_cast<std::size_t
>(numColors) + 1, 0);
376 if (n <= 0 || numColors <= 0)
379 auto cnt = Kokkos::subview(cursor, Kokkos::pair<int, int>(0, numColors));
380 Kokkos::deep_copy(space, cnt, 0);
381 Kokkos::parallel_for(
382 "peclet::dem::bucket_hist", Kokkos::RangePolicy<CpExec>(space, 0, n), KOKKOS_LAMBDA(
int i) {
383 const int c = colorOf(i);
385 Kokkos::atomic_add(&cursor(c), 1);
387 auto hCnt = Kokkos::create_mirror_view(cnt);
388 Kokkos::deep_copy(space, hCnt, cnt);
390 for (
int c = 0; c < numColors; ++c)
391 offs[
static_cast<std::size_t
>(c) + 1] = offs[
static_cast<std::size_t
>(c)] + hCnt(c);
392 for (
int c = 0; c < numColors; ++c)
393 hCnt(c) = offs[
static_cast<std::size_t
>(c)];
394 Kokkos::deep_copy(space, cnt, hCnt);
395 Kokkos::parallel_for(
396 "peclet::dem::bucket_scatter", Kokkos::RangePolicy<CpExec>(space, 0, n),
397 KOKKOS_LAMBDA(
int i) {
398 const int c = colorOf(i);
400 perm(Kokkos::atomic_fetch_add(&cursor(c), 1)) = i;
407KOKKOS_INLINE_FUNCTION
long long colorKey(
int idx) {
408 unsigned int z =
static_cast<unsigned>(idx) + 0x9e3779b9u;
409 z = (z ^ (z >> 16)) * 0x21f0aaadu;
410 z = (z ^ (z >> 15)) * 0x735a2d97u;
413 return (
static_cast<long long>(z & 0x7fffffffu) << 32) |
static_cast<unsigned>(idx);
420 return (
static_cast<std::uint64_t
>(
static_cast<unsigned>(idA)) << 32) | 0xFFFFFFFFu;
421 const unsigned u = (idA < idB) ? idA : idB;
422 const unsigned v = (idA < idB) ? idB : idA;
423 return (
static_cast<std::uint64_t
>(u) << 32) | v;
445 Kokkos::View<const int*, CpMem>
a,
b;
446 Kokkos::View<const unsigned char*, CpMem>
split;
448 KOKKOS_INLINE_FUNCTION
int slotA(
int e,
int def)
const {
449 if (
a.extent(0) > 0) {
456 KOKKOS_INLINE_FUNCTION
int slotB(
int e,
int def)
const {
457 if (
b.extent(0) > 0) {
465 KOKKOS_INLINE_FUNCTION
bool relax(
int sA,
int sB)
const {
466 return split.extent(0) > 0 && (
split(sA) != 0 || (sB >= 0 &&
split(sB) != 0));
472KOKKOS_INLINE_FUNCTION
void decodeKey(std::uint64_t key,
int& bodyA,
int& bodyB) {
473 const unsigned v =
static_cast<unsigned>(key & 0xFFFFFFFFu);
474 bodyA =
static_cast<int>((key >> 32) & 0x7FFFFFFFu);
475 bodyB = (v == 0xFFFFFFFFu) ? -1 :
static_cast<int>(v);
488 const bool flip = (idB >= 0 && idB < idA);
492 F4 n_aligned = flip ? n_vec :
F4{-n_vec.
x, -n_vec.
y, -n_vec.
z, 0.0f};
496 const F4 rA_mid{c.
rA.
x - shift.x, c.
rA.
y - shift.y, c.
rA.
z - shift.z, 0.0f};
497 const F4 rB_mid{c.
rB.
x + shift.x, c.
rB.
y + shift.y, c.
rB.
z + shift.z, 0.0f};
499 const F4 r_can = flip ? rB_mid : rA_mid;
500 const F4 r_other = flip ? rA_mid : rB_mid;
502 const F4 tau_can =
cross3(r_can, n_aligned);
503 const F4 tau_other =
cross3(r_other,
F4{-n_aligned.
x, -n_aligned.
y, -n_aligned.
z, 0.0f});
505 m.normal_sum =
F4{n_aligned.
x, n_aligned.
y, n_aligned.
z, 0.0f};
506 m.torque_armA_sum =
F4{tau_can.
x, tau_can.
y, tau_can.
z, 0.0f};
507 m.torque_armB_sum =
F4{tau_other.
x, tau_other.
y, tau_other.
z, 0.0f};
508 m.rA_sum =
F4{r_can.
x, r_can.
y, r_can.
z, 0.0f};
509 m.rB_sum =
F4{r_other.
x, r_other.
y, r_other.
z, 0.0f};
529 Kokkos::View<int*, CpMem> before(
530 Kokkos::view_alloc(space,
"peclet::dem::cp::partBefore", Kokkos::WithoutInitializing), n);
531 Kokkos::View<ContactC*, CpMem> ct = contacts;
533 Kokkos::parallel_scan(
534 "peclet::dem::cp::partScan", Kokkos::RangePolicy<CpExec>(space, 0, n),
535 KOKKOS_LAMBDA(
int i,
int& run,
const bool final) {
536 const int f = owns(ct(i)) ? 1 : 0;
542 Kokkos::View<ContactC*, CpMem> part(
543 Kokkos::view_alloc(space,
"peclet::dem::cp::part", Kokkos::WithoutInitializing), n);
544 const int no = ncOwned;
545 Kokkos::parallel_for(
546 "peclet::dem::cp::partScatter", Kokkos::RangePolicy<CpExec>(space, 0, n),
547 KOKKOS_LAMBDA(
int i) {
548 const bool f = owns(ct(i));
549 part(f ? before(i) : no + (i - before(i))) = ct(i);
551 Kokkos::deep_copy(space,
552 Kokkos::subview(contacts, std::pair<std::size_t, std::size_t>(
553 0,
static_cast<std::size_t
>(n))),
568 Kokkos::View<ManifoldC*, CpMem> outManifolds,
569 Kokkos::View<int, CpMem> outCount,
570 Kokkos::View<int*, CpMem> contactSlot = {},
571 int numOwnedContacts = -1,
572 int* numOwnedManifolds =
nullptr) {
575 Kokkos::deep_copy(space, outCount, 0);
576 if (numOwnedManifolds)
577 *numOwnedManifolds = 0;
582 Kokkos::View<std::uint64_t*, CpMem> keys(
583 Kokkos::view_alloc(space,
"peclet::dem::cp::keys", Kokkos::WithoutInitializing), n);
584 Kokkos::View<int*, CpMem> perm(
585 Kokkos::view_alloc(space,
"peclet::dem::cp::perm", Kokkos::WithoutInitializing), n);
586 const int nOwnedC = numOwnedContacts;
587 Kokkos::parallel_for(
588 "peclet::dem::cp::key", Kokkos::RangePolicy<CpExec>(space, 0, n), KOKKOS_LAMBDA(
int i) {
589 keys(i) =
pairKey(contacts(i)) |
595 Kokkos::Experimental::sort_by_key(space, keys, perm);
598 Kokkos::View<int*, CpMem> segId(
599 Kokkos::view_alloc(space,
"peclet::dem::cp::segId", Kokkos::WithoutInitializing), n);
601 Kokkos::parallel_scan(
602 "peclet::dem::cp::segscan", Kokkos::RangePolicy<CpExec>(space, 0, n),
603 KOKKOS_LAMBDA(
int p,
int& run,
const bool final) {
604 const bool isNew = (p == 0) || (keys(p) != keys(p - 1));
611 if (numOwnedManifolds) {
612 Kokkos::View<std::uint64_t*, CpMem> kk = keys;
614 Kokkos::parallel_reduce(
615 "peclet::dem::cp::ownedSegs", Kokkos::RangePolicy<CpExec>(space, 0, n),
616 KOKKOS_LAMBDA(
int p,
int& cnt) {
617 const bool leader = (p == 0) || (kk(p) != kk(p - 1));
622 *numOwnedManifolds = nOwnedM;
626 Kokkos::View<std::uint64_t*, CpMem> k = keys;
627 Kokkos::View<int*, CpMem> sid = segId;
628 Kokkos::View<ManifoldC*, CpMem> out = outManifolds;
629 Kokkos::parallel_for(
630 "peclet::dem::cp::init", Kokkos::RangePolicy<CpExec>(space, 0, n), KOKKOS_LAMBDA(
int p) {
631 const bool leader = (p == 0) || (k(p) != k(p - 1));
640 Kokkos::View<const ContactC*, CpMem> ct = contacts;
641 Kokkos::View<int*, CpMem> pm = perm;
642 Kokkos::parallel_for(
643 "peclet::dem::cp::accum", Kokkos::RangePolicy<CpExec>(space, 0, n), KOKKOS_LAMBDA(
int p) {
645 const int s = sid(p);
646 Kokkos::atomic_add(&out(s).num_points, m.num_points);
647 Kokkos::atomic_add(&out(s).normal_sum.x, m.normal_sum.x);
648 Kokkos::atomic_add(&out(s).normal_sum.y, m.normal_sum.y);
649 Kokkos::atomic_add(&out(s).normal_sum.z, m.normal_sum.z);
650 Kokkos::atomic_add(&out(s).torque_armA_sum.x, m.torque_armA_sum.x);
651 Kokkos::atomic_add(&out(s).torque_armA_sum.y, m.torque_armA_sum.y);
652 Kokkos::atomic_add(&out(s).torque_armA_sum.z, m.torque_armA_sum.z);
653 Kokkos::atomic_add(&out(s).torque_armB_sum.x, m.torque_armB_sum.x);
654 Kokkos::atomic_add(&out(s).torque_armB_sum.y, m.torque_armB_sum.y);
655 Kokkos::atomic_add(&out(s).torque_armB_sum.z, m.torque_armB_sum.z);
656 Kokkos::atomic_add(&out(s).rA_sum.x, m.rA_sum.x);
657 Kokkos::atomic_add(&out(s).rA_sum.y, m.rA_sum.y);
658 Kokkos::atomic_add(&out(s).rA_sum.z, m.rA_sum.z);
659 Kokkos::atomic_add(&out(s).rB_sum.x, m.rB_sum.x);
660 Kokkos::atomic_add(&out(s).rB_sum.y, m.rB_sum.y);
661 Kokkos::atomic_add(&out(s).rB_sum.z, m.rB_sum.z);
662 Kokkos::atomic_add(&out(s).wallVel_sum.x, m.wallVel_sum.x);
663 Kokkos::atomic_add(&out(s).wallVel_sum.y, m.wallVel_sum.y);
664 Kokkos::atomic_add(&out(s).wallVel_sum.z, m.wallVel_sum.z);
665 Kokkos::atomic_add(&out(s).restitution_sum, m.restitution_sum);
666 Kokkos::atomic_add(&out(s).friction_sum, m.friction_sum);
669 if (contactSlot.extent(0) >= (size_t)n) {
670 Kokkos::View<int*, CpMem> cs = contactSlot;
671 Kokkos::parallel_for(
672 "peclet::dem::cp::slotmap", Kokkos::RangePolicy<CpExec>(space, 0, n),
673 KOKKOS_LAMBDA(
int p) { cs(pm(p)) = sid(p); });
677 Kokkos::deep_copy(space, outCount, numSeg);
687 return (
static_cast<std::uint64_t
>(
static_cast<unsigned>(c.
bodyA)) << 32) |
688 static_cast<unsigned>(c.
bodyB);
698 Kokkos::View<const int*, CpMem>
start;
699 Kokkos::View<const int*, CpMem>
list;
700 KOKKOS_INLINE_FUNCTION
bool on()
const {
return start.extent(0) > 0; }
701 KOKKOS_INLINE_FUNCTION
int begin(
int u)
const {
return on() ?
start(u) : u; }
702 KOKKOS_INLINE_FUNCTION
int end(
int u)
const {
return on() ?
start(u + 1) : u + 1; }
703 KOKKOS_INLINE_FUNCTION
int contact(
int k)
const {
return on() ?
list(k) : k; }
717 Kokkos::View<int*, CpMem> unitStart,
718 Kokkos::View<int*, CpMem> unitContacts,
int numManifolds = -1) {
721 Kokkos::deep_copy(space, Kokkos::subview(unitStart, 0), 0);
725 auto writeIdentity = [&]() {
726 Kokkos::View<int*, CpMem> us = unitStart;
727 Kokkos::View<int*, CpMem> uc = unitContacts;
728 Kokkos::parallel_for(
729 "peclet::dem::cp::uident", Kokkos::RangePolicy<CpExec>(space, 0, n), KOKKOS_LAMBDA(
int p) {
733 Kokkos::deep_copy(space, Kokkos::subview(unitStart, n), n);
737 if (numManifolds == n)
738 return writeIdentity();
739 using Kokkos::view_alloc;
740 using Kokkos::WithoutInitializing;
741 Kokkos::View<std::uint64_t*, CpMem> keys(
742 view_alloc(space,
"peclet::dem::cp::ukeys", WithoutInitializing), n);
743 Kokkos::View<int*, CpMem> perm(view_alloc(space,
"peclet::dem::cp::uperm", WithoutInitializing),
745 Kokkos::parallel_for(
746 "peclet::dem::cp::ukey", Kokkos::RangePolicy<CpExec>(space, 0, n), KOKKOS_LAMBDA(
int i) {
747 keys(i) =
unitKey(contacts(i));
750 Kokkos::Experimental::sort_by_key(space, keys, perm);
751 Kokkos::View<int*, CpMem> segId(view_alloc(space,
"peclet::dem::cp::useg", WithoutInitializing),
754 Kokkos::parallel_scan(
755 "peclet::dem::cp::usegscan", Kokkos::RangePolicy<CpExec>(space, 0, n),
756 KOKKOS_LAMBDA(
int p,
int& run,
const bool final) {
757 if (p == 0 || keys(p) != keys(p - 1))
767 return writeIdentity();
769 Kokkos::View<int*, CpMem> segLead(
770 view_alloc(space,
"peclet::dem::cp::ulead", WithoutInitializing), numSeg);
771 Kokkos::deep_copy(space, segLead, n);
772 Kokkos::parallel_for(
773 "peclet::dem::cp::uleadmin", Kokkos::RangePolicy<CpExec>(space, 0, n),
774 KOKKOS_LAMBDA(
int p) { Kokkos::atomic_min(&segLead(segId(p)), perm(p)); });
778 Kokkos::parallel_for(
779 "peclet::dem::cp::ukey2", Kokkos::RangePolicy<CpExec>(space, 0, n), KOKKOS_LAMBDA(
int p) {
780 const int i = perm(p);
781 keys(p) = (
static_cast<std::uint64_t
>(
static_cast<unsigned>(segLead(segId(p)))) << 32) |
782 static_cast<unsigned>(i);
784 Kokkos::Experimental::sort_by_key(space, keys, perm);
785 Kokkos::View<int*, CpMem> us = unitStart;
786 Kokkos::View<int*, CpMem> uc = unitContacts;
788 Kokkos::parallel_scan(
789 "peclet::dem::cp::ustart", Kokkos::RangePolicy<CpExec>(space, 0, n),
790 KOKKOS_LAMBDA(
int p,
int& run,
const bool final) {
791 const bool isNew = (p == 0) || ((keys(p) >> 32) != (keys(p - 1) >> 32));
801 Kokkos::deep_copy(space, Kokkos::subview(unitStart, nu), n);
811 Kokkos::View<const int*, CpMem> contactSlot,
812 Kokkos::View<const ManifoldC*, CpMem> manifolds,
813 Kokkos::View<const float*, CpMem> lambdaAcc) {
815 Kokkos::parallel_for(
816 "peclet::dem::cp::pgs_friction_bound", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
817 KOKKOS_LAMBDA(
int i) {
818 const int s = contactSlot(i);
821 contacts(i).friction_lambda_n = lambdaAcc(s) /
static_cast<float>(np);
833 Kokkos::View<const float*, CpMem> posLambdaContact,
int numContacts,
834 Kokkos::View<const int*, CpMem> contactSlot, Kokkos::View<const ManifoldC*, CpMem> manifolds,
835 int numManifolds, Kokkos::View<const unsigned long long*, CpMem> keys,
836 Kokkos::View<const unsigned long long*, CpMem> prevKeysSorted,
int prevCount,
float dt,
837 Kokkos::View<float*, CpMem> scratchManifold, Kokkos::View<float*, CpMem> prevPosImpulse) {
839 auto sm = Kokkos::subview(scratchManifold, Kokkos::pair<int, int>(0, numManifolds));
840 Kokkos::deep_copy(space, sm, 0.0f);
841 Kokkos::parallel_for(
842 "peclet::dem::pos_load_reduce", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
843 KOKKOS_LAMBDA(
int i) {
844 const float l = posLambdaContact(i);
846 Kokkos::atomic_add(&scratchManifold(contactSlot(i)), l / dt);
848 Kokkos::parallel_for(
849 "peclet::dem::pos_load_scatter", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
850 KOKKOS_LAMBDA(
int idx) {
851 const unsigned long long k = keys(idx);
854 int lo = 0, hi = prevCount;
856 const int m = (lo + hi) >> 1;
857 if (prevKeysSorted(m) < k)
862 if (lo < prevCount && prevKeysSorted(lo) == k)
863 prevPosImpulse(lo) = scratchManifold(idx);
dem — portable POD types + math + analytic SDFs shared by the Kokkos kernel ports.
void commitPosImpulseKokkos(Kokkos::View< const float *, CpMem > posLambdaContact, int numContacts, Kokkos::View< const int *, CpMem > contactSlot, Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const unsigned long long *, CpMem > keys, Kokkos::View< const unsigned long long *, CpMem > prevKeysSorted, int prevCount, float dt, Kokkos::View< float *, CpMem > scratchManifold, Kokkos::View< float *, CpMem > prevPosImpulse)
After the position solve: convert the per-contact positional lambdas into an impulse- equivalent per ...
void frictionBoundFromLambdaKokkos(Kokkos::View< ContactC *, CpMem > contacts, int numContacts, Kokkos::View< const int *, CpMem > contactSlot, Kokkos::View< const ManifoldC *, CpMem > manifolds, Kokkos::View< const float *, CpMem > lambdaAcc)
PGS friction bound: overwrite each contact's friction_lambda_n with its manifold's converged PGS push...
int partitionContactsKokkos(Kokkos::View< ContactC *, CpMem > contacts, int n, const Owns &owns)
Stable partition of contacts [0, n) by owns(contact): the owned ones first, [0, ncOwned),...
void computeHeightLevelsKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const float *[3], CpMem > posPred, F3 gHat, Kokkos::View< int *, CpMem > heights, int numReal)
std::uint64_t pairKey(const ContactC &c)
Canonical pair key: (min<<32)|max, or (idA<<32)|0xFFFFFFFF for a boundary (idB<0) contact.
ManifoldC transformContact(const ContactC &c)
Per-contact transform to a single-point manifold, aligned to the canonical pair.
std::uint64_t unitKey(const ContactC &c)
Position-unit key (docs/contact_solve_framework.md §4.3, §12 S6): the canonical pair key for a body-b...
constexpr int wallContactId(int wallIndex)
Wall identity of a boundary contact (docs/contact_solve_framework.md §12 S6): a contact with a wall c...
void decodeKey(std::uint64_t key, int &bodyA, int &bodyB)
Decode the canonical (bodyA, bodyB) from a pair key (bodyB = -1 for boundary).
void buildColorBucketsKokkos(Kokkos::View< const int *, CpMem > colorOf, int n, int numColors, Kokkos::View< int *, CpMem > perm, Kokkos::View< int *, CpMem > cursor, std::vector< int > &offs)
Dense colour buckets (numColors <= 64): perm[offs[c] .
constexpr std::uint64_t kNonOwnedPairBit
Distributed step: bit 63 of a manifold-reduction key marks a contact this rank sees but does not own ...
F3 ldF3(const V &v, int i)
void markPersistentManifoldsKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const int *, CpMem > keyIdx, Kokkos::View< const unsigned long long *, CpMem > prevKeys, int prevCount, Kokkos::View< unsigned long long *, CpMem > outKeys, Kokkos::View< unsigned char *, CpMem > outFlags)
keyIdx maps a body slot to the identity the pair key is built from: the REAL index map on the single-...
constexpr int wallIndexOf(int bodyB)
void gatherWarmLambdaKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const int *, CpMem > keyIdx, Kokkos::View< const unsigned long long *, CpMem > prevKeys, Kokkos::View< const float *, CpMem > prevLambda, Kokkos::View< const float *[3], CpMem > prevLambdaT, Kokkos::View< const float *, CpMem > prevPosImpulse, Kokkos::View< const float *, CpMem > prevRestBank, Kokkos::View< const float *, CpMem > prevRestVPeak, int prevCount, Kokkos::View< unsigned long long *, CpMem > outKeys, Kokkos::View< float *, CpMem > outWarm, Kokkos::View< float *[3], CpMem > outWarmT, Kokkos::View< float *, CpMem > outPosImpulse, Kokkos::View< float *, CpMem > outRestBank, Kokkos::View< float *, CpMem > outRestVPeak, Kokkos::View< unsigned char *, CpMem > outMatched={}, bool dedupTwins=true)
Warm-start gather for the PGS velocity solve: per manifold, write its pair key and look up the previo...
void updateGroundedLevelsKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const float *[3], CpMem > posPred, F3 gHat, Kokkos::View< unsigned char *, CpMem > grounded, int numReal, int sweeps, int decay)
Guendelman support levels, warm-started: decay every body's level by decay, re-seed 255 at wall/plane...
int buildPositionUnitsKokkos(Kokkos::View< const ContactC *, CpMem > contacts, int n, Kokkos::View< int *, CpMem > unitStart, Kokkos::View< int *, CpMem > unitContacts, int numManifolds=-1)
Build the position units of contacts [0, n) (docs/contact_solve_framework.md §4.3,...
CpExec::memory_space CpMem
unsigned long long pairKeyFromGids(unsigned a, unsigned b)
The one 64-bit pair identity every gid-keyed ledger uses (the XPBD persistent-contact and warm-start ...
int reduceContactsToManifoldsKokkos(Kokkos::View< const ContactC *, CpMem > contacts, int n, Kokkos::View< ManifoldC *, CpMem > outManifolds, Kokkos::View< int, CpMem > outCount, Kokkos::View< int *, CpMem > contactSlot={}, int numOwnedContacts=-1, int *numOwnedManifolds=nullptr)
Reduce n contacts to manifolds (one per unique canonical pair).
void commitPairKeysLambdaKokkos(Kokkos::View< const unsigned long long *, CpMem > keys, Kokkos::View< const float *, CpMem > lambda, Kokkos::View< const float *[3], CpMem > lambdaT, Kokkos::View< const float *, CpMem > restBank, Kokkos::View< const float *, CpMem > restVPeak, Kokkos::View< unsigned long long *, CpMem > prevKeys, Kokkos::View< float *, CpMem > prevLambda, Kokkos::View< float *[3], CpMem > prevLambdaT, Kokkos::View< float *, CpMem > prevRestBank, Kokkos::View< float *, CpMem > prevRestVPeak, Kokkos::View< int *, CpMem > perm, int numManifolds, Kokkos::View< const int *, CpMem > color={}, Kokkos::View< int *, CpMem > prevColor={})
Save this substep's keys + converged impulses (normal AND tangential) and key-sort them for next subs...
long long colorKey(int idx)
splitmix32 finalizer: a well-mixed pseudo-random priority per edge index.
constexpr int kLevelInf
Height-from-floor BFS levels for the level-ordered ("multilevel") stabilization pass: 0 at a wall/pla...
constexpr int kColorUncolourable
void commitPairKeysKokkos(Kokkos::View< const unsigned long long *, CpMem > keys, Kokkos::View< unsigned long long *, CpMem > prevKeys, int numManifolds)
Copy this substep's keys into prevKeys and sort them for next substep's binary search.
constexpr int kColorPalette
Colouring palette (docs/contact_solve_framework.md §4.2 item 1): the greedy takes the lowest colour f...
int lowerBoundKey(const KeysView &keys, int count, unsigned long long key)
lower_bound over a ledger's sorted keys: the first index whose key is >= key (count when none).
bool normalOfA(const ContactC &c)
Which body's surface a contact's normal belongs to.
Kokkos::DefaultExecutionSpace CpExec
unsigned long long pairKeyOf(const ManifoldC &m, Kokkos::View< const int *, CpMem > realIdx)
Persistent-contact detection for the gravity-gated restitution rule.
Portable mirror of ManifoldConstraint.
The position units of a contact list as a CSR (§4.3): unit u owns the contacts list(start(u)) .
Kokkos::View< const int *, CpMem > start
Kokkos::View< const int *, CpMem > list
Per-edge slot overrides of a phase's hub copies (docs/contact_solve_framework.md §4....
Kokkos::View< const unsigned char *, CpMem > split
int slotA(int e, int def) const
bool relax(int sA, int sB) const
True iff the edge touches a mass-split slot (its step is scaled by omega before the clamp).
int slotB(int e, int def) const
Kokkos::View< const int *, CpMem > a
Kokkos::View< const int *, CpMem > b