11#ifndef DEM_NARROWPHASE_HPP
12#define DEM_NARROWPHASE_HPP
14#include <Kokkos_Core.hpp>
76using PosView = Kokkos::View<const float* [3], CpMem>;
77using QuatView = Kokkos::View<const float* [4], CpMem>;
78using ScalarF = Kokkos::View<const float*, CpMem>;
79using ScalarI = Kokkos::View<const int*, CpMem>;
80using ShellView = Kokkos::View<const float* [3], CpMem>;
81using GridView = Kokkos::View<const float*, CpMem>;
84 return F3{v(i, 0), v(i, 1), v(i, 2)};
87 return F4{v(i, 0), v(i, 1), v(i, 2), v(i, 3)};
100 const float cx = Kokkos::fmin(Kokkos::fmax(fx, 0.0f), (
float)(d.
nx - 1));
101 const float cy = Kokkos::fmin(Kokkos::fmax(fy, 0.0f), (
float)(d.
ny - 1));
102 const float cz = Kokkos::fmin(Kokkos::fmax(fz, 0.0f), (
float)(d.
nz - 1));
103 const int ix = (int)cx, iy = (
int)cy, iz = (int)cz;
104 const int ix1 = ix < d.
nx - 1 ? ix + 1 : ix;
105 const int iy1 = iy < d.
ny - 1 ? iy + 1 : iy;
106 const int iz1 = iz < d.
nz - 1 ? iz + 1 : iz;
107 const float tx = cx - ix, ty = cy - iy, tz = cz - iz;
108 const long nxny = (long)d.
nx * d.
ny;
110 auto at = [&](
int x,
int y,
int z) {
return grid(off + (
long)z * nxny + (
long)y * d.
nx + x); };
111 const float c00 = at(ix, iy, iz) * (1 - tx) + at(ix1, iy, iz) * tx;
112 const float c10 = at(ix, iy1, iz) * (1 - tx) + at(ix1, iy1, iz) * tx;
113 const float c01 = at(ix, iy, iz1) * (1 - tx) + at(ix1, iy, iz1) * tx;
114 const float c11 = at(ix, iy1, iz1) * (1 - tx) + at(ix1, iy1, iz1) * tx;
115 const float c0 = c00 * (1 - ty) + c10 * ty;
116 const float c1 = c01 * (1 - ty) + c11 * ty;
117 const float val = c0 * (1 - tz) + c1 * tz;
122 return val + Kokkos::sqrt(rx * rx + ry * ry + rz * rz);
132 const float cx = Kokkos::fmin(Kokkos::fmax(fx, 0.0f), (
float)(w.
nx - 1));
133 const float cy = Kokkos::fmin(Kokkos::fmax(fy, 0.0f), (
float)(w.
ny - 1));
134 const float cz = Kokkos::fmin(Kokkos::fmax(fz, 0.0f), (
float)(w.
nz - 1));
135 const int ix = (int)cx, iy = (
int)cy, iz = (int)cz;
136 const int ix1 = ix < w.
nx - 1 ? ix + 1 : ix;
137 const int iy1 = iy < w.
ny - 1 ? iy + 1 : iy;
138 const int iz1 = iz < w.
nz - 1 ? iz + 1 : iz;
139 const float tx = cx - ix, ty = cy - iy, tz = cz - iz;
140 const long nxny = (long)w.
nx * w.
ny;
142 auto at = [&](
int x,
int y,
int z) {
return grid(off + (
long)z * nxny + (
long)y * w.
nx + x); };
143 const float c00 = at(ix, iy, iz) * (1 - tx) + at(ix1, iy, iz) * tx;
144 const float c10 = at(ix, iy1, iz) * (1 - tx) + at(ix1, iy1, iz) * tx;
145 const float c01 = at(ix, iy, iz1) * (1 - tx) + at(ix1, iy, iz1) * tx;
146 const float c11 = at(ix, iy1, iz1) * (1 - tx) + at(ix1, iy1, iz1) * tx;
147 const float c0 = c00 * (1 - ty) + c10 * ty;
148 const float c1 = c01 * (1 - ty) + c11 * ty;
149 const float val = c0 * (1 - tz) + c1 * tz;
153 return val + Kokkos::sqrt(rx * rx + ry * ry + rz * rz);
168 Kokkos::View<const ShapeDesc*, CpMem> shapes,
ShellView shell,
169 float globalScale,
float margin,
170 Kokkos::View<ContactC*, CpMem> outContacts,
171 Kokkos::View<int, CpMem> outCount,
174 const int maxContacts =
static_cast<int>(outContacts.extent(0));
175 Kokkos::parallel_for(
176 "peclet::dem::np::contacts", Kokkos::RangePolicy<CpExec>(space, 0, numPairs),
177 KOKKOS_LAMBDA(
int idx) {
178 const int idA = pairs(idx, 0), idB = pairs(idx, 1);
179 const ShapeDesc dA = shapes(shapeId(idA));
180 const ShapeDesc dB = shapes(shapeId(idB));
182 const F4 qA =
loadF4(quat, idA), qB =
loadF4(quat, idB);
188 const float effScaleA = scale(idA) * globalScale, effScaleB = scale(idB) * globalScale;
190 const int countA = dA.numPoints;
191 const bool sphereA = (dA.type ==
SPHERE);
192 const int iter = (countA > 0) ? countA : 1;
194 for (
int k = 0; k < iter; ++k) {
196 float pointRadius = 0.0f;
198 const int s = dA.shellOffset + k;
199 pLocalA =
F3{shell(s, 0), shell(s, 1), shell(s, 2)};
200 }
else if (sphereA) {
201 pointRadius = dA.params.x * effScaleA;
206 const F3 pCanB =
scale3(pLocalB, 1.0f / effScaleB);
208 const float dist =
sdfEvalShape(pCanB, dB, sdfGrid) * effScaleB;
209 const float effDist = dist - pointRadius;
210 if (effDist >= margin)
214 Kokkos::atomic_max(&maxOverlap(), -effDist);
215 const int slot = Kokkos::atomic_fetch_add(&outCount(), 1);
216 if (slot >= maxContacts) {
217 Kokkos::atomic_add(&outCount(), -1);
221 const float eps = 1e-4f;
228 const float len =
len3(nLoc);
229 nLoc = (len > 1e-9f) ?
scale3(nLoc, 1.0f / len) :
F3{0, 1, 0};
232 const F3 pSurfA =
sub3(pWorld,
scale3(nWorld, pointRadius));
233 const F3 rA =
sub3(pSurfA, posA);
239 c.normal = F4{nWorld.x, nWorld.y, nWorld.z, 0.0f};
240 c.rA = F4{rA.x, rA.y, rA.z, 0.0f};
241 c.rB = F4{rB.x, rB.y, rB.z, 0.0f};
243 c.friction_lambda_n = 0.0f;
245 outContacts(slot) = c;
260 ScalarI shapeId, Kokkos::View<const ShapeDesc*, CpMem> shapes,
261 ShellView shell, Kokkos::View<const WallSdf*, CpMem> walls,
262 GridView wallGrid,
float globalScale,
float margin,
263 Kokkos::View<ContactC*, CpMem> outContacts,
264 Kokkos::View<int, CpMem> outCount,
265 Kokkos::View<float, CpMem> maxOverlap) {
267 const int maxContacts =
static_cast<int>(outContacts.extent(0));
268 Kokkos::parallel_for(
269 "peclet::dem::np::wallsdf", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
270 KOKKOS_LAMBDA(
int i) {
272 const float s = scale(i) * globalScale;
277 const float radius = baseR * s;
280 const float eps = 1e-4f;
282 for (
int wi = 0; wi < numWalls; ++wi) {
284 const int iter = (numPts > 0) ? numPts : 1;
285 for (
int k = 0; k < iter; ++k) {
292 const F3 pw =
add3(posA, rA);
301 const float ln =
len3(n);
302 n = (ln > 1e-9f) ?
scale3(n, 1.0f / ln) :
F3{0, 1, 0};
306 const float dist = (numPts > 0) ? sdf : sdf - radius;
309 const F3 rAeff = (numPts > 0) ? rA :
scale3(n, -radius);
310 const F3 pSurfA =
add3(posA, rAeff);
314 Kokkos::atomic_max(&maxOverlap(), -dist);
315 const int slot = Kokkos::atomic_fetch_add(&outCount(), 1);
316 if (slot >= maxContacts) {
317 Kokkos::atomic_add(&outCount(), -1);
327 c.normal =
F4{n.
x, n.y, n.z, 0.0f};
328 c.rA =
F4{rAeff.
x, rAeff.
y, rAeff.
z, 0.0f};
329 c.rB =
F4{pWall.
x, pWall.
y, pWall.
z, 0.0f};
331 c.friction_lambda_n = 0.0f;
333 c.boundaryVel =
F4{vWall.
x, vWall.
y, vWall.
z, 0.0f};
336 outContacts(slot) = c;
347 Kokkos::View<const ShapeDesc*, CpMem> shapes,
ShellView shell,
348 Kokkos::View<const PlaneP*, CpMem> planes,
float globalScale,
349 float margin, Kokkos::View<ContactC*, CpMem> outContacts,
350 Kokkos::View<int, CpMem> outCount,
351 Kokkos::View<float, CpMem> maxOverlap) {
353 const int maxContacts =
static_cast<int>(outContacts.extent(0));
354 Kokkos::parallel_for(
355 "peclet::dem::np::boundary", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
356 KOKKOS_LAMBDA(
int i) {
358 const float s = scale(i) * globalScale;
363 const float radius = baseR * s;
367 for (
int pi = 0; pi < numPlanes; ++pi) {
368 const PlaneP pl = planes(pi);
371 for (
int k = 0; k < numPts; ++k) {
375 const F3 pwk =
add3(posA, rA);
380 Kokkos::atomic_max(&maxOverlap(), -dist);
381 const int slot = Kokkos::atomic_fetch_add(&outCount(), 1);
382 if (slot >= maxContacts) {
383 Kokkos::atomic_add(&outCount(), -1);
390 c.rA =
F4{rA.
x, rA.
y, rA.
z, 0.0f};
393 c.friction_lambda_n = 0.0f;
395 outContacts(slot) = c;
402 Kokkos::atomic_max(&maxOverlap(), -dist);
403 const int slot = Kokkos::atomic_fetch_add(&outCount(), 1);
404 if (slot >= maxContacts) {
405 Kokkos::atomic_add(&outCount(), -1);
413 c.rA =
F4{rA.
x, rA.
y, rA.
z, 0.0f};
416 c.friction_lambda_n = 0.0f;
418 outContacts(slot) = c;
dem — portable POD types + math + analytic SDFs shared by the Kokkos kernel ports.
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)
Per-real-particle contacts against a static world-space wall SDF set (a drum barrel,...
float sdfEval(F3 p, int type, F4 params)
Kokkos::View< const float *[3], CpMem > ShellView
Kokkos::View< const float *, CpMem > GridView
float sampleWallSdf(F3 p, const WallSdf &w, GridView grid)
Trilinearly sample a static world-space wall SDF at world point p (same "clamp + residual" extension ...
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{})
Pair point-shell vs SDF contacts.
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...
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
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,...