31#ifndef DEM_SOLVE_DRIVER_FORCE_HPP
32#define DEM_SOLVE_DRIVER_FORCE_HPP
36#include <Kokkos_Core.hpp>
37#include <Kokkos_Sort.hpp>
54 const size_t need = P.
pairs.extent(0);
55 if (P.
hertzXi.extent(0) < need) {
56 P.
hertzXi = Kokkos::View<float* [3], CpMem>(
"hertzXi", need);
57 P.
hertzSnPair = Kokkos::View<float*, CpMem>(
"hertzSnPair", need);
58 P.
hertzKeys = Kokkos::View<unsigned long long*, CpMem>(
"hertzKeys", need);
59 P.
hertzPrevKeys = Kokkos::View<unsigned long long*, CpMem>(
"hertzPrevKeys", need);
60 P.
hertzPrevXi = Kokkos::View<float* [3], CpMem>(
"hertzPrevXi", need);
72 "peclet::dem::hertz_carry", Kokkos::RangePolicy<CpExec>(space, 0, np),
73 KOKKOS_LAMBDA(
int idx) {
74 const unsigned a = (unsigned)gid(pairs(idx, 0)), b = (unsigned)gid(pairs(idx, 1));
75 const unsigned lo = a < b ? a : b, hi = a < b ? b : a;
76 const unsigned long long k = ((
unsigned long long)hi << 32) | lo;
80 const int m = (l + h) >> 1;
86 const bool hit = (l < pc && pk(l) == k);
87 xi(idx, 0) = hit ? px(l, 0) : 0.0f;
88 xi(idx, 1) = hit ? px(l, 1) : 0.0f;
89 xi(idx, 2) = hit ? px(l, 2) : 0.0f;
94 auto sn = Kokkos::subview(P.
hertzSnPair, Kokkos::pair<int, int>(0, np));
95 Kokkos::deep_copy(space, sn, 0.0f);
113 const auto rng = Kokkos::pair<int, int>(0, n);
115 Kokkos::deep_copy(space, kd, Kokkos::subview(P.
hertzKeys, rng));
116 Kokkos::View<int*, CpMem> perm(
117 Kokkos::view_alloc(space,
"peclet::dem::hertz_perm", Kokkos::WithoutInitializing), n);
118 Kokkos::parallel_for(
119 "peclet::dem::hertz_iota", Kokkos::RangePolicy<CpExec>(space, 0, n),
120 KOKKOS_LAMBDA(
int i) { perm(i) = i; });
121 Kokkos::Experimental::sort_by_key(space, kd, perm);
124 Kokkos::parallel_for(
125 "peclet::dem::hertz_gather", Kokkos::RangePolicy<CpExec>(space, 0, n),
126 KOKKOS_LAMBDA(
int i) {
127 const int j = perm(i);
171 Kokkos::View<
const float* [4],
CpMem>(P.
quat),
179 throw std::runtime_error(
"step_hertz: periodic domains not supported");
181 throw std::runtime_error(
"step_hertz: analytic planes not supported (use an SDF wall)");
183 throw std::runtime_error(
"step_hertz: too many SDF walls");
194 Kokkos::parallel_for(
195 "peclet::dem::force_zero_ghost", Kokkos::RangePolicy<CpExec>(space, lo, hi),
196 KOKKOS_LAMBDA(
int i) {
197 for (
int c = 0; c < 3; ++c) {
209 Kokkos::parallel_for(
210 "peclet::dem::force_rad", Kokkos::RangePolicy<CpExec>(space, 0, n),
211 KOKKOS_LAMBDA(
int i) { rad(i) = scale(i) * gs * bR; });
218 float allMax(
float v)
const {
return v; }
219 float allMin(
float v)
const {
return v; }
227template <
class Law,
class Hooks>
229 const Hooks& hooks) {
236 Kokkos::parallel_for(
237 "peclet::dem::hertz_rad", Kokkos::RangePolicy<CpExec>(space, 0, P.
numReal),
238 KOKKOS_LAMBDA(
int i) { rad(i) = sc(i) * gs * bR; });
246 Kokkos::parallel_reduce(
247 "peclet::dem::hertz_minrad", Kokkos::RangePolicy<CpExec>(space, 0, P.
numReal),
248 KOKKOS_LAMBDA(
int i,
float& acc) {
252 Kokkos::Min<float>(m));
260 const float skin = skinFrac * hooks.allMin(minRad);
261 const float rebuildAt = 0.25f * skin * skin;
265 bool rebuild = hooks.allMax(P.
hertzNumPairs < 0 ? 1.0f : 0.0f) > 0.0f;
268 bool hasShapes =
false;
270 auto hs = Kokkos::create_mirror_view(P.
shapes);
271 Kokkos::deep_copy(hs, P.
shapes);
272 for (
size_t si = 0; si < hs.extent(0); ++si)
273 if (hs(si).numPoints > 0)
276 static const bool profile = std::getenv(
"PECLET_DEM_HERTZ_PROFILE") !=
nullptr;
277 double tPair = 0, tWall = 0, tInt = 0, tRebuild = 0, tCheck = 0;
280 for (
int k = 0; k < nsteps; ++k) {
282 law.commitHistory(P);
287 hooks.gatherGhosts(P);
288 law.rebuild(P, skin);
292 tRebuild += timer.seconds();
297 hooks.refreshGhostState(P, hasShapes);
299 hooks.clearGhostScratch(P);
300 law.pairForces(P, dt, hasShapes);
302 tPair += timer.seconds();
305 law.wallForces(P, dt, hasShapes);
307 tWall += timer.seconds();
313 tInt += timer.seconds();
316 if ((k & 15) == 15 || k == nsteps - 1) {
320 tCheck += timer.seconds();
326 std::printf(
"[hertz profile] steps=%d pairs=%d wallcand=%d rebuilds=%d | pair %.3fs wall %.3fs "
327 "integrate %.3fs check %.3fs rebuild %.3fs\n",
float hertzMaxDisp2Kokkos(int numReal, Kokkos::View< const float *[3], CpMem > pos, Kokkos::View< const float *[3], CpMem > refPos)
Max squared displacement since the last pair build (called only at rebuild checks – keeping this out ...
void zeroForceScratchKokkos(V3 dv, V3 dw, int lo, int hi)
Zero the force/torque accumulator rows [lo, hi) — under MPI the pair kernels atomically accumulate on...
Kokkos::View< float *, CpMem > Vf
Kokkos::View< const unsigned char *, CpMem > MatIdView
void hertzCommitHistory(Particles &P)
Save the current list (sorted by key) so the NEXT rebuild can carry the history.
void hertzPairForcesKokkos(Kokkos::View< const int *[2], CpMem > pairs, int numPairs, Kokkos::View< const float *[3], CpMem > pos, Kokkos::View< const float *[3], CpMem > vel, Kokkos::View< const float *[3], CpMem > angVel, Kokkos::View< const float *, CpMem > rad, Kokkos::View< const float *, CpMem > invMass, MatIdView matId, PairTableView pairTable, float eGlobal, float muGlobal, Kokkos::View< const float *, CpMem > hertzE, Kokkos::View< const float *, CpMem > hertzNu, float dt, Kokkos::View< float *[3], CpMem > xi, Kokkos::View< float *[3], CpMem > force, Kokkos::View< float *[3], CpMem > torque)
Cached-pair forces: overlap from current positions; history resets when a cached pair is currently se...
Kokkos::View< const float *, CpMem > PairTableView
Kokkos::View< float *[3], CpMem > V3
void hertzIntegrateKokkos(int numReal, Kokkos::View< float *[3], CpMem > force, Kokkos::View< float *[3], CpMem > torque, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, F3 gravity, float dt, Kokkos::View< float *[3], CpMem > vel, Kokkos::View< float *[3], CpMem > angVel, Kokkos::View< float *[3], CpMem > pos, Kokkos::View< float *[4], CpMem > quat={}, bool integrateOrientation=false)
Symplectic-Euler kick-drift (MUSEN-style) + displacement tracking for the Verlet rebuild.
void demStepForce(Particles &P, float dt, int nsteps, float skinFrac, const Law &law, const Hooks &hooks)
nsteps explicit force-based DEM steps of size dt with force law Law (see file comment).
CpExec::memory_space CpMem
int findCollisionsGrow(Particles &P, float margin)
Broad phase with an automatically-grown pair buffer.
void hertzRebuildPairs(Particles &P, float skin)
Rebuild the Hertz cached pair list with margin skin, carrying the Mindlin shear history across by pai...
void demStepHertz(Particles &P, float dt, int nsteps, float skinFrac)
nsteps of the soft-sphere Hertz-Mindlin engine — the single-GPU instantiation of the force-based driv...
void fillWorldRadiiKokkos(Vf scale, Vf rad, float gs, float bR, int n)
World radii rad(i) = scale(i) * globalScale * baseRadius over [0, n) (n = owned + ghosts after a halo...
void hertzWallForcesKokkos(Kokkos::View< const int *, CpMem > candSlots, int numCand, Kokkos::View< const WallSdf *, CpMem > walls, GridView wallGrid, Kokkos::View< const float *[3], CpMem > pos, Kokkos::View< const float *[3], CpMem > vel, Kokkos::View< const float *[3], CpMem > angVel, Kokkos::View< const float *, CpMem > rad, Kokkos::View< const float *, CpMem > invMass, MatIdView matId, PairTableView pairTable, float eGlobal, float muGlobal, Kokkos::View< const float *, CpMem > hertzE, Kokkos::View< const float *, CpMem > hertzNu, float dt, Kokkos::View< float *[3], CpMem > xiWall, int maxWalls, Kokkos::View< float *[3], CpMem > force, Kokkos::View< float *[3], CpMem > torque, Kokkos::View< const float *[4], CpMem > quat={}, Kokkos::View< const float *, CpMem > scale={}, ScalarI shapeId={}, Kokkos::View< const ShapeDesc *, CpMem > shapes={}, ShellView shell={}, float globalScale=1.0f, float contactRadiusFrac=0.5f, bool hasShapes=false, Kokkos::View< float *, CpMem > snWall={})
SDF-wall forces (per particle x wall).
int hertzBuildWallCandidatesKokkos(int numReal, int numWalls, Kokkos::View< const WallSdf *, CpMem > walls, GridView wallGrid, Kokkos::View< const float *[3], CpMem > pos, Kokkos::View< const float *, CpMem > rad, float skin, Kokkos::View< int *, CpMem > outSlots, Kokkos::View< int, CpMem > outCount)
Build the wall candidate list: particles within (radius + skin) of any wall's zero level.
Kokkos::DefaultExecutionSpace CpExec
void hertzShapePairForcesKokkos(Kokkos::View< const int *[2], CpMem > pairs, int numPairs, Kokkos::View< const float *[3], CpMem > pos, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< const float *[3], CpMem > vel, Kokkos::View< const float *[3], CpMem > angVel, Kokkos::View< const float *, CpMem > scale, ScalarI shapeId, Kokkos::View< const ShapeDesc *, CpMem > shapes, ShellView shell, GridView sdfGrid, float globalScale, float contactRadiusFrac, Kokkos::View< const float *, CpMem > invMass, MatIdView matId, PairTableView pairTable, float eGlobal, float muGlobal, Kokkos::View< const float *, CpMem > hertzE, Kokkos::View< const float *, CpMem > hertzNu, float dt, Kokkos::View< float *[3], CpMem > xi, Kokkos::View< float *, CpMem > snPrev, Kokkos::View< float *[3], CpMem > force, Kokkos::View< float *[3], CpMem > torque)
Non-spherical pairs: per-point Hertz springs over A's point shell against B's SDF (the same one-sided...
dem — portable (Kokkos) particle SoA container: the storage the dem flip pivots on.
dem — the shared contact-solve driver: the full modern velocity + position solve sequence (warm-start...
Soft-sphere Hertz–Mindlin DEM (reference force model) for SPHERES.
Hertz–Mindlin force-law policy (the first force law; see file comment).
void wallForces(Particles &P, float dt, bool hasShapes) const
void rebuild(Particles &P, float skin) const
void commitHistory(Particles &P) const
void validate(const Particles &P) const
Engine-support restrictions of this law (probed once per call).
void pairForces(Particles &P, float dt, bool hasShapes) const
Kokkos::View< WallSdf *, CpMem > walls
Kokkos::View< float *, CpMem > hertzE
Kokkos::View< float *[3], CpMem > hertzXi
Kokkos::View< unsigned char *, CpMem > materialId
Kokkos::View< int, CpMem > hertzWallCandCount
Kokkos::View< int *[2], CpMem > pairs
Kokkos::View< int *, CpMem > hertzWallCand
Kokkos::View< float, CpMem > hertzDispMax
static constexpr int kHertzMaxWalls
Kokkos::View< float *, CpMem > pairMaterials
Kokkos::View< ShapeDesc *, CpMem > shapes
Kokkos::View< unsigned long long *, CpMem > hertzPrevKeys
float hertzContactRadiusFrac
Kokkos::View< float *, CpMem > hertzNu
Kokkos::View< unsigned long long *, CpMem > hertzKeys
Kokkos::View< float *, CpMem > hertzSnWall
Kokkos::View< float *[3], CpMem > hertzRefPos
Kokkos::View< float *, CpMem > sdfGrid
Kokkos::View< float *, CpMem > hertzSnPair
Kokkos::View< float *, CpMem > wallGrid
Kokkos::View< float *[3], CpMem > shell
Kokkos::View< float *[3], CpMem > hertzXiWall
Kokkos::View< float *[3], CpMem > hertzPrevXi
Single-GPU hooks: no ghosts, reductions are already global. Everything inlines away.
static constexpr bool distributed
float allMin(float v) const
float allMax(float v) const
void clearGhostScratch(Particles &) const
void gatherGhosts(Particles &) const
void refreshGhostState(Particles &, bool) const