33#ifndef DEM_SOLVER_MULTILEVEL_HPP
34#define DEM_SOLVER_MULTILEVEL_HPP
36#include <Kokkos_Core.hpp>
72 Kokkos::View<float* [3], CpMem>
velG;
73 Kokkos::View<float* [3], CpMem>
velG0;
74 Kokkos::View<float*, CpMem>
massG;
75 Kokkos::View<int*, CpMem>
grp;
76 Kokkos::View<int*, CpMem>
mate;
85 Kokkos::View<float* [3], CpMem>
angG;
86 Kokkos::View<float* [3], CpMem>
angG0;
87 Kokkos::View<float* [6], CpMem>
invIG;
88 Kokkos::View<double* [7], CpMem>
accD;
95 Kokkos::View<const float* [3], CpMem>
posPred;
97 Kokkos::View<const float* [4], CpMem>
quat;
98 Kokkos::View<const float* [3], CpMem>
invIc;
104KOKKOS_INLINE_FUNCTION
float effMass(
float invMass) {
105 return 1.0f / Kokkos::fmax(invMass, 1e-30f);
125 Kokkos::View<const int*, CpMem> mColor,
126 Kokkos::View<const float*, CpMem> vn0,
127 Kokkos::View<
const float* [3],
CpMem> vt0,
128 Kokkos::View<const unsigned char*, CpMem> persistent,
129 Kokkos::View<
const float* [3],
CpMem> posPred,
F3 gHat,
130 float qsThr,
int gateMask) {
135 if (Kokkos::fabs(vn0(idx)) > qsThr)
143 const F3 vt{vt0(idx, 0), vt0(idx, 1), vt0(idx, 2)};
144 if (
dot3(vt, vt) > qsThr * qsThr)
151 const float up = -(dx.
x * gHat.
x + dx.
y * gHat.
y + dx.
z * gHat.
z);
152 return Kokkos::fabs(up) > 0.3f * Kokkos::sqrt(
dot3(dx, dx));
158KOKKOS_INLINE_FUNCTION
bool canonical(
const Kokkos::View<
const float* [3],
CpMem>& posPred,
159 const Kokkos::View<const int*, CpMem>& realIdx,
int slot) {
160 const int r = realIdx(slot);
161 return posPred(slot, 0) == posPred(r, 0) && posPred(slot, 1) == posPred(r, 1) &&
162 posPred(slot, 2) == posPred(r, 2);
170 const Kokkos::View<
const float* [3],
CpMem>& originG,
171 const Kokkos::View<
const float* [3],
CpMem>& comOffG,
173 const F3 e{posPred(i, 0) - originG(gi, 0), posPred(i, 1) - originG(gi, 1),
174 posPred(i, 2) - originG(gi, 2)};
175 return F3{e.
x - comOffG(gi, 0), e.y - comOffG(gi, 1), e.z - comOffG(gi, 2)};
181 const float ix = 1.0f / Kokkos::fmax(invIc.
x, 1e-30f);
182 if (invIc.
x == invIc.
y && invIc.
y == invIc.
z)
184 const float iy = 1.0f / Kokkos::fmax(invIc.
y, 1e-30f);
185 const float iz = 1.0f / Kokkos::fmax(invIc.
z, 1e-30f);
193 return F3{S(g, 0) * v.
x + S(g, 3) * v.
y + S(g, 4) * v.
z,
194 S(g, 3) * v.
x + S(g, 1) * v.
y + S(g, 5) * v.
z,
195 S(g, 4) * v.
x + S(g, 5) * v.
y + S(g, 2) * v.
z};
203 return D3{a.
y * b.
z - a.
z * b.
y, a.
z * b.
x - a.
x * b.
z, a.
x * b.
y - a.
y * b.
x};
205KOKKOS_INLINE_FUNCTION
D3 rotateD(
double qx,
double qy,
double qz,
double qw,
D3 v) {
206 const D3 qv{qx, qy, qz};
208 const D3 t{2.0 * c.
x, 2.0 * c.
y, 2.0 * c.
z};
210 return D3{v.
x + t.x * qw + c2.
x, v.
y + t.y * qw + c2.
y, v.
z + t.z * qw + c2.
z};
224 Kokkos::View<
const float* [3],
CpMem> posPred,
225 Kokkos::View<const float*, CpMem> invMass,
226 Kokkos::View<
const float* [4],
CpMem> quat,
227 Kokkos::View<
const float* [3],
CpMem> invIc) {
233 auto invIG = S.
invIG;
234 const Kokkos::View<const float* [3], CpMem> Oc(S.
originG), Cc(S.
comOffG);
235 Kokkos::parallel_for(
236 "peclet::dem::ml_geo_reset", Kokkos::RangePolicy<CpExec>(space, 0, ng), KOKKOS_LAMBDA(
int g) {
237 for (
int c = 0; c < 7; ++c)
240 Kokkos::parallel_for(
241 "peclet::dem::ml_geo_origin_accum", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
242 KOKKOS_LAMBDA(
int i) {
244 const int g = grp(i);
245 Kokkos::atomic_add(&acc(g, 0), mu);
246 for (
int c = 0; c < 3; ++c)
247 Kokkos::atomic_add(&acc(g, 1 + c), mu *
static_cast<double>(posPred(i, c)));
249 Kokkos::parallel_for(
250 "peclet::dem::ml_geo_origin", Kokkos::RangePolicy<CpExec>(space, 0, ng),
251 KOKKOS_LAMBDA(
int g) {
252 for (
int c = 0; c < 3; ++c) {
253 O(off + g, c) =
static_cast<float>(acc(g, 1 + c) / acc(g, 0));
257 Kokkos::parallel_for(
258 "peclet::dem::ml_geo_com_accum", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
259 KOKKOS_LAMBDA(
int i) {
261 const int g = grp(i);
262 for (
int c = 0; c < 3; ++c) {
263 const float e = posPred(i, c) - Oc(off + g, c);
264 Kokkos::atomic_add(&acc(g, 1 + c), mu *
static_cast<double>(e));
267 Kokkos::parallel_for(
268 "peclet::dem::ml_geo_com", Kokkos::RangePolicy<CpExec>(space, 0, ng), KOKKOS_LAMBDA(
int g) {
269 for (
int c = 0; c < 3; ++c)
270 C(off + g, c) =
static_cast<float>(acc(g, 1 + c) / acc(g, 0));
271 for (
int c = 1; c < 7; ++c)
274 Kokkos::parallel_for(
275 "peclet::dem::ml_geo_inertia_accum", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
276 KOKKOS_LAMBDA(
int i) {
278 const int g = grp(i);
280 const double dx = df.
x, dy = df.
y, dz = df.
z;
281 const double d2 = dx * dx + dy * dy + dz * dz;
283 const double ix = 1.0 / Kokkos::fmax(
static_cast<double>(invIc(i, 0)), 1e-30);
284 if (invIc(i, 0) == invIc(i, 1) && invIc(i, 1) == invIc(i, 2)) {
285 J[0] = J[1] = J[2] = ix;
286 J[3] = J[4] = J[5] = 0.0;
288 const double iy = 1.0 / Kokkos::fmax(
static_cast<double>(invIc(i, 1)), 1e-30);
289 const double iz = 1.0 / Kokkos::fmax(
static_cast<double>(invIc(i, 2)), 1e-30);
290 const double qx = quat(i, 0), qy = quat(i, 1), qz = quat(i, 2), qw = quat(i, 3);
293 for (
int k = 0; k < 3; ++k) {
294 const mldetail::D3 ek{k == 0 ? 1.0 : 0.0, k == 1 ? 1.0 : 0.0, k == 2 ? 1.0 : 0.0};
301 J[3] = 0.5 * (col[1].
x + col[0].
y);
302 J[4] = 0.5 * (col[2].
x + col[0].
z);
303 J[5] = 0.5 * (col[2].
y + col[1].
z);
305 Kokkos::atomic_add(&acc(g, 1), J[0] + mu * (d2 - dx * dx));
306 Kokkos::atomic_add(&acc(g, 2), J[1] + mu * (d2 - dy * dy));
307 Kokkos::atomic_add(&acc(g, 3), J[2] + mu * (d2 - dz * dz));
308 Kokkos::atomic_add(&acc(g, 4), J[3] - mu * dx * dy);
309 Kokkos::atomic_add(&acc(g, 5), J[4] - mu * dx * dz);
310 Kokkos::atomic_add(&acc(g, 6), J[5] - mu * dy * dz);
313 Kokkos::parallel_reduce(
314 "peclet::dem::ml_geo_invert", Kokkos::RangePolicy<CpExec>(space, 0, ng),
315 KOKKOS_LAMBDA(
int g,
int& nb) {
316 const double xx = acc(g, 1), yy = acc(g, 2), zz = acc(g, 3);
317 const double xy = acc(g, 4), xz = acc(g, 5), yz = acc(g, 6);
318 const double c00 = yy * zz - yz * yz, c01 = xz * yz - xy * zz, c02 = xy * yz - xz * yy;
319 const double c11 = xx * zz - xz * xz, c12 = xy * xz - xx * yz, c22 = xx * yy - xy * xy;
320 const double det = xx * c00 + xy * c01 + xz * c02;
321 if (!(det > 0.0) || !Kokkos::isfinite(det)) {
322 for (
int c = 0; c < 6; ++c)
323 invIG(off + g, c) = 0.0f;
327 const double inv = 1.0 / det;
328 invIG(off + g, 0) =
static_cast<float>(c00 * inv);
329 invIG(off + g, 1) =
static_cast<float>(c11 * inv);
330 invIG(off + g, 2) =
static_cast<float>(c22 * inv);
331 invIG(off + g, 3) =
static_cast<float>(c01 * inv);
332 invIG(off + g, 4) =
static_cast<float>(c02 * inv);
333 invIG(off + g, 5) =
static_cast<float>(c12 * inv);
338 throw std::logic_error(
"multilevel: a rigid aggregate's inertia is singular (§2.3 step 5)");
348 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
349 Kokkos::View<const int*, CpMem> realIdx, Kokkos::View<const int*, CpMem> mColor,
350 Kokkos::View<const float*, CpMem> vn0, Kokkos::View<
const float* [3],
CpMem> vt0,
351 Kokkos::View<const unsigned char*, CpMem> persistent,
352 Kokkos::View<
const float* [3],
CpMem> posPred,
F3 gHat,
353 Kokkos::View<const float*, CpMem> invMass,
float qsThr,
int gateMask,
int numReal,
MlScratch& S,
354 Kokkos::View<long long*, CpMem> winner, Kokkos::View<std::uint64_t*, CpMem> colorMask,
355 bool excludeImmovable =
false, Kokkos::View<const unsigned char*, CpMem> asleep = {},
356 const MlBodyViews& body = {}) {
359 if (numManifolds <= 0 || numReal <= 0)
364 const bool haveAsleep = asleep.extent(0) > 0;
367 Kokkos::parallel_for(
368 "peclet::dem::ml_grp_init", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
369 KOKKOS_LAMBDA(
int i) { grp(i) = i; });
371 Kokkos::parallel_for(
372 "peclet::dem::ml_packed_init", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
373 KOKKOS_LAMBDA(
int idx) { cp(idx) = ~0ll; });
376 int ngPrev = numReal;
377 int parentOff = 0, groupOff = 0;
378 const int parentCap =
static_cast<int>(S.
parent.extent(0));
379 const int groupCap =
static_cast<int>(S.
invMassG.extent(0));
380 const bool haveGeometry = S.
invIG.extent(0) >= S.
invMassG.extent(0) &&
381 static_cast<int>(S.
accD.extent(0)) >= numReal &&
382 body.quat.extent(0) > 0 && body.invIc.extent(0) > 0;
385 if (parentOff + ngPrev > parentCap)
388 Kokkos::parallel_for(
389 "peclet::dem::ml_match_reset", Kokkos::RangePolicy<CpExec>(space, 0, ngPrev),
390 KOKKOS_LAMBDA(
int g) { winner(g) = -1; });
393 Kokkos::parallel_for(
394 "peclet::dem::ml_match_contend", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
395 KOKKOS_LAMBDA(
int idx) {
396 const ManifoldC m = manifolds(idx);
398 gHat, qsThr, gateMask))
403 if (excludeImmovable &&
404 (invMass(realIdx(m.bodyA)) == 0.0f || invMass(realIdx(m.bodyB)) == 0.0f ||
405 (haveAsleep && (asleep(realIdx(m.bodyA)) || asleep(realIdx(m.bodyB))))))
412 const int gA = grp(realIdx(m.bodyA)), gB = grp(realIdx(m.bodyB));
415 const long long key =
colorKey(idx);
416 Kokkos::atomic_max(&winner(gA), key);
417 Kokkos::atomic_max(&winner(gB), key);
420 Kokkos::parallel_for(
421 "peclet::dem::ml_mate_init", Kokkos::RangePolicy<CpExec>(space, 0, ngPrev),
422 KOKKOS_LAMBDA(
int g) { mate(g) = g; });
423 Kokkos::parallel_for(
424 "peclet::dem::ml_match_commit", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
425 KOKKOS_LAMBDA(
int idx) {
426 const ManifoldC m = manifolds(idx);
428 gHat, qsThr, gateMask))
430 if (excludeImmovable &&
431 (invMass(realIdx(m.bodyA)) == 0.0f || invMass(realIdx(m.bodyB)) == 0.0f ||
432 (haveAsleep && (asleep(realIdx(m.bodyA)) || asleep(realIdx(m.bodyB))))))
439 const int gA = grp(realIdx(m.bodyA)), gB = grp(realIdx(m.bodyB));
442 const long long key =
colorKey(idx);
443 if (winner(gA) == key && winner(gB) == key) {
454 const int off = parentOff;
455 Kokkos::parallel_scan(
456 "peclet::dem::ml_compact", Kokkos::RangePolicy<CpExec>(space, 0, ngPrev),
457 KOKKOS_LAMBDA(
int g,
int& run,
const bool final) {
458 const bool leader = (mate(g) >= g);
462 parent(off + g) = run - 1;
465 Kokkos::parallel_for(
466 "peclet::dem::ml_parent_nonleader", Kokkos::RangePolicy<CpExec>(space, 0, ngPrev),
467 KOKKOS_LAMBDA(
int g) {
469 parent(off + g) = parent(off + mate(g));
472 if (ngNew >= ngPrev || ngNew > (9 * ngPrev) / 10)
474 if (groupOff + ngNew > groupCap)
480 const int off = parentOff;
481 Kokkos::parallel_for(
482 "peclet::dem::ml_grp_compose", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
483 KOKKOS_LAMBDA(
int i) { grp(i) = parent(off + grp(i)); });
488 auto massG = S.
massG;
490 const int off = groupOff;
491 Kokkos::parallel_for(
492 "peclet::dem::ml_mass_reset", Kokkos::RangePolicy<CpExec>(space, 0, ngNew),
493 KOKKOS_LAMBDA(
int g) { massG(off + g) = 0.0f; });
494 Kokkos::parallel_for(
495 "peclet::dem::ml_mass_accum", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
496 KOKKOS_LAMBDA(
int i) {
499 Kokkos::parallel_for(
500 "peclet::dem::ml_mass_invert", Kokkos::RangePolicy<CpExec>(space, 0, ngNew),
501 KOKKOS_LAMBDA(
int g) { invMassG(off + g) = 1.0f / massG(off + g); });
513 const int slotShift = 6 * (lvl - 1);
514 Kokkos::parallel_for(
515 "peclet::dem::ml_color_mask_reset", Kokkos::RangePolicy<CpExec>(space, 0, ngNew),
516 KOKKOS_LAMBDA(
int g) { colorMask(g) = 0; });
519 int remaining = 1, prevRemaining = -1, maxc = -1;
520 for (
int round = 0; round < 64 && remaining > 0; ++round) {
521 Kokkos::parallel_for(
522 "peclet::dem::ml_color_reset_winner", Kokkos::RangePolicy<CpExec>(space, 0, ngNew),
523 KOKKOS_LAMBDA(
int g) { winner(g) = -1; });
524 Kokkos::parallel_for(
525 "peclet::dem::ml_color_contend", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
526 KOKKOS_LAMBDA(
int idx) {
527 const ManifoldC m = manifolds(idx);
533 const int gA = grp(realIdx(m.bodyA));
534 const int gB = (m.bodyB >= 0) ? grp(realIdx(m.bodyB)) : -1;
537 const long long key =
colorKey(idx);
538 Kokkos::atomic_max(&winner(gA), key);
540 Kokkos::atomic_max(&winner(gB), key);
543 Kokkos::parallel_reduce(
544 "peclet::dem::ml_color_commit", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
545 KOKKOS_LAMBDA(
int idx,
int& acc) {
546 const ManifoldC m = manifolds(idx);
552 const int gA = grp(realIdx(m.bodyA));
553 const int gB = (m.bodyB >= 0) ? grp(realIdx(m.bodyB)) : -1;
556 const long long key =
colorKey(idx);
557 if (winner(gA) != key || (gB >= 0 && winner(gB) != key)) {
561 std::uint64_t forbidden = colorMask(gA);
563 forbidden |= colorMask(gB);
568 cp(idx) &= ~kMlPendingSkipBit;
571 cp(idx) = (cp(idx) & ~(63ll << slotShift)) | (
static_cast<long long>(c) << slotShift);
572 const std::uint64_t bit = std::uint64_t(1) << c;
573 colorMask(gA) |= bit;
575 colorMask(gB) |= bit;
579 if (rem == prevRemaining)
584 Kokkos::parallel_for(
585 "peclet::dem::ml_color_clear_skip", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
587 Kokkos::parallel_reduce(
588 "peclet::dem::ml_color_max", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
589 KOKKOS_LAMBDA(
int idx,
int& mx) {
590 const int c =
static_cast<int>((cp(idx) >> slotShift) & 63);
594 Kokkos::Max<int>(maxc));
596 H.numColors.push_back(maxc + 1);
598 H.groupOff.push_back(groupOff);
599 H.numGroups.push_back(ngNew);
600 H.parentOff.push_back(parentOff);
624 Kokkos::View<int*, CpMem> colorScratch,
625 Kokkos::View<int*, CpMem> perm,
626 Kokkos::View<int*, CpMem> cursor,
627 std::vector<std::vector<int>>& offs) {
628 offs.assign(
static_cast<std::size_t
>(H.
numLevels), {});
629 if (numManifolds <= 0)
632 for (
int lvl = 1; lvl <= H.
numLevels; ++lvl) {
633 const int slotShift = 6 * (lvl - 1);
634 const int nCol = H.
numColors[
static_cast<std::size_t
>(lvl) - 1];
636 auto cs = colorScratch;
637 Kokkos::parallel_for(
638 "peclet::dem::ml_bucket_colors", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
639 KOKKOS_LAMBDA(
int idx) {
640 const int c =
static_cast<int>((cp(idx) >> slotShift) & 63);
641 cs(idx) = (c < nCol) ? c : -1;
644 Kokkos::subview(perm, Kokkos::pair<int, int>((lvl - 1) * numManifolds, lvl * numManifolds));
646 cursor, offs[
static_cast<std::size_t
>(lvl) - 1]);
662 Kokkos::View<const int*, CpMem>
grp;
663 Kokkos::View<float* [3], CpMem>
velG;
670 Kokkos::View<const float* [3], CpMem>
originG;
671 Kokkos::View<const float* [3], CpMem>
comOffG;
672 Kokkos::View<float* [3], CpMem>
angG;
673 Kokkos::View<float* [3], CpMem>
angG0;
674 Kokkos::View<const float* [6], CpMem>
invIG;
683 KOKKOS_FUNCTION
bool row(
int idx,
int off,
Row& r)
const {
692 const float invN = 1.0f /
static_cast<float>(m.
num_points);
694 const float lenN = Kokkos::sqrt(
dot3(Nsum, Nsum));
699 const F3 diffCenters = (gB < 0) ? rAavg :
sub3(rAavg, rBavg);
700 const F3 vA{
velG(off + gA, 0),
velG(off + gA, 1),
velG(off + gA, 2)};
706 float vn =
dot3(
sub3(vA, vB), Nsum);
707 const float alignment =
dot3(Nsum, diffCenters);
708 const float sgn = (alignment > 0.0f) ? 1.0f : -1.0f;
709 const float invMA =
invMassG(off + gA);
710 const float invMB = (gB >= 0) ?
invMassG(off + gB) : 0.0f;
711 float w =
dot3(Nsum, Nsum) * (invMA + invMB);
719 const F3 wA{
angG(off + gA, 0),
angG(off + gA, 1),
angG(off + gA, 2)};
720 vn = vn +
dot3(TA, wA);
727 const F3 wB{
angG(off + gB, 0),
angG(off + gB, 1),
angG(off + gB, 2)};
728 vn = vn +
dot3(TB, wB);
735 r =
Row{gA, gB, Nsum, TA, TB, lenN, vn, sgn, invMA, invMB, w};
739 KOKKOS_FUNCTION
void solveOne(
int idx,
int off)
const {
741 if (!
row(idx, off, r))
743 const int gA = r.
gA, gB = r.
gB;
744 const float w = r.
w, invMA = r.
invMA, invMB = r.
invMB;
745 const float vtil = r.
sgn * r.
vn;
746 const float dp = vtil / w;
748 float pNew = pOld + dp;
751 const float d = pNew - pOld;
756 const float lambda = -r.
sgn * d;
758 velG(off + gA, 0) += J.
x * invMA;
759 velG(off + gA, 1) += J.
y * invMA;
760 velG(off + gA, 2) += J.
z * invMA;
762 velG(off + gB, 0) -= J.
x * invMB;
763 velG(off + gB, 1) -= J.
y * invMB;
764 velG(off + gB, 2) -= J.
z * invMB;
767 angG(off + gA, 0) += dwA.
x;
768 angG(off + gA, 1) += dwA.
y;
769 angG(off + gA, 2) += dwA.
z;
772 angG(off + gB, 0) += dwB.
x;
773 angG(off + gB, 1) += dwB.
y;
774 angG(off + gB, 2) += dwB.
z;
782 KOKKOS_FUNCTION
void restrictAngular(
int i,
int off,
int g,
float m,
const VV& velPred)
const {
784 const F3 v{velPred(i, 0), velPred(i, 1), velPred(i, 2)};
789 Kokkos::atomic_add(&
angG(off + g, 0), L.
x);
790 Kokkos::atomic_add(&
angG(off + g, 1), L.
y);
791 Kokkos::atomic_add(&
angG(off + g, 2), L.
z);
802 template <
class VV,
class VV0>
803 KOKKOS_FUNCTION
void prolongRigid(
int i,
int off,
int g,
const VV& velPred,
804 const VV0& velG0)
const {
808#if defined(PECLET_DEM_TEST_MUTANT) && PECLET_DEM_TEST_MUTANT == 8
809 const F3 cr{0.0f, 0.0f, 0.0f};
814 velPred(i, 0) += (
velG(off + g, 0) - velG0(off + g, 0)) + cr.
x;
815 velPred(i, 1) += (
velG(off + g, 1) - velG0(off + g, 1)) + cr.
y;
816 velPred(i, 2) += (
velG(off + g, 2) - velG0(off + g, 2)) + cr.
z;
826 Kokkos::View<const int*, CpMem> realIdx,
const MlScratch& S,
827 Kokkos::View<float*, CpMem> lambdaAcc,
828 Kokkos::View<float, CpMem> maxApproach,
829 Kokkos::View<const float*, CpMem> restRel,
833 Kokkos::View<const int*, CpMem>(S.
grp),
835 Kokkos::View<const float*, CpMem>(S.
invMassG),
840 Kokkos::View<const float* [3], CpMem>(S.
originG),
841 Kokkos::View<const float* [3], CpMem>(S.
comOffG),
844 Kokkos::View<const float* [6], CpMem>(S.
invIG)};
863 Kokkos::View<unsigned*, CpMem>
bar;
868#ifdef KOKKOS_ENABLE_CUDA
875__device__
inline void demMlCoarseCycleDevice(
876 const MlCoarseSweep& f,
const Kokkos::View<const int*, CpMem>& parent,
877 const Kokkos::View<const float*, CpMem>& invMass,
878 const Kokkos::View<
float* [3],
CpMem>& velPred,
const Kokkos::View<
float* [3],
CpMem>& velG0,
879 const Kokkos::View<const float*, CpMem>& massG,
const Kokkos::View<int*, CpMem>& grpW,
880 const Kokkos::View<const int*, CpMem>& bkPerm,
const Kokkos::View<const int*, CpMem>& offsDev,
881 const MlFusedMeta& meta,
unsigned* bar,
unsigned& k,
int tid,
int stride) {
882 for (
int i = tid; i < meta.
numReal; i += stride)
884 demGridBarrier(bar, k++);
885 for (
int lvl = 1; lvl <= meta.
numLevels; ++lvl) {
888 for (
int i = tid; i < meta.
numReal; i += stride)
889 grpW(i) = parent(pOff + grpW(i));
890 demGridBarrier(bar, k++);
891 for (
int g = tid; g < ng; g += stride) {
892 f.
velG(off + g, 0) = f.
velG(off + g, 1) = f.
velG(off + g, 2) = 0.0f;
893 f.
angG(off + g, 0) = f.
angG(off + g, 1) = f.
angG(off + g, 2) = 0.0f;
895 demGridBarrier(bar, k++);
896 for (
int i = tid; i < meta.
numReal; i += stride) {
898 const int g = grpW(i);
899 Kokkos::atomic_add(&f.
velG(off + g, 0), m * velPred(i, 0));
900 Kokkos::atomic_add(&f.
velG(off + g, 1), m * velPred(i, 1));
901 Kokkos::atomic_add(&f.
velG(off + g, 2), m * velPred(i, 2));
904 demGridBarrier(bar, k++);
905 for (
int g = tid; g < ng; g += stride) {
906 const float invM = 1.0f / massG(off + g);
907 for (
int c = 0; c < 3; ++c) {
908 f.
velG(off + g, c) *= invM;
909 velG0(off + g, c) = f.
velG(off + g, c);
913 demGridBarrier(bar, k++);
915 for (
int color = 0; color < nCol; ++color) {
916 const int b = offsDev(meta.
offsBase[lvl - 1] + color);
917 const int e = offsDev(meta.
offsBase[lvl - 1] + color + 1);
920 for (
int i2 = b + tid; i2 < e; i2 += stride)
922 demGridBarrier(bar, k++);
924 for (
int i = tid; i < meta.
numReal; i += stride)
926 demGridBarrier(bar, k++);
930__global__
void demFusedCoarseCycleK(
931 MlCoarseSweep f, Kokkos::View<const int*, CpMem> parent,
932 Kokkos::View<const float*, CpMem> invMass, Kokkos::View<
float* [3],
CpMem> velPred,
933 Kokkos::View<
float* [3],
CpMem> velG0, Kokkos::View<const float*, CpMem> massG,
934 Kokkos::View<int*, CpMem> grpW, Kokkos::View<const int*, CpMem> bkPerm,
935 Kokkos::View<const int*, CpMem> offsDev, MlFusedMeta meta,
unsigned* bar) {
936 const int stride = gridDim.x * blockDim.x;
937 const int tid = blockIdx.x * blockDim.x + threadIdx.x;
939 demMlCoarseCycleDevice(f, parent, invMass, velPred, velG0, massG, grpW, bkPerm, offsDev, meta,
940 bar, k, tid, stride);
948__global__
void demFusedMlLoopK(
949 PGSManifoldSweep fine, Kokkos::View<const int*, CpMem> vPerm,
950 Kokkos::View<const int*, CpMem> vOffs,
int vCols, MlCoarseSweep f,
951 Kokkos::View<const int*, CpMem> parent, Kokkos::View<const float*, CpMem> invMass,
952 Kokkos::View<
float* [3],
CpMem> velPred, Kokkos::View<
float* [3],
CpMem> velG0,
953 Kokkos::View<const float*, CpMem> massG, Kokkos::View<int*, CpMem> grpW,
954 Kokkos::View<const int*, CpMem> bkPerm, Kokkos::View<const int*, CpMem> offsDev,
955 MlFusedMeta meta,
int maxIters,
float tol,
float* resQS,
unsigned* bar) {
956 const int stride = gridDim.x * blockDim.x;
957 const int tid = blockIdx.x * blockDim.x + threadIdx.x;
959 for (
int it = 0; it < maxIters; ++it) {
962 demGridBarrier(bar, k++);
963 for (
int c = 0; c < vCols; ++c) {
964 const int b = vOffs(c), e = vOffs(c + 1);
967 for (
int i = b + tid; i < e; i += stride)
968 fine.solveOne(vPerm(i));
969 demGridBarrier(bar, k++);
971 demMlCoarseCycleDevice(f, parent, invMass, velPred, velG0, massG, grpW, bkPerm, offsDev, meta,
972 bar, k, tid, stride);
973 const float r = *
reinterpret_cast<volatile float*
>(resQS);
976 demGridBarrier(bar, k++);
981#ifdef KOKKOS_ENABLE_CUDA
985inline bool demLaunchFusedMlLoop(
986 CpExec& space,
const PGSManifoldSweep& fine, Kokkos::View<const int*, CpMem> vPerm,
987 const FusedSweepCtx& velCtx,
int vCols, Kokkos::View<const ManifoldC*, CpMem> manifolds,
988 Kokkos::View<const int*, CpMem> realIdx, Kokkos::View<const float*, CpMem> invMass,
989 Kokkos::View<
float* [3],
CpMem> velPred, Kokkos::View<float*, CpMem> lambdaAcc,
990 Kokkos::View<float, CpMem> maxApproachQS, Kokkos::View<const float*, CpMem> restRel,
991 MlScratch& S, Kokkos::View<const int*, CpMem> bkPerm,
const MlFusedCtx& ml,
int maxIters,
992 float tol,
const MlBodyViews& body) {
994 std::min(demFusedMaxGrid(demFusedMlLoopK), (
static_cast<int>(ml.bar.extent(0)) - 1) / 8);
995 if (maxGrid <= 0 || vCols <= 0 || velCtx.maxBucket <= 0 || ml.maxWork <= 0)
997 const MlCoarseSweep f =
998 makeMlCoarseSweep(manifolds, realIdx, S, lambdaAcc, maxApproachQS, restRel, body);
999 const int work = std::max(velCtx.maxBucket, ml.maxWork);
1000 const int want = (work + kFusedBlock - 1) / kFusedBlock;
1001 const int grid = want < maxGrid ? want : maxGrid;
1002 cudaStream_t str = space.cuda_stream();
1003 cudaMemsetAsync(ml.bar.data(), 0, (
static_cast<std::size_t
>(grid) * 8 + 1) *
sizeof(
unsigned),
1005 demFusedMlLoopK<<<grid, kFusedBlock, 0, str>>>(
1006 fine, vPerm, velCtx.offsDev, vCols, f, Kokkos::View<const int*, CpMem>(S.parent), invMass,
1007 velPred, S.velG0, Kokkos::View<const float*, CpMem>(S.massG), S.grp, bkPerm, ml.offsDev,
1008 ml.meta, maxIters, tol, maxApproachQS.data(), ml.bar.data());
1018 const std::vector<std::vector<int>>& bkOffs,
int numManifolds,
1019 int numReal,
int coarseSweeps,
1020 Kokkos::View<int*, CpMem> offsDev,
1021 Kokkos::View<unsigned*, CpMem> bar) {
1023#ifdef KOKKOS_ENABLE_CUDA
1026 std::vector<int> flat;
1030 int maxWork = numReal;
1031 for (
int lvl = 1; lvl <= H.
numLevels; ++lvl) {
1032 const auto& lo = bkOffs[
static_cast<std::size_t
>(lvl) - 1];
1033 if (lo.size() !=
static_cast<std::size_t
>(H.
numColors[lvl - 1]) + 1)
1040 ctx.
meta.
offsBase[lvl - 1] =
static_cast<int>(flat.size());
1041 for (std::size_t c = 0; c + 1 < lo.size(); ++c)
1042 maxWork = std::max(maxWork, lo[c + 1] - lo[c]);
1043 flat.insert(flat.end(), lo.begin(), lo.end());
1045 if (flat.empty() || flat.size() > offsDev.extent(0))
1047 const Kokkos::View<const int*, Kokkos::HostSpace, Kokkos::MemoryUnmanaged> h(flat.data(),
1049 auto d = Kokkos::subview(offsDev, Kokkos::pair<std::size_t, std::size_t>(0, flat.size()));
1050 Kokkos::deep_copy(space, d, h);
1051 ctx.
offsDev = Kokkos::View<const int*, CpMem>(offsDev);
1068 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
1069 Kokkos::View<const int*, CpMem> realIdx, Kokkos::View<const float*, CpMem> invMass,
1070 Kokkos::View<
float* [3],
CpMem> velPred, Kokkos::View<float*, CpMem> lambdaAcc,
1072 int coarseSweeps,
const MlBodyViews& body, Kokkos::View<const float*, CpMem> restRel = {},
1073 const std::vector<std::vector<int>>* bkOffs =
nullptr,
1074 Kokkos::View<const int*, CpMem> bkPerm = {},
const MlFusedCtx* fused =
nullptr) {
1078 const MlCoarseSweep fSweep =
1079 makeMlCoarseSweep(manifolds, realIdx, S, lambdaAcc, maxApproach, restRel, body);
1080#ifdef KOKKOS_ENABLE_CUDA
1081 if (fused && fused->maxWork > 0) {
1082 const int maxGrid = std::min(demFusedMaxGrid(demFusedCoarseCycleK),
1083 (
static_cast<int>(fused->bar.extent(0)) - 1) / 8);
1085 const int want = (fused->maxWork + kFusedBlock - 1) / kFusedBlock;
1086 const int grid = want < maxGrid ? want : maxGrid;
1087 cudaStream_t str = space.cuda_stream();
1088 cudaMemsetAsync(fused->bar.data(), 0,
1089 (
static_cast<std::size_t
>(grid) * 8 + 1) *
sizeof(
unsigned), str);
1090 demFusedCoarseCycleK<<<grid, kFusedBlock, 0, str>>>(
1091 fSweep, Kokkos::View<const int*, CpMem>(S.
parent), invMass, velPred, S.
velG0,
1092 Kokkos::View<const float*, CpMem>(S.
massG), S.
grp, bkPerm, fused->offsDev, fused->meta,
1102 Kokkos::parallel_for(
1103 "peclet::dem::ml_cycle_grp_init", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
1104 KOKKOS_LAMBDA(
int i) { grp(i) = i; });
1106 for (
int lvl = 1; lvl <= H.
numLevels; ++lvl) {
1107 const int off = H.
groupOff[lvl - 1];
1111 const int slotShift = 6 * (lvl - 1);
1115 Kokkos::parallel_for(
1116 "peclet::dem::ml_cycle_compose", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
1117 KOKKOS_LAMBDA(
int i) { grp(i) = parent(pOff + grp(i)); });
1122 auto velG0 = S.
velG0;
1123 auto massG = S.
massG;
1124 Kokkos::parallel_for(
1125 "peclet::dem::ml_restrict_reset", Kokkos::RangePolicy<CpExec>(space, 0, ng),
1126 KOKKOS_LAMBDA(
int g) {
1127 velG(off + g, 0) = velG(off + g, 1) = velG(off + g, 2) = 0.0f;
1128 fSweep.angG(off + g, 0) = fSweep.angG(off + g, 1) = fSweep.angG(off + g, 2) = 0.0f;
1130 Kokkos::parallel_for(
1131 "peclet::dem::ml_restrict_accum", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
1132 KOKKOS_LAMBDA(
int i) {
1134 const int g = grp(i);
1135 Kokkos::atomic_add(&velG(off + g, 0), m * velPred(i, 0));
1136 Kokkos::atomic_add(&velG(off + g, 1), m * velPred(i, 1));
1137 Kokkos::atomic_add(&velG(off + g, 2), m * velPred(i, 2));
1138 fSweep.restrictAngular(i, off, g, m, velPred);
1140 Kokkos::parallel_for(
1141 "peclet::dem::ml_restrict_norm", Kokkos::RangePolicy<CpExec>(space, 0, ng),
1142 KOKKOS_LAMBDA(
int g) {
1143 const float invM = 1.0f / massG(off + g);
1144 for (
int c = 0; c < 3; ++c) {
1145 velG(off + g, c) *= invM;
1146 velG0(off + g, c) = velG(off + g, c);
1148 fSweep.normalizeAngular(off + g);
1151 for (
int s = 0; s < coarseSweeps; ++s) {
1152 for (
int color = 0; color < nCol; ++color) {
1154 const bool dense = bkOffs !=
nullptr;
1155 int rb = 0, re = numManifolds;
1157 const auto& lo = (*bkOffs)[
static_cast<std::size_t
>(lvl) - 1];
1158 rb = (lvl - 1) * numManifolds + lo[
static_cast<std::size_t
>(color)];
1159 re = (lvl - 1) * numManifolds + lo[
static_cast<std::size_t
>(color) + 1];
1163 Kokkos::parallel_for(
1164 "peclet::dem::ml_coarse_pgs", Kokkos::RangePolicy<CpExec>(space, rb, re),
1165 KOKKOS_LAMBDA(
int i2) {
1166 const int idx = dense ? bkPerm(i2) : i2;
1167 if (!dense &&
static_cast<int>((cp(idx) >> slotShift) & 63) != color)
1169 fSweep.solveOne(idx, off);
1174 auto velG0 = S.
velG0;
1175 Kokkos::parallel_for(
1176 "peclet::dem::ml_prolongate", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
1177 KOKKOS_LAMBDA(
int i) { fSweep.prolongRigid(i, off, grp(i), velPred, velG0); });
bool eligible(const ManifoldC &m, int idx, Kokkos::View< const int *, CpMem > mColor, Kokkos::View< const float *, CpMem > vn0, Kokkos::View< const float *[3], CpMem > vt0, Kokkos::View< const unsigned char *, CpMem > persistent, Kokkos::View< const float *[3], CpMem > posPred, F3 gHat, float qsThr, int gateMask)
Coarse-eligibility of a manifold: active + base-coloured (non-dup) + quasi-static approach (|vn0| <= ...
float effMass(float invMass)
Effective mass with fixed bodies (invMass == 0) mapped to a huge-but-finite mass so the momentum-weig...
F3 symMul(const V6 &S, int g, F3 v)
Symmetric 3x3 (xx, yy, zz, xy, xz, yz) times v.
F3 spinInertiaTimes(F4 q, F3 invIc, F3 w)
J_q w: the member's world-frame spin inertia R diag(1 / max(invIc, 1e-30)) R^T applied to w (§2....
D3 rotateD(double qx, double qy, double qz, double qw, D3 v)
bool canonical(const Kokkos::View< const float *[3], CpMem > &posPred, const Kokkos::View< const int *, CpMem > &realIdx, int slot)
A slot sits at its canonical body's position, bitwise (false for a periodic image).
F3 memberOffset(const VX &posPred, int i, const Kokkos::View< const float *[3], CpMem > &originG, const Kokkos::View< const float *[3], CpMem > &comOffG, int gi)
Member offset from its group's centre of mass, d_q = fl(fl(x_q - O_g) - c_g) (§2.3 step 2): recompute...
constexpr int kGatePersistent
Optional eligibility gates beyond the quasi-static approach test (a SELECTION, not a sink – impulses ...
constexpr int kMlSlotSkip
void multilevelCoarseCycleKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< float *[3], CpMem > velPred, Kokkos::View< float *, CpMem > lambdaAcc, Kokkos::View< float, CpMem > maxApproach, int numReal, const ContactHierarchy &H, MlScratch &S, int coarseSweeps, const MlBodyViews &body, Kokkos::View< const float *, CpMem > restRel={}, const std::vector< std::vector< int > > *bkOffs=nullptr, Kokkos::View< const int *, CpMem > bkPerm={}, const MlFusedCtx *fused=nullptr)
void buildColorBucketsKokkos(Kokkos::View< const int *, CpMem > colorOf, int n, int numColors, Kokkos::View< int *, CpMem > perm, Kokkos::View< int *, CpMem > cursor, std::vector< int > &offs)
Dense colour buckets (numColors <= 64): perm[offs[c] .
F3 invRotateVector(F4 q, F3 v)
F3 ldF3(const V &v, int i)
F3 rotateVector(F4 q, F3 v)
int buildMlLevelGeometryKokkos(const MlScratch &S, int off, int ng, int numReal, Kokkos::View< const float *[3], CpMem > posPred, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< const float *[3], CpMem > invIc)
The per-level geometry of the rigid coarse space (docs/contact_physics_followups.md §2....
constexpr long long kMlPendingSkipBit
Transient mark of the coarse colouring (docs/contact_solve_framework.md §4.4, "the multilevel coarse ...
CpExec::memory_space CpMem
void buildCoarseBucketsKokkos(const ContactHierarchy &H, MlScratch &S, int numManifolds, Kokkos::View< int *, CpMem > colorScratch, Kokkos::View< int *, CpMem > perm, Kokkos::View< int *, CpMem > cursor, std::vector< std::vector< int > > &offs)
One multilevel stabilization cycle over an already-built hierarchy: fine colored smoothing is the cal...
long long colorKey(int idx)
splitmix32 finalizer: a well-mixed pseudo-random priority per edge index.
MlCoarseSweep makeMlCoarseSweep(Kokkos::View< const ManifoldC *, CpMem > manifolds, Kokkos::View< const int *, CpMem > realIdx, const MlScratch &S, Kokkos::View< float *, CpMem > lambdaAcc, Kokkos::View< float, CpMem > maxApproach, Kokkos::View< const float *, CpMem > restRel, const MlBodyViews &body)
The coarse sweep over the multilevel scratch + the body views (the rigid views stay empty when the la...
ContactHierarchy buildContactHierarchyKokkos(Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const int *, CpMem > mColor, Kokkos::View< const float *, CpMem > vn0, Kokkos::View< const float *[3], CpMem > vt0, Kokkos::View< const unsigned char *, CpMem > persistent, Kokkos::View< const float *[3], CpMem > posPred, F3 gHat, Kokkos::View< const float *, CpMem > invMass, float qsThr, int gateMask, int numReal, MlScratch &S, Kokkos::View< long long *, CpMem > winner, Kokkos::View< std::uint64_t *, CpMem > colorMask, bool excludeImmovable=false, Kokkos::View< const unsigned char *, CpMem > asleep={}, const MlBodyViews &body={})
Build the aggregation hierarchy + per-level crossing-manifold colorings.
MlFusedCtx demMakeMlFusedCtx(CpExec &space, const ContactHierarchy &H, const std::vector< std::vector< int > > &bkOffs, int numManifolds, int numReal, int coarseSweeps, Kokkos::View< int *, CpMem > offsDev, Kokkos::View< unsigned *, CpMem > bar)
Build the fused-coarse-cycle context from an already-built hierarchy + its dense buckets: flatten the...
constexpr int kMlMaxLevels
6-bit colour slots per level in the packed word; slot value 63 = not crossing / not eligible at that ...
Kokkos::DefaultExecutionSpace CpExec
dem — fused colour sweeps: one persistent kernel per sweep instead of one kernel launch per colour.
dem — portable (Kokkos) manifold velocity solve (normal restitution impulse).
Portable mirror of ManifoldConstraint.
The per-body inputs of the rigid coarse space (§2.3-§2.5): the predicted positions (the member offset...
Kokkos::View< const float *[3], CpMem > posPred
Kokkos::View< const float *[3], CpMem > invIc
Kokkos::View< float *[3], CpMem > angVelPred
Kokkos::View< const float *[4], CpMem > quat
One coarse row, evaluated up to its effective mass (the part solveOne shares with the oracle test's s...
The per-manifold coarse-PGS body (e = 0, shared fine accumulator) lives in MlCoarseSweep so the per-c...
void prolongRigid(int i, int off, int g, const VV &velPred, const VV0 &velG0) const
v_q += (V_g - V0_g) + (Omega_g - Omega0_g) x d_q; w_q += Omega_g - Omega0_g.
bool row(int idx, int off, Row &r) const
Kokkos::View< float *[3], CpMem > velG
Kokkos::View< float *[3], CpMem > angG
Kokkos::View< float *, CpMem > lambdaAcc
Kokkos::View< const ManifoldC *, CpMem > manifolds
Kokkos::View< const float *[3], CpMem > comOffG
Kokkos::View< const int *, CpMem > grp
Kokkos::View< const float *[3], CpMem > originG
Kokkos::View< const int *, CpMem > realIdx
Kokkos::View< const float *, CpMem > restRel
Kokkos::View< const float *[6], CpMem > invIG
Kokkos::View< float *[3], CpMem > angG0
Kokkos::View< const float *, CpMem > invMassG
void normalizeAngular(int gi) const
Omega_g = invI_g L_g, snapshot Omega0_g.
Kokkos::View< float, CpMem > maxApproach
void solveOne(int idx, int off) const
void restrictAngular(int i, int off, int g, float m, const VV &velPred) const
L_g += mu_q (d_q x v_q) + J_q w_q (3 float atomics per member).
Fused-coarse-cycle context: flat per-level colour offsets on device + barrier + meta.
Kokkos::View< unsigned *, CpMem > bar
Kokkos::View< const int *, CpMem > offsDev
Device scratch for the multilevel pass, sized once (see Particles::allocate).
Kokkos::View< int *, CpMem > mate
Kokkos::View< float *[3], CpMem > originG
Kokkos::View< float *[3], CpMem > comOffG
Kokkos::View< float *, CpMem > invMassG
Kokkos::View< double *[7], CpMem > accD
Kokkos::View< float *[3], CpMem > angG
Kokkos::View< float *, CpMem > massG
Kokkos::View< float *[3], CpMem > angG0
Kokkos::View< float *[3], CpMem > velG0
Kokkos::View< int *, CpMem > grp
Kokkos::View< long long *, CpMem > colorPacked
Kokkos::View< int *, CpMem > parent
Kokkos::View< float *[6], CpMem > invIG
Kokkos::View< float *[3], CpMem > velG
Double-precision copy of dem_portable's rotateVector (the same formula), for the build.