9#ifndef DEM_SOLVER_VELOCITY_HPP
10#define DEM_SOLVER_VELOCITY_HPP
13#include <Kokkos_Core.hpp>
25KOKKOS_INLINE_FUNCTION
F3 ld3(Kokkos::View<
const float* [3],
CpMem> v,
int i) {
26 return F3{v(i, 0), v(i, 1), v(i, 2)};
31 if (invIlocal.
x == invIlocal.
y && invIlocal.
y == invIlocal.
z)
32 return dot3(tau, tau) * invIlocal.
x;
34 return t.
x * t.
x * invIlocal.
x + t.
y * t.
y * invIlocal.
y + t.
z * t.
z * invIlocal.
z;
47 int numManifolds, Kokkos::View<const int*, CpMem> realIdx,
48 Kokkos::View<int*, CpMem> counts,
bool dedupTwins =
true) {
51 "peclet::dem::count_velocity_jacobi", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
52 KOKKOS_LAMBDA(
int idx) {
56 const int realA = realIdx(m.
bodyA);
58 const int realB = realIdx(m.
bodyB);
59 if (dedupTwins && realA > realB)
61 Kokkos::atomic_add(&counts(realB), 1);
63 Kokkos::atomic_add(&counts(realA), 1);
77 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
78 Kokkos::View<const float*, CpMem> invMass, Kokkos::View<
const float* [3],
CpMem> invInertia,
79 Kokkos::View<
const float* [4],
CpMem> quat, Kokkos::View<
const float* [3],
CpMem> velPred,
80 Kokkos::View<
const float* [3],
CpMem> angVelPred, Kokkos::View<const int*, CpMem> realIdx,
81 float growthRate,
float restitutionNormal,
float restVelThreshold,
82 Kokkos::View<
float* [3],
CpMem> deltaVel, Kokkos::View<
float* [3],
CpMem> deltaAngVel,
83 Kokkos::View<int*, CpMem> velCounts, Kokkos::View<const int*, CpMem> onlyColor = {},
84 int colorFilter = 0, Kokkos::View<const unsigned char*, CpMem> persistent = {},
85 Kokkos::View<const float* [3], CpMem> posPred = {}, F3 gHat = {},
86 Kokkos::View<const unsigned char*, CpMem> grounded = {},
bool massSplit =
false,
87 bool dedupTwins =
true) {
91 const bool filt = onlyColor.extent(0) > 0;
92 const bool usePersist = persistent.extent(0) > 0;
94 "peclet::dem::solve_velocity", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
95 KOKKOS_LAMBDA(
int idx) {
96 if (filt && onlyColor(idx) != colorFilter)
98 const ManifoldC m = manifolds(idx);
99 if (m.num_points <= 0)
102 const int idA = m.bodyA, idB = m.bodyB;
103 const int realA = realIdx(idA);
106 realB = realIdx(idB);
107 if (dedupTwins && realA > realB)
111 const float invMassA = invMass(realA);
112 const float invMassB = (idB >= 0) ? invMass(realB) : 0.0f;
113 const F3 invIA =
ld3(invInertia, realA);
114 const F3 invIB = (idB >= 0) ?
ld3(invInertia, realB) : F3{0, 0, 0};
115 const F4 qA = F4{quat(realA, 0), quat(realA, 1), quat(realA, 2), quat(realA, 3)};
116 const F4 qB = (idB >= 0)
117 ? F4{quat(realB, 0), quat(realB, 1), quat(realB, 2), quat(realB, 3)}
120 const F3 vA =
ld3(velPred, realA), wA =
ld3(angVelPred, realA);
121 F3 vB{0, 0, 0}, wB{0, 0, 0};
123 vB =
ld3(velPred, realB);
124 wB =
ld3(angVelPred, realB);
127 const F3 Nsum{m.normal_sum.x, m.normal_sum.y, m.normal_sum.z};
128 const F3 TauA{m.torque_armA_sum.x, m.torque_armA_sum.y, m.torque_armA_sum.z};
129 const F3 TauB{m.torque_armB_sum.x, m.torque_armB_sum.y, m.torque_armB_sum.z};
131 const float invN = 1.0f /
static_cast<float>(m.num_points);
136 float restitution = restitutionNormal;
138 vB =
scale3(F3{m.wallVel_sum.x, m.wallVel_sum.y, m.wallVel_sum.z}, invN);
140 const float ra = m.restitution_sum * invN;
144 const F3 rAavg =
scale3(F3{m.rA_sum.x, m.rA_sum.y, m.rA_sum.z}, invN);
145 const F3 rBavg =
scale3(F3{m.rB_sum.x, m.rB_sum.y, m.rB_sum.z}, invN);
147 const float lenN = Kokkos::sqrt(
dot3(Nsum, Nsum));
159 const F3 diffCenters = (idB < 0) ? rAavg :
sub3(rAavg, rBavg);
160 const F3 vGrowth =
scale3(diffCenters, growthRate);
162 float vn =
dot3(vA, Nsum) +
dot3(wA, TauA) +
dot3(vB, F3{-Nsum.x, -Nsum.y, -Nsum.z}) +
164 vn +=
dot3(vGrowth, Nsum);
166 const float alignment =
dot3(Nsum, diffCenters);
167 if (alignment > 0.0f) {
175 const float Nsq =
dot3(Nsum, Nsum);
176 float wA_n = Nsq * invMassA +
genInvMass(TauA, invIA, qA);
177 float wB_n = Nsq * invMassB +
genInvMass(TauB, invIB, qB);
191 bool applyA =
true, applyB =
true;
192 if (usePersist && persistent(idx) != 0 && idB >= 0) {
193 const F3 dx =
sub3(
ldF3(posPred, idA),
ldF3(posPred, idB));
194 const float up = -(dx.x * gHat.x + dx.y * gHat.y + dx.z * gHat.z);
195 const float thr = 0.3f * Kokkos::sqrt(
dot3(dx, dx));
196 const float riseThr =
197 4.0f * restVelThreshold;
201 if (up > thr && -
dot3(vB, gHat) <= riseThr && grounded(realB) > 0) {
206 }
else if (up < -thr && -
dot3(vA, gHat) <= riseThr && grounded(realA) > 0) {
212 const float wTotal = wA_n + wB_n;
217 massSplit ?
static_cast<float>(velCounts(realA)) * wA_n +
218 ((idB >= 0) ?
static_cast<float>(velCounts(realB)) * wB_n : 0.0f)
228 if (Kokkos::fabs(vn) < restVelThreshold * lenN)
231 const float lambda = (-restitution * vn - vn) / wSolve;
233 const F3 Jlin =
scale3(Nsum, lambda);
234 const F3 JangA =
scale3(TauA, lambda);
235 const F3 JangB =
scale3(TauB, lambda);
239 Kokkos::atomic_add(&deltaVel(realA, 0), Jlin.x * invMassA);
240 Kokkos::atomic_add(&deltaVel(realA, 1), Jlin.y * invMassA);
241 Kokkos::atomic_add(&deltaVel(realA, 2), Jlin.z * invMassA);
245 const F3 dwl{Jl.x * invIA.x, Jl.y * invIA.y, Jl.z * invIA.z};
247 Kokkos::atomic_add(&deltaAngVel(realA, 0), dww.x);
248 Kokkos::atomic_add(&deltaAngVel(realA, 1), dww.y);
249 Kokkos::atomic_add(&deltaAngVel(realA, 2), dww.z);
252 if (idB >= 0 && applyB) {
253 Kokkos::atomic_add(&deltaVel(realB, 0), -Jlin.x * invMassB);
254 Kokkos::atomic_add(&deltaVel(realB, 1), -Jlin.y * invMassB);
255 Kokkos::atomic_add(&deltaVel(realB, 2), -Jlin.z * invMassB);
257 const F3 dwl{Jl.x * invIB.x, Jl.y * invIB.y, Jl.z * invIB.z};
259 Kokkos::atomic_add(&deltaAngVel(realB, 0), dww.x);
260 Kokkos::atomic_add(&deltaAngVel(realB, 1), dww.y);
261 Kokkos::atomic_add(&deltaAngVel(realB, 2), dww.z);
263 Kokkos::atomic_add(&velCounts(realB), 1);
265 if (applyA && !massSplit)
266 Kokkos::atomic_add(&velCounts(realA), 1);
278template <
class V3,
class Vi>
280 V3 deltaAngVel,
Vi velCounts,
bool averaged =
true) {
282 Kokkos::parallel_for(
283 "peclet::dem::apply_vel_avg", Kokkos::RangePolicy<CpExec>(space, 0, n), KOKKOS_LAMBDA(
int i) {
284 const int count = velCounts(i);
290 const float f = averaged ? Kokkos::fmin(1.0f, 2.0f /
static_cast<float>(count)) : 1.0f;
291 for (
int c = 0; c < 3; ++c) {
292 velPred(i, c) += deltaVel(i, c) * f;
293 angVelPred(i, c) += deltaAngVel(i, c) * f;
294 deltaVel(i, c) = 0.0f;
295 deltaAngVel(i, c) = 0.0f;
324 Kokkos::View<const int*, CpMem> realIdx,
int numReal,
325 Kokkos::View<int*, CpMem> mColor,
326 Kokkos::View<long long*, CpMem> bodyWinner,
327 Kokkos::View<std::uint64_t*, CpMem> bodyMask,
int& leftover,
328 Kokkos::View<const unsigned char*, CpMem> sleepMask = {},
329 SlotOverride ov = {},
bool dedupTwins =
true,
int vertexSpan = 0) {
332 if (numManifolds <= 0 || numReal <= 0)
336 const int nV = vertexSpan > 0 ? vertexSpan : numReal;
337 const bool sleepOn = sleepMask.extent(0) > 0;
338 Kokkos::parallel_for(
339 "peclet::dem::color_init_bodies", Kokkos::RangePolicy<CpExec>(space, 0, nV),
340 KOKKOS_LAMBDA(
int i) { bodyMask(i) = 0; });
342 Kokkos::parallel_for(
343 "peclet::dem::color_init_manifolds", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
344 KOKKOS_LAMBDA(
int idx) {
345 const ManifoldC m = manifolds(idx);
346 if (m.num_points <= 0 || (sleepOn && sleepMask(idx))) {
352 if (dedupTwins && m.bodyB >= 0 && realIdx(m.bodyA) > realIdx(m.bodyB)) {
359 int remaining = 1, prevRemaining = -1;
364 const int maxRounds = numManifolds + 2;
365 for (
int round = 0; round < maxRounds && remaining > 0; ++round) {
366 Kokkos::parallel_for(
367 "peclet::dem::color_reset_winner", Kokkos::RangePolicy<CpExec>(space, 0, nV),
368 KOKKOS_LAMBDA(
int i) { bodyWinner(i) = -1; });
369 Kokkos::parallel_for(
370 "peclet::dem::color_contend", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
371 KOKKOS_LAMBDA(
int idx) {
372 if (mColor(idx) != -1)
374 const ManifoldC m = manifolds(idx);
375 const long long key =
colorKey(idx);
376 Kokkos::atomic_max(&bodyWinner(ov.slotA(idx, realIdx(m.bodyA))), key);
378 Kokkos::atomic_max(&bodyWinner(ov.slotB(idx, realIdx(m.bodyB))), key);
381 Kokkos::parallel_reduce(
382 "peclet::dem::color_commit", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
383 KOKKOS_LAMBDA(
int idx,
int& acc) {
384 if (mColor(idx) != -1)
386 const ManifoldC m = manifolds(idx);
387 const int ea = ov.slotA(idx, realIdx(m.bodyA));
388 const int eb = (m.bodyB >= 0) ? ov.slotB(idx, realIdx(m.bodyB)) : -1;
390 const long long key =
colorKey(idx);
391 if (bodyWinner(ea) != key || (eb >= 0 && bodyWinner(eb) != key)) {
395 std::uint64_t forbidden = bodyMask(ea);
397 forbidden |= bodyMask(eb);
401#if defined(PECLET_DEM_TEST_MUTANT) && PECLET_DEM_TEST_MUTANT == 3
410 const std::uint64_t bit = std::uint64_t(1) << c;
417 if (rem == prevRemaining)
424 Kokkos::parallel_reduce(
425 "peclet::dem::color_max", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
426 KOKKOS_LAMBDA(
int idx,
int& mx) {
427 if (mColor(idx) > mx)
430 Kokkos::Max<int>(maxc));
431 Kokkos::parallel_reduce(
432 "peclet::dem::color_leftover", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
433 KOKKOS_LAMBDA(
int idx,
int& acc) {
459 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
460 Kokkos::View<const int*, CpMem> realIdx,
int numReal,
461 Kokkos::View<const unsigned long long*, CpMem> prevKeys,
462 Kokkos::View<const int*, CpMem> prevColor,
int prevCount, Kokkos::View<int*, CpMem> mColor,
463 Kokkos::View<long long*, CpMem> bodyWinner, Kokkos::View<std::uint64_t*, CpMem> bodyMask,
464 int& leftover,
bool forceFull, Kokkos::View<const unsigned char*, CpMem> sleepMask = {},
465 bool dedupTwins =
true) {
468 if (numManifolds <= 0 || numReal <= 0)
470 Kokkos::parallel_for(
471 "peclet::dem::icolor_init_bodies", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
472 KOKKOS_LAMBDA(
int i) { bodyMask(i) = 0; });
473 const bool full = forceFull || prevCount <= 0;
474 const bool sleepOn = sleepMask.extent(0) > 0;
477 Kokkos::parallel_for(
478 "peclet::dem::icolor_seed", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
479 KOKKOS_LAMBDA(
int idx) {
480 const ManifoldC m = manifolds(idx);
481 if (m.num_points <= 0 || (sleepOn && sleepMask(idx))) {
485 if (dedupTwins && m.bodyB >= 0 && realIdx(m.bodyA) > realIdx(m.bodyB)) {
491 const unsigned long long k =
pairKeyOf(m, realIdx);
492 int lo = 0, hi = prevCount;
494 const int mid = (lo + hi) >> 1;
495 if (prevKeys(mid) < k)
500 if (lo < prevCount && prevKeys(lo) == k) {
501 const int pc = prevColor(lo);
510 Kokkos::parallel_for(
511 "peclet::dem::icolor_seed_mask", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
512 KOKKOS_LAMBDA(
int idx) {
513 const int c = mColor(idx);
516 const ManifoldC m = manifolds(idx);
517 const std::uint64_t bit = std::uint64_t(1) << c;
518 Kokkos::atomic_or(&bodyMask(realIdx(m.bodyA)), bit);
520 Kokkos::atomic_or(&bodyMask(realIdx(m.bodyB)), bit);
524 int remaining = 1, prevRemaining = -1;
525 const int maxRounds = numManifolds + 2;
526 for (
int round = 0; round < maxRounds && remaining > 0; ++round) {
527 Kokkos::parallel_for(
528 "peclet::dem::icolor_reset_winner", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
529 KOKKOS_LAMBDA(
int i) { bodyWinner(i) = -1; });
530 Kokkos::parallel_for(
531 "peclet::dem::icolor_contend", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
532 KOKKOS_LAMBDA(
int idx) {
533 if (mColor(idx) != -1)
535 const ManifoldC m = manifolds(idx);
536 const long long key =
colorKey(idx);
537 Kokkos::atomic_max(&bodyWinner(realIdx(m.bodyA)), key);
539 Kokkos::atomic_max(&bodyWinner(realIdx(m.bodyB)), key);
542 Kokkos::parallel_reduce(
543 "peclet::dem::icolor_commit", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
544 KOKKOS_LAMBDA(
int idx,
int& acc) {
545 if (mColor(idx) != -1)
547 const ManifoldC m = manifolds(idx);
548 const int ea = realIdx(m.bodyA);
549 const int eb = (m.bodyB >= 0) ? realIdx(m.bodyB) : -1;
550 const long long key =
colorKey(idx);
551 if (bodyWinner(ea) != key || (eb >= 0 && bodyWinner(eb) != key)) {
555 std::uint64_t forbidden = bodyMask(ea);
557 forbidden |= bodyMask(eb);
561#if defined(PECLET_DEM_TEST_MUTANT) && PECLET_DEM_TEST_MUTANT == 3
570 const std::uint64_t bit = std::uint64_t(1) << c;
577 if (rem == prevRemaining)
583 Kokkos::parallel_reduce(
584 "peclet::dem::icolor_max", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
585 KOKKOS_LAMBDA(
int idx,
int& mx) {
586 if (mColor(idx) > mx)
589 Kokkos::Max<int>(maxc));
590 Kokkos::parallel_reduce(
591 "peclet::dem::icolor_leftover", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
592 KOKKOS_LAMBDA(
int idx,
int& acc) {
615inline void computeVn0Kokkos(Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
616 Kokkos::View<
const float* [3],
CpMem> velPred,
617 Kokkos::View<
const float* [3],
CpMem> angVelPred,
618 Kokkos::View<const int*, CpMem> realIdx,
float growthRate,
619 Kokkos::View<float*, CpMem> vn0, Kokkos::View<
float* [3],
CpMem> vt0,
620 bool dedupTwins =
true) {
623 Kokkos::parallel_for(
624 "peclet::dem::pgs_vn0", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
625 KOKKOS_LAMBDA(
int idx) {
630 const int realA = realIdx(idA);
631 if (dedupTwins && idB >= 0 && realA > realIdx(idB))
633 const float invN = 1.0f /
static_cast<float>(m.
num_points);
637 const F3 vA = ld3(velPred, realA), wA = ld3(angVelPred, realA);
638 F3 vB{0, 0, 0}, wB{0, 0, 0};
640 vB = ld3(velPred, realIdx(idB));
641 wB = ld3(angVelPred, realIdx(idB));
647 const F3 diffCenters = (idB < 0) ? rAavg :
sub3(rAavg, rBavg);
648 const F3 vGrowth =
scale3(diffCenters, growthRate);
649 float vn =
dot3(vA, Nsum) +
dot3(wA, TauA) +
dot3(vB,
F3{-Nsum.
x, -Nsum.y, -Nsum.z}) +
651 vn +=
dot3(vGrowth, Nsum);
656 const float lenN = Kokkos::sqrt(
dot3(Nsum, Nsum));
659 const F3 nhat =
scale3(Nsum, 1.0f / lenN);
660 const int realB2 = (idB >= 0) ? realIdx(idB) : -1;
661 const F3 wAv = ld3(angVelPred, realA);
662 const F3 wBv = (realB2 >= 0) ? ld3(angVelPred, realB2) :
F3{0, 0, 0};
686 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
687 Kokkos::View<const float*, CpMem> invMass, Kokkos::View<
const float* [3],
CpMem> invInertia,
688 Kokkos::View<
const float* [4],
CpMem> quat, Kokkos::View<
const float* [3],
CpMem> velPred,
689 Kokkos::View<
const float* [3],
CpMem> angVelPred, Kokkos::View<const int*, CpMem> realIdx,
690 float growthRate,
float restitutionNormal,
float restVelThreshold,
691 Kokkos::View<const float*, CpMem> vn0, Kokkos::View<const float*, CpMem> lambdaAcc,
692 Kokkos::View<const float*, CpMem> restRel, Kokkos::View<float*, CpMem> restBank,
693 Kokkos::View<float*, CpMem> restVPeak,
bool dedupTwins =
true) {
697 Kokkos::parallel_for(
698 "peclet::dem::rest_bank_update", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
699 KOKKOS_LAMBDA(
int idx) {
704 const int realA = realIdx(idA);
705 const int realB = (idB >= 0) ? realIdx(idB) : idB;
706 if (dedupTwins && idB >= 0 && realA > realB)
708 const float invN = 1.0f /
static_cast<float>(m.
num_points);
710 const float lenN = Kokkos::sqrt(
dot3(Nsum, Nsum));
715 const F3 vA = ld3(velPred, realA), wA = ld3(angVelPred, realA);
716 F3 vB{0, 0, 0}, wB{0, 0, 0};
718 vB = ld3(velPred, realB);
719 wB = ld3(angVelPred, realB);
725 const F3 diffCenters = (idB < 0) ? rAavg :
sub3(rAavg, rBavg);
726 const F3 vGrowth =
scale3(diffCenters, growthRate);
727 float vn =
dot3(vA, Nsum) +
dot3(wA, TauA) +
dot3(vB,
F3{-Nsum.
x, -Nsum.y, -Nsum.z}) +
729 vn +=
dot3(vGrowth, Nsum);
730 const float alignment =
dot3(Nsum, diffCenters);
731 const float sgn = (alignment > 0.0f) ? 1.0f : -1.0f;
734 const F3 invIA = ld3(invInertia, realA);
735 const F3 invIB = (idB >= 0) ? ld3(invInertia, realB) :
F3{0, 0, 0};
736 const F4 qA =
F4{quat(realA, 0), quat(realA, 1), quat(realA, 2), quat(realA, 3)};
737 const F4 qB = (idB >= 0)
738 ?
F4{quat(realB, 0), quat(realB, 1), quat(realB, 2), quat(realB, 3)}
740 const float Nsq =
dot3(Nsum, Nsum);
741 const float wTotal = Nsq * invMass(realA) + genInvMass(TauA, invIA, qA) +
742 Nsq * ((idB >= 0) ? invMass(realB) : 0.0f) +
743 genInvMass(TauB, invIB, qB);
746 float e = restitutionNormal;
752 const float v0til = sgn * vn0(idx);
753 const float vtilEnd = sgn * vn;
754 float owed = restBank(idx);
756 float vPeak = restVPeak(idx) * (1.0f - 1.0f / 256.0f);
757 if (v0til > restVelThreshold * lenN)
758 vPeak = Kokkos::fmax(vPeak, v0til / lenN);
759 if (vPeak > restVelThreshold) {
765 const float pR = Kokkos::fmax(0.0f, -vtilEnd) / wTotal * lenN;
766 const float pTot = Kokkos::fmax(lambdaAcc(idx), 0.0f) * lenN;
767 const float pC = Kokkos::fmax(0.0f, pTot - pR);
768 owed += e * pC - pR - Kokkos::fmax(restRel(idx), 0.0f) * lenN;
769 restBank(idx) = Kokkos::fmax(owed, 0.0f);
770 restVPeak(idx) = vPeak;
772 restBank(idx) = 0.0f;
773 restVPeak(idx) = 0.0f;
785 Kokkos::View<float*, CpMem> orphanVPeak,
int numOwned,
786 float restVelThreshold) {
788 Kokkos::parallel_for(
789 "peclet::dem::rest_orphan_decay", Kokkos::RangePolicy<CpExec>(space, 0, numOwned),
790 KOKKOS_LAMBDA(
int i) {
791 const float decayed = orphanVPeak(i) * (1.0f - 1.0f / 64.0f);
792 if (decayed <= restVelThreshold || orphan(i) <= 0.0f) {
794 orphanVPeak(i) = 0.0f;
796 orphan(i) *= (1.0f - 1.0f / 64.0f);
797 orphanVPeak(i) = decayed;
815 Kokkos::View<const unsigned long long*, CpMem> prevKeys,
816 Kokkos::View<const float*, CpMem> prevRestBank, Kokkos::View<const float*, CpMem> prevRestVPeak,
817 Kokkos::View<const unsigned char*, CpMem> matched,
int prevCount,
818 Kokkos::View<const float*, CpMem> invMass, Kokkos::View<float*, CpMem> orphan,
819 Kokkos::View<float*, CpMem> orphanVPeak, Kokkos::View<const int*, CpMem> gidSorted = {},
820 Kokkos::View<const int*, CpMem> slotSorted = {},
int ownedLimit = -1) {
826 const int nMap =
static_cast<int>(gidSorted.extent(0));
827 const int nBody =
static_cast<int>(orphan.extent(0));
828 Kokkos::parallel_for(
829 "peclet::dem::rest_orphan_scatter", Kokkos::RangePolicy<CpExec>(space, 0, prevCount),
830 KOKKOS_LAMBDA(
int e) {
833 const float owed = prevRestBank(e);
836 const unsigned long long k = prevKeys(e);
839 const unsigned hi =
static_cast<unsigned>(k >> 32);
840 const unsigned lo =
static_cast<unsigned>(k & 0xFFFFFFFFu);
841 auto resolve = [&](
unsigned id) ->
int {
843 return (
static_cast<int>(
id) < nBody) ?
static_cast<int>(
id) : -1;
846 const int m = (a + b) >> 1;
847 if (gidSorted(m) <
static_cast<int>(id))
852 return (a < nMap && gidSorted(a) ==
static_cast<int>(id)) ? slotSorted(a) : -1;
854 const int sA = resolve(hi);
855 if (ownedLimit >= 0 && !(sA >= 0 && sA < ownedLimit))
857 const int sB = (lo != 0xFFFFFFFFu) ? resolve(lo) : -1;
858 const float vpk = prevRestVPeak(e);
860 if (lo != 0xFFFFFFFFu) {
861 if (sB >= 0 && sA >= 0) {
862 const float wA = invMass(sA), wB = invMass(sB);
864 shareA = wB / (wA + wB);
869 if (sA >= 0 && shareA > 0.0f) {
870 Kokkos::atomic_add(&orphan(sA), owed * shareA);
871 Kokkos::atomic_max(&orphanVPeak(sA), vpk);
873 if (sB >= 0 && shareA < 1.0f) {
874 Kokkos::atomic_add(&orphan(sB), owed * (1.0f - shareA));
875 Kokkos::atomic_max(&orphanVPeak(sB), vpk);
888 Kokkos::parallel_reduce(
889 "peclet::dem::rest_bank_stats", Kokkos::RangePolicy<CpExec>(0, n),
890 KOKKOS_LAMBDA(
int i,
double& ls,
float& lm,
int& lc) {
891 const float v = bank(i);
898 s, Kokkos::Max<float>(mx), Kokkos::Sum<int>(cnt));
911 int numManifolds, Kokkos::View<const int*, CpMem> realIdx,
912 Kokkos::View<const unsigned char*, CpMem> persistent,
913 Kokkos::View<const unsigned char*, CpMem> grounded,
914 Kokkos::View<
const float* [3],
CpMem> posPred,
915 Kokkos::View<
const float* [3],
CpMem> velPred,
F3 gHat,
916 float riseThr, Kokkos::View<unsigned char*, CpMem> sideFlag,
917 Kokkos::View<const float*, CpMem> vn0,
float approachThr,
918 bool dedupTwins =
true) {
921 Kokkos::parallel_for(
922 "peclet::dem::pgs_side_flags", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
923 KOKKOS_LAMBDA(
int idx) {
924 const unsigned char wasPersistent = persistent(idx);
934 if (Kokkos::fabs(vn0(idx)) > approachThr)
936 const int realA = realIdx(m.
bodyA), realB = realIdx(m.
bodyB);
937 if (dedupTwins && realA > realB)
940 const float up = -(dx.
x * gHat.
x + dx.
y * gHat.
y + dx.
z * gHat.
z);
941 const float thr = 0.3f * Kokkos::sqrt(
dot3(dx, dx));
942 if (up > thr && -
dot3(ld3(velPred, realB), gHat) <= riseThr && grounded(realB) > 0) {
944 }
else if (up < -thr && -
dot3(ld3(velPred, realA), gHat) <= riseThr &&
945 grounded(realA) > 0) {
953 Kokkos::View<const float*, CpMem> invMass,
954 Kokkos::View<
const float* [3],
CpMem> invInertia,
955 Kokkos::View<
const float* [4],
CpMem> quat,
956 Kokkos::View<
float* [3],
CpMem> velPred,
957 Kokkos::View<
float* [3],
CpMem> angVelPred,
958 Kokkos::View<const int*, CpMem> realIdx,
959 Kokkos::View<const float*, CpMem> warmP,
960 Kokkos::View<
float* [3],
CpMem> warmT,
bool dedupTwins =
true) {
963 Kokkos::parallel_for(
964 "peclet::dem::pgs_warm_apply", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
965 KOKKOS_LAMBDA(
int idx) {
966 const float p = warmP(idx);
967 F3 lt{warmT(idx, 0), warmT(idx, 1), warmT(idx, 2)};
968 const bool hasT = (lt.x != 0.0f || lt.y != 0.0f || lt.z != 0.0f);
969 if (p == 0.0f && !hasT)
975 const int realA = realIdx(idA);
976 const int realB = (idB >= 0) ? realIdx(idB) : idB;
977 if (dedupTwins && idB >= 0 && realA > realB)
979 const float invN = 1.0f /
static_cast<float>(m.
num_points);
985 const F3 diffCenters = (idB < 0) ? rAavg :
sub3(rAavg, rBavg);
986 const float alignment =
dot3(Nsum, diffCenters);
987 const float sgn = (alignment > 0.0f) ? 1.0f : -1.0f;
988 const float lambda = -sgn * p;
989 const F3 Jlin =
scale3(Nsum, lambda);
990 const float invMassA = invMass(realA);
991 const float invMassB = (idB >= 0) ? invMass(realB) : 0.0f;
992 Kokkos::atomic_add(&velPred(realA, 0), Jlin.
x * invMassA);
993 Kokkos::atomic_add(&velPred(realA, 1), Jlin.
y * invMassA);
994 Kokkos::atomic_add(&velPred(realA, 2), Jlin.
z * invMassA);
996 const F4 qA =
F4{quat(realA, 0), quat(realA, 1), quat(realA, 2), quat(realA, 3)};
998 const F3 invIA = ld3(invInertia, realA);
999 const F3 dwl{Jl.
x * invIA.
x, Jl.
y * invIA.
y, Jl.
z * invIA.
z};
1001 Kokkos::atomic_add(&angVelPred(realA, 0), dww.
x);
1002 Kokkos::atomic_add(&angVelPred(realA, 1), dww.
y);
1003 Kokkos::atomic_add(&angVelPred(realA, 2), dww.
z);
1006 Kokkos::atomic_add(&velPred(realB, 0), -Jlin.
x * invMassB);
1007 Kokkos::atomic_add(&velPred(realB, 1), -Jlin.
y * invMassB);
1008 Kokkos::atomic_add(&velPred(realB, 2), -Jlin.
z * invMassB);
1009 const F4 qB =
F4{quat(realB, 0), quat(realB, 1), quat(realB, 2), quat(realB, 3)};
1011 const F3 invIB = ld3(invInertia, realB);
1012 const F3 dwl{Jl.
x * invIB.
x, Jl.
y * invIB.
y, Jl.
z * invIB.
z};
1014 Kokkos::atomic_add(&angVelPred(realB, 0), dww.
x);
1015 Kokkos::atomic_add(&angVelPred(realB, 1), dww.
y);
1016 Kokkos::atomic_add(&angVelPred(realB, 2), dww.
z);
1022 const float lenN2 = Kokkos::sqrt(
dot3(Nsum, Nsum));
1023 if (lenN2 > 1e-9f) {
1024 const F3 nhat =
scale3(Nsum, 1.0f / lenN2);
1027 warmT(idx, 0) = lt.x;
1028 warmT(idx, 1) = lt.y;
1029 warmT(idx, 2) = lt.z;
1030 Kokkos::atomic_add(&velPred(realA, 0), lt.x * invMassA);
1031 Kokkos::atomic_add(&velPred(realA, 1), lt.y * invMassA);
1032 Kokkos::atomic_add(&velPred(realA, 2), lt.z * invMassA);
1034 const F4 qA =
F4{quat(realA, 0), quat(realA, 1), quat(realA, 2), quat(realA, 3)};
1036 const F3 invIA = ld3(invInertia, realA);
1037 const F3 dwl{Jl.
x * invIA.
x, Jl.
y * invIA.
y, Jl.
z * invIA.
z};
1039 Kokkos::atomic_add(&angVelPred(realA, 0), dww.
x);
1040 Kokkos::atomic_add(&angVelPred(realA, 1), dww.
y);
1041 Kokkos::atomic_add(&angVelPred(realA, 2), dww.
z);
1044 Kokkos::atomic_add(&velPred(realB, 0), -lt.x * invMassB);
1045 Kokkos::atomic_add(&velPred(realB, 1), -lt.y * invMassB);
1046 Kokkos::atomic_add(&velPred(realB, 2), -lt.z * invMassB);
1047 const F4 qB =
F4{quat(realB, 0), quat(realB, 1), quat(realB, 2), quat(realB, 3)};
1049 const F3 invIB = ld3(invInertia, realB);
1050 const F3 dwl{Jl.
x * invIB.
x, Jl.
y * invIB.
y, Jl.
z * invIB.
z};
1052 Kokkos::atomic_add(&angVelPred(realB, 0), dww.
x);
1053 Kokkos::atomic_add(&angVelPred(realB, 1), dww.
y);
1054 Kokkos::atomic_add(&angVelPred(realB, 2), dww.
z);
1074 Kokkos::View<const float* [4], CpMem>
quat;
1087 Kokkos::View<const float*, CpMem>
vn0;
1091 Kokkos::View<const float* [3], CpMem>
vt0;
1129 const int realB = (idB >= 0) ?
ov.
slotB(idx,
realIdx(idB)) : idB;
1130 const float invMassA =
invMass(realA);
1131 const float invMassB = (idB >= 0) ?
invMass(realB) : 0.0f;
1133 const F3 invIB = (idB >= 0) ? ld3(
invInertia, realB) :
F3{0, 0, 0};
1138 F3 vB{0, 0, 0}, wB{0, 0, 0};
1139 const float invN = 1.0f /
static_cast<float>(m.
num_points);
1152 const float eMat = restitution;
1158 const float lenN = Kokkos::sqrt(
dot3(Nsum, Nsum));
1161 const F3 diffCenters = (idB < 0) ? rAavg :
sub3(rAavg, rBavg);
1163 float vn =
dot3(vA, Nsum) +
dot3(wA, TauA) +
dot3(vB,
F3{-Nsum.
x, -Nsum.y, -Nsum.z}) +
1165 vn +=
dot3(vGrowth, Nsum);
1166 const float alignment =
dot3(Nsum, diffCenters);
1167 const float sgn = (alignment > 0.0f) ? 1.0f : -1.0f;
1168 const float Nsq =
dot3(Nsum, Nsum);
1169 float wA_n = Nsq * invMassA + genInvMass(TauA, invIA, qA);
1170 float wB_n = Nsq * invMassB + genInvMass(TauB, invIB, qB);
1175 bool applyA =
true, applyB =
true;
1176 const unsigned char sf =
sideFlag(idx);
1181 }
else if (sf == 2) {
1186 const float wTotal = wA_n + wB_n;
1190 const float v0til = sgn *
vn0(idx);
1207 const float target =
1209 ? ((v0til > 0.0f) ? -restitution * v0til : 0.0f)
1211 const float vtil = sgn * vn;
1212 float dp = (vtil - target) / wTotal;
1216 float pNew = pOld + dp;
1219 const float dApplied = pNew - pOld;
1220 if (dApplied != 0.0f) {
1222 Kokkos::atomic_max(&
maxApproach(), Kokkos::fabs(dApplied) * wTotal / lenN);
1224 Kokkos::atomic_max(&
maxApproachQS(), Kokkos::fabs(dApplied) * wTotal / lenN);
1225 const float lambda = -sgn * dApplied;
1226 const F3 Jlin =
scale3(Nsum, lambda);
1227 const F3 JangA =
scale3(TauA, lambda);
1228 const F3 JangB =
scale3(TauB, lambda);
1230 velPred(realA, 0) += Jlin.
x * invMassA;
1231 velPred(realA, 1) += Jlin.
y * invMassA;
1232 velPred(realA, 2) += Jlin.
z * invMassA;
1235 const F3 dwl{Jl.
x * invIA.
x, Jl.
y * invIA.
y, Jl.
z * invIA.
z};
1242 if (idB >= 0 && applyB) {
1243 velPred(realB, 0) += -Jlin.
x * invMassB;
1244 velPred(realB, 1) += -Jlin.
y * invMassB;
1245 velPred(realB, 2) += -Jlin.
z * invMassB;
1247 const F3 dwl{Jl.
x * invIB.
x, Jl.
y * invIB.
y, Jl.
z * invIB.
z};
1274 float orphA = 0.0f, orphB = 0.0f;
1285 if ((owed > 0.0f || orphA > 0.0f || orphB > 0.0f) && vPeak > 0.0f) {
1287 F3 vB3{0, 0, 0}, wB3{0, 0, 0};
1294 float vn3 =
dot3(vA3, Nsum) +
dot3(wA3, TauA) +
dot3(vB3,
F3{-Nsum.
x, -Nsum.y, -Nsum.z}) +
1296 vn3 +=
dot3(vGrowth, Nsum);
1297 const float vtil3 = sgn * vn3;
1302 const bool relA =
true, relB = (idB >= 0);
1303 const float wRel = wTotal;
1310 const float vTphys = eMat * vPeak;
1311 const float targetR = -vTphys * lenN;
1312 const float dpR = (vtil3 - targetR) / wRel;
1313 float cap = owed / lenN;
1314 const float rOld =
restRel(idx);
1315 float rNew = rOld + dpR;
1322 if (rNew > cap && (orphA > 0.0f || orphB > 0.0f)) {
1323 float need = (rNew - cap) * lenN;
1326 const float d = Kokkos::fmin(need, orphA);
1331 if (need > 0.0f && orphB > 0.0f) {
1332 const float d = Kokkos::fmin(need, orphB);
1344 const float dR = rNew - rOld;
1347 Kokkos::atomic_max(&
maxApproach(), Kokkos::fabs(dR) * wRel / lenN);
1349 Kokkos::atomic_max(&
maxApproachQS(), Kokkos::fabs(dR) * wRel / lenN);
1350 const float lambdaR = -sgn * dR;
1351 const F3 JlinR =
scale3(Nsum, lambdaR);
1353 velPred(realA, 0) += JlinR.
x * invMassA;
1354 velPred(realA, 1) += JlinR.
y * invMassA;
1355 velPred(realA, 2) += JlinR.
z * invMassA;
1357 const F3 dwl{Jl.
x * invIA.
x, Jl.
y * invIA.
y, Jl.
z * invIA.
z};
1364 velPred(realB, 0) += -JlinR.
x * invMassB;
1365 velPred(realB, 1) += -JlinR.
y * invMassB;
1366 velPred(realB, 2) += -JlinR.
z * invMassB;
1368 const F3 dwl{Jl.
x * invIB.
x, Jl.
y * invIB.
y, Jl.
z * invIB.
z};
1392 const bool haveOld = (ltOld.x != 0.0f || ltOld.y != 0.0f || ltOld.z != 0.0f);
1393 if (mu > 0.0f || haveOld) {
1394 const F3 nhat =
scale3(Nsum, 1.0f / lenN);
1396 F3 vB2{0, 0, 0}, wB2{0, 0, 0};
1414 if (beta != 0.0f &&
vt0.extent(0) > 0) {
1419 const float vtLen = Kokkos::sqrt(
dot3(vtErr, vtErr));
1421 float wT = invMassA + invMassB;
1422 if (vtLen > 1e-9f) {
1423 const F3 that =
scale3(vtErr, 1.0f / vtLen);
1424 float wAt = (sf == 2) ? 0.0f : invMassA + genInvMass(
cross3v(rAavg, that), invIA, qA);
1425 float wBt = (sf == 1 || idB < 0)
1427 : invMassB + genInvMass(
cross3v(rBavg, that), invIB, qB);
1430 ltNew =
add3(ltOld,
scale3(that, -vtLen / wT));
1436 float nTot = Kokkos::fmax(
lambdaAcc(idx), 0.0f) * lenN;
1442 const float bound = mu * nTot;
1443 const float ltLen = Kokkos::sqrt(
dot3(ltNew, ltNew));
1445 ltNew = (bound > 0.0f) ?
scale3(ltNew, bound / ltLen) :
F3{0, 0, 0};
1446 const F3 dApp =
sub3(ltNew, ltOld);
1447 if (dApp.
x != 0.0f || dApp.
y != 0.0f || dApp.
z != 0.0f) {
1451 const float dLen = Kokkos::sqrt(
dot3(dApp, dApp));
1458 velPred(realA, 0) += dApp.
x * invMassA;
1459 velPred(realA, 1) += dApp.
y * invMassA;
1460 velPred(realA, 2) += dApp.
z * invMassA;
1462 const F3 dwl{Jl.
x * invIA.
x, Jl.
y * invIA.
y, Jl.
z * invIA.
z};
1468 if (idB >= 0 && applyB) {
1469 velPred(realB, 0) += -dApp.
x * invMassB;
1470 velPred(realB, 1) += -dApp.
y * invMassB;
1471 velPred(realB, 2) += -dApp.
z * invMassB;
1473 const F3 dwl{Jl.
x * invIB.
x, Jl.
y * invIB.
y, Jl.
z * invIB.
z};
1490 Kokkos::View<const ManifoldC*, CpMem> manifolds, Kokkos::View<const float*, CpMem> invMass,
1491 Kokkos::View<
const float* [3],
CpMem> invInertia, Kokkos::View<
const float* [4],
CpMem> quat,
1492 Kokkos::View<
float* [3],
CpMem> velPred, Kokkos::View<
float* [3],
CpMem> angVelPred,
1493 Kokkos::View<const int*, CpMem> realIdx,
float growthRate,
float restitutionNormal,
1494 float restVelThreshold, Kokkos::View<float, CpMem> maxApproach,
1495 Kokkos::View<float, CpMem> maxApproachQS, Kokkos::View<float*, CpMem> lambdaAcc,
1496 Kokkos::View<const float*, CpMem> vn0, Kokkos::View<const unsigned char*, CpMem> sideFlag,
1497 Kokkos::View<
float* [3],
CpMem> lambdaT,
float frictionDynamic,
1498 Kokkos::View<
const float* [3],
CpMem> vt0,
float restitutionTangent,
1499 Kokkos::View<const float*, CpMem> posImpulse, Kokkos::View<float*, CpMem> restBank,
1500 Kokkos::View<float*, CpMem> restRel, Kokkos::View<const float*, CpMem> restVPeak,
1501 Kokkos::View<float*, CpMem> restOrphan, Kokkos::View<const float*, CpMem> restOrphanVPeak,
1503 return PGSManifoldSweep{manifolds,
1514 maxApproachQS.data() ? maxApproachQS : maxApproach,
1535 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
1536 Kokkos::View<const int*, CpMem> mColor,
int numColors,
1537 Kokkos::View<const float*, CpMem> invMass, Kokkos::View<
const float* [3],
CpMem> invInertia,
1538 Kokkos::View<
const float* [4],
CpMem> quat, Kokkos::View<
float* [3],
CpMem> velPred,
1539 Kokkos::View<
float* [3],
CpMem> angVelPred, Kokkos::View<const int*, CpMem> realIdx,
1540 float growthRate,
float restitutionNormal,
float restVelThreshold,
1541 Kokkos::View<float, CpMem> maxApproach, Kokkos::View<float*, CpMem> lambdaAcc,
1542 Kokkos::View<const float*, CpMem> vn0, Kokkos::View<const unsigned char*, CpMem> sideFlag,
1543 Kokkos::View<
float* [3],
CpMem> lambdaT,
float frictionDynamic,
1544 Kokkos::View<
const float* [3],
CpMem> vt0 = {},
float restitutionTangent = 0.0f,
1545 Kokkos::View<const float*, CpMem> posImpulse = {},
1546 Kokkos::View<float, CpMem> maxApproachQS = {}, Kokkos::View<float*, CpMem> restBank = {},
1547 Kokkos::View<float*, CpMem> restRel = {}, Kokkos::View<const float*, CpMem> restVPeak = {},
1548 Kokkos::View<float*, CpMem> restOrphan = {},
1549 Kokkos::View<const float*, CpMem> restOrphanVPeak = {},
1550 Kokkos::View<const int*, CpMem> colorPerm = {},
const std::vector<int>* colorOffs =
nullptr,
1551 const FusedSweepCtx* fused =
nullptr,
const FusedLoopSpec* loop =
nullptr,
1552 SlotOverride ov = {}) {
1555 manifolds, invMass, invInertia, quat, velPred, angVelPred, realIdx, growthRate,
1556 restitutionNormal, restVelThreshold, maxApproach, maxApproachQS, lambdaAcc, vn0, sideFlag,
1557 lambdaT, frictionDynamic, vt0, restitutionTangent, posImpulse, restBank, restRel, restVPeak,
1558 restOrphan, restOrphanVPeak, ov);
1563#ifdef KOKKOS_ENABLE_CUDA
1565 if (fused && fused->maxBucket > 0 && colorOffs)
1566 return demLaunchFusedSweepLoop(space, f, colorPerm, *fused, numColors, *loop,
1567 (maxApproachQS.data() ? maxApproachQS : maxApproach).data());
1570 if (fused && fused->maxBucket > 0 && colorOffs &&
1571 demLaunchFusedColorSweep(space, f, colorPerm, *fused, numColors))
1582 for (
int color = 0; color < numColors; ++color) {
1584 const int b = (*colorOffs)[color], e = (*colorOffs)[color + 1];
1587 Kokkos::parallel_for(
1588 "peclet::dem::solve_velocity_pgs", Kokkos::RangePolicy<CpExec>(space, b, e),
1589 KOKKOS_LAMBDA(
int i2) { f.solveOne(colorPerm(i2)); });
1591 Kokkos::parallel_for(
1592 "peclet::dem::solve_velocity_pgs", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
1593 KOKKOS_LAMBDA(
int idx) {
1594 if (mColor(idx) == color)
1611 int numManifolds, Kokkos::View<const int*, CpMem> realIdx,
1612 Kokkos::View<const int*, CpMem> mColor,
1613 Kokkos::View<const int*, CpMem> heights,
1614 Kokkos::View<int*, CpMem> keys,
1615 Kokkos::View<int*, CpMem> perm,
1616 std::vector<std::pair<int, int>>& buckets) {
1618 if (numManifolds <= 0)
1621 Kokkos::parallel_for(
1622 "peclet::dem::level_keys", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
1623 KOKKOS_LAMBDA(
int idx) {
1625 const int c = mColor(idx);
1628 keys(idx) = INT_MAX;
1631 int h = heights(realIdx(m.
bodyA));
1633 const int hB = heights(realIdx(m.
bodyB));
1639 keys(idx) = h * 64 + c;
1641 const auto rng = Kokkos::pair<int, int>(0, numManifolds);
1642 auto kd = Kokkos::subview(keys, rng);
1643 auto pd = Kokkos::subview(perm, rng);
1644 Kokkos::Experimental::sort_by_key(space, kd, pd);
1645 auto hk = Kokkos::create_mirror_view(kd);
1646 Kokkos::deep_copy(space, hk, kd);
1647 for (
int b = 0; b < numManifolds && hk(b) != INT_MAX;) {
1649 while (e < numManifolds && hk(e) == hk(b))
1651 buckets.emplace_back(b, e);
1663 Kokkos::View<const int*, CpMem> perm,
1664 const std::vector<std::pair<int, int>>& buckets,
1667 const int nb =
static_cast<int>(buckets.size());
1668 for (
int i = 0; i < nb; ++i) {
1669 const auto [b, e] = buckets[topDown ? nb - 1 - i : i];
1670 Kokkos::parallel_for(
1671 "peclet::dem::solve_velocity_pgs_lvl", Kokkos::RangePolicy<CpExec>(space, b, e),
1672 KOKKOS_LAMBDA(
int i2) { f.
solveOne(perm(i2)); });
1689KOKKOS_INLINE_FUNCTION
int xHolder(
unsigned long long mask,
long long t,
int numColors) {
1690 const int tc =
static_cast<int>(t % numColors);
1691 if ((mask >> tc) & 1ull)
1693 int j =
static_cast<int>(t %
xPopcount(mask));
1694 for (
int b = 0; b < 64; ++b)
1695 if ((mask >> b) & 1ull) {
1706 Kokkos::View<const unsigned long long*, CpMem>
mask;
1709 KOKKOS_INLINE_FUNCTION
bool fires(
int slotA,
int slotB)
const {
1710 if (
mask.extent(0) == 0)
1712 const unsigned long long mA =
mask(slotA);
1716 const unsigned long long mB =
mask(slotB);
1727 Kokkos::View<const int*, CpMem> realIdx,
1728 Kokkos::View<const unsigned long long*, CpMem> velMask,
1729 Kokkos::View<unsigned char*, CpMem> gate,
int color,
int numColors,
1731 Kokkos::parallel_for(
1732 "peclet::dem::x_gate", Kokkos::RangePolicy<CpExec>(0, nm), KOKKOS_LAMBDA(
int idx) {
1735 const unsigned long long mA = velMask(realIdx(m.
bodyA));
1739 const unsigned long long mB = velMask(realIdx(m.
bodyB));
1743 gate(idx) = fire ? 1 : 0;
1749 Kokkos::parallel_reduce(
1750 "peclet::dem::x_unfired", Kokkos::RangePolicy<CpExec>(0, nm),
1751 KOKKOS_LAMBDA(
int idx,
long long& acc) { acc += gate(idx) == 0 ? 1 : 0; }, n);
1763 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
1764 Kokkos::View<const int*, CpMem> mColor,
int numColors,
1765 Kokkos::View<const float*, CpMem> invMass, Kokkos::View<
const float* [3],
CpMem> invInertia,
1766 Kokkos::View<
const float* [4],
CpMem> quat, Kokkos::View<
float* [3],
CpMem> velPred,
1767 Kokkos::View<
float* [3],
CpMem> angVelPred, Kokkos::View<const int*, CpMem> realIdx,
1768 float growthRate,
float restitutionNormal,
float restVelThreshold,
1769 Kokkos::View<float, CpMem> maxApproach,
1770 Kokkos::View<const unsigned char*, CpMem> persistent = {},
1771 Kokkos::View<const float* [3], CpMem> posPred = {}, F3 gHat = {},
1772 Kokkos::View<const unsigned char*, CpMem> grounded = {}, SlotOverride ov = {},
1773 XGateSpec xg = {}) {
1777 const bool usePersist = persistent.extent(0) > 0;
1778 for (
int color = 0; color < numColors; ++color) {
1779 Kokkos::parallel_for(
1780 "peclet::dem::solve_velocity_gs", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
1781 KOKKOS_LAMBDA(
int idx) {
1782 if (mColor(idx) != color)
1784 const ManifoldC m = manifolds(idx);
1786 const int idA = m.bodyA, idB = m.bodyB;
1788 const int realA = ov.slotA(idx, realIdx(idA));
1789 const int realB = (idB >= 0) ? ov.slotB(idx, realIdx(idB)) : idB;
1791 const float invMassA = invMass(realA);
1792 const float invMassB = (idB >= 0) ? invMass(realB) : 0.0f;
1793 const F3 invIA =
ld3(invInertia, realA);
1794 const F3 invIB = (idB >= 0) ?
ld3(invInertia, realB) : F3{0, 0, 0};
1795 const F4 qA = F4{quat(realA, 0), quat(realA, 1), quat(realA, 2), quat(realA, 3)};
1796 const F4 qB = (idB >= 0)
1797 ? F4{quat(realB, 0), quat(realB, 1), quat(realB, 2), quat(realB, 3)}
1800 const F3 vA =
ld3(velPred, realA), wA =
ld3(angVelPred, realA);
1801 F3 vB{0, 0, 0}, wB{0, 0, 0};
1803 vB =
ld3(velPred, realB);
1804 wB =
ld3(angVelPred, realB);
1807 const F3 Nsum{m.normal_sum.x, m.normal_sum.y, m.normal_sum.z};
1808 const F3 TauA{m.torque_armA_sum.x, m.torque_armA_sum.y, m.torque_armA_sum.z};
1809 const F3 TauB{m.torque_armB_sum.x, m.torque_armB_sum.y, m.torque_armB_sum.z};
1811 const float invN = 1.0f /
static_cast<float>(m.num_points);
1813 float restitution = restitutionNormal;
1815 vB =
scale3(F3{m.wallVel_sum.x, m.wallVel_sum.y, m.wallVel_sum.z}, invN);
1817 const float ra = m.restitution_sum * invN;
1821 const F3 rAavg =
scale3(F3{m.rA_sum.x, m.rA_sum.y, m.rA_sum.z}, invN);
1822 const F3 rBavg =
scale3(F3{m.rB_sum.x, m.rB_sum.y, m.rB_sum.z}, invN);
1824 const float lenN = Kokkos::sqrt(
dot3(Nsum, Nsum));
1828 const F3 diffCenters = (idB < 0) ? rAavg :
sub3(rAavg, rBavg);
1829 const F3 vGrowth =
scale3(diffCenters, growthRate);
1831 float vn =
dot3(vA, Nsum) +
dot3(wA, TauA) +
dot3(vB, F3{-Nsum.x, -Nsum.y, -Nsum.z}) +
1833 vn +=
dot3(vGrowth, Nsum);
1835 const float alignment =
dot3(Nsum, diffCenters);
1836 if (alignment > 0.0f) {
1844 const float Nsq =
dot3(Nsum, Nsum);
1845 float wA_n = Nsq * invMassA +
genInvMass(TauA, invIA, qA);
1846 float wB_n = Nsq * invMassB +
genInvMass(TauB, invIB, qB);
1848 bool applyA =
true, applyB =
true;
1849 if (usePersist && persistent(idx) != 0 && idB >= 0) {
1850 const F3 dx =
sub3(
ldF3(posPred, idA),
ldF3(posPred, idB));
1851 const float up = -(dx.x * gHat.x + dx.y * gHat.y + dx.z * gHat.z);
1852 const float thr = 0.3f * Kokkos::sqrt(
dot3(dx, dx));
1853 const float riseThr = 4.0f * restVelThreshold;
1854 if (up > thr && -
dot3(vB, gHat) <= riseThr && grounded(realB) > 0) {
1858 }
else if (up < -thr && -
dot3(vA, gHat) <= riseThr && grounded(realA) > 0) {
1864 const float wTotal = wA_n + wB_n;
1871 Kokkos::atomic_max(&maxApproach(), Kokkos::fabs(vn) / lenN);
1875 if (!xg.fires(realIdx(idA), idB >= 0 ? realIdx(idB) : -1))
1878 if (Kokkos::fabs(vn) < restVelThreshold * lenN)
1881 const float lambda = (-restitution * vn - vn) / wTotal;
1883 const F3 Jlin =
scale3(Nsum, lambda);
1884 const F3 JangA =
scale3(TauA, lambda);
1885 const F3 JangB =
scale3(TauB, lambda);
1889 velPred(realA, 0) += Jlin.x * invMassA;
1890 velPred(realA, 1) += Jlin.y * invMassA;
1891 velPred(realA, 2) += Jlin.z * invMassA;
1894 const F3 dwl{Jl.x * invIA.x, Jl.y * invIA.y, Jl.z * invIA.z};
1896 angVelPred(realA, 0) += dww.x;
1897 angVelPred(realA, 1) += dww.y;
1898 angVelPred(realA, 2) += dww.z;
1901 if (idB >= 0 && applyB) {
1902 velPred(realB, 0) += -Jlin.x * invMassB;
1903 velPred(realB, 1) += -Jlin.y * invMassB;
1904 velPred(realB, 2) += -Jlin.z * invMassB;
1906 const F3 dwl{Jl.x * invIB.x, Jl.y * invIB.y, Jl.z * invIB.z};
1908 angVelPred(realB, 0) += dww.x;
1909 angVelPred(realB, 1) += dww.y;
1910 angVelPred(realB, 2) += dww.z;
dem — portable POD types + math + analytic SDFs shared by the Kokkos kernel ports.
float genInvMass(F3 tau, F3 invIlocal, F4 q)
F3 ld3(Kokkos::View< const float *[3], CpMem > v, int i)
int xHolder(unsigned long long mask, long long t, int numColors)
int colorManifoldsKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, int numReal, Kokkos::View< int *, CpMem > mColor, Kokkos::View< long long *, CpMem > bodyWinner, Kokkos::View< std::uint64_t *, CpMem > bodyMask, int &leftover, Kokkos::View< const unsigned char *, CpMem > sleepMask={}, SlotOverride ov={}, bool dedupTwins=true, int vertexSpan=0)
Greedy graph-colour the manifolds: no two manifolds sharing a real body get the same colour.
void computeXGateKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int nm, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const unsigned long long *, CpMem > velMask, Kokkos::View< unsigned char *, CpMem > gate, int color, int numColors, long long t)
The per-manifold fire gate of sync interval t: 1 iff this rank (colour color) holds every endpoint wh...
bool solveVelocityPGSKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > mColor, int numColors, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< float *[3], CpMem > velPred, Kokkos::View< float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, float growthRate, float restitutionNormal, float restVelThreshold, Kokkos::View< float, CpMem > maxApproach, Kokkos::View< float *, CpMem > lambdaAcc, Kokkos::View< const float *, CpMem > vn0, Kokkos::View< const unsigned char *, CpMem > sideFlag, Kokkos::View< float *[3], CpMem > lambdaT, float frictionDynamic, Kokkos::View< const float *[3], CpMem > vt0={}, float restitutionTangent=0.0f, Kokkos::View< const float *, CpMem > posImpulse={}, Kokkos::View< float, CpMem > maxApproachQS={}, Kokkos::View< float *, CpMem > restBank={}, Kokkos::View< float *, CpMem > restRel={}, Kokkos::View< const float *, CpMem > restVPeak={}, Kokkos::View< float *, CpMem > restOrphan={}, Kokkos::View< const float *, CpMem > restOrphanVPeak={}, Kokkos::View< const int *, CpMem > colorPerm={}, const std::vector< int > *colorOffs=nullptr, const FusedSweepCtx *fused=nullptr, const FusedLoopSpec *loop=nullptr, SlotOverride ov={})
Returns true when the sweep (or, with loop, the whole iteration loop) was submitted; false ONLY in lo...
int xPopcount(unsigned long long m)
Rank-level X (docs/contact_solve_framework.md §1.4): the holder of a body whose velocity activity mas...
void countVelocityJacobiKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< int *, CpMem > counts, bool dedupTwins=true)
Mass-split Jacobi count pass (docs/contact_solve_framework.md §3.1, D3): per body (REAL index),...
Kokkos::View< int *, CpMem > Vi
Kokkos::View< float *[3], CpMem > V3
F3 invRotateVector(F4 q, F3 v)
F3 ldF3(const V &v, int i)
void computeSideFlagsKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const unsigned char *, CpMem > persistent, Kokkos::View< const unsigned char *, CpMem > grounded, Kokkos::View< const float *[3], CpMem > posPred, Kokkos::View< const float *[3], CpMem > velPred, F3 gHat, float riseThr, Kokkos::View< unsigned char *, CpMem > sideFlag, Kokkos::View< const float *, CpMem > vn0, float approachThr, bool dedupTwins=true)
Decide each persistent contact's treatment ONCE per substep (before any impulse is applied): 0 = symm...
F3 rotateVector(F4 q, F3 v)
int colorManifoldsIncrementalKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, int numReal, Kokkos::View< const unsigned long long *, CpMem > prevKeys, Kokkos::View< const int *, CpMem > prevColor, int prevCount, Kokkos::View< int *, CpMem > mColor, Kokkos::View< long long *, CpMem > bodyWinner, Kokkos::View< std::uint64_t *, CpMem > bodyMask, int &leftover, bool forceFull, Kokkos::View< const unsigned char *, CpMem > sleepMask={}, bool dedupTwins=true)
Incremental (warm-started) manifold colouring for the single-GPU PGS path.
void decayBodyOrphanKokkos(Kokkos::View< float *, CpMem > orphan, Kokkos::View< float *, CpMem > orphanVPeak, int numOwned, float restVelThreshold)
Orphan-account aging, once per substep over the OWNED bodies: both the balance and the carried event ...
void scatterOrphanBanksKokkos(Kokkos::View< const unsigned long long *, CpMem > prevKeys, Kokkos::View< const float *, CpMem > prevRestBank, Kokkos::View< const float *, CpMem > prevRestVPeak, Kokkos::View< const unsigned char *, CpMem > matched, int prevCount, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< float *, CpMem > orphan, Kokkos::View< float *, CpMem > orphanVPeak, Kokkos::View< const int *, CpMem > gidSorted={}, Kokkos::View< const int *, CpMem > slotSorted={}, int ownedLimit=-1)
Orphan transfer: previous-ledger entries NOT matched by any current manifold (their pair died this su...
CpExec::memory_space CpMem
void solveVelocityKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< const float *[3], CpMem > velPred, Kokkos::View< const float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, float growthRate, float restitutionNormal, float restVelThreshold, Kokkos::View< float *[3], CpMem > deltaVel, Kokkos::View< float *[3], CpMem > deltaAngVel, Kokkos::View< int *, CpMem > velCounts, Kokkos::View< const int *, CpMem > onlyColor={}, int colorFilter=0, Kokkos::View< const unsigned char *, CpMem > persistent={}, Kokkos::View< const float *[3], CpMem > posPred={}, F3 gHat={}, Kokkos::View< const unsigned char *, CpMem > grounded={}, bool massSplit=false, bool dedupTwins=true)
Accumulate normal-restitution velocity deltas for numManifolds manifolds.
void buildLevelColorBucketsKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const int *, CpMem > mColor, Kokkos::View< const int *, CpMem > heights, Kokkos::View< int *, CpMem > keys, Kokkos::View< int *, CpMem > perm, std::vector< std::pair< int, int > > &buckets)
Bucket the active coloured manifolds by (support level, colour) for the level-ordered ("multilevel") ...
long long countUnfiredKokkos(Kokkos::View< const unsigned char *, CpMem > gate, int nm)
Diagnostics (iterCounters): the number of gated-off manifolds of one sweep.
void solveVelocityPGSBucketsKokkos(const PGSManifoldSweep &f, Kokkos::View< const int *, CpMem > perm, const std::vector< std::pair< int, int > > &buckets, bool topDown)
Level-ordered symmetric sweep: launch one PGS kernel per (level, colour) bucket, ascending (bottom-up...
long long colorKey(int idx)
splitmix32 finalizer: a well-mixed pseudo-random priority per edge index.
void applyVelocityDeltasAveragedKokkos(int n, V3 velPred, V3 angVelPred, V3 deltaVel, V3 deltaAngVel, Vi velCounts, bool averaged=true)
Apply the accumulated velocity deltas AVERAGED by the per-body manifold count — the velocity- solve t...
constexpr int kColorUncolourable
void warmStartApplyKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< float *[3], CpMem > velPred, Kokkos::View< float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const float *, CpMem > warmP, Kokkos::View< float *[3], CpMem > warmT, bool dedupTwins=true)
Apply the warm-start impulses up front (order-independent: fixed impulses, atomic adds).
void computeVn0Kokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const float *[3], CpMem > velPred, Kokkos::View< const float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, float growthRate, Kokkos::View< float *, CpMem > vn0, Kokkos::View< float *[3], CpMem > vt0, bool dedupTwins=true)
-— Warm-started projected Gauss-Seidel (PGS) velocity solve -— Nonsmooth contact dynamics (Moreau-Jea...
void solveVelocityColoredGSKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > mColor, int numColors, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< float *[3], CpMem > velPred, Kokkos::View< float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, float growthRate, float restitutionNormal, float restVelThreshold, Kokkos::View< float, CpMem > maxApproach, Kokkos::View< const unsigned char *, CpMem > persistent={}, Kokkos::View< const float *[3], CpMem > posPred={}, F3 gHat={}, Kokkos::View< const unsigned char *, CpMem > grounded={}, SlotOverride ov={}, XGateSpec xg={})
Colored Gauss–Seidel normal-restitution solve: sweep the numColors colour classes in order,...
constexpr int kColorPalette
Colouring palette (docs/contact_solve_framework.md §4.2 item 1): the greedy takes the lowest colour f...
std::tuple< double, float, int > restBankStatsKokkos(Kokkos::View< const float *, CpMem > bank, int n)
Poisson-restitution diagnostics: (sum, max, count>0) over the committed owed-impulse store (namespace...
PGSManifoldSweep makePGSManifoldSweep(Kokkos::View< const ManifoldC *, CpMem > manifolds, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< float *[3], CpMem > velPred, Kokkos::View< float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, float growthRate, float restitutionNormal, float restVelThreshold, Kokkos::View< float, CpMem > maxApproach, Kokkos::View< float, CpMem > maxApproachQS, Kokkos::View< float *, CpMem > lambdaAcc, Kokkos::View< const float *, CpMem > vn0, Kokkos::View< const unsigned char *, CpMem > sideFlag, Kokkos::View< float *[3], CpMem > lambdaT, float frictionDynamic, Kokkos::View< const float *[3], CpMem > vt0, float restitutionTangent, Kokkos::View< const float *, CpMem > posImpulse, Kokkos::View< float *, CpMem > restBank, Kokkos::View< float *, CpMem > restRel, Kokkos::View< const float *, CpMem > restVPeak, Kokkos::View< float *, CpMem > restOrphan, Kokkos::View< const float *, CpMem > restOrphanVPeak, SlotOverride ov={})
Build the shared per-manifold sweep functor (the colored launch loop, the fused kernels and the fused...
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.
void updateRestitutionBankKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< const float *[3], CpMem > velPred, Kokkos::View< const float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, float growthRate, float restitutionNormal, float restVelThreshold, Kokkos::View< const float *, CpMem > vn0, Kokkos::View< const float *, CpMem > lambdaAcc, Kokkos::View< const float *, CpMem > restRel, Kokkos::View< float *, CpMem > restBank, Kokkos::View< float *, CpMem > restVPeak, bool dedupTwins=true)
Event-level (Poisson) restitution bookkeeping, once per substep AFTER all velocity phases (restitutio...
dem — fused colour sweeps: one persistent kernel per sweep instead of one kernel launch per colour.
Portable mirror of ManifoldConstraint.
One full colored PGS sweep.
Kokkos::View< float, CpMem > maxApproach
Kokkos::View< float, CpMem > maxApproachQS
Kokkos::View< float *[3], CpMem > lambdaT
Kokkos::View< const float *, CpMem > restOrphanVPeak
Kokkos::View< const float *, CpMem > invMass
Kokkos::View< const float *[4], CpMem > quat
Kokkos::View< const int *, CpMem > realIdx
Kokkos::View< const float *[3], CpMem > vt0
Kokkos::View< const float *[3], CpMem > invInertia
Kokkos::View< const unsigned char *, CpMem > sideFlag
Kokkos::View< float *, CpMem > lambdaAcc
void solveOne(int idx) const
Kokkos::View< float *[3], CpMem > velPred
Kokkos::View< const float *, CpMem > restVPeak
Kokkos::View< const float *, CpMem > vn0
Kokkos::View< float *, CpMem > restOrphan
Kokkos::View< const float *, CpMem > posImpulse
Kokkos::View< const ManifoldC *, CpMem > manifolds
Kokkos::View< float *[3], CpMem > angVelPred
Kokkos::View< float *, CpMem > restRel
Kokkos::View< float *, CpMem > restBank
Per-edge slot overrides of a phase's hub copies (docs/contact_solve_framework.md §4....
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
Rank-level X evaluated inline in the one-shot sweep (only for the manifolds of the colour being swept...
Kokkos::View< const unsigned long long *, CpMem > mask
bool fires(int slotA, int slotB) const