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;
40 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
41 Kokkos::View<const float*, CpMem> invMass, Kokkos::View<
const float* [3],
CpMem> invInertia,
42 Kokkos::View<
const float* [4],
CpMem> quat, Kokkos::View<
const float* [3],
CpMem> velPred,
43 Kokkos::View<
const float* [3],
CpMem> angVelPred, Kokkos::View<const int*, CpMem> realIdx,
44 float growthRate,
float restitutionNormal,
float restVelThreshold,
45 Kokkos::View<
float* [3],
CpMem> deltaVel, Kokkos::View<
float* [3],
CpMem> deltaAngVel,
46 Kokkos::View<int*, CpMem> velCounts, Kokkos::View<const int*, CpMem> onlyColor = {},
47 int colorFilter = 0, Kokkos::View<const unsigned char*, CpMem> persistent = {},
48 Kokkos::View<const float* [3], CpMem> posPred = {},
F3 gHat = {},
49 Kokkos::View<const unsigned char*, CpMem> grounded = {}) {
53 const bool filt = onlyColor.extent(0) > 0;
54 const bool usePersist = persistent.extent(0) > 0;
56 "peclet::dem::solve_velocity", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
57 KOKKOS_LAMBDA(
int idx) {
58 if (filt && onlyColor(idx) != colorFilter)
60 const ManifoldC m = manifolds(idx);
61 if (m.num_points <= 0)
64 const int idA = m.bodyA, idB = m.bodyB;
65 const int realA = realIdx(idA);
73 const float invMassA = invMass(realA);
74 const float invMassB = (idB >= 0) ? invMass(realB) : 0.0f;
75 const F3 invIA =
ld3(invInertia, realA);
76 const F3 invIB = (idB >= 0) ?
ld3(invInertia, realB) :
F3{0, 0, 0};
77 const F4 qA = F4{quat(realA, 0), quat(realA, 1), quat(realA, 2), quat(realA, 3)};
78 const F4 qB = (idB >= 0)
79 ? F4{quat(realB, 0), quat(realB, 1), quat(realB, 2), quat(realB, 3)}
82 const F3 vA =
ld3(velPred, realA), wA =
ld3(angVelPred, realA);
83 F3 vB{0, 0, 0}, wB{0, 0, 0};
85 vB =
ld3(velPred, realB);
86 wB =
ld3(angVelPred, realB);
89 const F3 Nsum{m.normal_sum.x, m.normal_sum.y, m.normal_sum.z};
90 const F3 TauA{m.torque_armA_sum.x, m.torque_armA_sum.y, m.torque_armA_sum.z};
91 const F3 TauB{m.torque_armB_sum.x, m.torque_armB_sum.y, m.torque_armB_sum.z};
93 const float invN = 1.0f /
static_cast<float>(m.num_points);
98 float restitution = restitutionNormal;
100 vB =
scale3(
F3{m.wallVel_sum.x, m.wallVel_sum.y, m.wallVel_sum.z}, invN);
102 const float ra = m.restitution_sum * invN;
106 const F3 rAavg =
scale3(
F3{m.rA_sum.x, m.rA_sum.y, m.rA_sum.z}, invN);
107 const F3 rBavg =
scale3(
F3{m.rB_sum.x, m.rB_sum.y, m.rB_sum.z}, invN);
109 const float lenN = Kokkos::sqrt(
dot3(Nsum, Nsum));
121 const F3 diffCenters = (idB < 0) ? rAavg :
sub3(rAavg, rBavg);
122 const F3 vGrowth =
scale3(diffCenters, growthRate);
124 float vn =
dot3(vA, Nsum) +
dot3(wA, TauA) +
dot3(vB,
F3{-Nsum.x, -Nsum.y, -Nsum.z}) +
126 vn +=
dot3(vGrowth, Nsum);
128 const float alignment =
dot3(Nsum, diffCenters);
129 if (alignment > 0.0f) {
137 const float Nsq =
dot3(Nsum, Nsum);
138 float wA_n = Nsq * invMassA +
genInvMass(TauA, invIA, qA);
139 float wB_n = Nsq * invMassB +
genInvMass(TauB, invIB, qB);
153 bool applyA =
true, applyB =
true;
154 if (usePersist && persistent(idx) != 0 && idB >= 0) {
156 const float up = -(dx.x * gHat.x + dx.y * gHat.y + dx.z * gHat.z);
157 const float thr = 0.3f * Kokkos::sqrt(
dot3(dx, dx));
158 const float riseThr =
159 4.0f * restVelThreshold;
163 if (up > thr && -
dot3(vB, gHat) <= riseThr && grounded(realB) > 0) {
168 }
else if (up < -thr && -
dot3(vA, gHat) <= riseThr && grounded(realA) > 0) {
174 const float wTotal = wA_n + wB_n;
185 if (Kokkos::fabs(vn) < restVelThreshold * lenN)
188 const float lambda = (-restitution * vn - vn) / wTotal;
190 const F3 Jlin =
scale3(Nsum, lambda);
191 const F3 JangA =
scale3(TauA, lambda);
192 const F3 JangB =
scale3(TauB, lambda);
196 Kokkos::atomic_add(&deltaVel(realA, 0), Jlin.x * invMassA);
197 Kokkos::atomic_add(&deltaVel(realA, 1), Jlin.y * invMassA);
198 Kokkos::atomic_add(&deltaVel(realA, 2), Jlin.z * invMassA);
202 const F3 dwl{Jl.x * invIA.x, Jl.y * invIA.y, Jl.z * invIA.z};
204 Kokkos::atomic_add(&deltaAngVel(realA, 0), dww.x);
205 Kokkos::atomic_add(&deltaAngVel(realA, 1), dww.y);
206 Kokkos::atomic_add(&deltaAngVel(realA, 2), dww.z);
209 if (idB >= 0 && applyB) {
210 Kokkos::atomic_add(&deltaVel(realB, 0), -Jlin.x * invMassB);
211 Kokkos::atomic_add(&deltaVel(realB, 1), -Jlin.y * invMassB);
212 Kokkos::atomic_add(&deltaVel(realB, 2), -Jlin.z * invMassB);
214 const F3 dwl{Jl.x * invIB.x, Jl.y * invIB.y, Jl.z * invIB.z};
216 Kokkos::atomic_add(&deltaAngVel(realB, 0), dww.x);
217 Kokkos::atomic_add(&deltaAngVel(realB, 1), dww.y);
218 Kokkos::atomic_add(&deltaAngVel(realB, 2), dww.z);
219 Kokkos::atomic_add(&velCounts(realB), 1);
222 Kokkos::atomic_add(&velCounts(realA), 1);
232template <
class V3,
class Vi>
234 V3 deltaAngVel,
Vi velCounts) {
236 Kokkos::parallel_for(
237 "peclet::dem::apply_vel_avg", Kokkos::RangePolicy<CpExec>(space, 0, n), KOKKOS_LAMBDA(
int i) {
238 const int count = velCounts(i);
244 const float f = Kokkos::fmin(1.0f, 2.0f /
static_cast<float>(count));
245 for (
int c = 0; c < 3; ++c) {
246 velPred(i, c) += deltaVel(i, c) * f;
247 angVelPred(i, c) += deltaAngVel(i, c) * f;
248 deltaVel(i, c) = 0.0f;
249 deltaAngVel(i, c) = 0.0f;
278 Kokkos::View<const int*, CpMem> realIdx,
int numReal,
279 Kokkos::View<int*, CpMem> mColor,
280 Kokkos::View<long long*, CpMem> bodyWinner,
281 Kokkos::View<std::uint64_t*, CpMem> bodyMask,
int& leftover,
282 Kokkos::View<const unsigned char*, CpMem> sleepMask = {}) {
285 if (numManifolds <= 0 || numReal <= 0)
287 const bool sleepOn = sleepMask.extent(0) > 0;
288 Kokkos::parallel_for(
289 "peclet::dem::color_init_bodies", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
290 KOKKOS_LAMBDA(
int i) { bodyMask(i) = 0; });
292 Kokkos::parallel_for(
293 "peclet::dem::color_init_manifolds", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
294 KOKKOS_LAMBDA(
int idx) {
295 const ManifoldC m = manifolds(idx);
296 if (m.num_points <= 0 || (sleepOn && sleepMask(idx))) {
300 if (m.bodyB >= 0 && realIdx(m.bodyA) > realIdx(m.bodyB)) {
307 int remaining = 1, prevRemaining = -1;
308 const int maxRounds = numReal + 2;
309 for (
int round = 0; round < maxRounds && remaining > 0; ++round) {
310 Kokkos::parallel_for(
311 "peclet::dem::color_reset_winner", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
312 KOKKOS_LAMBDA(
int i) { bodyWinner(i) = -1; });
313 Kokkos::parallel_for(
314 "peclet::dem::color_contend", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
315 KOKKOS_LAMBDA(
int idx) {
316 if (mColor(idx) != -1)
318 const ManifoldC m = manifolds(idx);
319 const long long key =
colorKey(idx);
320 Kokkos::atomic_max(&bodyWinner(realIdx(m.bodyA)), key);
322 Kokkos::atomic_max(&bodyWinner(realIdx(m.bodyB)), key);
325 Kokkos::parallel_reduce(
326 "peclet::dem::color_commit", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
327 KOKKOS_LAMBDA(
int idx,
int& acc) {
328 if (mColor(idx) != -1)
330 const ManifoldC m = manifolds(idx);
331 const int ea = realIdx(m.bodyA);
332 const int eb = (m.bodyB >= 0) ? realIdx(m.bodyB) : -1;
334 const long long key =
colorKey(idx);
335 if (bodyWinner(ea) != key || (eb >= 0 && bodyWinner(eb) != key)) {
339 std::uint64_t forbidden = bodyMask(ea);
341 forbidden |= bodyMask(eb);
343 while (c < 62 && (forbidden & (std::uint64_t(1) << c)))
346 const std::uint64_t bit = std::uint64_t(1) << c;
353 if (rem == prevRemaining)
361 Kokkos::parallel_reduce(
362 "peclet::dem::color_max", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
363 KOKKOS_LAMBDA(
int idx,
int& mx) {
364 if (mColor(idx) > mx)
367 Kokkos::Max<int>(maxc));
368 Kokkos::parallel_reduce(
369 "peclet::dem::color_leftover", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
370 KOKKOS_LAMBDA(
int idx,
int& acc) {
371 if (mColor(idx) == -1)
396 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
397 Kokkos::View<const int*, CpMem> realIdx,
int numReal,
398 Kokkos::View<const unsigned long long*, CpMem> prevKeys,
399 Kokkos::View<const int*, CpMem> prevColor,
int prevCount, Kokkos::View<int*, CpMem> mColor,
400 Kokkos::View<long long*, CpMem> bodyWinner, Kokkos::View<std::uint64_t*, CpMem> bodyMask,
401 int& leftover,
bool forceFull, Kokkos::View<const unsigned char*, CpMem> sleepMask = {}) {
404 if (numManifolds <= 0 || numReal <= 0)
406 Kokkos::parallel_for(
407 "peclet::dem::icolor_init_bodies", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
408 KOKKOS_LAMBDA(
int i) { bodyMask(i) = 0; });
409 const bool full = forceFull || prevCount <= 0;
410 const bool sleepOn = sleepMask.extent(0) > 0;
413 Kokkos::parallel_for(
414 "peclet::dem::icolor_seed", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
415 KOKKOS_LAMBDA(
int idx) {
416 const ManifoldC m = manifolds(idx);
417 if (m.num_points <= 0 || (sleepOn && sleepMask(idx))) {
421 if (m.bodyB >= 0 && realIdx(m.bodyA) > realIdx(m.bodyB)) {
427 const unsigned long long k =
pairKeyOf(m, realIdx);
428 int lo = 0, hi = prevCount;
430 const int mid = (lo + hi) >> 1;
431 if (prevKeys(mid) < k)
436 if (lo < prevCount && prevKeys(lo) == k) {
437 const int pc = prevColor(lo);
446 Kokkos::parallel_for(
447 "peclet::dem::icolor_seed_mask", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
448 KOKKOS_LAMBDA(
int idx) {
449 const int c = mColor(idx);
452 const ManifoldC m = manifolds(idx);
453 const std::uint64_t bit = std::uint64_t(1) << c;
454 Kokkos::atomic_or(&bodyMask(realIdx(m.bodyA)), bit);
456 Kokkos::atomic_or(&bodyMask(realIdx(m.bodyB)), bit);
460 int remaining = 1, prevRemaining = -1;
461 const int maxRounds = numReal + 2;
462 for (
int round = 0; round < maxRounds && remaining > 0; ++round) {
463 Kokkos::parallel_for(
464 "peclet::dem::icolor_reset_winner", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
465 KOKKOS_LAMBDA(
int i) { bodyWinner(i) = -1; });
466 Kokkos::parallel_for(
467 "peclet::dem::icolor_contend", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
468 KOKKOS_LAMBDA(
int idx) {
469 if (mColor(idx) != -1)
471 const ManifoldC m = manifolds(idx);
472 const long long key =
colorKey(idx);
473 Kokkos::atomic_max(&bodyWinner(realIdx(m.bodyA)), key);
475 Kokkos::atomic_max(&bodyWinner(realIdx(m.bodyB)), key);
478 Kokkos::parallel_reduce(
479 "peclet::dem::icolor_commit", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
480 KOKKOS_LAMBDA(
int idx,
int& acc) {
481 if (mColor(idx) != -1)
483 const ManifoldC m = manifolds(idx);
484 const int ea = realIdx(m.bodyA);
485 const int eb = (m.bodyB >= 0) ? realIdx(m.bodyB) : -1;
486 const long long key =
colorKey(idx);
487 if (bodyWinner(ea) != key || (eb >= 0 && bodyWinner(eb) != key)) {
491 std::uint64_t forbidden = bodyMask(ea);
493 forbidden |= bodyMask(eb);
495 while (c < 62 && (forbidden & (std::uint64_t(1) << c)))
498 const std::uint64_t bit = std::uint64_t(1) << c;
505 if (rem == prevRemaining)
511 Kokkos::parallel_reduce(
512 "peclet::dem::icolor_max", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
513 KOKKOS_LAMBDA(
int idx,
int& mx) {
514 if (mColor(idx) > mx)
517 Kokkos::Max<int>(maxc));
518 Kokkos::parallel_reduce(
519 "peclet::dem::icolor_leftover", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
520 KOKKOS_LAMBDA(
int idx,
int& acc) {
521 if (mColor(idx) == -1)
543inline void computeVn0Kokkos(Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
544 Kokkos::View<
const float* [3],
CpMem> velPred,
545 Kokkos::View<
const float* [3],
CpMem> angVelPred,
546 Kokkos::View<const int*, CpMem> realIdx,
float growthRate,
547 Kokkos::View<float*, CpMem> vn0, Kokkos::View<
float* [3],
CpMem> vt0) {
550 Kokkos::parallel_for(
551 "peclet::dem::pgs_vn0", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
552 KOKKOS_LAMBDA(
int idx) {
557 const int realA = realIdx(idA);
558 if (idB >= 0 && realA > realIdx(idB))
560 const float invN = 1.0f /
static_cast<float>(m.
num_points);
564 const F3 vA = ld3(velPred, realA), wA = ld3(angVelPred, realA);
565 F3 vB{0, 0, 0}, wB{0, 0, 0};
567 vB = ld3(velPred, realIdx(idB));
568 wB = ld3(angVelPred, realIdx(idB));
574 const F3 diffCenters = (idB < 0) ? rAavg :
sub3(rAavg, rBavg);
575 const F3 vGrowth =
scale3(diffCenters, growthRate);
576 float vn =
dot3(vA, Nsum) +
dot3(wA, TauA) +
dot3(vB,
F3{-Nsum.
x, -Nsum.y, -Nsum.z}) +
578 vn +=
dot3(vGrowth, Nsum);
583 const float lenN = Kokkos::sqrt(
dot3(Nsum, Nsum));
586 const F3 nhat =
scale3(Nsum, 1.0f / lenN);
587 const int realB2 = (idB >= 0) ? realIdx(idB) : -1;
588 const F3 wAv = ld3(angVelPred, realA);
589 const F3 wBv = (realB2 >= 0) ? ld3(angVelPred, realB2) :
F3{0, 0, 0};
613 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
614 Kokkos::View<const float*, CpMem> invMass, Kokkos::View<
const float* [3],
CpMem> invInertia,
615 Kokkos::View<
const float* [4],
CpMem> quat, Kokkos::View<
const float* [3],
CpMem> velPred,
616 Kokkos::View<
const float* [3],
CpMem> angVelPred, Kokkos::View<const int*, CpMem> realIdx,
617 float growthRate,
float restitutionNormal,
float restVelThreshold,
618 Kokkos::View<const float*, CpMem> vn0, Kokkos::View<const float*, CpMem> lambdaAcc,
619 Kokkos::View<const float*, CpMem> restRel, Kokkos::View<float*, CpMem> restBank,
620 Kokkos::View<float*, CpMem> restVPeak) {
624 Kokkos::parallel_for(
625 "peclet::dem::rest_bank_update", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
626 KOKKOS_LAMBDA(
int idx) {
631 const int realA = realIdx(idA);
632 const int realB = (idB >= 0) ? realIdx(idB) : idB;
633 if (idB >= 0 && realA > realB)
635 const float invN = 1.0f /
static_cast<float>(m.
num_points);
637 const float lenN = Kokkos::sqrt(
dot3(Nsum, Nsum));
642 const F3 vA = ld3(velPred, realA), wA = ld3(angVelPred, realA);
643 F3 vB{0, 0, 0}, wB{0, 0, 0};
645 vB = ld3(velPred, realB);
646 wB = ld3(angVelPred, realB);
652 const F3 diffCenters = (idB < 0) ? rAavg :
sub3(rAavg, rBavg);
653 const F3 vGrowth =
scale3(diffCenters, growthRate);
654 float vn =
dot3(vA, Nsum) +
dot3(wA, TauA) +
dot3(vB,
F3{-Nsum.
x, -Nsum.y, -Nsum.z}) +
656 vn +=
dot3(vGrowth, Nsum);
657 const float alignment =
dot3(Nsum, diffCenters);
658 const float sgn = (alignment > 0.0f) ? 1.0f : -1.0f;
661 const F3 invIA = ld3(invInertia, realA);
662 const F3 invIB = (idB >= 0) ? ld3(invInertia, realB) :
F3{0, 0, 0};
663 const F4 qA =
F4{quat(realA, 0), quat(realA, 1), quat(realA, 2), quat(realA, 3)};
664 const F4 qB = (idB >= 0)
665 ?
F4{quat(realB, 0), quat(realB, 1), quat(realB, 2), quat(realB, 3)}
667 const float Nsq =
dot3(Nsum, Nsum);
668 const float wTotal = Nsq * invMass(realA) + genInvMass(TauA, invIA, qA) +
669 Nsq * ((idB >= 0) ? invMass(realB) : 0.0f) +
670 genInvMass(TauB, invIB, qB);
673 float e = restitutionNormal;
679 const float v0til = sgn * vn0(idx);
680 const float vtilEnd = sgn * vn;
681 float owed = restBank(idx);
683 float vPeak = restVPeak(idx) * (1.0f - 1.0f / 256.0f);
684 if (v0til > restVelThreshold * lenN)
685 vPeak = Kokkos::fmax(vPeak, v0til / lenN);
686 if (vPeak > restVelThreshold) {
692 const float pR = Kokkos::fmax(0.0f, -vtilEnd) / wTotal * lenN;
693 const float pTot = Kokkos::fmax(lambdaAcc(idx), 0.0f) * lenN;
694 const float pC = Kokkos::fmax(0.0f, pTot - pR);
695 owed += e * pC - pR - Kokkos::fmax(restRel(idx), 0.0f) * lenN;
696 restBank(idx) = Kokkos::fmax(owed, 0.0f);
697 restVPeak(idx) = vPeak;
699 restBank(idx) = 0.0f;
700 restVPeak(idx) = 0.0f;
712 Kokkos::View<float*, CpMem> orphanVPeak,
int numOwned,
713 float restVelThreshold) {
715 Kokkos::parallel_for(
716 "peclet::dem::rest_orphan_decay", Kokkos::RangePolicy<CpExec>(space, 0, numOwned),
717 KOKKOS_LAMBDA(
int i) {
718 const float decayed = orphanVPeak(i) * (1.0f - 1.0f / 64.0f);
719 if (decayed <= restVelThreshold || orphan(i) <= 0.0f) {
721 orphanVPeak(i) = 0.0f;
723 orphan(i) *= (1.0f - 1.0f / 64.0f);
724 orphanVPeak(i) = decayed;
739 Kokkos::View<const float*, CpMem> prevRestBank,
740 Kokkos::View<const float*, CpMem> prevRestVPeak,
741 Kokkos::View<const unsigned char*, CpMem> matched,
742 int prevCount, Kokkos::View<const float*, CpMem> invMass,
743 Kokkos::View<float*, CpMem> orphan,
744 Kokkos::View<float*, CpMem> orphanVPeak,
745 Kokkos::View<const int*, CpMem> gidSorted = {},
746 Kokkos::View<const int*, CpMem> slotSorted = {}) {
748 const int nMap =
static_cast<int>(gidSorted.extent(0));
749 const int nBody =
static_cast<int>(orphan.extent(0));
750 Kokkos::parallel_for(
751 "peclet::dem::rest_orphan_scatter", Kokkos::RangePolicy<CpExec>(space, 0, prevCount),
752 KOKKOS_LAMBDA(
int e) {
755 const float owed = prevRestBank(e);
758 const unsigned long long k = prevKeys(e);
761 const unsigned hi =
static_cast<unsigned>(k >> 32);
762 const unsigned lo =
static_cast<unsigned>(k & 0xFFFFFFFFu);
763 auto resolve = [&](
unsigned id) ->
int {
765 return (
static_cast<int>(
id) < nBody) ?
static_cast<int>(
id) : -1;
768 const int m = (a + b) >> 1;
769 if (gidSorted(m) <
static_cast<int>(id))
774 return (a < nMap && gidSorted(a) ==
static_cast<int>(id)) ? slotSorted(a) : -1;
776 const int sA = resolve(hi);
777 const int sB = (lo != 0xFFFFFFFFu) ? resolve(lo) : -1;
778 const float vpk = prevRestVPeak(e);
780 if (lo != 0xFFFFFFFFu) {
781 if (sB >= 0 && sA >= 0) {
782 const float wA = invMass(sA), wB = invMass(sB);
784 shareA = wB / (wA + wB);
789 if (sA >= 0 && shareA > 0.0f) {
790 Kokkos::atomic_add(&orphan(sA), owed * shareA);
791 Kokkos::atomic_max(&orphanVPeak(sA), vpk);
793 if (sB >= 0 && shareA < 1.0f) {
794 Kokkos::atomic_add(&orphan(sB), owed * (1.0f - shareA));
795 Kokkos::atomic_max(&orphanVPeak(sB), vpk);
808 Kokkos::parallel_reduce(
809 "peclet::dem::rest_bank_stats", Kokkos::RangePolicy<CpExec>(0, n),
810 KOKKOS_LAMBDA(
int i,
double& ls,
float& lm,
int& lc) {
811 const float v = bank(i);
818 s, Kokkos::Max<float>(mx), Kokkos::Sum<int>(cnt));
831 int numManifolds, Kokkos::View<const int*, CpMem> realIdx,
832 Kokkos::View<const unsigned char*, CpMem> persistent,
833 Kokkos::View<const unsigned char*, CpMem> grounded,
834 Kokkos::View<
const float* [3],
CpMem> posPred,
835 Kokkos::View<
const float* [3],
CpMem> velPred,
F3 gHat,
836 float riseThr, Kokkos::View<unsigned char*, CpMem> sideFlag,
837 Kokkos::View<const float*, CpMem> vn0,
float approachThr) {
840 Kokkos::parallel_for(
841 "peclet::dem::pgs_side_flags", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
842 KOKKOS_LAMBDA(
int idx) {
843 const unsigned char wasPersistent = persistent(idx);
853 if (Kokkos::fabs(vn0(idx)) > approachThr)
855 const int realA = realIdx(m.
bodyA), realB = realIdx(m.
bodyB);
859 const float up = -(dx.
x * gHat.
x + dx.
y * gHat.
y + dx.
z * gHat.
z);
860 const float thr = 0.3f * Kokkos::sqrt(
dot3(dx, dx));
861 if (up > thr && -
dot3(ld3(velPred, realB), gHat) <= riseThr && grounded(realB) > 0) {
863 }
else if (up < -thr && -
dot3(ld3(velPred, realA), gHat) <= riseThr &&
864 grounded(realA) > 0) {
872 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
873 Kokkos::View<const float*, CpMem> invMass, Kokkos::View<
const float* [3],
CpMem> invInertia,
874 Kokkos::View<
const float* [4],
CpMem> quat, Kokkos::View<
float* [3],
CpMem> velPred,
875 Kokkos::View<
float* [3],
CpMem> angVelPred, Kokkos::View<const int*, CpMem> realIdx,
876 Kokkos::View<const float*, CpMem> warmP, Kokkos::View<
float* [3],
CpMem> warmT) {
879 Kokkos::parallel_for(
880 "peclet::dem::pgs_warm_apply", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
881 KOKKOS_LAMBDA(
int idx) {
882 const float p = warmP(idx);
883 F3 lt{warmT(idx, 0), warmT(idx, 1), warmT(idx, 2)};
884 const bool hasT = (lt.x != 0.0f || lt.y != 0.0f || lt.z != 0.0f);
885 if (p == 0.0f && !hasT)
891 const int realA = realIdx(idA);
892 const int realB = (idB >= 0) ? realIdx(idB) : idB;
893 if (idB >= 0 && realA > realB)
895 const float invN = 1.0f /
static_cast<float>(m.
num_points);
901 const F3 diffCenters = (idB < 0) ? rAavg :
sub3(rAavg, rBavg);
902 const float alignment =
dot3(Nsum, diffCenters);
903 const float sgn = (alignment > 0.0f) ? 1.0f : -1.0f;
904 const float lambda = -sgn * p;
905 const F3 Jlin =
scale3(Nsum, lambda);
906 const float invMassA = invMass(realA);
907 const float invMassB = (idB >= 0) ? invMass(realB) : 0.0f;
908 Kokkos::atomic_add(&velPred(realA, 0), Jlin.
x * invMassA);
909 Kokkos::atomic_add(&velPred(realA, 1), Jlin.
y * invMassA);
910 Kokkos::atomic_add(&velPred(realA, 2), Jlin.
z * invMassA);
912 const F4 qA =
F4{quat(realA, 0), quat(realA, 1), quat(realA, 2), quat(realA, 3)};
914 const F3 invIA = ld3(invInertia, realA);
915 const F3 dwl{Jl.
x * invIA.
x, Jl.
y * invIA.
y, Jl.
z * invIA.
z};
917 Kokkos::atomic_add(&angVelPred(realA, 0), dww.
x);
918 Kokkos::atomic_add(&angVelPred(realA, 1), dww.
y);
919 Kokkos::atomic_add(&angVelPred(realA, 2), dww.
z);
922 Kokkos::atomic_add(&velPred(realB, 0), -Jlin.
x * invMassB);
923 Kokkos::atomic_add(&velPred(realB, 1), -Jlin.
y * invMassB);
924 Kokkos::atomic_add(&velPred(realB, 2), -Jlin.
z * invMassB);
925 const F4 qB =
F4{quat(realB, 0), quat(realB, 1), quat(realB, 2), quat(realB, 3)};
927 const F3 invIB = ld3(invInertia, realB);
928 const F3 dwl{Jl.
x * invIB.
x, Jl.
y * invIB.
y, Jl.
z * invIB.
z};
930 Kokkos::atomic_add(&angVelPred(realB, 0), dww.
x);
931 Kokkos::atomic_add(&angVelPred(realB, 1), dww.
y);
932 Kokkos::atomic_add(&angVelPred(realB, 2), dww.
z);
938 const float lenN2 = Kokkos::sqrt(
dot3(Nsum, Nsum));
940 const F3 nhat =
scale3(Nsum, 1.0f / lenN2);
943 warmT(idx, 0) = lt.x;
944 warmT(idx, 1) = lt.y;
945 warmT(idx, 2) = lt.z;
946 Kokkos::atomic_add(&velPred(realA, 0), lt.x * invMassA);
947 Kokkos::atomic_add(&velPred(realA, 1), lt.y * invMassA);
948 Kokkos::atomic_add(&velPred(realA, 2), lt.z * invMassA);
950 const F4 qA =
F4{quat(realA, 0), quat(realA, 1), quat(realA, 2), quat(realA, 3)};
952 const F3 invIA = ld3(invInertia, realA);
953 const F3 dwl{Jl.
x * invIA.
x, Jl.
y * invIA.
y, Jl.
z * invIA.
z};
955 Kokkos::atomic_add(&angVelPred(realA, 0), dww.
x);
956 Kokkos::atomic_add(&angVelPred(realA, 1), dww.
y);
957 Kokkos::atomic_add(&angVelPred(realA, 2), dww.
z);
960 Kokkos::atomic_add(&velPred(realB, 0), -lt.x * invMassB);
961 Kokkos::atomic_add(&velPred(realB, 1), -lt.y * invMassB);
962 Kokkos::atomic_add(&velPred(realB, 2), -lt.z * invMassB);
963 const F4 qB =
F4{quat(realB, 0), quat(realB, 1), quat(realB, 2), quat(realB, 3)};
965 const F3 invIB = ld3(invInertia, realB);
966 const F3 dwl{Jl.
x * invIB.
x, Jl.
y * invIB.
y, Jl.
z * invIB.
z};
968 Kokkos::atomic_add(&angVelPred(realB, 0), dww.
x);
969 Kokkos::atomic_add(&angVelPred(realB, 1), dww.
y);
970 Kokkos::atomic_add(&angVelPred(realB, 2), dww.
z);
989 Kokkos::View<const float* [4], CpMem>
quat;
1002 Kokkos::View<const float*, CpMem>
vn0;
1006 Kokkos::View<const float* [3], CpMem>
vt0;
1052 const int realA =
realIdx(idA);
1053 const int realB = (idB >= 0) ?
realIdx(idB) : idB;
1054 const float invMassA =
invMass(realA);
1055 const float invMassB = (idB >= 0) ?
invMass(realB) : 0.0f;
1057 const F3 invIB = (idB >= 0) ? ld3(
invInertia, realB) :
F3{0, 0, 0};
1062 F3 vB{0, 0, 0}, wB{0, 0, 0};
1063 const float invN = 1.0f /
static_cast<float>(m.
num_points);
1076 const float eMat = restitution;
1082 const float lenN = Kokkos::sqrt(
dot3(Nsum, Nsum));
1085 const F3 diffCenters = (idB < 0) ? rAavg :
sub3(rAavg, rBavg);
1087 float vn =
dot3(vA, Nsum) +
dot3(wA, TauA) +
dot3(vB,
F3{-Nsum.
x, -Nsum.y, -Nsum.z}) +
1089 vn +=
dot3(vGrowth, Nsum);
1090 const float alignment =
dot3(Nsum, diffCenters);
1091 const float sgn = (alignment > 0.0f) ? 1.0f : -1.0f;
1092 const float Nsq =
dot3(Nsum, Nsum);
1093 float wA_n = Nsq * invMassA + genInvMass(TauA, invIA, qA);
1094 float wB_n = Nsq * invMassB + genInvMass(TauB, invIB, qB);
1099 bool applyA =
true, applyB =
true;
1100 const unsigned char sf =
sideFlag(idx);
1105 }
else if (sf == 2) {
1110 const float wTotal = wA_n + wB_n;
1114 const float v0til = sgn *
vn0(idx);
1125 const float target = (v0til > 0.0f) ? -restitution * v0til : 0.0f;
1126 const float vtil = sgn * vn;
1127 const float dp = (vtil - target) / wTotal;
1129 float pNew = pOld + dp;
1132 const float dApplied = pNew - pOld;
1133 if (dApplied != 0.0f) {
1135 Kokkos::atomic_max(&
maxApproach(), Kokkos::fabs(dApplied) * wTotal / lenN);
1137 Kokkos::atomic_max(&
maxApproachQS(), Kokkos::fabs(dApplied) * wTotal / lenN);
1138 const float lambda = -sgn * dApplied;
1139 const F3 Jlin =
scale3(Nsum, lambda);
1140 const F3 JangA =
scale3(TauA, lambda);
1141 const F3 JangB =
scale3(TauB, lambda);
1143 velPred(realA, 0) += Jlin.
x * invMassA;
1144 velPred(realA, 1) += Jlin.
y * invMassA;
1145 velPred(realA, 2) += Jlin.
z * invMassA;
1148 const F3 dwl{Jl.
x * invIA.
x, Jl.
y * invIA.
y, Jl.
z * invIA.
z};
1155 if (idB >= 0 && applyB) {
1156 velPred(realB, 0) += -Jlin.
x * invMassB;
1157 velPred(realB, 1) += -Jlin.
y * invMassB;
1158 velPred(realB, 2) += -Jlin.
z * invMassB;
1160 const F3 dwl{Jl.
x * invIB.
x, Jl.
y * invIB.
y, Jl.
z * invIB.
z};
1187 float orphA = 0.0f, orphB = 0.0f;
1198 if ((owed > 0.0f || orphA > 0.0f || orphB > 0.0f) && vPeak > 0.0f) {
1200 F3 vB3{0, 0, 0}, wB3{0, 0, 0};
1207 float vn3 =
dot3(vA3, Nsum) +
dot3(wA3, TauA) +
dot3(vB3,
F3{-Nsum.
x, -Nsum.y, -Nsum.z}) +
1209 vn3 +=
dot3(vGrowth, Nsum);
1210 const float vtil3 = sgn * vn3;
1213 bool relA =
true, relB = (idB >= 0);
1214 float wRel = wTotal;
1216 const F3 dx =
sub3(rBavg, rAavg);
1218 const float thr3 = 0.3f * Kokkos::sqrt(
dot3(dx, dx));
1221 wRel = Nsq * invMassA + genInvMass(TauA, invIA, qA);
1224 wRel = Nsq * invMassB + genInvMass(TauB, invIB, qB);
1233 const float vTphys = eMat * vPeak;
1234 const float targetR = -vTphys * lenN;
1235 const float dpR = (vtil3 - targetR) / wRel;
1236 float cap = owed / lenN;
1237 const float rOld =
restRel(idx);
1238 float rNew = rOld + dpR;
1245 if (rNew > cap && (orphA > 0.0f || orphB > 0.0f)) {
1246 float need = (rNew - cap) * lenN;
1249 const float d = Kokkos::fmin(need, orphA);
1254 if (need > 0.0f && orphB > 0.0f) {
1255 const float d = Kokkos::fmin(need, orphB);
1267 const float dR = rNew - rOld;
1270 Kokkos::atomic_max(&
maxApproach(), Kokkos::fabs(dR) * wRel / lenN);
1272 Kokkos::atomic_max(&
maxApproachQS(), Kokkos::fabs(dR) * wRel / lenN);
1273 const float lambdaR = -sgn * dR;
1274 const F3 JlinR =
scale3(Nsum, lambdaR);
1276 velPred(realA, 0) += JlinR.
x * invMassA;
1277 velPred(realA, 1) += JlinR.
y * invMassA;
1278 velPred(realA, 2) += JlinR.
z * invMassA;
1280 const F3 dwl{Jl.
x * invIA.
x, Jl.
y * invIA.
y, Jl.
z * invIA.
z};
1287 velPred(realB, 0) += -JlinR.
x * invMassB;
1288 velPred(realB, 1) += -JlinR.
y * invMassB;
1289 velPred(realB, 2) += -JlinR.
z * invMassB;
1291 const F3 dwl{Jl.
x * invIB.
x, Jl.
y * invIB.
y, Jl.
z * invIB.
z};
1315 const bool haveOld = (ltOld.x != 0.0f || ltOld.y != 0.0f || ltOld.z != 0.0f);
1316 if (mu > 0.0f || haveOld) {
1317 const F3 nhat =
scale3(Nsum, 1.0f / lenN);
1319 F3 vB2{0, 0, 0}, wB2{0, 0, 0};
1337 if (beta != 0.0f &&
vt0.extent(0) > 0) {
1342 const float vtLen = Kokkos::sqrt(
dot3(vtErr, vtErr));
1344 float wT = invMassA + invMassB;
1345 if (vtLen > 1e-9f) {
1346 const F3 that =
scale3(vtErr, 1.0f / vtLen);
1347 float wAt = (sf == 2) ? 0.0f : invMassA + genInvMass(
cross3v(rAavg, that), invIA, qA);
1348 float wBt = (sf == 1 || idB < 0)
1350 : invMassB + genInvMass(
cross3v(rBavg, that), invIB, qB);
1353 ltNew =
add3(ltOld,
scale3(that, -vtLen / wT));
1359 float nTot = Kokkos::fmax(
lambdaAcc(idx), 0.0f) * lenN;
1365 const float bound = mu * nTot;
1366 const float ltLen = Kokkos::sqrt(
dot3(ltNew, ltNew));
1368 ltNew = (bound > 0.0f) ?
scale3(ltNew, bound / ltLen) :
F3{0, 0, 0};
1369 const F3 dApp =
sub3(ltNew, ltOld);
1370 if (dApp.
x != 0.0f || dApp.
y != 0.0f || dApp.
z != 0.0f) {
1374 const float dLen = Kokkos::sqrt(
dot3(dApp, dApp));
1381 velPred(realA, 0) += dApp.
x * invMassA;
1382 velPred(realA, 1) += dApp.
y * invMassA;
1383 velPred(realA, 2) += dApp.
z * invMassA;
1385 const F3 dwl{Jl.
x * invIA.
x, Jl.
y * invIA.
y, Jl.
z * invIA.
z};
1391 if (idB >= 0 && applyB) {
1392 velPred(realB, 0) += -dApp.
x * invMassB;
1393 velPred(realB, 1) += -dApp.
y * invMassB;
1394 velPred(realB, 2) += -dApp.
z * invMassB;
1396 const F3 dwl{Jl.
x * invIB.
x, Jl.
y * invIB.
y, Jl.
z * invIB.
z};
1413 Kokkos::View<const ManifoldC*, CpMem> manifolds, Kokkos::View<const float*, CpMem> invMass,
1414 Kokkos::View<
const float* [3],
CpMem> invInertia, Kokkos::View<
const float* [4],
CpMem> quat,
1415 Kokkos::View<
float* [3],
CpMem> velPred, Kokkos::View<
float* [3],
CpMem> angVelPred,
1416 Kokkos::View<const int*, CpMem> realIdx,
float growthRate,
float restitutionNormal,
1417 float restVelThreshold, Kokkos::View<float, CpMem> maxApproach,
1418 Kokkos::View<float, CpMem> maxApproachQS, Kokkos::View<float*, CpMem> lambdaAcc,
1419 Kokkos::View<const float*, CpMem> vn0, Kokkos::View<const unsigned char*, CpMem> sideFlag,
1420 Kokkos::View<
float* [3],
CpMem> lambdaT,
float frictionDynamic,
1421 Kokkos::View<
const float* [3],
CpMem> vt0,
float restitutionTangent,
1422 Kokkos::View<const float*, CpMem> posImpulse, Kokkos::View<float*, CpMem> restBank,
1423 Kokkos::View<float*, CpMem> restRel, Kokkos::View<const unsigned char*, CpMem> restPersistent,
1424 Kokkos::View<const float*, CpMem> restVPeak,
F3 restGHat,
1425 Kokkos::View<const unsigned char*, CpMem> restGrounded,
bool restNewtonOff,
bool restOneSided,
1426 Kokkos::View<float*, CpMem> restOrphan, Kokkos::View<const float*, CpMem> restOrphanVPeak) {
1438 maxApproachQS.data() ? maxApproachQS : maxApproach,
1463 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
1464 Kokkos::View<const int*, CpMem> mColor,
int numColors,
1465 Kokkos::View<const float*, CpMem> invMass, Kokkos::View<
const float* [3],
CpMem> invInertia,
1466 Kokkos::View<
const float* [4],
CpMem> quat, Kokkos::View<
float* [3],
CpMem> velPred,
1467 Kokkos::View<
float* [3],
CpMem> angVelPred, Kokkos::View<const int*, CpMem> realIdx,
1468 float growthRate,
float restitutionNormal,
float restVelThreshold,
1469 Kokkos::View<float, CpMem> maxApproach, Kokkos::View<float*, CpMem> lambdaAcc,
1470 Kokkos::View<const float*, CpMem> vn0, Kokkos::View<const unsigned char*, CpMem> sideFlag,
1471 Kokkos::View<
float* [3],
CpMem> lambdaT,
float frictionDynamic,
1472 Kokkos::View<
const float* [3],
CpMem> vt0 = {},
float restitutionTangent = 0.0f,
1473 Kokkos::View<const float*, CpMem> posImpulse = {},
1474 Kokkos::View<float, CpMem> maxApproachQS = {}, Kokkos::View<float*, CpMem> restBank = {},
1475 Kokkos::View<float*, CpMem> restRel = {},
1476 Kokkos::View<const unsigned char*, CpMem> restPersistent = {},
1477 Kokkos::View<const float*, CpMem> restVPeak = {},
F3 restGHat = {},
1478 Kokkos::View<const unsigned char*, CpMem> restGrounded = {},
bool restNewtonOff =
false,
1479 bool restOneSided =
false, Kokkos::View<float*, CpMem> restOrphan = {},
1480 Kokkos::View<const float*, CpMem> restOrphanVPeak = {},
1481 Kokkos::View<const int*, CpMem> colorPerm = {},
const std::vector<int>* colorOffs =
nullptr,
1482 const FusedSweepCtx* fused =
nullptr,
const FusedLoopSpec* loop =
nullptr) {
1485 manifolds, invMass, invInertia, quat, velPred, angVelPred, realIdx, growthRate,
1486 restitutionNormal, restVelThreshold, maxApproach, maxApproachQS, lambdaAcc, vn0, sideFlag,
1487 lambdaT, frictionDynamic, vt0, restitutionTangent, posImpulse, restBank, restRel,
1488 restPersistent, restVPeak, restGHat, restGrounded, restNewtonOff, restOneSided, restOrphan,
1494#ifdef KOKKOS_ENABLE_CUDA
1496 if (fused && fused->maxBucket > 0 && colorOffs)
1497 return demLaunchFusedSweepLoop(space, f, colorPerm, *fused, numColors, *loop,
1498 (maxApproachQS.data() ? maxApproachQS : maxApproach).data());
1501 if (fused && fused->maxBucket > 0 && colorOffs &&
1502 demLaunchFusedColorSweep(space, f, colorPerm, *fused, numColors))
1513 for (
int color = 0; color < numColors; ++color) {
1515 const int b = (*colorOffs)[color], e = (*colorOffs)[color + 1];
1518 Kokkos::parallel_for(
1519 "peclet::dem::solve_velocity_pgs", Kokkos::RangePolicy<CpExec>(space, b, e),
1520 KOKKOS_LAMBDA(
int i2) { f.solveOne(colorPerm(i2)); });
1522 Kokkos::parallel_for(
1523 "peclet::dem::solve_velocity_pgs", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
1524 KOKKOS_LAMBDA(
int idx) {
1525 if (mColor(idx) == color)
1542 int numManifolds, Kokkos::View<const int*, CpMem> realIdx,
1543 Kokkos::View<const int*, CpMem> mColor,
1544 Kokkos::View<const int*, CpMem> heights,
1545 Kokkos::View<int*, CpMem> keys,
1546 Kokkos::View<int*, CpMem> perm,
1547 std::vector<std::pair<int, int>>& buckets) {
1549 if (numManifolds <= 0)
1552 Kokkos::parallel_for(
1553 "peclet::dem::level_keys", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
1554 KOKKOS_LAMBDA(
int idx) {
1556 const int c = mColor(idx);
1559 keys(idx) = INT_MAX;
1562 int h = heights(realIdx(m.
bodyA));
1564 const int hB = heights(realIdx(m.
bodyB));
1570 keys(idx) = h * 64 + c;
1572 const auto rng = Kokkos::pair<int, int>(0, numManifolds);
1573 auto kd = Kokkos::subview(keys, rng);
1574 auto pd = Kokkos::subview(perm, rng);
1575 Kokkos::Experimental::sort_by_key(space, kd, pd);
1576 auto hk = Kokkos::create_mirror_view(kd);
1577 Kokkos::deep_copy(space, hk, kd);
1578 for (
int b = 0; b < numManifolds && hk(b) != INT_MAX;) {
1580 while (e < numManifolds && hk(e) == hk(b))
1582 buckets.emplace_back(b, e);
1594 Kokkos::View<const int*, CpMem> perm,
1595 const std::vector<std::pair<int, int>>& buckets,
1598 const int nb =
static_cast<int>(buckets.size());
1599 for (
int i = 0; i < nb; ++i) {
1600 const auto [b, e] = buckets[topDown ? nb - 1 - i : i];
1601 Kokkos::parallel_for(
1602 "peclet::dem::solve_velocity_pgs_lvl", Kokkos::RangePolicy<CpExec>(space, b, e),
1603 KOKKOS_LAMBDA(
int i2) { f.
solveOne(perm(i2)); });
1616 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
1617 Kokkos::View<const int*, CpMem> mColor,
int numColors,
1618 Kokkos::View<const float*, CpMem> invMass, Kokkos::View<
const float* [3],
CpMem> invInertia,
1619 Kokkos::View<
const float* [4],
CpMem> quat, Kokkos::View<
float* [3],
CpMem> velPred,
1620 Kokkos::View<
float* [3],
CpMem> angVelPred, Kokkos::View<const int*, CpMem> realIdx,
1621 float growthRate,
float restitutionNormal,
float restVelThreshold,
1622 Kokkos::View<float, CpMem> maxApproach,
1623 Kokkos::View<const unsigned char*, CpMem> persistent = {},
1624 Kokkos::View<const float* [3], CpMem> posPred = {},
F3 gHat = {},
1625 Kokkos::View<const unsigned char*, CpMem> grounded = {}) {
1629 const bool usePersist = persistent.extent(0) > 0;
1630 for (
int color = 0; color < numColors; ++color) {
1631 Kokkos::parallel_for(
1632 "peclet::dem::solve_velocity_gs", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
1633 KOKKOS_LAMBDA(
int idx) {
1634 if (mColor(idx) != color)
1636 const ManifoldC m = manifolds(idx);
1638 const int idA = m.bodyA, idB = m.bodyB;
1639 const int realA = realIdx(idA);
1640 const int realB = (idB >= 0) ? realIdx(idB) : idB;
1642 const float invMassA = invMass(realA);
1643 const float invMassB = (idB >= 0) ? invMass(realB) : 0.0f;
1644 const F3 invIA =
ld3(invInertia, realA);
1645 const F3 invIB = (idB >= 0) ?
ld3(invInertia, realB) :
F3{0, 0, 0};
1646 const F4 qA = F4{quat(realA, 0), quat(realA, 1), quat(realA, 2), quat(realA, 3)};
1647 const F4 qB = (idB >= 0)
1648 ? F4{quat(realB, 0), quat(realB, 1), quat(realB, 2), quat(realB, 3)}
1651 const F3 vA =
ld3(velPred, realA), wA =
ld3(angVelPred, realA);
1652 F3 vB{0, 0, 0}, wB{0, 0, 0};
1654 vB =
ld3(velPred, realB);
1655 wB =
ld3(angVelPred, realB);
1658 const F3 Nsum{m.normal_sum.x, m.normal_sum.y, m.normal_sum.z};
1659 const F3 TauA{m.torque_armA_sum.x, m.torque_armA_sum.y, m.torque_armA_sum.z};
1660 const F3 TauB{m.torque_armB_sum.x, m.torque_armB_sum.y, m.torque_armB_sum.z};
1662 const float invN = 1.0f /
static_cast<float>(m.num_points);
1664 float restitution = restitutionNormal;
1666 vB =
scale3(
F3{m.wallVel_sum.x, m.wallVel_sum.y, m.wallVel_sum.z}, invN);
1668 const float ra = m.restitution_sum * invN;
1672 const F3 rAavg =
scale3(
F3{m.rA_sum.x, m.rA_sum.y, m.rA_sum.z}, invN);
1673 const F3 rBavg =
scale3(
F3{m.rB_sum.x, m.rB_sum.y, m.rB_sum.z}, invN);
1675 const float lenN = Kokkos::sqrt(
dot3(Nsum, Nsum));
1679 const F3 diffCenters = (idB < 0) ? rAavg :
sub3(rAavg, rBavg);
1680 const F3 vGrowth =
scale3(diffCenters, growthRate);
1682 float vn =
dot3(vA, Nsum) +
dot3(wA, TauA) +
dot3(vB,
F3{-Nsum.x, -Nsum.y, -Nsum.z}) +
1684 vn +=
dot3(vGrowth, Nsum);
1686 const float alignment =
dot3(Nsum, diffCenters);
1687 if (alignment > 0.0f) {
1695 const float Nsq =
dot3(Nsum, Nsum);
1696 float wA_n = Nsq * invMassA +
genInvMass(TauA, invIA, qA);
1697 float wB_n = Nsq * invMassB +
genInvMass(TauB, invIB, qB);
1699 bool applyA =
true, applyB =
true;
1700 if (usePersist && persistent(idx) != 0 && idB >= 0) {
1702 const float up = -(dx.x * gHat.x + dx.y * gHat.y + dx.z * gHat.z);
1703 const float thr = 0.3f * Kokkos::sqrt(
dot3(dx, dx));
1704 const float riseThr = 4.0f * restVelThreshold;
1705 if (up > thr && -
dot3(vB, gHat) <= riseThr && grounded(realB) > 0) {
1709 }
else if (up < -thr && -
dot3(vA, gHat) <= riseThr && grounded(realA) > 0) {
1715 const float wTotal = wA_n + wB_n;
1722 Kokkos::atomic_max(&maxApproach(), Kokkos::fabs(vn) / lenN);
1724 if (Kokkos::fabs(vn) < restVelThreshold * lenN)
1727 const float lambda = (-restitution * vn - vn) / wTotal;
1729 const F3 Jlin =
scale3(Nsum, lambda);
1730 const F3 JangA =
scale3(TauA, lambda);
1731 const F3 JangB =
scale3(TauB, lambda);
1735 velPred(realA, 0) += Jlin.x * invMassA;
1736 velPred(realA, 1) += Jlin.y * invMassA;
1737 velPred(realA, 2) += Jlin.z * invMassA;
1740 const F3 dwl{Jl.x * invIA.x, Jl.y * invIA.y, Jl.z * invIA.z};
1742 angVelPred(realA, 0) += dww.x;
1743 angVelPred(realA, 1) += dww.y;
1744 angVelPred(realA, 2) += dww.z;
1747 if (idB >= 0 && applyB) {
1748 velPred(realB, 0) += -Jlin.x * invMassB;
1749 velPred(realB, 1) += -Jlin.y * invMassB;
1750 velPred(realB, 2) += -Jlin.z * invMassB;
1752 const F3 dwl{Jl.x * invIB.x, Jl.y * invIB.y, Jl.z * invIB.z};
1754 angVelPred(realB, 0) += dww.x;
1755 angVelPred(realB, 1) += dww.y;
1756 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)
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)
Apply the warm-start impulses up front (order-independent: fixed impulses, atomic adds).
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)
Event-level (Poisson) restitution bookkeeping, once per substep AFTER all velocity phases (restitutio...
void applyVelocityDeltasAveragedKokkos(int n, V3 velPred, V3 angVelPred, V3 deltaVel, V3 deltaAngVel, Vi velCounts)
Apply the accumulated velocity deltas AVERAGED by the per-body manifold count — the velocity- solve t...
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={})
Incremental (warm-started) manifold colouring for the single-GPU PGS path.
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={})
Orphan transfer: previous-ledger entries NOT matched by any current manifold (their pair died this su...
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)
-— Warm-started projected Gauss-Seidel (PGS) velocity solve -— Nonsmooth contact dynamics (Moreau-Jea...
Kokkos::View< int *, CpMem > Vi
Kokkos::View< float *[3], CpMem > V3
F3 invRotateVector(F4 q, F3 v)
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={})
Accumulate normal-restitution velocity deltas for numManifolds manifolds.
F3 ldF3(const V &v, int i)
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 unsigned char *, CpMem > restPersistent, Kokkos::View< const float *, CpMem > restVPeak, F3 restGHat, Kokkos::View< const unsigned char *, CpMem > restGrounded, bool restNewtonOff, bool restOneSided, Kokkos::View< float *, CpMem > restOrphan, Kokkos::View< const float *, CpMem > restOrphanVPeak)
Build the shared per-manifold sweep functor (the colored launch loop, the fused kernels and the fused...
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={})
Colored Gauss–Seidel normal-restitution solve: sweep the numColors colour classes in order,...
F3 rotateVector(F4 q, F3 v)
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 ...
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 unsigned char *, CpMem > restPersistent={}, Kokkos::View< const float *, CpMem > restVPeak={}, F3 restGHat={}, Kokkos::View< const unsigned char *, CpMem > restGrounded={}, bool restNewtonOff=false, bool restOneSided=false, 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)
Returns true when the sweep (or, with loop, the whole iteration loop) was submitted; false ONLY in lo...
CpExec::memory_space CpMem
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") ...
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={})
Greedy graph-colour the manifolds: no two manifolds sharing a real body get the same colour.
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.
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...
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)
Decide each persistent contact's treatment ONCE per substep (before any impulse is applied): 0 = symm...
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.
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 unsigned char *, CpMem > restPersistent
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< const unsigned char *, CpMem > restGrounded
Kokkos::View< float *, CpMem > restRel
Kokkos::View< float *, CpMem > restBank