11#ifndef DEM_NARROWPHASE_HPP
12#define DEM_NARROWPHASE_HPP
14#include <Kokkos_Core.hpp>
81using MatIdView = Kokkos::View<const unsigned char*, CpMem>;
84using PosView = Kokkos::View<const float* [3], CpMem>;
85using QuatView = Kokkos::View<const float* [4], CpMem>;
86using ScalarF = Kokkos::View<const float*, CpMem>;
87using ScalarI = Kokkos::View<const int*, CpMem>;
88using ShellView = Kokkos::View<const float* [3], CpMem>;
89using GridView = Kokkos::View<const float*, CpMem>;
92 return F3{v(i, 0), v(i, 1), v(i, 2)};
95 return F4{v(i, 0), v(i, 1), v(i, 2), v(i, 3)};
108 const float cx = Kokkos::fmin(Kokkos::fmax(fx, 0.0f), (
float)(d.
nx - 1));
109 const float cy = Kokkos::fmin(Kokkos::fmax(fy, 0.0f), (
float)(d.
ny - 1));
110 const float cz = Kokkos::fmin(Kokkos::fmax(fz, 0.0f), (
float)(d.
nz - 1));
111 const int ix = (int)cx, iy = (
int)cy, iz = (int)cz;
112 const int ix1 = ix < d.
nx - 1 ? ix + 1 : ix;
113 const int iy1 = iy < d.
ny - 1 ? iy + 1 : iy;
114 const int iz1 = iz < d.
nz - 1 ? iz + 1 : iz;
115 const float tx = cx - ix, ty = cy - iy, tz = cz - iz;
116 const long nxny = (long)d.
nx * d.
ny;
118 auto at = [&](
int x,
int y,
int z) {
return grid(off + (
long)z * nxny + (
long)y * d.
nx + x); };
119 const float c00 = at(ix, iy, iz) * (1 - tx) + at(ix1, iy, iz) * tx;
120 const float c10 = at(ix, iy1, iz) * (1 - tx) + at(ix1, iy1, iz) * tx;
121 const float c01 = at(ix, iy, iz1) * (1 - tx) + at(ix1, iy, iz1) * tx;
122 const float c11 = at(ix, iy1, iz1) * (1 - tx) + at(ix1, iy1, iz1) * tx;
123 const float c0 = c00 * (1 - ty) + c10 * ty;
124 const float c1 = c01 * (1 - ty) + c11 * ty;
125 const float val = c0 * (1 - tz) + c1 * tz;
130 return val + Kokkos::sqrt(rx * rx + ry * ry + rz * rz);
145 const float cx = Kokkos::fmin(Kokkos::fmax(fx, 0.0f), (
float)(w.
nx - 1));
146 const float cy = Kokkos::fmin(Kokkos::fmax(fy, 0.0f), (
float)(w.
ny - 1));
147 const float cz = Kokkos::fmin(Kokkos::fmax(fz, 0.0f), (
float)(w.
nz - 1));
148 const int ix = (int)cx, iy = (
int)cy, iz = (int)cz;
149 const int ix1 = ix < w.
nx - 1 ? ix + 1 : ix;
150 const int iy1 = iy < w.
ny - 1 ? iy + 1 : iy;
151 const int iz1 = iz < w.
nz - 1 ? iz + 1 : iz;
152 const float tx = cx - ix, ty = cy - iy, tz = cz - iz;
153 const long nxny = (long)w.
nx * w.
ny;
155 auto at = [&](
int x,
int y,
int z) {
return grid(off + (
long)z * nxny + (
long)y * w.
nx + x); };
156 const float c00 = at(ix, iy, iz) * (1 - tx) + at(ix1, iy, iz) * tx;
157 const float c10 = at(ix, iy1, iz) * (1 - tx) + at(ix1, iy1, iz) * tx;
158 const float c01 = at(ix, iy, iz1) * (1 - tx) + at(ix1, iy, iz1) * tx;
159 const float c11 = at(ix, iy1, iz1) * (1 - tx) + at(ix1, iy1, iz1) * tx;
160 const float c0 = c00 * (1 - ty) + c10 * ty;
161 const float c1 = c01 * (1 - ty) + c11 * ty;
162 const float val = c0 * (1 - tz) + c1 * tz;
166 return val - Kokkos::sqrt(rx * rx + ry * ry + rz * rz);
181 Kokkos::View<const ShapeDesc*, CpMem> shapes,
ShellView shell,
182 float globalScale,
float margin,
183 Kokkos::View<ContactC*, CpMem> outContacts,
184 Kokkos::View<int, CpMem> outCount,
188 const int maxContacts =
static_cast<int>(outContacts.extent(0));
189 Kokkos::parallel_for(
190 "peclet::dem::np::contacts", Kokkos::RangePolicy<CpExec>(space, 0, numPairs),
191 KOKKOS_LAMBDA(
int idx) {
192 const int idA = pairs(idx, 0), idB = pairs(idx, 1);
193 const ShapeDesc dA = shapes(shapeId(idA));
194 const ShapeDesc dB = shapes(shapeId(idB));
196 const F4 qA =
loadF4(quat, idA), qB =
loadF4(quat, idB);
202 const float effScaleA = scale(idA) * globalScale, effScaleB = scale(idB) * globalScale;
204 const int countA = dA.numPoints;
205 const bool sphereA = (dA.type ==
SPHERE);
206 const int iter = (countA > 0) ? countA : 1;
208 for (
int k = 0; k < iter; ++k) {
210 float pointRadius = 0.0f;
212 const int s = dA.shellOffset + k;
213 pLocalA =
F3{shell(s, 0), shell(s, 1), shell(s, 2)};
214 }
else if (sphereA) {
215 pointRadius = dA.params.x * effScaleA;
220 const F3 pCanB =
scale3(pLocalB, 1.0f / effScaleB);
222 const float dist =
sdfEvalShape(pCanB, dB, sdfGrid) * effScaleB;
223 const float effDist = dist - pointRadius;
224 if (effDist >= margin)
228 Kokkos::atomic_max(&maxOverlap(), -effDist);
229 const int slot = Kokkos::atomic_fetch_add(&outCount(), 1);
230 if (slot >= maxContacts) {
231 Kokkos::atomic_add(&outCount(), -1);
235 const float eps = 1e-4f;
242 const float len =
len3(nLoc);
243 nLoc = (len > 1e-9f) ?
scale3(nLoc, 1.0f / len) :
F3{0, 1, 0};
246 const F3 pSurfA =
sub3(pWorld,
scale3(nWorld, pointRadius));
247 const F3 rA =
sub3(pSurfA, posA);
253 c.normal = F4{nWorld.x, nWorld.y, nWorld.z, 0.0f};
254 c.rA = F4{rA.x, rA.y, rA.z, 0.0f};
255 c.rB = F4{rB.x, rB.y, rB.z, 0.0f};
257 c.friction_lambda_n = 0.0f;
259 if (pairTable.extent(0) > 0) {
260 const int t = (int(matId(idA)) *
kMaxMaterials + int(matId(idB))) * 2;
261 c.boundaryRestitution = pairTable(t);
262 c.boundaryFriction = pairTable(t + 1);
264 outContacts(slot) = c;
279 ScalarI shapeId, Kokkos::View<const ShapeDesc*, CpMem> shapes,
280 ShellView shell, Kokkos::View<const WallSdf*, CpMem> walls,
281 GridView wallGrid,
float globalScale,
float margin,
282 Kokkos::View<ContactC*, CpMem> outContacts,
283 Kokkos::View<int, CpMem> outCount,
284 Kokkos::View<float, CpMem> maxOverlap,
287 const int maxContacts =
static_cast<int>(outContacts.extent(0));
288 Kokkos::parallel_for(
289 "peclet::dem::np::wallsdf", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
290 KOKKOS_LAMBDA(
int i) {
292 const float s = scale(i) * globalScale;
293 const ShapeDesc d = shapes(shapeId(i));
294 float baseR = d.params.x;
297 const float radius = baseR * s;
298 const int numPts = d.numPoints;
299 const F4 qA =
loadF4(quat, i);
300 const float eps = 1e-4f;
302 for (
int wi = 0; wi < numWalls; ++wi) {
303 const WallSdf w = walls(wi);
304 const int iter = (numPts > 0) ? numPts : 1;
305 for (
int k = 0; k < iter; ++k) {
309 const int si = d.shellOffset + k;
312 const F3 pw =
add3(posA, rA);
321 const float ln =
len3(n);
322 n = (ln > 1e-9f) ?
scale3(n, 1.0f / ln) :
F3{0, 1, 0};
326 const float dist = (numPts > 0) ? sdf : sdf - radius;
329 const F3 rAeff = (numPts > 0) ? rA :
scale3(n, -radius);
330 const F3 pSurfA =
add3(posA, rAeff);
334 Kokkos::atomic_max(&maxOverlap(), -dist);
335 const int slot = Kokkos::atomic_fetch_add(&outCount(), 1);
336 if (slot >= maxContacts) {
337 Kokkos::atomic_add(&outCount(), -1);
341 const F3 r =
sub3(pWall, w.center);
347 c.normal = F4{n.x, n.y, n.z, 0.0f};
348 c.rA = F4{rAeff.x, rAeff.y, rAeff.z, 0.0f};
349 c.rB = F4{pWall.x, pWall.y, pWall.z, 0.0f};
351 c.friction_lambda_n = 0.0f;
353 c.boundaryVel = F4{vWall.x, vWall.y, vWall.z, 0.0f};
354 if (w.materialId >= 0 && pairTable.extent(0) > 0) {
355 const int t = (int(matId(i)) *
kMaxMaterials + w.materialId) * 2;
356 c.boundaryRestitution = pairTable(t);
357 c.boundaryFriction = pairTable(t + 1);
359 c.boundaryRestitution = w.restitution;
360 c.boundaryFriction = w.friction;
362 outContacts(slot) = c;
373 Kokkos::View<const ShapeDesc*, CpMem> shapes,
ShellView shell,
374 Kokkos::View<const PlaneP*, CpMem> planes,
float globalScale,
375 float margin, Kokkos::View<ContactC*, CpMem> outContacts,
376 Kokkos::View<int, CpMem> outCount,
377 Kokkos::View<float, CpMem> maxOverlap) {
379 const int maxContacts =
static_cast<int>(outContacts.extent(0));
380 Kokkos::parallel_for(
381 "peclet::dem::np::boundary", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
382 KOKKOS_LAMBDA(
int i) {
384 const float s = scale(i) * globalScale;
389 const float radius = baseR * s;
393 for (
int pi = 0; pi < numPlanes; ++pi) {
394 const PlaneP pl = planes(pi);
397 for (
int k = 0; k < numPts; ++k) {
401 const F3 pwk =
add3(posA, rA);
406 Kokkos::atomic_max(&maxOverlap(), -dist);
407 const int slot = Kokkos::atomic_fetch_add(&outCount(), 1);
408 if (slot >= maxContacts) {
409 Kokkos::atomic_add(&outCount(), -1);
416 c.rA =
F4{rA.
x, rA.
y, rA.
z, 0.0f};
419 c.friction_lambda_n = 0.0f;
421 outContacts(slot) = c;
428 Kokkos::atomic_max(&maxOverlap(), -dist);
429 const int slot = Kokkos::atomic_fetch_add(&outCount(), 1);
430 if (slot >= maxContacts) {
431 Kokkos::atomic_add(&outCount(), -1);
439 c.rA =
F4{rA.
x, rA.
y, rA.
z, 0.0f};
442 c.friction_lambda_n = 0.0f;
444 outContacts(slot) = c;
dem — portable POD types + math + analytic SDFs shared by the Kokkos kernel ports.
void detectContactsKokkos(Kokkos::View< const int *[2], CpMem > pairs, int numPairs, PosView pos, QuatView quat, ScalarF scale, ScalarI shapeId, Kokkos::View< const ShapeDesc *, CpMem > shapes, ShellView shell, float globalScale, float margin, Kokkos::View< ContactC *, CpMem > outContacts, Kokkos::View< int, CpMem > outCount, Kokkos::View< float, CpMem > maxOverlap, GridView sdfGrid=GridView{}, MatIdView matId=MatIdView{}, PairTableView pairTable=PairTableView{})
Pair point-shell vs SDF contacts.
float sdfEval(F3 p, int type, F4 params)
Kokkos::View< const float *[3], CpMem > ShellView
Kokkos::View< const float *, CpMem > GridView
Kokkos::View< const unsigned char *, CpMem > MatIdView
float sampleWallSdf(F3 p, const WallSdf &w, GridView grid)
Trilinearly sample a static world-space wall SDF at world point p.
Kokkos::View< const float *, CpMem > PairTableView
F3 invRotateVector(F4 q, F3 v)
Kokkos::View< const int *, CpMem > ScalarI
void detectBoundaryKokkos(int numReal, int numPlanes, PosView pos, QuatView quat, ScalarF scale, ScalarI shapeId, Kokkos::View< const ShapeDesc *, CpMem > shapes, ShellView shell, Kokkos::View< const PlaneP *, CpMem > planes, float globalScale, float margin, Kokkos::View< ContactC *, CpMem > outContacts, Kokkos::View< int, CpMem > outCount, Kokkos::View< float, CpMem > maxOverlap)
Per-real-particle contacts against explicit planes (point-shell shapes test each surface point; analy...
void detectWallSdfKokkos(int numReal, int numWalls, PosView pos, QuatView quat, ScalarF scale, ScalarI shapeId, Kokkos::View< const ShapeDesc *, CpMem > shapes, ShellView shell, Kokkos::View< const WallSdf *, CpMem > walls, GridView wallGrid, float globalScale, float margin, Kokkos::View< ContactC *, CpMem > outContacts, Kokkos::View< int, CpMem > outCount, Kokkos::View< float, CpMem > maxOverlap, MatIdView matId=MatIdView{}, PairTableView pairTable=PairTableView{})
Per-real-particle contacts against a static world-space wall SDF set (a drum barrel,...
F3 loadF3(PosView v, int i)
float sdfEvalShape(F3 p, const ShapeDesc &d, GridView grid)
Canonical-space SDF of a shape: analytic dispatch, or a trilinear grid sample for an imported grid SD...
F3 rotateVector(F4 q, F3 v)
CpExec::memory_space CpMem
F4 loadF4(QuatView v, int i)
Kokkos::View< const float *[4], CpMem > QuatView
float sampleGridSdf(F3 p, const ShapeDesc &d, GridView grid)
Trilinearly sample an imported grid SDF at canonical point p.
Kokkos::View< const float *[3], CpMem > PosView
constexpr int kMaxMaterials
Pair-material lookup: flat [K*K*2] table, entry ((a*K + b)*2) = restitution, +1 = friction.
Kokkos::View< const float *, CpMem > ScalarF
Kokkos::DefaultExecutionSpace CpExec
Portable mirror of ShapeDescriptor (analytic fields + a flat-array point shell).
Static, world-space SDF container/geometry the particles collide against (a drum barrel,...