28#ifndef DEM_SOLVER_MULTILEVEL_HPP
29#define DEM_SOLVER_MULTILEVEL_HPP
31#include <Kokkos_Core.hpp>
60 Kokkos::View<float* [3], CpMem>
velG;
61 Kokkos::View<float* [3], CpMem>
velG0;
62 Kokkos::View<float*, CpMem>
massG;
63 Kokkos::View<int*, CpMem>
grp;
64 Kokkos::View<int*, CpMem>
mate;
70KOKKOS_INLINE_FUNCTION
float effMass(
float invMass) {
71 return 1.0f / Kokkos::fmax(invMass, 1e-30f);
91 Kokkos::View<const int*, CpMem> mColor,
92 Kokkos::View<const float*, CpMem> vn0,
93 Kokkos::View<
const float* [3],
CpMem> vt0,
94 Kokkos::View<const unsigned char*, CpMem> persistent,
95 Kokkos::View<
const float* [3],
CpMem> posPred,
F3 gHat,
96 float qsThr,
int gateMask) {
101 if (Kokkos::fabs(vn0(idx)) > qsThr)
109 const F3 vt{vt0(idx, 0), vt0(idx, 1), vt0(idx, 2)};
110 if (
dot3(vt, vt) > qsThr * qsThr)
117 const float up = -(dx.
x * gHat.
x + dx.
y * gHat.
y + dx.
z * gHat.
z);
118 return Kokkos::fabs(up) > 0.3f * Kokkos::sqrt(
dot3(dx, dx));
129 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
130 Kokkos::View<const int*, CpMem> realIdx, Kokkos::View<const int*, CpMem> mColor,
131 Kokkos::View<const float*, CpMem> vn0, Kokkos::View<
const float* [3],
CpMem> vt0,
132 Kokkos::View<const unsigned char*, CpMem> persistent,
133 Kokkos::View<
const float* [3],
CpMem> posPred,
F3 gHat,
134 Kokkos::View<const float*, CpMem> invMass,
float qsThr,
int gateMask,
int numReal,
MlScratch& S,
135 Kokkos::View<long long*, CpMem> winner, Kokkos::View<std::uint64_t*, CpMem> colorMask,
136 bool excludeImmovable =
false, Kokkos::View<const unsigned char*, CpMem> asleep = {}) {
139 if (numManifolds <= 0 || numReal <= 0)
144 const bool haveAsleep = asleep.extent(0) > 0;
147 Kokkos::parallel_for(
148 "peclet::dem::ml_grp_init", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
149 KOKKOS_LAMBDA(
int i) { grp(i) = i; });
151 Kokkos::parallel_for(
152 "peclet::dem::ml_packed_init", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
153 KOKKOS_LAMBDA(
int idx) { cp(idx) = ~0ll; });
156 int ngPrev = numReal;
157 int parentOff = 0, groupOff = 0;
158 const int parentCap =
static_cast<int>(S.
parent.extent(0));
159 const int groupCap =
static_cast<int>(S.
invMassG.extent(0));
162 if (parentOff + ngPrev > parentCap)
165 Kokkos::parallel_for(
166 "peclet::dem::ml_match_reset", Kokkos::RangePolicy<CpExec>(space, 0, ngPrev),
167 KOKKOS_LAMBDA(
int g) { winner(g) = -1; });
170 Kokkos::parallel_for(
171 "peclet::dem::ml_match_contend", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
172 KOKKOS_LAMBDA(
int idx) {
173 const ManifoldC m = manifolds(idx);
175 gHat, qsThr, gateMask))
180 if (excludeImmovable &&
181 (invMass(realIdx(m.bodyA)) == 0.0f || invMass(realIdx(m.bodyB)) == 0.0f ||
182 (haveAsleep && (asleep(realIdx(m.bodyA)) || asleep(realIdx(m.bodyB))))))
184 const int gA = grp(realIdx(m.bodyA)), gB = grp(realIdx(m.bodyB));
187 const long long key =
colorKey(idx);
188 Kokkos::atomic_max(&winner(gA), key);
189 Kokkos::atomic_max(&winner(gB), key);
192 Kokkos::parallel_for(
193 "peclet::dem::ml_mate_init", Kokkos::RangePolicy<CpExec>(space, 0, ngPrev),
194 KOKKOS_LAMBDA(
int g) { mate(g) = g; });
195 Kokkos::parallel_for(
196 "peclet::dem::ml_match_commit", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
197 KOKKOS_LAMBDA(
int idx) {
198 const ManifoldC m = manifolds(idx);
200 gHat, qsThr, gateMask))
202 if (excludeImmovable &&
203 (invMass(realIdx(m.bodyA)) == 0.0f || invMass(realIdx(m.bodyB)) == 0.0f ||
204 (haveAsleep && (asleep(realIdx(m.bodyA)) || asleep(realIdx(m.bodyB))))))
206 const int gA = grp(realIdx(m.bodyA)), gB = grp(realIdx(m.bodyB));
209 const long long key =
colorKey(idx);
210 if (winner(gA) == key && winner(gB) == key) {
221 const int off = parentOff;
222 Kokkos::parallel_scan(
223 "peclet::dem::ml_compact", Kokkos::RangePolicy<CpExec>(space, 0, ngPrev),
224 KOKKOS_LAMBDA(
int g,
int& run,
const bool final) {
225 const bool leader = (mate(g) >= g);
229 parent(off + g) = run - 1;
232 Kokkos::parallel_for(
233 "peclet::dem::ml_parent_nonleader", Kokkos::RangePolicy<CpExec>(space, 0, ngPrev),
234 KOKKOS_LAMBDA(
int g) {
236 parent(off + g) = parent(off + mate(g));
239 if (ngNew >= ngPrev || ngNew > (9 * ngPrev) / 10)
241 if (groupOff + ngNew > groupCap)
247 const int off = parentOff;
248 Kokkos::parallel_for(
249 "peclet::dem::ml_grp_compose", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
250 KOKKOS_LAMBDA(
int i) { grp(i) = parent(off + grp(i)); });
255 auto massG = S.
massG;
257 const int off = groupOff;
258 Kokkos::parallel_for(
259 "peclet::dem::ml_mass_reset", Kokkos::RangePolicy<CpExec>(space, 0, ngNew),
260 KOKKOS_LAMBDA(
int g) { massG(off + g) = 0.0f; });
261 Kokkos::parallel_for(
262 "peclet::dem::ml_mass_accum", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
263 KOKKOS_LAMBDA(
int i) {
266 Kokkos::parallel_for(
267 "peclet::dem::ml_mass_invert", Kokkos::RangePolicy<CpExec>(space, 0, ngNew),
268 KOKKOS_LAMBDA(
int g) { invMassG(off + g) = 1.0f / massG(off + g); });
276 const int slotShift = 6 * (lvl - 1);
277 Kokkos::parallel_for(
278 "peclet::dem::ml_color_mask_reset", Kokkos::RangePolicy<CpExec>(space, 0, ngNew),
279 KOKKOS_LAMBDA(
int g) { colorMask(g) = 0; });
282 int remaining = 1, prevRemaining = -1, maxc = -1;
283 for (
int round = 0; round < 64 && remaining > 0; ++round) {
284 Kokkos::parallel_for(
285 "peclet::dem::ml_color_reset_winner", Kokkos::RangePolicy<CpExec>(space, 0, ngNew),
286 KOKKOS_LAMBDA(
int g) { winner(g) = -1; });
287 Kokkos::parallel_for(
288 "peclet::dem::ml_color_contend", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
289 KOKKOS_LAMBDA(
int idx) {
290 const ManifoldC m = manifolds(idx);
296 const int gA = grp(realIdx(m.bodyA));
297 const int gB = (m.bodyB >= 0) ? grp(realIdx(m.bodyB)) : -1;
300 const long long key =
colorKey(idx);
301 Kokkos::atomic_max(&winner(gA), key);
303 Kokkos::atomic_max(&winner(gB), key);
306 Kokkos::parallel_reduce(
307 "peclet::dem::ml_color_commit", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
308 KOKKOS_LAMBDA(
int idx,
int& acc) {
309 const ManifoldC m = manifolds(idx);
315 const int gA = grp(realIdx(m.bodyA));
316 const int gB = (m.bodyB >= 0) ? grp(realIdx(m.bodyB)) : -1;
319 const long long key =
colorKey(idx);
320 if (winner(gA) != key || (gB >= 0 && winner(gB) != key)) {
324 std::uint64_t forbidden = colorMask(gA);
326 forbidden |= colorMask(gB);
328 while (c <
kMlSlotSkip - 1 && (forbidden & (std::uint64_t(1) << c)))
330 cp(idx) = (cp(idx) & ~(63ll << slotShift)) | (
static_cast<long long>(c) << slotShift);
331 const std::uint64_t bit = std::uint64_t(1) << c;
332 colorMask(gA) |= bit;
334 colorMask(gB) |= bit;
338 if (rem == prevRemaining)
343 Kokkos::parallel_reduce(
344 "peclet::dem::ml_color_max", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
345 KOKKOS_LAMBDA(
int idx,
int& mx) {
346 const int c =
static_cast<int>((cp(idx) >> slotShift) & 63);
350 Kokkos::Max<int>(maxc));
352 H.numColors.push_back(maxc + 1);
354 H.groupOff.push_back(groupOff);
355 H.numGroups.push_back(ngNew);
356 H.parentOff.push_back(parentOff);
380 Kokkos::View<int*, CpMem> colorScratch,
381 Kokkos::View<int*, CpMem> perm,
382 Kokkos::View<int*, CpMem> cursor,
383 std::vector<std::vector<int>>& offs) {
384 offs.assign(
static_cast<std::size_t
>(H.
numLevels), {});
385 if (numManifolds <= 0)
388 for (
int lvl = 1; lvl <= H.
numLevels; ++lvl) {
389 const int slotShift = 6 * (lvl - 1);
390 const int nCol = H.
numColors[
static_cast<std::size_t
>(lvl) - 1];
392 auto cs = colorScratch;
393 Kokkos::parallel_for(
394 "peclet::dem::ml_bucket_colors", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
395 KOKKOS_LAMBDA(
int idx) {
396 const int c =
static_cast<int>((cp(idx) >> slotShift) & 63);
397 cs(idx) = (c < nCol) ? c : -1;
400 Kokkos::subview(perm, Kokkos::pair<int, int>((lvl - 1) * numManifolds, lvl * numManifolds));
402 cursor, offs[
static_cast<std::size_t
>(lvl) - 1]);
413 Kokkos::View<const int*, CpMem>
grp;
414 Kokkos::View<float* [3], CpMem>
velG;
420 KOKKOS_FUNCTION
void solveOne(
int idx,
int off)
const {
429 const float invN = 1.0f /
static_cast<float>(m.
num_points);
431 const float lenN = Kokkos::sqrt(
dot3(Nsum, Nsum));
436 const F3 diffCenters = (gB < 0) ? rAavg :
sub3(rAavg, rBavg);
437 const F3 vA{
velG(off + gA, 0),
velG(off + gA, 1),
velG(off + gA, 2)};
443 const float vn =
dot3(
sub3(vA, vB), Nsum);
444 const float alignment =
dot3(Nsum, diffCenters);
445 const float sgn = (alignment > 0.0f) ? 1.0f : -1.0f;
446 const float invMA =
invMassG(off + gA);
447 const float invMB = (gB >= 0) ?
invMassG(off + gB) : 0.0f;
448 const float w =
dot3(Nsum, Nsum) * (invMA + invMB);
451 const float vtil = sgn * vn;
452 const float dp = vtil / w;
454 float pNew = pOld + dp;
457 const float d = pNew - pOld;
461 Kokkos::atomic_max(&
maxApproach(), Kokkos::fabs(d) * w / lenN);
462 const float lambda = -sgn * d;
464 velG(off + gA, 0) += J.
x * invMA;
465 velG(off + gA, 1) += J.
y * invMA;
466 velG(off + gA, 2) += J.
z * invMA;
468 velG(off + gB, 0) -= J.
x * invMB;
469 velG(off + gB, 1) -= J.
y * invMB;
470 velG(off + gB, 2) -= J.
z * invMB;
491 Kokkos::View<unsigned*, CpMem>
bar;
496#ifdef KOKKOS_ENABLE_CUDA
503__device__
inline void demMlCoarseCycleDevice(
504 const MlCoarseSweep& f,
const Kokkos::View<const int*, CpMem>& parent,
505 const Kokkos::View<const float*, CpMem>& invMass,
506 const Kokkos::View<
float* [3],
CpMem>& velPred,
const Kokkos::View<
float* [3],
CpMem>& velG0,
507 const Kokkos::View<const float*, CpMem>& massG,
const Kokkos::View<int*, CpMem>& grpW,
508 const Kokkos::View<const int*, CpMem>& bkPerm,
const Kokkos::View<const int*, CpMem>& offsDev,
509 const MlFusedMeta& meta,
unsigned* bar,
unsigned& k,
int tid,
int stride) {
510 for (
int i = tid; i < meta.
numReal; i += stride)
512 demGridBarrier(bar, k++);
513 for (
int lvl = 1; lvl <= meta.
numLevels; ++lvl) {
516 for (
int i = tid; i < meta.
numReal; i += stride)
517 grpW(i) = parent(pOff + grpW(i));
518 demGridBarrier(bar, k++);
519 for (
int g = tid; g < ng; g += stride)
520 f.
velG(off + g, 0) = f.
velG(off + g, 1) = f.
velG(off + g, 2) = 0.0f;
521 demGridBarrier(bar, k++);
522 for (
int i = tid; i < meta.
numReal; i += stride) {
524 const int g = grpW(i);
525 Kokkos::atomic_add(&f.
velG(off + g, 0), m * velPred(i, 0));
526 Kokkos::atomic_add(&f.
velG(off + g, 1), m * velPred(i, 1));
527 Kokkos::atomic_add(&f.
velG(off + g, 2), m * velPred(i, 2));
529 demGridBarrier(bar, k++);
530 for (
int g = tid; g < ng; g += stride) {
531 const float invM = 1.0f / massG(off + g);
532 for (
int c = 0; c < 3; ++c) {
533 f.
velG(off + g, c) *= invM;
534 velG0(off + g, c) = f.
velG(off + g, c);
537 demGridBarrier(bar, k++);
539 for (
int color = 0; color < nCol; ++color) {
540 const int b = offsDev(meta.
offsBase[lvl - 1] + color);
541 const int e = offsDev(meta.
offsBase[lvl - 1] + color + 1);
544 for (
int i2 = b + tid; i2 < e; i2 += stride)
546 demGridBarrier(bar, k++);
548 for (
int i = tid; i < meta.
numReal; i += stride) {
549 const int g = grpW(i);
550 velPred(i, 0) += f.
velG(off + g, 0) - velG0(off + g, 0);
551 velPred(i, 1) += f.
velG(off + g, 1) - velG0(off + g, 1);
552 velPred(i, 2) += f.
velG(off + g, 2) - velG0(off + g, 2);
554 demGridBarrier(bar, k++);
558__global__
void demFusedCoarseCycleK(
559 MlCoarseSweep f, Kokkos::View<const int*, CpMem> parent,
560 Kokkos::View<const float*, CpMem> invMass, Kokkos::View<
float* [3],
CpMem> velPred,
561 Kokkos::View<
float* [3],
CpMem> velG0, Kokkos::View<const float*, CpMem> massG,
562 Kokkos::View<int*, CpMem> grpW, Kokkos::View<const int*, CpMem> bkPerm,
563 Kokkos::View<const int*, CpMem> offsDev, MlFusedMeta meta,
unsigned* bar) {
564 const int stride = gridDim.x * blockDim.x;
565 const int tid = blockIdx.x * blockDim.x + threadIdx.x;
567 demMlCoarseCycleDevice(f, parent, invMass, velPred, velG0, massG, grpW, bkPerm, offsDev, meta,
568 bar, k, tid, stride);
576__global__
void demFusedMlLoopK(
577 PGSManifoldSweep fine, Kokkos::View<const int*, CpMem> vPerm,
578 Kokkos::View<const int*, CpMem> vOffs,
int vCols, MlCoarseSweep f,
579 Kokkos::View<const int*, CpMem> parent, Kokkos::View<const float*, CpMem> invMass,
580 Kokkos::View<
float* [3],
CpMem> velPred, Kokkos::View<
float* [3],
CpMem> velG0,
581 Kokkos::View<const float*, CpMem> massG, Kokkos::View<int*, CpMem> grpW,
582 Kokkos::View<const int*, CpMem> bkPerm, Kokkos::View<const int*, CpMem> offsDev,
583 MlFusedMeta meta,
int maxIters,
float tol,
float* resQS,
unsigned* bar) {
584 const int stride = gridDim.x * blockDim.x;
585 const int tid = blockIdx.x * blockDim.x + threadIdx.x;
587 for (
int it = 0; it < maxIters; ++it) {
590 demGridBarrier(bar, k++);
591 for (
int c = 0; c < vCols; ++c) {
592 const int b = vOffs(c), e = vOffs(c + 1);
595 for (
int i = b + tid; i < e; i += stride)
596 fine.solveOne(vPerm(i));
597 demGridBarrier(bar, k++);
599 demMlCoarseCycleDevice(f, parent, invMass, velPred, velG0, massG, grpW, bkPerm, offsDev, meta,
600 bar, k, tid, stride);
601 const float r = *
reinterpret_cast<volatile float*
>(resQS);
604 demGridBarrier(bar, k++);
609#ifdef KOKKOS_ENABLE_CUDA
613inline bool demLaunchFusedMlLoop(
614 CpExec& space,
const PGSManifoldSweep& fine, Kokkos::View<const int*, CpMem> vPerm,
615 const FusedSweepCtx& velCtx,
int vCols, Kokkos::View<const ManifoldC*, CpMem> manifolds,
616 Kokkos::View<const int*, CpMem> realIdx, Kokkos::View<const float*, CpMem> invMass,
617 Kokkos::View<
float* [3],
CpMem> velPred, Kokkos::View<float*, CpMem> lambdaAcc,
618 Kokkos::View<float, CpMem> maxApproachQS, Kokkos::View<const float*, CpMem> restRel,
619 MlScratch& S, Kokkos::View<const int*, CpMem> bkPerm,
const MlFusedCtx& ml,
int maxIters,
622 std::min(demFusedMaxGrid(demFusedMlLoopK), (
static_cast<int>(ml.bar.extent(0)) - 1) / 8);
623 if (maxGrid <= 0 || vCols <= 0 || velCtx.maxBucket <= 0 || ml.maxWork <= 0)
625 const MlCoarseSweep f{manifolds,
627 Kokkos::View<const int*, CpMem>(S.grp),
629 Kokkos::View<const float*, CpMem>(S.invMassG),
633 const int work = std::max(velCtx.maxBucket, ml.maxWork);
634 const int want = std::min((work + kFusedBlock - 1) / kFusedBlock, std::max(1, demFusedGridCap()));
635 const int grid = want < maxGrid ? want : maxGrid;
636 cudaStream_t str = space.cuda_stream();
637 cudaMemsetAsync(ml.bar.data(), 0, (
static_cast<std::size_t
>(grid) * 8 + 1) *
sizeof(
unsigned),
639 demFusedMlLoopK<<<grid, kFusedBlock, 0, str>>>(
640 fine, vPerm, velCtx.offsDev, vCols, f, Kokkos::View<const int*, CpMem>(S.parent), invMass,
641 velPred, S.velG0, Kokkos::View<const float*, CpMem>(S.massG), S.grp, bkPerm, ml.offsDev,
642 ml.meta, maxIters, tol, maxApproachQS.data(), ml.bar.data());
652 const std::vector<std::vector<int>>& bkOffs,
int numManifolds,
653 int numReal,
int coarseSweeps,
654 Kokkos::View<int*, CpMem> offsDev,
655 Kokkos::View<unsigned*, CpMem> bar) {
657#ifdef KOKKOS_ENABLE_CUDA
660 std::vector<int>
flat;
664 int maxWork = numReal;
665 for (
int lvl = 1; lvl <= H.
numLevels; ++lvl) {
666 const auto& lo = bkOffs[
static_cast<std::size_t
>(lvl) - 1];
667 if (lo.size() !=
static_cast<std::size_t
>(H.
numColors[lvl - 1]) + 1)
675 for (std::size_t c = 0; c + 1 < lo.size(); ++c)
676 maxWork = std::max(maxWork, lo[c + 1] - lo[c]);
677 flat.insert(
flat.end(), lo.begin(), lo.end());
679 if (
flat.empty() ||
flat.size() > offsDev.extent(0))
681 const Kokkos::View<const int*, Kokkos::HostSpace, Kokkos::MemoryUnmanaged> h(
flat.data(),
683 auto d = Kokkos::subview(offsDev, Kokkos::pair<std::size_t, std::size_t>(0,
flat.size()));
684 Kokkos::deep_copy(space, d, h);
685 ctx.
offsDev = Kokkos::View<const int*, CpMem>(offsDev);
702 Kokkos::View<const ManifoldC*, CpMem> manifolds,
int numManifolds,
703 Kokkos::View<const int*, CpMem> realIdx, Kokkos::View<const float*, CpMem> invMass,
704 Kokkos::View<
float* [3],
CpMem> velPred, Kokkos::View<float*, CpMem> lambdaAcc,
706 int coarseSweeps, Kokkos::View<const float*, CpMem> restRel = {},
707 const std::vector<std::vector<int>>* bkOffs =
nullptr,
708 Kokkos::View<const int*, CpMem> bkPerm = {},
const MlFusedCtx* fused =
nullptr) {
710#ifdef KOKKOS_ENABLE_CUDA
711 if (fused && fused->maxWork > 0) {
712 const MlCoarseSweep f{manifolds,
714 Kokkos::View<const int*, CpMem>(S.
grp),
716 Kokkos::View<const float*, CpMem>(S.
invMassG),
720 const int maxGrid = std::min(demFusedMaxGrid(demFusedCoarseCycleK),
721 (
static_cast<int>(fused->bar.extent(0)) - 1) / 8);
723 const int want = std::min((fused->maxWork + kFusedBlock - 1) / kFusedBlock,
724 std::max(1, demFusedGridCap()));
725 const int grid = want < maxGrid ? want : maxGrid;
726 cudaStream_t str = space.cuda_stream();
727 cudaMemsetAsync(fused->bar.data(), 0,
728 (
static_cast<std::size_t
>(grid) * 8 + 1) *
sizeof(
unsigned), str);
729 demFusedCoarseCycleK<<<grid, kFusedBlock, 0, str>>>(
730 f, Kokkos::View<const int*, CpMem>(S.
parent), invMass, velPred, S.
velG0,
731 Kokkos::View<const float*, CpMem>(S.
massG), S.
grp, bkPerm, fused->offsDev, fused->meta,
741 Kokkos::parallel_for(
742 "peclet::dem::ml_cycle_grp_init", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
743 KOKKOS_LAMBDA(
int i) { grp(i) = i; });
745 for (
int lvl = 1; lvl <= H.
numLevels; ++lvl) {
746 const int off = H.
groupOff[lvl - 1];
750 const int slotShift = 6 * (lvl - 1);
754 Kokkos::parallel_for(
755 "peclet::dem::ml_cycle_compose", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
756 KOKKOS_LAMBDA(
int i) { grp(i) = parent(pOff + grp(i)); });
760 auto velG0 = S.
velG0;
761 auto massG = S.
massG;
762 Kokkos::parallel_for(
763 "peclet::dem::ml_restrict_reset", Kokkos::RangePolicy<CpExec>(space, 0, ng),
764 KOKKOS_LAMBDA(
int g) { velG(off + g, 0) = velG(off + g, 1) = velG(off + g, 2) = 0.0f; });
765 Kokkos::parallel_for(
766 "peclet::dem::ml_restrict_accum", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
767 KOKKOS_LAMBDA(
int i) {
769 const int g = grp(i);
770 Kokkos::atomic_add(&velG(off + g, 0), m * velPred(i, 0));
771 Kokkos::atomic_add(&velG(off + g, 1), m * velPred(i, 1));
772 Kokkos::atomic_add(&velG(off + g, 2), m * velPred(i, 2));
774 Kokkos::parallel_for(
775 "peclet::dem::ml_restrict_norm", Kokkos::RangePolicy<CpExec>(space, 0, ng),
776 KOKKOS_LAMBDA(
int g) {
777 const float invM = 1.0f / massG(off + g);
778 for (
int c = 0; c < 3; ++c) {
779 velG(off + g, c) *= invM;
780 velG0(off + g, c) = velG(off + g, c);
786 const MlCoarseSweep fSweep{manifolds,
788 Kokkos::View<const int*, CpMem>(S.
grp),
790 Kokkos::View<const float*, CpMem>(S.
invMassG),
794 for (
int s = 0; s < coarseSweeps; ++s) {
795 for (
int color = 0; color < nCol; ++color) {
797 const bool dense = bkOffs !=
nullptr;
798 int rb = 0, re = numManifolds;
800 const auto& lo = (*bkOffs)[
static_cast<std::size_t
>(lvl) - 1];
801 rb = (lvl - 1) * numManifolds + lo[
static_cast<std::size_t
>(color)];
802 re = (lvl - 1) * numManifolds + lo[
static_cast<std::size_t
>(color) + 1];
806 Kokkos::parallel_for(
807 "peclet::dem::ml_coarse_pgs", Kokkos::RangePolicy<CpExec>(space, rb, re),
808 KOKKOS_LAMBDA(
int i2) {
809 const int idx = dense ? bkPerm(i2) : i2;
810 if (!dense &&
static_cast<int>((cp(idx) >> slotShift) & 63) != color)
812 fSweep.solveOne(idx, off);
818 auto velG0 = S.
velG0;
819 Kokkos::parallel_for(
820 "peclet::dem::ml_prolongate", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
821 KOKKOS_LAMBDA(
int i) {
822 const int g = grp(i);
823 velPred(i, 0) += velG(off + g, 0) - velG0(off + g, 0);
824 velPred(i, 1) += velG(off + g, 1) - velG0(off + g, 1);
825 velPred(i, 2) += velG(off + g, 2) - velG0(off + g, 2);
static nb::ndarray< nb::numpy, float > flat(std::vector< float > &&v)
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...
constexpr int kGatePersistent
Optional eligibility gates beyond the quasi-static approach test (a SELECTION, not a sink – impulses ...
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, Kokkos::View< const float *, CpMem > restRel={}, const std::vector< std::vector< int > > *bkOffs=nullptr, Kokkos::View< const int *, CpMem > bkPerm={}, const MlFusedCtx *fused=nullptr)
constexpr int kMlSlotSkip
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 ldF3(const V &v, int i)
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={})
Build the aggregation hierarchy + per-level crossing-manifold colorings.
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.
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-manifold coarse-PGS body (translation-only, e = 0, shared fine accumulator) lives in MlCoarse...
Kokkos::View< float *[3], CpMem > velG
Kokkos::View< float *, CpMem > lambdaAcc
Kokkos::View< const ManifoldC *, CpMem > manifolds
Kokkos::View< const int *, CpMem > grp
Kokkos::View< const int *, CpMem > realIdx
Kokkos::View< const float *, CpMem > restRel
Kokkos::View< const float *, CpMem > invMassG
Kokkos::View< float, CpMem > maxApproach
void solveOne(int idx, int off) const
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 *, CpMem > invMassG
Kokkos::View< float *, CpMem > massG
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 *[3], CpMem > velG