34#ifndef DEM_SOLVE_DRIVER_FORCE_HPP
35#define DEM_SOLVE_DRIVER_FORCE_HPP
39#include <Kokkos_Core.hpp>
40#include <Kokkos_Sort.hpp>
57 const size_t need = P.
pairs.extent(0);
58 if (P.
hertzXi.extent(0) < need) {
59 P.
hertzXi = Kokkos::View<float* [3], CpMem>(
"hertzXi", need);
60 P.
hertzSnPair = Kokkos::View<float*, CpMem>(
"hertzSnPair", need);
61 P.
hertzKeys = Kokkos::View<unsigned long long*, CpMem>(
"hertzKeys", need);
62 P.
hertzPrevKeys = Kokkos::View<unsigned long long*, CpMem>(
"hertzPrevKeys", need);
63 P.
hertzPrevXi = Kokkos::View<float* [3], CpMem>(
"hertzPrevXi", need);
75 "peclet::dem::hertz_carry", Kokkos::RangePolicy<CpExec>(space, 0, np),
76 KOKKOS_LAMBDA(
int idx) {
85 const int a0 = pairs(idx, 0), b0 = pairs(idx, 1);
86 if (gid(a0) > gid(b0)) {
90 const unsigned long long k =
91 pairKeyFromGids((
unsigned)gid(pairs(idx, 0)), (
unsigned)gid(pairs(idx, 1)));
94 const bool hit = (l < pc && pk(l) == k);
95 xi(idx, 0) = hit ? px(l, 0) : 0.0f;
96 xi(idx, 1) = hit ? px(l, 1) : 0.0f;
97 xi(idx, 2) = hit ? px(l, 2) : 0.0f;
102 auto sn = Kokkos::subview(P.
hertzSnPair, Kokkos::pair<int, int>(0, np));
103 Kokkos::deep_copy(space, sn, 0.0f);
121 const auto rng = Kokkos::pair<int, int>(0, n);
123 Kokkos::deep_copy(space, kd, Kokkos::subview(P.
hertzKeys, rng));
124 Kokkos::View<int*, CpMem> perm(
125 Kokkos::view_alloc(space,
"peclet::dem::hertz_perm", Kokkos::WithoutInitializing), n);
126 Kokkos::parallel_for(
127 "peclet::dem::hertz_iota", Kokkos::RangePolicy<CpExec>(space, 0, n),
128 KOKKOS_LAMBDA(
int i) { perm(i) = i; });
129 Kokkos::Experimental::sort_by_key(space, kd, perm);
132 Kokkos::parallel_for(
133 "peclet::dem::hertz_gather", Kokkos::RangePolicy<CpExec>(space, 0, n), KOKKOS_LAMBDA(
int i) {
134 const int j = perm(i);
178 Kokkos::View<
const float* [4],
CpMem>(P.
quat), Kokkos::View<const float*, CpMem>(P.
scale),
185 throw std::runtime_error(
"step_hertz: periodic domains not supported");
187 throw std::runtime_error(
"step_hertz: analytic planes not supported (use an SDF wall)");
189 throw std::runtime_error(
"step_hertz: too many SDF walls");
200 Kokkos::parallel_for(
201 "peclet::dem::force_zero_ghost", Kokkos::RangePolicy<CpExec>(space, lo, hi),
202 KOKKOS_LAMBDA(
int i) {
203 for (
int c = 0; c < 3; ++c) {
214 float allMax(
float v)
const {
return v; }
215 float allMin(
float v)
const {
return v; }
223template <
class Law,
class Hooks>
225 const Hooks& hooks) {
234 Kokkos::parallel_reduce(
235 "peclet::dem::hertz_minrad", Kokkos::RangePolicy<CpExec>(space, 0, P.
numReal),
236 KOKKOS_LAMBDA(
int i,
float& acc) {
240 Kokkos::Min<float>(m));
248 const float skin = skinFrac * hooks.allMin(minRad);
249 const float rebuildAt = 0.25f * skin * skin;
253 bool rebuild = hooks.allMax(P.
hertzNumPairs < 0 ? 1.0f : 0.0f) > 0.0f;
256 bool hasShapes =
false;
258 auto hs = Kokkos::create_mirror_view(P.
shapes);
259 Kokkos::deep_copy(hs, P.
shapes);
260 for (
size_t si = 0; si < hs.extent(0); ++si)
261 if (hs(si).numPoints > 0)
264 static const bool profile = std::getenv(
"PECLET_DEM_HERTZ_PROFILE") !=
nullptr;
265 double tPair = 0, tWall = 0, tInt = 0, tRebuild = 0, tCheck = 0;
268 for (
int k = 0; k < nsteps; ++k) {
270 law.commitHistory(P);
275 hooks.gatherGhosts(P);
276 law.rebuild(P, skin);
280 tRebuild += timer.seconds();
285 hooks.refreshGhostState(P, hasShapes);
287 hooks.clearGhostScratch(P);
288 law.pairForces(P, dt, hasShapes);
290 tPair += timer.seconds();
293 law.wallForces(P, dt, hasShapes);
295 tWall += timer.seconds();
301 tInt += timer.seconds();
304 if ((k & 15) == 15 || k == nsteps - 1) {
308 tCheck += timer.seconds();
315 "[hertz profile] steps=%d pairs=%d wallcand=%d rebuilds=%d | pair %.3fs wall %.3fs "
316 "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< const unsigned char *, CpMem > MatIdView
void hertzCommitHistory(Particles &P)
Save the current list (sorted by key) so the NEXT rebuild can carry the history.
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
unsigned long long pairKeyFromGids(unsigned a, unsigned b)
The one 64-bit pair identity every gid-keyed ledger uses (the XPBD persistent-contact and warm-start ...
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): the owned span before a step,...
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.
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, Kokkos::View< const float *, CpMem > scale={}, ScalarI shapeId={}, Kokkos::View< const ShapeDesc *, CpMem > shapes={}, float globalScale=1.0f)
Cached-pair forces: overlap from current positions; history resets when a cached pair is currently se...
int lowerBoundKey(const KeysView &keys, int count, unsigned long long key)
lower_bound over a ledger's sorted keys: the first index whose key is >= key (count when none).
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