10#ifndef DEM_SOLVER_POSITION_HPP
11#define DEM_SOLVER_POSITION_HPP
13#include <Kokkos_Core.hpp>
25 return invM + rn.
x * rn.
x * invI.
x + rn.
y * rn.
y * invI.
y + rn.
z * rn.
z * invI.
z;
28 return F4{0.5f * (dTheta.
x * q.
w + dTheta.
y * q.
z - dTheta.
z * q.
y),
29 0.5f * (dTheta.
y * q.
w + dTheta.
z * q.
x - dTheta.
x * q.
z),
30 0.5f * (dTheta.
z * q.
w + dTheta.
x * q.
y - dTheta.
y * q.
x),
31 0.5f * (-dTheta.
x * q.
x - dTheta.
y * q.
y - dTheta.
z * q.
z)};
37 Kokkos::View<const ContactC*, CpMem> contacts,
int numContacts,
38 Kokkos::View<const float*, CpMem> invMass, Kokkos::View<
const float* [3],
CpMem> posPred,
39 Kokkos::View<
const float* [4],
CpMem> quatPred,
40 Kokkos::View<
const float* [4],
CpMem> quatStatic,
41 Kokkos::View<
const float* [3],
CpMem> invInertia, Kokkos::View<
float* [3],
CpMem> deltaPos,
42 Kokkos::View<
float* [4],
CpMem> deltaQuat, Kokkos::View<int*, CpMem> constraintCounts,
43 Kokkos::View<float, CpMem> maxOverlap, Kokkos::View<const int*, CpMem> onlyColor = {},
44 int colorFilter = 0) {
47 const bool filt = onlyColor.extent(0) > 0;
49 "peclet::dem::solve_position", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
50 KOKKOS_LAMBDA(
int idx) {
51 if (filt && onlyColor(idx) != colorFilter)
53 const ContactC c = contacts(idx);
54 const int idA = c.bodyA, idB = c.bodyB;
55 const float invMassA = invMass(idA);
56 const float invMassB = (idB >= 0) ? invMass(idB) : 0.0f;
58 const F3 pA =
ldF3(posPred, idA);
59 const F4 qA =
ldF4(quatPred, idA);
63 pB =
ldF3(posPred, idB);
64 qB =
ldF4(quatPred, idB);
70 F3 rB{c.rB.x, c.rB.y, c.rB.z};
71 F3 n{c.normal.x, c.normal.y, c.normal.z};
80 n =
F3{c.normal.x, c.normal.y, c.normal.z};
81 const F3 pAsurf =
add3(pA, rA);
82 C =
dot3(
sub3(pAsurf,
F3{c.rB.x, c.rB.y, c.rB.z}), n);
84 const F3 pAc =
add3(pA, rA);
85 const F3 pBc =
add3(pB, rB);
91 const F3 invIA =
ldF3(invInertia, idA);
92 const F3 invIB = (idB >= 0) ?
ldF3(invInertia, idB) :
F3{0, 0, 0};
93 const float wTotal =
computeW(rA, n, invMassA, invIA) +
computeW(rB, n, invMassB, invIB);
97 const float dLambda = -C / wTotal;
100 Kokkos::atomic_add(&deltaPos(idA, 0), n.x * dLambda * invMassA);
101 Kokkos::atomic_add(&deltaPos(idA, 1), n.y * dLambda * invMassA);
102 Kokkos::atomic_add(&deltaPos(idA, 2), n.z * dLambda * invMassA);
105 const F3 dTheta{rn.x * invIA.x * dLambda, rn.y * invIA.y * dLambda,
106 rn.z * invIA.z * dLambda};
114 Kokkos::atomic_add(&deltaPos(idB, 0), -n.x * dLambda * invMassB);
115 Kokkos::atomic_add(&deltaPos(idB, 1), -n.y * dLambda * invMassB);
116 Kokkos::atomic_add(&deltaPos(idB, 2), -n.z * dLambda * invMassB);
118 const F3 dTheta{-rn.x * invIB.x * dLambda, -rn.y * invIB.y * dLambda,
119 -rn.z * invIB.z * dLambda};
125 Kokkos::atomic_add(&constraintCounts(idB), 1);
127 Kokkos::atomic_add(&constraintCounts(idA), 1);
130 Kokkos::atomic_max(&maxOverlap(), -C);
155 int numBodies, Kokkos::View<int*, CpMem> cColor,
156 Kokkos::View<long long*, CpMem> bodyWinner,
157 Kokkos::View<std::uint64_t*, CpMem> bodyMask,
int& leftover,
158 Kokkos::View<const unsigned char*, CpMem> sleepMask = {}) {
161 if (numContacts <= 0 || numBodies <= 0)
163 const bool sleepOn = sleepMask.extent(0) > 0;
164 Kokkos::parallel_for(
165 "peclet::dem::pcolor_init_bodies", Kokkos::RangePolicy<CpExec>(space, 0, numBodies),
166 KOKKOS_LAMBDA(
int i) { bodyMask(i) = 0; });
167 Kokkos::parallel_for(
168 "peclet::dem::pcolor_init_contacts", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
169 KOKKOS_LAMBDA(
int idx) { cColor(idx) = (sleepOn && sleepMask(idx)) ? -2 : -1; });
171 int remaining = 1, prevRemaining = -1;
172 const int maxRounds = numBodies + 2;
173 for (
int round = 0; round < maxRounds && remaining > 0; ++round) {
174 Kokkos::parallel_for(
175 "peclet::dem::pcolor_reset_winner", Kokkos::RangePolicy<CpExec>(space, 0, numBodies),
176 KOKKOS_LAMBDA(
int i) { bodyWinner(i) = -1; });
177 Kokkos::parallel_for(
178 "peclet::dem::pcolor_contend", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
179 KOKKOS_LAMBDA(
int idx) {
180 if (cColor(idx) != -1)
182 const ContactC c = contacts(idx);
183 const long long key =
colorKey(idx);
184 Kokkos::atomic_max(&bodyWinner(c.bodyA), key);
186 Kokkos::atomic_max(&bodyWinner(c.bodyB), key);
189 Kokkos::parallel_reduce(
190 "peclet::dem::pcolor_commit", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
191 KOKKOS_LAMBDA(
int idx,
int& acc) {
192 if (cColor(idx) != -1)
194 const ContactC c = contacts(idx);
195 const int ea = c.bodyA;
196 const int eb = c.bodyB;
197 const long long key =
colorKey(idx);
198 if (bodyWinner(ea) != key || (eb >= 0 && bodyWinner(eb) != key)) {
202 std::uint64_t forbidden = bodyMask(ea);
204 forbidden |= bodyMask(eb);
206 while (col < 62 && (forbidden & (std::uint64_t(1) << col)))
209 const std::uint64_t bit = std::uint64_t(1) << col;
219 if (rem == prevRemaining)
226 Kokkos::parallel_reduce(
227 "peclet::dem::pcolor_max", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
228 KOKKOS_LAMBDA(
int idx,
int& mx) {
229 if (cColor(idx) > mx)
232 Kokkos::Max<int>(maxc));
233 Kokkos::parallel_reduce(
234 "peclet::dem::pcolor_leftover", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
235 KOKKOS_LAMBDA(
int idx,
int& acc) {
236 if (cColor(idx) == -1)
254 Kokkos::View<const ContactC*, CpMem> contacts,
int numContacts,
int numBodies,
255 Kokkos::View<const unsigned long long*, CpMem> prevKeys,
256 Kokkos::View<const int*, CpMem> prevColor,
int prevCount, Kokkos::View<int*, CpMem> cColor,
257 Kokkos::View<unsigned long long*, CpMem> keysOut, Kokkos::View<long long*, CpMem> bodyWinner,
258 Kokkos::View<std::uint64_t*, CpMem> bodyMask,
int& leftover,
bool forceFull,
259 Kokkos::View<const unsigned char*, CpMem> sleepMask = {}) {
262 if (numContacts <= 0 || numBodies <= 0)
264 const bool full0 = forceFull || prevCount <= 0;
265 const bool sleepOn = sleepMask.extent(0) > 0;
268 Kokkos::parallel_for(
269 "peclet::dem::pcolor_i_seed", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
270 KOKKOS_LAMBDA(
int idx) {
271 const ContactC c = contacts(idx);
272 const unsigned long long k =
pairKey(c);
274 if (sleepOn && sleepMask(idx)) {
280 int lo = 0, hi = prevCount;
282 const int mid = (lo + hi) >> 1;
283 if (prevKeys(mid) < k)
288 if (lo < prevCount && prevKeys(lo) == k) {
289 const int pc = prevColor(lo);
296 Kokkos::parallel_for(
297 "peclet::dem::pcolor_i_init_bodies", Kokkos::RangePolicy<CpExec>(space, 0, numBodies),
298 KOKKOS_LAMBDA(
int i) { bodyMask(i) = 0; });
308 Kokkos::parallel_for(
309 "peclet::dem::pcolor_i_mask", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
310 KOKKOS_LAMBDA(
int idx) {
311 const int col = cColor(idx);
314 const ContactC c = contacts(idx);
315 const std::uint64_t bit = std::uint64_t(1) << col;
316 bool conflict = ((Kokkos::atomic_fetch_or(&bodyMask(c.bodyA), bit) >> col) & 1) != 0;
318 conflict |= ((Kokkos::atomic_fetch_or(&bodyMask(c.bodyB), bit) >> col) & 1) != 0;
325 int remaining = 1, prevRemaining = -1;
326 const int maxRounds = numBodies + 2;
327 for (
int round = 0; round < maxRounds && remaining > 0; ++round) {
328 Kokkos::parallel_for(
329 "peclet::dem::pcolor_i_reset_winner", Kokkos::RangePolicy<CpExec>(space, 0, numBodies),
330 KOKKOS_LAMBDA(
int i) { bodyWinner(i) = -1; });
331 Kokkos::parallel_for(
332 "peclet::dem::pcolor_i_contend", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
333 KOKKOS_LAMBDA(
int idx) {
334 if (cColor(idx) != -1)
336 const ContactC c = contacts(idx);
337 const long long key =
colorKey(idx);
338 Kokkos::atomic_max(&bodyWinner(c.bodyA), key);
340 Kokkos::atomic_max(&bodyWinner(c.bodyB), key);
343 Kokkos::parallel_reduce(
344 "peclet::dem::pcolor_i_commit", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
345 KOKKOS_LAMBDA(
int idx,
int& acc) {
346 if (cColor(idx) != -1)
348 const ContactC c = contacts(idx);
349 const int ea = c.bodyA;
350 const int eb = c.bodyB;
351 const long long key =
colorKey(idx);
352 if (bodyWinner(ea) != key || (eb >= 0 && bodyWinner(eb) != key)) {
356 std::uint64_t forbidden = bodyMask(ea);
358 forbidden |= bodyMask(eb);
360 while (col < 62 && (forbidden & (std::uint64_t(1) << col)))
363 const std::uint64_t bit = std::uint64_t(1) << col;
370 if (rem == prevRemaining)
376 Kokkos::parallel_reduce(
377 "peclet::dem::pcolor_i_max", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
378 KOKKOS_LAMBDA(
int idx,
int& mx) {
379 if (cColor(idx) > mx)
382 Kokkos::Max<int>(maxc));
383 Kokkos::parallel_reduce(
384 "peclet::dem::pcolor_i_leftover", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
385 KOKKOS_LAMBDA(
int idx,
int& acc) {
386 if (cColor(idx) == -1)
396 Kokkos::View<const int*, CpMem> color,
397 Kokkos::View<unsigned long long*, CpMem> prevKeys,
398 Kokkos::View<int*, CpMem> prevColor,
399 Kokkos::View<int*, CpMem> perm,
int numContacts) {
400 if (numContacts <= 0)
403 const int n = numContacts;
404 const auto rng = Kokkos::pair<int, int>(0, n);
405 auto kd = Kokkos::subview(prevKeys, rng);
406 Kokkos::deep_copy(space, kd, Kokkos::subview(keys, rng));
407 Kokkos::parallel_for(
408 "peclet::dem::pcolor_commit_iota", Kokkos::RangePolicy<CpExec>(space, 0, n),
409 KOKKOS_LAMBDA(
int i) { perm(i) = i; });
411 auto pd = Kokkos::subview(perm, rng);
412 Kokkos::Experimental::sort_by_key(space, kd, pd);
414 Kokkos::View<int*, CpMem> pc = prevColor;
415 Kokkos::View<const int*, CpMem> c = color;
416 Kokkos::parallel_for(
417 "peclet::dem::pcolor_commit_gather", Kokkos::RangePolicy<CpExec>(space, 0, n),
418 KOKKOS_LAMBDA(
int i) { pc(i) = c(perm(i)); });
439 const float invMassA =
invMass(idA);
440 const float invMassB = (idB >= 0) ?
invMass(idB) : 0.0f;
464 const F3 pAsurf =
add3(pA, rA);
467 const F3 pAc =
add3(pA, rA);
468 const F3 pBc =
add3(pB, rB);
476 const float wTotal = computeW(rA, n, invMassA, invIA) + computeW(rB, n, invMassB, invIB);
479 const float dLambda = -C / wTotal;
489 posPred(idA, 0) += n.x * dLambda * invMassA;
490 posPred(idA, 1) += n.y * dLambda * invMassA;
491 posPred(idA, 2) += n.z * dLambda * invMassA;
493 posPred(idB, 0) += -n.x * dLambda * invMassB;
494 posPred(idB, 1) += -n.y * dLambda * invMassB;
495 posPred(idB, 2) += -n.z * dLambda * invMassB;
510 Kokkos::View<const ContactC*, CpMem> contacts,
int numContacts,
511 Kokkos::View<const int*, CpMem> cColor,
int numColors,
512 Kokkos::View<const float*, CpMem> invMass, Kokkos::View<
float* [3],
CpMem> posPred,
513 Kokkos::View<
const float* [4],
CpMem> quatPred,
514 Kokkos::View<
const float* [4],
CpMem> quatStatic,
515 Kokkos::View<
const float* [3],
CpMem> invInertia, Kokkos::View<float, CpMem> maxOverlap,
516 Kokkos::View<float*, CpMem> posLambdaAcc = {}, Kokkos::View<const int*, CpMem> colorPerm = {},
517 const std::vector<int>* colorOffs =
nullptr,
const FusedSweepCtx* fused =
nullptr,
518 const FusedLoopSpec* loop =
nullptr) {
520 const PositionContactSweep f{contacts, invMass, posPred, quatPred,
521 quatStatic, invInertia, maxOverlap, posLambdaAcc};
522#ifdef KOKKOS_ENABLE_CUDA
524 if (fused && fused->maxBucket > 0 && colorOffs)
525 return demLaunchFusedSweepLoop(space, f, colorPerm, *fused, numColors, *loop,
529 if (fused && fused->maxBucket > 0 && colorOffs &&
530 demLaunchFusedColorSweep(space, f, colorPerm, *fused, numColors))
537 for (
int color = 0; color < numColors; ++color) {
539 const int b = (*colorOffs)[color], e = (*colorOffs)[color + 1];
542 Kokkos::parallel_for(
543 "peclet::dem::solve_position_gs", Kokkos::RangePolicy<CpExec>(space, b, e),
544 KOKKOS_LAMBDA(
int i2) { f.solveOne(colorPerm(i2)); });
546 Kokkos::parallel_for(
547 "peclet::dem::solve_position_gs", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
548 KOKKOS_LAMBDA(
int idx) {
549 if (cColor(idx) == color)
dem — portable POD types + math + analytic SDFs shared by the Kokkos kernel ports.
F4 deltaQuat(F3 dTheta, F4 q)
float computeW(F3 r, F3 dir, float invM, F3 invI)
void commitContactColorKokkos(Kokkos::View< const unsigned long long *, CpMem > keys, Kokkos::View< const int *, CpMem > color, Kokkos::View< unsigned long long *, CpMem > prevKeys, Kokkos::View< int *, CpMem > prevColor, Kokkos::View< int *, CpMem > perm, int numContacts)
Commit this substep's per-contact (key, colour) sorted by key, for next substep's warm gather.
std::uint64_t pairKey(const ContactC &c)
Canonical pair key: (min<<32)|max, or (idA<<32)|0xFFFFFFFF for a boundary (idB<0) contact.
bool solvePositionColoredGSKokkos(Kokkos::View< const ContactC *, CpMem > contacts, int numContacts, Kokkos::View< const int *, CpMem > cColor, int numColors, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< float *[3], CpMem > posPred, Kokkos::View< const float *[4], CpMem > quatPred, Kokkos::View< const float *[4], CpMem > quatStatic, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< float, CpMem > maxOverlap, Kokkos::View< float *, CpMem > posLambdaAcc={}, Kokkos::View< const int *, CpMem > colorPerm={}, const std::vector< int > *colorOffs=nullptr, const FusedSweepCtx *fused=nullptr, const FusedLoopSpec *loop=nullptr)
Colored Gauss–Seidel XPBD overlap solve: sweep the numColors colour classes in order,...
int colorContactsKokkos(Kokkos::View< const ContactC *, CpMem > contacts, int numContacts, int numBodies, Kokkos::View< int *, CpMem > cColor, 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 contacts (raw bodyA/bodyB): no two contacts sharing a body get the same colou...
F3 ldF3(const V &v, int i)
void solvePositionKokkos(Kokkos::View< const ContactC *, CpMem > contacts, int numContacts, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > posPred, Kokkos::View< const float *[4], CpMem > quatPred, Kokkos::View< const float *[4], CpMem > quatStatic, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< float *[3], CpMem > deltaPos, Kokkos::View< float *[4], CpMem > deltaQuat, Kokkos::View< int *, CpMem > constraintCounts, Kokkos::View< float, CpMem > maxOverlap, Kokkos::View< const int *, CpMem > onlyColor={}, int colorFilter=0)
Accumulate XPBD position corrections for numContacts contacts.
F3 rotateVector(F4 q, F3 v)
CpExec::memory_space CpMem
F4 ldF4(const V &v, int i)
int colorContactsIncrementalKokkos(Kokkos::View< const ContactC *, CpMem > contacts, int numContacts, int numBodies, Kokkos::View< const unsigned long long *, CpMem > prevKeys, Kokkos::View< const int *, CpMem > prevColor, int prevCount, Kokkos::View< int *, CpMem > cColor, Kokkos::View< unsigned long long *, CpMem > keysOut, 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) contact colouring for the single-GPU PGS position solve.
long long colorKey(int idx)
splitmix32 finalizer: a well-mixed pseudo-random priority per edge index.
Kokkos::DefaultExecutionSpace CpExec
dem — fused colour sweeps: one persistent kernel per sweep instead of one kernel launch per colour.