peclet-dem 1.1.0
Performance-portable XPBD Discrete Element Method (Kokkos + ArborX)
Loading...
Searching...
No Matches
peclet::dem Namespace Reference

Namespaces

namespace  detail
 
namespace  mldetail
 

Classes

struct  ContactC
 Portable mirror of ParticleSystem.cuh ContactConstraint (the fields this reduction touches). More...
 
struct  ContactHierarchy
 Host-side description of one built hierarchy (offsets into the packed group pools). More...
 
struct  CudaIterGraph
 
struct  DebugContactCapture
 TEST-ONLY host capture of one distributed substep's contact ownership (C++ only; filled by demStepMpi when Particles::debugCapture is set, never read by the step): the owned contacts [0, ncOwned) after the partition and the owned predicted state they were detected on. More...
 
struct  Domain
 
struct  F3
 
struct  F4
 
struct  FusedLoopSpec
 Device-side iteration loop: run up to maxIters sweeps of the colour classes inside ONE kernel, evaluating the adaptive stop on-device (the same residual the host loop read back between launches — the readback round trip and the CUDA-graph capture disappear entirely). More...
 
struct  FusedSweepCtx
 Device-side context for a fused colour sweep: the colour offsets (numColors+1, uploaded from buildColorBucketsKokkos's host vector), the barrier counter, and the largest colour bucket (grid sizing). More...
 
struct  HertzMindlinLaw
 Hertz–Mindlin force-law policy (the first force law; see file comment). More...
 
struct  KE2
 Berendsen thermostat: reduce translational + rotational KE over real particles, compute the two scaling factors, and rescale linear/angular velocities. More...
 
struct  ManifoldC
 Portable mirror of ManifoldConstraint. More...
 
struct  MlBodyViews
 The per-body inputs of the rigid coarse space (§2.3-§2.5): the predicted positions (the member offsets d_q), the predicted angular velocities (restricted and prolonged), the orientations of the fine PGS and the coarse vertices' inverse inertia invIc = invInertia k / max(1, a). More...
 
struct  MlCoarseSweep
 The per-manifold coarse-PGS body (e = 0, shared fine accumulator) lives in MlCoarseSweep so the per-colour launch loop and the fused whole-cycle kernel share it verbatim. More...
 
struct  MlFusedCtx
 Fused-coarse-cycle context: flat per-level colour offsets on device + barrier + meta. More...
 
struct  MlFusedMeta
 Host-POD description of a built hierarchy for the fused coarse-cycle kernel (fixed-size arrays: kernel argument by value). More...
 
struct  MlScratch
 Device scratch for the multilevel pass, sized once (see Particles::allocate). More...
 
struct  Particles
 
struct  PGSManifoldSweep
 One full colored PGS sweep. More...
 
struct  PhaseCopies
 One solve phase's body copies (docs/contact_solve_framework.md §4.4, §4.5, §WO-4 item 2): the hub copies of the colouring (extra slots [slotBase, slotBase + nCopies) appended to the SoA, per-edge slot overrides) and the fold GROUPS – one per body updated through more than one slot in the phase (a hub's base + copies; in demStep's position phase also the periodic images of a body). More...
 
struct  PlaneP
 
struct  PositionContactSweep
 The per-contact overlap-projection body lives in PositionContactSweep so the colored launch loop and the fused colour sweep (solver_fused.hpp) share it verbatim: solveOne(idx) must only run concurrently on contacts that are body-disjoint within one launch (a colour class). More...
 
struct  PosUnits
 The position units of a contact list as a CSR (§4.3): unit u owns the contacts list(start(u)) . More...
 
struct  RankK
 Rank-level M (docs/contact_solve_framework.md §13.3) as the hooks expose it to the driver: whether this rank reconciles copies with neighbours at all (it sends or receives ghosts). More...
 
struct  ShapeDesc
 Portable mirror of ShapeDescriptor (analytic fields + a flat-array point shell). More...
 
class  ShapeRegistry
 Host-side shape registry (Layer 1). More...
 
class  Simulation
 Host-facing facade with std::vector setters/getters (binding-agnostic). More...
 
struct  SlotOverride
 Per-edge slot overrides of a phase's hub copies (docs/contact_solve_framework.md §4.4 item 5) plus the per-slot mass-split flag and relaxation of §4.5. More...
 
struct  SoloForceHooks
 Single-GPU hooks: no ghosts, reductions are already global. Everything inlines away. More...
 
struct  SoloSolveHooks
 Single-GPU hooks: no ghost refresh, residuals are already global. Everything inlines away. More...
 
struct  SplitStats
 diagnostics.split_stats() (docs/contact_solve_framework.md §WO-4 item 4, §13.5 WO-4b): the last substep's copies. More...
 
struct  WallSdf
 Static, world-space SDF container/geometry the particles collide against (a drum barrel, a hopper, a vibrating tray, ...). More...
 
struct  XGateSpec
 Rank-level X evaluated inline in the one-shot sweep (only for the manifolds of the colour being swept): the same test as computeXGateKokkos, without a separate pass over every manifold per iteration. More...
 

Typedefs

using BpExec = Kokkos::DefaultExecutionSpace
 
using BpMem = BpExec::memory_space
 
using CpExec = Kokkos::DefaultExecutionSpace
 
using CpMem = CpExec::memory_space
 
using V3 = Kokkos::View< float *[3], CpMem >
 
using V4 = Kokkos::View< float *[4], CpMem >
 
using Vf = Kokkos::View< float *, CpMem >
 
using Vi = Kokkos::View< int *, CpMem >
 
using PairTableView = Kokkos::View< const float *, CpMem >
 
using MatIdView = Kokkos::View< const unsigned char *, CpMem >
 
using PosView = Kokkos::View< const float *[3], CpMem >
 
using QuatView = Kokkos::View< const float *[4], CpMem >
 
using ScalarF = Kokkos::View< const float *, CpMem >
 
using ScalarI = Kokkos::View< const int *, CpMem >
 
using ShellView = Kokkos::View< const float *[3], CpMem >
 
using GridView = Kokkos::View< const float *, CpMem >
 
using FrManifoldCounts = Kokkos::View< float *[2], CpMem >
 

Enumerations

enum  ShapeKind {
  SHAPE_GRID_SDF = 0 , SPHERE = 1 , HOLLOW_CYLINDER = 2 , BOX = 3 ,
  SHAPE_SCENE = 4
}
 

Functions

template<class PosV , class RadV , class PairsV , class CountV >
int findCollisionsArborX (PosV pos, RadV rad, int numParticles, int numReal, float margin, PairsV outPairs, CountV outCount, float boxCap=0.0f)
 Emit candidate collision pairs (i<j) for real particles into outPairs/outCount.
 
bool normalOfA (const ContactC &c)
 Which body's surface a contact's normal belongs to.
 
constexpr int wallContactId (int wallIndex)
 Wall identity of a boundary contact (docs/contact_solve_framework.md §12 S6): a contact with a wall carries bodyB = -1 - wallIndex, where wallIndex runs over the analytic planes first ([0, numPlanes)) and then the SDF walls (numPlanes + w).
 
constexpr int wallIndexOf (int bodyB)
 
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 ledgers, the Hertz–Mindlin history): (min(a, b) << 32) | max(a, b), symmetric in (a, b) so a pair is one key whichever body is A.
 
template<class KeysView >
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).
 
unsigned long long pairKeyOf (const ManifoldC &m, Kokkos::View< const int *, CpMem > realIdx)
 Persistent-contact detection for the gravity-gated restitution rule.
 
void markPersistentManifoldsKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const int *, CpMem > keyIdx, Kokkos::View< const unsigned long long *, CpMem > prevKeys, int prevCount, Kokkos::View< unsigned long long *, CpMem > outKeys, Kokkos::View< unsigned char *, CpMem > outFlags)
 keyIdx maps a body slot to the identity the pair key is built from: the REAL index map on the single-GPU path (stable within a run), or the GLOBAL particle id under MPI (stable across ranks, halo rebuilds and ownership migration — local slots are neither).
 
void gatherWarmLambdaKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const int *, CpMem > keyIdx, Kokkos::View< const unsigned long long *, CpMem > prevKeys, Kokkos::View< const float *, CpMem > prevLambda, Kokkos::View< const float *[3], CpMem > prevLambdaT, Kokkos::View< const float *, CpMem > prevPosImpulse, Kokkos::View< const float *, CpMem > prevRestBank, Kokkos::View< const float *, CpMem > prevRestVPeak, int prevCount, Kokkos::View< unsigned long long *, CpMem > outKeys, Kokkos::View< float *, CpMem > outWarm, Kokkos::View< float *[3], CpMem > outWarmT, Kokkos::View< float *, CpMem > outPosImpulse, Kokkos::View< float *, CpMem > outRestBank, Kokkos::View< float *, CpMem > outRestVPeak, Kokkos::View< unsigned char *, CpMem > outMatched={}, bool dedupTwins=true)
 Warm-start gather for the PGS velocity solve: per manifold, write its pair key and look up the previous substep's converged push impulse (0 for a new contact).
 
void commitPairKeysLambdaKokkos (Kokkos::View< const unsigned long long *, CpMem > keys, Kokkos::View< const float *, CpMem > lambda, Kokkos::View< const float *[3], CpMem > lambdaT, Kokkos::View< const float *, CpMem > restBank, Kokkos::View< const float *, CpMem > restVPeak, Kokkos::View< unsigned long long *, CpMem > prevKeys, Kokkos::View< float *, CpMem > prevLambda, Kokkos::View< float *[3], CpMem > prevLambdaT, Kokkos::View< float *, CpMem > prevRestBank, Kokkos::View< float *, CpMem > prevRestVPeak, Kokkos::View< int *, CpMem > perm, int numManifolds, Kokkos::View< const int *, CpMem > color={}, Kokkos::View< int *, CpMem > prevColor={})
 Save this substep's keys + converged impulses (normal AND tangential) and key-sort them for next substep's gather.
 
void commitPairKeysKokkos (Kokkos::View< const unsigned long long *, CpMem > keys, Kokkos::View< unsigned long long *, CpMem > prevKeys, int numManifolds)
 Copy this substep's keys into prevKeys and sort them for next substep's binary search.
 
void updateGroundedLevelsKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const float *[3], CpMem > posPred, F3 gHat, Kokkos::View< unsigned char *, CpMem > grounded, int numReal, int sweeps, int decay)
 Guendelman support levels, warm-started: decay every body's level by decay, re-seed 255 at wall/plane contacts, then sweeps monotone propagation passes lower -> upper (255 -> 254 -> ...) through the manifold graph.
 
void computeHeightLevelsKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const float *[3], CpMem > posPred, F3 gHat, Kokkos::View< int *, CpMem > heights, int numReal)
 
void buildColorBucketsKokkos (Kokkos::View< const int *, CpMem > colorOf, int n, int numColors, Kokkos::View< int *, CpMem > perm, Kokkos::View< int *, CpMem > cursor, std::vector< int > &offs)
 Dense colour buckets (numColors <= 64): perm[offs[c] .
 
long long colorKey (int idx)
 splitmix32 finalizer: a well-mixed pseudo-random priority per edge index.
 
std::uint64_t pairKey (const ContactC &c)
 Canonical pair key: (min<<32)|max, or (idA<<32)|0xFFFFFFFF for a boundary (idB<0) contact.
 
void decodeKey (std::uint64_t key, int &bodyA, int &bodyB)
 Decode the canonical (bodyA, bodyB) from a pair key (bodyB = -1 for boundary).
 
ManifoldC transformContact (const ContactC &c)
 Per-contact transform to a single-point manifold, aligned to the canonical pair.
 
template<class Owns >
int partitionContactsKokkos (Kokkos::View< ContactC *, CpMem > contacts, int n, const Owns &owns)
 Stable partition of contacts [0, n) by owns(contact): the owned ones first, [0, ncOwned), then the rest, each part in its old relative order (distributed step, docs/mpi_momentum_conservation.md §2.2).
 
int reduceContactsToManifoldsKokkos (Kokkos::View< const ContactC *, CpMem > contacts, int n, Kokkos::View< ManifoldC *, CpMem > outManifolds, Kokkos::View< int, CpMem > outCount, Kokkos::View< int *, CpMem > contactSlot={}, int numOwnedContacts=-1, int *numOwnedManifolds=nullptr)
 Reduce n contacts to manifolds (one per unique canonical pair).
 
std::uint64_t unitKey (const ContactC &c)
 Position-unit key (docs/contact_solve_framework.md §4.3, §12 S6): the canonical pair key for a body-body contact, and (bodyA << 32) | (unsigned)bodyB for a wall contact, which separates the walls of one body (bodyB = -1 - wallIndex, so the low word is 0xFFFFFFFF - wallIndex >= 2^31, never a body slot).
 
int buildPositionUnitsKokkos (Kokkos::View< const ContactC *, CpMem > contacts, int n, Kokkos::View< int *, CpMem > unitStart, Kokkos::View< int *, CpMem > unitContacts, int numManifolds=-1)
 Build the position units of contacts [0, n) (docs/contact_solve_framework.md §4.3, §12 S6): a unit is the set of contacts of one body pair, or of one body and one wall.
 
void frictionBoundFromLambdaKokkos (Kokkos::View< ContactC *, CpMem > contacts, int numContacts, Kokkos::View< const int *, CpMem > contactSlot, Kokkos::View< const ManifoldC *, CpMem > manifolds, Kokkos::View< const float *, CpMem > lambdaAcc)
 PGS friction bound: overwrite each contact's friction_lambda_n with its manifold's converged PGS push impulse (lambdaAcc, shared equally over the manifold's contact points).
 
void commitPosImpulseKokkos (Kokkos::View< const float *, CpMem > posLambdaContact, int numContacts, Kokkos::View< const int *, CpMem > contactSlot, Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const unsigned long long *, CpMem > keys, Kokkos::View< const unsigned long long *, CpMem > prevKeysSorted, int prevCount, float dt, Kokkos::View< float *, CpMem > scratchManifold, Kokkos::View< float *, CpMem > prevPosImpulse)
 After the position solve: convert the per-contact positional lambdas into an impulse- equivalent per manifold (lambda_pos / dt has force units; x dt back to impulse => just lambda_pos * m_eff... the positional lambda already carries 1/w mass weighting, so the impulse equivalent over the substep is lambda_pos / dt * dt = lambda_pos / (w*...) – we store lambda_pos/dt * dt = lambda_pos scaled by 1/dt to velocity-impulse units) and write it into the sorted prev-store so next substep's warm gather can top up the Coulomb bound.
 
F3 add3 (F3 a, F3 b)
 
F3 sub3 (F3 a, F3 b)
 
F3 scale3 (F3 a, float s)
 
float dot3 (F3 a, F3 b)
 
float len3 (F3 v)
 
F3 cross3v (F3 a, F3 b)
 
F4 cross3 (F4 a, F4 b)
 
template<class V >
F3 ldF3 (const V &v, int i)
 
template<class V >
F4 ldF4 (const V &v, int i)
 
F3 rotateVector (F4 q, F3 v)
 
F3 invRotateVector (F4 q, F3 v)
 
F4 quatInverse (F4 q)
 
F4 quatMult (F4 a, F4 b)
 
peclet::core::Vec3< float > toCoreVec (F3 p)
 F3 -> core's Real-templated point type. Layout-identical; this is a type change, not a copy.
 
float sdfSphere (F3 p, F4 params)
 
float sdfHollowCylinder (F3 p, F4 params)
 
float sdfBox (F3 p, F4 params)
 
float sdfEval (F3 p, int type, F4 params)
 
void predictVelocityKokkos (int n, V3 pos, Vf invMass, V3 vel, V4 quat, V3 angVel, V3 invInertia, V3 posPred, V4 quatPred, V3 velPred, V3 angVelPred, V3 deltaPos, V4 deltaQuat, V3 deltaVel, V3 deltaAngVel, Vi constraintCounts, F3 gravity, float dt, V3 extForce, V3 extTorque)
 Predict velocity (gravity + gyroscopic precession), speculative position, and clear all deltas.
 
void applyVelocityDeltasKokkos (int n, V3 velPred, V3 angVelPred, V3 deltaVel, V3 deltaAngVel)
 Add accumulated velocity/angular deltas onto the predicted velocity, then clear the delta buffers.
 
void applyVelocityAndPredictPositionKokkos (int n, V3 pos, Vf invMass, V3 vel, V4 quat, V3 velPred, V3 angVelPred, V3 posPred, V4 quatPred, V3 angVel, float dt)
 Re-integration: persist solved velocity, trapezoidal position predict, quaternion integrate.
 
void computeContactCountsKokkos (Kokkos::View< const ContactC *, CpMem > contacts, int numContacts, Vi constraintCounts)
 Per-contact constraint-count pre-pass (active contacts, dist <= 0).
 
void applyUpdatesKokkos (int n, V3 posPred, V3 velPred, V3 deltaPos, V3 deltaVel, Vi constraintCounts, bool averaged=true)
 Jacobi count-averaged apply of position/velocity deltas, then clear deltas + counts.
 
void finalCommitKokkos (int n, V3 pos, Vf invMass, V3 posPred, V4 quat, V4 quatPred, Domain dom)
 Final commit: periodic wrap of the predicted position into the domain, commit position + quat.
 
void applyThermostatKokkos (int numReal, V3 vel, Vf invMass, V3 angVel, V3 invInertia, V4 quat, double kB, double tau, double Ttarget, float dt)
 
void updateGrowthScalesKokkos (int n, Vf scale, Vf targetScale, float factor)
 Growth mode: scale = target * factor (when active).
 
void writeLammpsDump (const std::string &filename, int step, const std::vector< float > &pos, const std::vector< float > &vel, const std::vector< float > &quat, const std::vector< float > &radii, const float *boxMin, const float *boxMax, bool pbcEnabled)
 
void writeSdfVti (const std::string &filename, const std::vector< float > &grid, int rx, int ry, int rz, const float *minB, const float *maxB)
 
F3 loadF3 (PosView v, int i)
 
F4 loadF4 (QuatView v, int i)
 
float sampleGridSdf (F3 p, const ShapeDesc &d, GridView grid)
 Trilinearly sample an imported grid SDF at canonical point p.
 
float sampleWallSdf (F3 p, const WallSdf &w, GridView grid)
 Trilinearly sample a static world-space wall SDF at world point p.
 
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 SDF.
 
F3 sdfGradShape (F3 p, const ShapeDesc &d, GridView grid)
 Outward gradient of a shape's SDF at canonical point p, NOT normalised (callers normalise, matching the previous finite-difference code).
 
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{}, bool twoWay=false)
 Pair point-shell vs SDF contacts.
 
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{}, int wallIdBase=0)
 Per-real-particle contacts against a static world-space wall SDF set (a drum barrel, hopper, vibrating tray, ...).
 
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; analytic spheres use centre-minus-radius).
 
std::vector< float > generateSdfKokkos (int rx, int ry, int rz, F3 dmin, F3 dmax, int numReal, PosView pos, QuatView quat, ScalarF scale, ScalarI shapeId, Kokkos::View< const ShapeDesc *, CpMem > shapes, bool px, bool py, bool pz, GridView sdfGrid=GridView{}, float globalScale=1.0f)
 globalScale multiplies every per-particle scale, exactly as the narrow-phase does (narrowphase.hpp: effScale = scale(i) * globalScale).
 
int calculateGhostCapacity (int nReal, Domain dom, float skin)
 Padded particle-array capacity that leaves room for the periodic ghosts generateGhostsKokkos will emit: the real count plus an estimate of the boundary-shell ghost population, gated on which axes are actually periodic.
 
void generateGhostsKokkos (int numReal, int capacity, Domain dom, float skin, V3 pos, Vf invMass, V3 posPred, V3 vel, V3 velPred, V4 quat, V4 quatPred, V3 angVel, V3 angVelPred, Vf scale, Vi shapeId, Vi realIndices, Kokkos::View< int, CpMem > topGhost, Vi gid=Vi{}, Kokkos::View< unsigned char *, CpMem > materialId={})
 Generate periodic ghosts for particles [0,numReal).
 
std::vector< F3 > genCylinderShell (float radius, float height, float thickness, float spacing)
 
std::vector< F3 > genBoxShell (float hx, float hy, float hz, float spacing)
 
void freezeAsleepKokkos (int numReal, Kokkos::View< const unsigned char *, CpMem > asleep, Kokkos::View< const float *[3], CpMem > pos, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< float *[3], CpMem > posPred, Kokkos::View< float *[4], CpMem > quatPred, Kokkos::View< float *[3], CpMem > velPred, Kokkos::View< float *[3], CpMem > angVelPred)
 After predictVelocity: re-freeze the currently-asleep bodies so gravity/prediction do not move them — velPred = angVelPred = 0, posPred = pos, quatPred = quat.
 
void buildInvMassEffKokkos (int numBodies, Kokkos::View< const unsigned char *, CpMem > asleep, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< float *, CpMem > invMassEff, float sleeperFrac)
 Effective inverse mass for the solve: a sleeping body (real, or a periodic ghost whose real is asleep) is immovable — invMassEff = 0.
 
void computeManifoldSleepKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const unsigned char *, CpMem > asleep, Kokkos::View< unsigned char *, CpMem > manifoldSleep)
 Per-manifold "both endpoints asleep" flag (a static wall, bodyB < 0, counts as asleep).
 
void computeContactSleepKokkos (Kokkos::View< const ContactC *, CpMem > contacts, int numContacts, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const unsigned char *, CpMem > asleep, Kokkos::View< unsigned char *, CpMem > contactSleep)
 Per-contact twin of computeManifoldSleepKokkos for the position colouring.
 
void wakeDisturbedKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const float *[3], CpMem > velPred, float wakeSpeed, Kokkos::View< unsigned char *, CpMem > asleep, Kokkos::View< unsigned char *, CpMem > sleepCounter, Kokkos::View< unsigned char *, CpMem > movingWall, Kokkos::View< int *, CpMem > curCount, int numReal)
 Wake pass (before the solve): a sleeper is woken when actually disturbed.
 
void wakeContactChangeKokkos (int numReal, Kokkos::View< const int *, CpMem > curCount, Kokkos::View< int *, CpMem > prevCount, Kokkos::View< const unsigned char *, CpMem > movingWall, Kokkos::View< unsigned char *, CpMem > asleep, Kokkos::View< unsigned char *, CpMem > sleepCounter, bool wakeOnChange)
 Contact-set-change wake rule (b): wake any still-asleep body whose live contact count differs from the stored one (a support gained or lost), then refresh the store.
 
void updateSleepKokkos (int numReal, Kokkos::View< const float *[3], CpMem > vel, Kokkos::View< const float *[3], CpMem > angVel, Kokkos::View< const float *, CpMem > rad, Kokkos::View< const unsigned char *, CpMem > grounded, Kokkos::View< const unsigned char *, CpMem > movingWall, Kokkos::View< unsigned char *, CpMem > asleep, Kokkos::View< unsigned char *, CpMem > sleepCounter, float sleepSpeed, int K, Kokkos::View< float *[3], CpMem > velOut, Kokkos::View< float *[3], CpMem > angVelOut)
 Sleep detection (after the commit): an AWAKE, grounded body whose linear AND angular motion has stayed below sleepSpeed for K consecutive substeps is put to sleep (velocity + spin zeroed).
 
template<class V >
void growCopyView (V &v, std::size_t n, const char *label)
 Grow-only (re)allocation of a copy-machinery view to at least n entries.
 
void velocityEdgeEndsKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int nm, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const int *, CpMem > color, Kokkos::View< int *, CpMem > eA, Kokkos::View< int *, CpMem > eB)
 Velocity-phase colouring edges: the vertices realIdx(bodyA / bodyB) of each manifold (-1: a wall end, or both ends of an inactive (-2) manifold).
 
void positionEdgeEndsKokkos (Kokkos::View< const ContactC *, CpMem > contacts, const PosUnits &units, int nu, Kokkos::View< const int *, CpMem > color, Kokkos::View< int *, CpMem > eA, Kokkos::View< int *, CpMem > eB)
 Position-phase colouring edges: one per unit, the leader contact's raw (bodyA, bodyB); active = the unit is not sleep-masked (colour != -2).
 
void vertexDegreeKokkos (Kokkos::View< const int *, CpMem > eA, Kokkos::View< const int *, CpMem > eB, int nE, int nV, Kokkos::View< int *, CpMem > deg)
 Per-vertex active degree over [0, nV) (§4.4 step 1). Wall edges have one endpoint.
 
void buildHubCopiesKokkos (Kokkos::View< const int *, CpMem > eA, Kokkos::View< const int *, CpMem > eB, int nE, int nV, int slotBase, Kokkos::View< const int *, CpMem > deg, PhaseCopies &H)
 Build one phase's hub copies (§4.4 steps 2, 3 and 5): every vertex with d > kHubEdgeBudget active edges gets s = ceil(d / 32) copies – its own slot plus s - 1 slots appended at [slotBase, slotBase + nCopies).
 
void buildCopyGroupsKokkos (Kokkos::View< const int *, CpMem > realIdx, int span, int numReal, Kokkos::View< const int *, CpMem > deg, Kokkos::View< const float *[3], CpMem > imageShift, PhaseCopies &H, bool identityBodies=false)
 Build the fold groups of a phase (§4.4 "local fold", §WO-4 item 2): every body with more than one slot among [0, span) (the phase's vertex slots) and the hub copies.
 
void buildSolveViewsKokkos (Particles &P, const PhaseCopies &H, int nSlots, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia)
 Solve views of a phase (§4.5): invMassSolve / invInertiaSolve = the phase's masses on every slot in [0, nSlots), times k on every member of a group, and splitSlot = (k > 1).
 
void seedCopySlotStateKokkos (Particles &P, const PhaseCopies &H)
 Seed the copy slots' read-only state from their hub vertex (§4.4 step 6): orientation, local inverse inertia and grounded level (velocity), predicted orientation (position).
 
void markCopySeedsKokkos (const PhaseCopies &H, Kokkos::View< float *[3], CpMem > x, Kokkos::View< float *[3], CpMem > w, Kokkos::View< float *, CpMem > orphan, Kokkos::View< float *, CpMem > orphanPeak, bool shareFromBase=false)
 Mark the phase seed sigma of every group and re-seed its members from the base (§4.4 step 6, and after every rank sync): x(q) = sigma + shift(q); w likewise (velocity, no shift).
 
void foldCopiesKokkos (CpExec space, const PhaseCopies &H, Kokkos::View< float *[3], CpMem > x, Kokkos::View< float *[3], CpMem > w, Kokkos::View< float *, CpMem > orphan, Kokkos::View< float *, CpMem > orphanPeak, Kokkos::View< float, CpMem > consensus={})
 The local fold (§4.4), once per iteration after the sweep: per group T = sum over the ACTIVE members of (x(q) - sigma - shift(q)) (the M-consensus of §1.3: an inactive member carries no increment of its own, only the re-seeded consensus); the base takes sigma + T / k and every member is re-seeded to it (plus its shift).
 
void reseedCopiesKokkos (CpExec space, const PhaseCopies &H, Kokkos::View< float *[3], CpMem > x, Kokkos::View< float *[3], CpMem > w)
 Re-seed every member from its base, sigma unchanged (the multilevel pass, after the coarse cycle has moved the bases; §4.4 "copies re-seeded again after the coarse cycle").
 
void unfoldOrphanKokkos (const PhaseCopies &H, Kokkos::View< float *, CpMem > orphan)
 Orphan accounts back from share form (before a rank sync and at the end of the velocity phase): the base holds the body's balance, the sum of its members' shares.
 
void activityHitVelocityKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int nm, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const int *, CpMem > color, SlotOverride ov, Kokkos::View< unsigned char *, CpMem > hit, int nSlots)
 Rank-level activity pass (docs/contact_solve_framework.md §13.3 C4; the distributed step only), velocity phase: hit(slot) = 1 for every slot that is an end – through the hub-copy overrides, else realIdx – of an owned manifold of colour >= 0 in the final colouring.
 
void activityHitPositionKokkos (Kokkos::View< const ContactC *, CpMem > contacts, const PosUnits &units, int nu, Kokkos::View< const int *, CpMem > color, SlotOverride ov, Kokkos::View< unsigned char *, CpMem > hit, int nSlots)
 The same over the position phase's units (raw slots of the leader contact, through the overrides).
 
void finishActivityKokkos (Kokkos::View< const unsigned char *, CpMem > hit, int nSlots, PhaseCopies &H, Kokkos::View< int *, CpMem > a)
 a(q) from the pass (§13.3): hit(q) for a slot outside any group; at a hub base the number of its group's members (base + copies) that were hit, which also refills groupActive and groupK (floored at 1, WO-4's local fold divisor).
 
void buildSolveViewsRankKKokkos (Particles &P, const PhaseCopies &H, int nSlots, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const int *, CpMem > kRank)
 Solve views under rank-level M (§13.3, §4.5): invMassSolve / invInertiaSolve = k x the body's masses on every slot in [0, nSlots) (nSlots = slotBase + the phase's copies), with k the slot's global active copy count kRank (owner-computed, forwarded); a hub copy takes its base's k.
 
void buildInvMassCoarseKokkos (Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const int *, CpMem > k, Kokkos::View< const int *, CpMem > a, int n, Kokkos::View< float *, CpMem > out)
 The multilevel coarse vertex's inverse mass (§13.2): invMass(q) (k(q) / max(1, a(q))), the ratio computed first so that k = a gives invMass bit for bit.
 
void buildInvInertiaCoarseKokkos (Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const int *, CpMem > k, Kokkos::View< const int *, CpMem > a, int n, Kokkos::View< float *[3], CpMem > out)
 The multilevel coarse vertex's inverse inertia (docs/contact_physics_followups.md §2.5): the same fraction a / k of the body's inertia as its mass, invInertia(q) (k(q) / max(1, a(q))) per component, the ratio computed first exactly as buildInvMassCoarseKokkos (k = a gives invInertia bit for bit).
 
int countInactiveAggregatedKokkos (Kokkos::View< const int *, CpMem > parent, int off, int nV, int nGroups1, Kokkos::View< const int *, CpMem > a)
 Debug-build check (§13.2, §13.5 WO-5 item 4): the vertices with a = 0 that sit in a level-1 multilevel group of >= 2 members (must be none: an inactive copy never joins a coarse edge).
 
int countSplitBodiesKokkos (const PhaseCopies &H)
 Count the bodies whose group has k > 1 (split_stats).
 
int countSplitAggregatedKokkos (Kokkos::View< const int *, CpMem > parent, int off, int nV, int nGroups1, Kokkos::View< const unsigned char *, CpMem > split)
 split_stats.mlHubAggregated (§13.2's positive control): the vertices v in [0, nV) with split(v) != 0 (k > 1) whose level-1 multilevel group – parent(off + v), the first level's parent map – has at least two members.
 
int countLightHubsKokkos (const PhaseCopies &H, Kokkos::View< const int *, CpMem > eA, Kokkos::View< const int *, CpMem > eB, int nE, int nV, Kokkos::View< const float *, CpMem > invMass)
 Light hubs (split_stats, R-P3): velocity hubs whose mass is below 10x the mean mass of their partners over the hub's active edges.
 
int readInt (Kokkos::View< int, CpMem > v)
 
float readFloat (Kokkos::View< float, CpMem > v)
 
void fillGidBaseKokkos (Vi gid, int n, int base)
 gid(i) = base + i over [0, n) — the per-rank global-id re-base of the distributed step (namespace scope: nvcc forbids KOKKOS_LAMBDA in member functions).
 
float maxOwnedRadius (const Particles &P)
 Largest effective particle radius over the owned set (= max scale × globalScale, growth included).
 
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, owned + ghosts after periodic ghost generation or a halo gather.
 
int findCollisionsGrow (Particles &P, float margin)
 Broad phase with an automatically-grown pair buffer.
 
int findCollisionsVerlet (Particles &P, float margin, float maxRad)
 Verlet-cached impulse broadphase (single-GPU, non-periodic).
 
int * demIterCountSlot (Particles &P, int slot)
 Diagnostics (docs/contact_solve_framework.md §12 S12): the device scalar a fused main loop writes its iteration count to (slot 0 velocity, 1 position), allocated on first use.
 
int demReadIterCount (const Particles &P, int slot)
 The count a fused main loop wrote (one small readback, only with P.iterCounters on); -1 with the counters off (no fence, no copy).
 
template<class Hooks >
void demSolveContacts (Particles &P, int nc, int nm, int nBodies, Kokkos::View< const int *, CpMem > keyIdx, const Hooks &hooks)
 One full velocity + position contact solve over the already-built contacts/manifolds (see file comment).
 
void hertzRebuildPairs (Particles &P, float skin)
 Rebuild the Hertz cached pair list with margin skin, carrying the Mindlin shear history across by pair key (permutation sort of the OLD list, binary search from the NEW).
 
void hertzCommitHistory (Particles &P)
 Save the current list (sorted by key) so the NEXT rebuild can carry the history.
 
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 onto ghost slots too (Newton's third law), but only owned rows are consumed and cleared by the integrator; without this the ghost rows grow without bound.
 
template<class Law , class Hooks >
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).
 
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 driver (kept as the historical entry point; see demStepForce).
 
void contactArmsMid (const ContactC &c, F3 &rA, F3 &rB)
 The single application point of a body-body contact: both lever arms moved to the contact midpoint, rA - dist n / 2 and rB + dist n / 2, so x_A + rA = x_B + rB (the shift of transformContact, contact_preprocessing.hpp).
 
void accumulateNormalImpulseKokkos (Kokkos::View< ContactC *, CpMem > contacts, int numContacts, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[3], CpMem > velPred, Kokkos::View< const float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, float growthRate, bool dedupTwins=true)
 Force-chain normal load, accumulated over the velocity iterations: contacts(idx).friction_lambda_n += approach / w_n.
 
void computePlaneLoadKokkos (Kokkos::View< ContactC *, CpMem > contacts, int numContacts, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[3], CpMem > velPred, Kokkos::View< const float *[3], CpMem > angVelPred, FrManifoldCounts planeFriction)
 Plane (idB<0) one-shot loads.
 
void countFrictionContactsKokkos (Kokkos::View< const ContactC *, CpMem > contacts, int numContacts, Kokkos::View< const int *, CpMem > realIdx, FrManifoldCounts planeFriction)
 Per-body active-contact count into planeFriction(:,1).
 
void solveContactFrictionKokkos (Kokkos::View< const ContactC *, CpMem > contacts, int numContacts, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< const float *[3], CpMem > velPred, Kokkos::View< const float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, FrManifoldCounts planeFriction, float frictionDynamic, Kokkos::View< float *[3], CpMem > deltaVel, Kokkos::View< float *[3], CpMem > deltaAngVel)
 One count-averaged Coulomb friction sweep.
 
bool demFusedWanted (bool graphReplayAvailable, int mode)
 Fused-sweep policy.
 
FusedSweepCtx demMakeFusedCtx (CpExec &space, const std::vector< int > &offs, Kokkos::View< int *, CpMem > offsDev, Kokkos::View< unsigned *, CpMem > bar)
 Fill a FusedSweepCtx from buildColorBucketsKokkos's host offsets: async-upload them into the pooled device view and record the largest bucket.
 
void hertzPairEMu (int matA, int matB, PairTableView pairTable, float eGlobal, float muGlobal, float &e, float &mu)
 Pairwise (e, mu) lookup shared with the narrowphase convention: pair table if present, global fallback otherwise.
 
float hertzBetaD (float e)
 Restitution -> damping ratio beta_d = ln e / sqrt(ln^2 e + pi^2) (e in (0, 1]; e <= 0 -> -1, the critically-damped limit used by LIGGGHTS for e -> 0).
 
F3 hertzForce (float delta, F3 nhat, F3 vrel, float rStar, float mStar, float eStar, float gStar, float e, float mu, float dt, F3 &xi)
 One Hertz–Mindlin force evaluation for a sphere-sphere or sphere-wall contact.
 
float hertzSphereRadius (int i, Kokkos::View< const float *, CpMem > rad, Kokkos::View< const float *, CpMem > scale, ScalarI shapeId, Kokkos::View< const ShapeDesc *, CpMem > shapes, float globalScale)
 A body's Hertz sphere radius: a SPHERE shape's own radius, scale * globalScale * params.x (the radius the XPBD narrow phase probes with); otherwise the world reach radius rad(i).
 
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 separated.
 
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 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.
 
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 of the integrate kernel avoids a per-step single-address atomic).
 
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 sampling convention as the narrowphase), ONE Mindlin patch history per pair.
 
int buildMlLevelGeometryKokkos (const MlScratch &S, int off, int ng, int numReal, Kokkos::View< const float *[3], CpMem > posPred, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< const float *[3], CpMem > invIc)
 The per-level geometry of the rigid coarse space (docs/contact_physics_followups.md §2.3), over the composed body -> group map S.grp of the level just built (groups [off, off + ng)):
 
ContactHierarchy buildContactHierarchyKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const int *, CpMem > mColor, Kokkos::View< const float *, CpMem > vn0, Kokkos::View< const float *[3], CpMem > vt0, Kokkos::View< const unsigned char *, CpMem > persistent, Kokkos::View< const float *[3], CpMem > posPred, F3 gHat, Kokkos::View< const float *, CpMem > invMass, float qsThr, int gateMask, int numReal, MlScratch &S, Kokkos::View< long long *, CpMem > winner, Kokkos::View< std::uint64_t *, CpMem > colorMask, bool excludeImmovable=false, Kokkos::View< const unsigned char *, CpMem > asleep={}, const MlBodyViews &body={})
 Build the aggregation hierarchy + per-level crossing-manifold colorings.
 
void buildCoarseBucketsKokkos (const ContactHierarchy &H, MlScratch &S, int numManifolds, Kokkos::View< int *, CpMem > colorScratch, Kokkos::View< int *, CpMem > perm, Kokkos::View< int *, CpMem > cursor, std::vector< std::vector< int > > &offs)
 One multilevel stabilization cycle over an already-built hierarchy: fine colored smoothing is the caller's business; this runs the coarse leg (fine -> coarse), updating velPred in place.
 
MlCoarseSweep makeMlCoarseSweep (Kokkos::View< const ManifoldC *, CpMem > manifolds, Kokkos::View< const int *, CpMem > realIdx, const MlScratch &S, Kokkos::View< float *, CpMem > lambdaAcc, Kokkos::View< float, CpMem > maxApproach, Kokkos::View< const float *, CpMem > restRel, const MlBodyViews &body)
 The coarse sweep over the multilevel scratch + the body views (the rigid views stay empty when the lazy pools are).
 
MlFusedCtx demMakeMlFusedCtx (CpExec &space, const ContactHierarchy &H, const std::vector< std::vector< int > > &bkOffs, int numManifolds, int numReal, int coarseSweeps, Kokkos::View< int *, CpMem > offsDev, Kokkos::View< unsigned *, CpMem > bar)
 Build the fused-coarse-cycle context from an already-built hierarchy + its dense buckets: flatten the per-level colour offsets into the pooled device view (async upload) and record the grid-sizing work bound.
 
void multilevelCoarseCycleKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< float *[3], CpMem > velPred, Kokkos::View< float *, CpMem > lambdaAcc, Kokkos::View< float, CpMem > maxApproach, int numReal, const ContactHierarchy &H, MlScratch &S, int coarseSweeps, const MlBodyViews &body, Kokkos::View< const float *, CpMem > restRel={}, const std::vector< std::vector< int > > *bkOffs=nullptr, Kokkos::View< const int *, CpMem > bkPerm={}, const MlFusedCtx *fused=nullptr)
 
void countPositionJacobiKokkos (Kokkos::View< const ContactC *, CpMem > contacts, int numContacts, Kokkos::View< int *, CpMem > counts)
 Mass-split Jacobi count pass (docs/contact_solve_framework.md §3.1, D3): per body slot, the number of contacts the Jacobi position solve visits (every contact; a wall side counts nothing).
 
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, bool massSplit=false)
 Accumulate XPBD position corrections for numContacts contacts.
 
bool unitAsleep (const PosUnits &units, const Kokkos::View< const unsigned char *, CpMem > &sleepMask, int u)
 A position unit is sleep-inactive iff all its contacts are sleep-masked (§4.3).
 
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={}, PosUnits units={}, SlotOverride ov={}, int vertexSpan=0)
 Greedy graph-colour the contacts (raw bodyA/bodyB): no two contacts sharing a body get the same colour.
 
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={}, PosUnits units={})
 Incremental (warm-started) contact colouring for the single-GPU PGS position solve.
 
void expandUnitColorsKokkos (const PosUnits &units, int numUnits, Kokkos::View< const int *, CpMem > unitColor, Kokkos::View< int *, CpMem > contactColor)
 Copy each position unit's colour onto its contacts (contactColor(i) = unitColor(unit of i)).
 
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.
 
bool solvePositionColoredGSKokkos (Kokkos::View< const ContactC *, CpMem > contacts, const PosUnits &units, 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, SlotOverride ov={}, Kokkos::View< float, CpMem > posResidual={}, float omega=1.0f)
 Colored Gauss–Seidel XPBD overlap solve: sweep the numColors colour classes in order, applying each contact's non-penetration correction directly to posPred (in place, translation only).
 
void countVelocityJacobiKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< int *, CpMem > counts, bool dedupTwins=true)
 Mass-split Jacobi count pass (docs/contact_solve_framework.md §3.1, D3): per body (REAL index), the number of manifolds the Jacobi velocity solve visits – num_points > 0, one manifold per periodic pair (the solve's realA > realB dedup); a wall side counts nothing.
 
void solveVelocityKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< const float *[3], CpMem > velPred, Kokkos::View< const float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, float growthRate, float restitutionNormal, float restVelThreshold, Kokkos::View< float *[3], CpMem > deltaVel, Kokkos::View< float *[3], CpMem > deltaAngVel, Kokkos::View< int *, CpMem > velCounts, Kokkos::View< const int *, CpMem > onlyColor={}, int colorFilter=0, Kokkos::View< const unsigned char *, CpMem > persistent={}, Kokkos::View< const float *[3], CpMem > posPred={}, F3 gHat={}, Kokkos::View< const unsigned char *, CpMem > grounded={}, bool massSplit=false, bool dedupTwins=true)
 Accumulate normal-restitution velocity deltas for numManifolds manifolds.
 
template<class V3 , class Vi >
void applyVelocityDeltasAveragedKokkos (int n, V3 velPred, V3 angVelPred, V3 deltaVel, V3 deltaAngVel, Vi velCounts, bool averaged=true)
 Apply the accumulated velocity deltas AVERAGED by the per-body manifold count — the velocity- solve twin of the position solve's constraint-count averaging (applyUpdatesKokkos).
 
int colorManifoldsKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, int numReal, Kokkos::View< int *, CpMem > mColor, Kokkos::View< long long *, CpMem > bodyWinner, Kokkos::View< std::uint64_t *, CpMem > bodyMask, int &leftover, Kokkos::View< const unsigned char *, CpMem > sleepMask={}, SlotOverride ov={}, bool dedupTwins=true, int vertexSpan=0)
 Greedy graph-colour the manifolds: no two manifolds sharing a real body get the same colour.
 
int colorManifoldsIncrementalKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, int numReal, Kokkos::View< const unsigned long long *, CpMem > prevKeys, Kokkos::View< const int *, CpMem > prevColor, int prevCount, Kokkos::View< int *, CpMem > mColor, Kokkos::View< long long *, CpMem > bodyWinner, Kokkos::View< std::uint64_t *, CpMem > bodyMask, int &leftover, bool forceFull, Kokkos::View< const unsigned char *, CpMem > sleepMask={}, bool dedupTwins=true)
 Incremental (warm-started) manifold colouring for the single-GPU PGS path.
 
void computeVn0Kokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const float *[3], CpMem > velPred, Kokkos::View< const float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, float growthRate, Kokkos::View< float *, CpMem > vn0, Kokkos::View< float *[3], CpMem > vt0, bool dedupTwins=true)
 -— Warm-started projected Gauss-Seidel (PGS) velocity solve -— Nonsmooth contact dynamics (Moreau-Jean; Stewart-Trinkle LCP): per manifold an ACCUMULATED push impulse p >= 0 along the manifold normal, updated by colored GS sweeps and projected to p >= 0 (a contact may push, never pull; over-push is retracted in later sweeps).
 
void updateRestitutionBankKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< const float *[3], CpMem > velPred, Kokkos::View< const float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, float growthRate, float restitutionNormal, float restVelThreshold, Kokkos::View< const float *, CpMem > vn0, Kokkos::View< const float *, CpMem > lambdaAcc, Kokkos::View< const float *, CpMem > restRel, Kokkos::View< float *, CpMem > restBank, Kokkos::View< float *, CpMem > restVPeak, bool dedupTwins=true)
 Event-level (Poisson) restitution bookkeeping, once per substep AFTER all velocity phases (restitutionModel == 1 only).
 
void decayBodyOrphanKokkos (Kokkos::View< float *, CpMem > orphan, Kokkos::View< float *, CpMem > orphanVPeak, int numOwned, float restVelThreshold)
 Orphan-account aging, once per substep over the OWNED bodies: both the balance and the carried event peak decay 1/64 per substep (e-fold ~3 ms at dt = 5e-5 — long enough for the rebound payout, which completes within ~2 ms of turnaround, short enough that a BURIED impactor's slowly re-fed credit cannot keep fluidizing its crater: with the pair-state 1/256 decay the 100k Dosta plateau crept 0.007 deeper), and once the peak ages below the resting threshold the whole account evaporates — stranded credit on a settling body never pops the pile later.
 
void scatterOrphanBanksKokkos (Kokkos::View< const unsigned long long *, CpMem > prevKeys, Kokkos::View< const float *, CpMem > prevRestBank, Kokkos::View< const float *, CpMem > prevRestVPeak, Kokkos::View< const unsigned char *, CpMem > matched, int prevCount, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< float *, CpMem > orphan, Kokkos::View< float *, CpMem > orphanVPeak, Kokkos::View< const int *, CpMem > gidSorted={}, Kokkos::View< const int *, CpMem > slotSorted={}, int ownedLimit=-1)
 Orphan transfer: previous-ledger entries NOT matched by any current manifold (their pair died this substep) credit their remaining owed budget to the endpoint BODIES, mass-weighted — the heavier endpoint keeps the larger share (energy of a later release scales J^2/2m, so the light grain is the dangerous store and the heavy impactor is both the safe one and the event's physical carrier); boundary pairs (wall endpoint) credit everything to the particle.
 
std::tuple< double, float, int > restBankStatsKokkos (Kokkos::View< const float *, CpMem > bank, int n)
 Poisson-restitution diagnostics: (sum, max, count>0) over the committed owed-impulse store (namespace scope: nvcc forbids KOKKOS_LAMBDA in member functions).
 
void computeSideFlagsKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const unsigned char *, CpMem > persistent, Kokkos::View< const unsigned char *, CpMem > grounded, Kokkos::View< const float *[3], CpMem > posPred, Kokkos::View< const float *[3], CpMem > velPred, F3 gHat, float riseThr, Kokkos::View< unsigned char *, CpMem > sideFlag, Kokkos::View< const float *, CpMem > vn0, float approachThr, bool dedupTwins=true)
 Decide each persistent contact's treatment ONCE per substep (before any impulse is applied): 0 = symmetric momentum-conserving PGS, 1 = one-sided with B as the held ground side, 2 = A held.
 
void warmStartApplyKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< float *[3], CpMem > velPred, Kokkos::View< float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const float *, CpMem > warmP, Kokkos::View< float *[3], CpMem > warmT, bool dedupTwins=true)
 Apply the warm-start impulses up front (order-independent: fixed impulses, atomic adds).
 
PGSManifoldSweep makePGSManifoldSweep (Kokkos::View< const ManifoldC *, CpMem > manifolds, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< float *[3], CpMem > velPred, Kokkos::View< float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, float growthRate, float restitutionNormal, float restVelThreshold, Kokkos::View< float, CpMem > maxApproach, Kokkos::View< float, CpMem > maxApproachQS, Kokkos::View< float *, CpMem > lambdaAcc, Kokkos::View< const float *, CpMem > vn0, Kokkos::View< const unsigned char *, CpMem > sideFlag, Kokkos::View< float *[3], CpMem > lambdaT, float frictionDynamic, Kokkos::View< const float *[3], CpMem > vt0, float restitutionTangent, Kokkos::View< const float *, CpMem > posImpulse, Kokkos::View< float *, CpMem > restBank, Kokkos::View< float *, CpMem > restRel, Kokkos::View< const float *, CpMem > restVPeak, Kokkos::View< float *, CpMem > restOrphan, Kokkos::View< const float *, CpMem > restOrphanVPeak, SlotOverride ov={})
 Build the shared per-manifold sweep functor (the colored launch loop, the fused kernels and the fused multilevel iteration loop all run this one body).
 
bool solveVelocityPGSKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > mColor, int numColors, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< float *[3], CpMem > velPred, Kokkos::View< float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, float growthRate, float restitutionNormal, float restVelThreshold, Kokkos::View< float, CpMem > maxApproach, Kokkos::View< float *, CpMem > lambdaAcc, Kokkos::View< const float *, CpMem > vn0, Kokkos::View< const unsigned char *, CpMem > sideFlag, Kokkos::View< float *[3], CpMem > lambdaT, float frictionDynamic, Kokkos::View< const float *[3], CpMem > vt0={}, float restitutionTangent=0.0f, Kokkos::View< const float *, CpMem > posImpulse={}, Kokkos::View< float, CpMem > maxApproachQS={}, Kokkos::View< float *, CpMem > restBank={}, Kokkos::View< float *, CpMem > restRel={}, Kokkos::View< const float *, CpMem > restVPeak={}, Kokkos::View< float *, CpMem > restOrphan={}, Kokkos::View< const float *, CpMem > restOrphanVPeak={}, Kokkos::View< const int *, CpMem > colorPerm={}, const std::vector< int > *colorOffs=nullptr, const FusedSweepCtx *fused=nullptr, const FusedLoopSpec *loop=nullptr, SlotOverride ov={})
 Returns true when the sweep (or, with loop, the whole iteration loop) was submitted; false ONLY in loop mode when the fused path is unavailable — the caller then runs its own host-side iteration loop (which calls back in per-iteration mode).
 
void buildLevelColorBucketsKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const int *, CpMem > mColor, Kokkos::View< const int *, CpMem > heights, Kokkos::View< int *, CpMem > keys, Kokkos::View< int *, CpMem > perm, std::vector< std::pair< int, int > > &buckets)
 Bucket the active coloured manifolds by (support level, colour) for the level-ordered ("multilevel") stabilization sweeps.
 
void solveVelocityPGSBucketsKokkos (const PGSManifoldSweep &f, Kokkos::View< const int *, CpMem > perm, const std::vector< std::pair< int, int > > &buckets, bool topDown)
 Level-ordered symmetric sweep: launch one PGS kernel per (level, colour) bucket, ascending (bottom-up: a chain's load drains towards the floor in ~one pass) or descending (top-down: the return pass carries rebound waves back up).
 
int xPopcount (unsigned long long m)
 Rank-level X (docs/contact_solve_framework.md §1.4): the holder of a body whose velocity activity mask has several bits (ranks) in sync interval t – the colour t mod C if it is in the mask, else the (t mod popcount)-th set bit (lowest = 0th).
 
int xHolder (unsigned long long mask, long long t, int numColors)
 
void computeXGateKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int nm, Kokkos::View< const int *, CpMem > realIdx, Kokkos::View< const unsigned long long *, CpMem > velMask, Kokkos::View< unsigned char *, CpMem > gate, int color, int numColors, long long t)
 The per-manifold fire gate of sync interval t: 1 iff this rank (colour color) holds every endpoint whose mask names two or more ranks.
 
long long countUnfiredKokkos (Kokkos::View< const unsigned char *, CpMem > gate, int nm)
 Diagnostics (iterCounters): the number of gated-off manifolds of one sweep.
 
void solveVelocityColoredGSKokkos (Kokkos::View< const ManifoldC *, CpMem > manifolds, int numManifolds, Kokkos::View< const int *, CpMem > mColor, int numColors, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[4], CpMem > quat, Kokkos::View< float *[3], CpMem > velPred, Kokkos::View< float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, float growthRate, float restitutionNormal, float restVelThreshold, Kokkos::View< float, CpMem > maxApproach, Kokkos::View< const unsigned char *, CpMem > persistent={}, Kokkos::View< const float *[3], CpMem > posPred={}, F3 gHat={}, Kokkos::View< const unsigned char *, CpMem > grounded={}, SlotOverride ov={}, XGateSpec xg={})
 Colored Gauss–Seidel normal-restitution solve: sweep the numColors colour classes in order, applying each manifold's impulse directly to velPred/angVelPred (in place).
 
void growContactBuffers (Particles &P, long nBodies, long floorWant=0)
 Grow every maxContacts-sized collision buffer to match the CURRENT particle capacity.
 
int narrowPhaseGrow (Particles &P, int np, float margin)
 Narrow phase with an automatically-grown contact buffer.
 
void demStep (Particles &P)
 One full XPBD DEM substep over the particle SoA (mirrors simulation.cpp Simulation::step()).
 
float computeOverlapsKokkos (Particles &P)
 Max pair interpenetration on the committed state (faithful to CUDA Simulation::compute_overlaps): copy committed pos/quat into the predicted buffers, regenerate the periodic ghosts from that state, then run the same broad/narrow phase as demStep and return the recorded max overlap.
 

Variables

constexpr int kLevelInf = 1 << 28
 Height-from-floor BFS levels for the level-ordered ("multilevel") stabilization pass: 0 at a wall/plane contact, else 1 + min over supporting contacts, kLevelInf with no contact path to the floor.
 
constexpr std::uint64_t kNonOwnedPairBit = 1ull << 63
 Distributed step: bit 63 of a manifold-reduction key marks a contact this rank sees but does not own (docs/mpi_momentum_conservation.md §2.2), so the sort puts the owned manifolds first.
 
constexpr int kColorPalette = 64
 Colouring palette (docs/contact_solve_framework.md §4.2 item 1): the greedy takes the lowest colour free at both endpoints among bits 0..63 of the uint64 mask and never forces one.
 
constexpr int kColorUncolourable = -3
 
constexpr int kMaxMaterials = 8
 Pair-material lookup: flat [K*K*2] table, entry ((a*K + b)*2) = restitution, +1 = friction.
 
constexpr double kPi = 3.14159265358979323846
 
constexpr int kHubEdgeBudget = 32
 Hub edge budget (§4.4): a colouring vertex with more active edges is split into ceil(d / kHubEdgeBudget) copies, so no copy carries more than 32 and the 64-colour greedy provably succeeds (§1.6).
 
constexpr float kSplitOmegaVelocity = 1.0f
 Split relaxation (§4.5, §13.1): the PGS normal step on an edge touching a mass-split slot is scaled by kSplitOmegaVelocity before the clamp (1: no relaxation; the pre-designed lever of R-F2, legitimate there because the PGS normal multiplier is accumulated and its clamp can retract an overshoot).
 
constexpr int kMlMaxLevels = 10
 6-bit colour slots per level in the packed word; slot value 63 = not crossing / not eligible at that level (skipped by the coarse sweeps).
 
constexpr int kMlSlotSkip = 63
 
constexpr long long kMlPendingSkipBit = 1ll << 62
 Transient mark of the coarse colouring (docs/contact_solve_framework.md §4.4, "the multilevel coarse colouring"): an edge that won its groups but found no free colour keeps slot 63 and is skipped at this level.
 

Typedef Documentation

◆ BpExec

using peclet::dem::BpExec = typedef Kokkos::DefaultExecutionSpace

Definition at line 21 of file broadphase_arborx.hpp.

◆ BpMem

using peclet::dem::BpMem = typedef BpExec::memory_space

Definition at line 22 of file broadphase_arborx.hpp.

◆ CpExec

using peclet::dem::CpExec = typedef Kokkos::DefaultExecutionSpace

Definition at line 29 of file contact_preprocessing.hpp.

◆ CpMem

using peclet::dem::CpMem = typedef CpExec::memory_space

Definition at line 30 of file contact_preprocessing.hpp.

◆ V3

using peclet::dem::V3 = typedef Kokkos::View<float* [3], CpMem>

Definition at line 24 of file integration.hpp.

◆ V4

using peclet::dem::V4 = typedef Kokkos::View<float* [4], CpMem>

Definition at line 25 of file integration.hpp.

◆ Vf

using peclet::dem::Vf = typedef Kokkos::View<float*, CpMem>

Definition at line 26 of file integration.hpp.

◆ Vi

using peclet::dem::Vi = typedef Kokkos::View<int*, CpMem>

Definition at line 27 of file integration.hpp.

◆ PairTableView

using peclet::dem::PairTableView = typedef Kokkos::View<const float*, CpMem>

Definition at line 100 of file narrowphase.hpp.

◆ MatIdView

using peclet::dem::MatIdView = typedef Kokkos::View<const unsigned char*, CpMem>

Definition at line 101 of file narrowphase.hpp.

◆ PosView

using peclet::dem::PosView = typedef Kokkos::View<const float* [3], CpMem>

Definition at line 104 of file narrowphase.hpp.

◆ QuatView

using peclet::dem::QuatView = typedef Kokkos::View<const float* [4], CpMem>

Definition at line 105 of file narrowphase.hpp.

◆ ScalarF

using peclet::dem::ScalarF = typedef Kokkos::View<const float*, CpMem>

Definition at line 106 of file narrowphase.hpp.

◆ ScalarI

using peclet::dem::ScalarI = typedef Kokkos::View<const int*, CpMem>

Definition at line 107 of file narrowphase.hpp.

◆ ShellView

using peclet::dem::ShellView = typedef Kokkos::View<const float* [3], CpMem>

Definition at line 108 of file narrowphase.hpp.

◆ GridView

using peclet::dem::GridView = typedef Kokkos::View<const float*, CpMem>

Definition at line 109 of file narrowphase.hpp.

◆ FrManifoldCounts

using peclet::dem::FrManifoldCounts = typedef Kokkos::View<float* [2], CpMem>

Definition at line 26 of file solver_friction.hpp.

Enumeration Type Documentation

◆ ShapeKind

Enumerator
SHAPE_GRID_SDF 
SPHERE 
HOLLOW_CYLINDER 
BOX 
SHAPE_SCENE 

Definition at line 90 of file dem_portable.hpp.

Function Documentation

◆ findCollisionsArborX()

template<class PosV , class RadV , class PairsV , class CountV >
int peclet::dem::findCollisionsArborX ( PosV  pos,
RadV  rad,
int  numParticles,
int  numReal,
float  margin,
PairsV  outPairs,
CountV  outCount,
float  boxCap = 0.0f 
)
inline

Emit candidate collision pairs (i<j) for real particles into outPairs/outCount.

pos[numParticles][3] positions of all particles (real first, then ghosts) rad[numParticles] effective radius per particle (scale * global_scale) numReal only particles [0,numReal) issue queries (own real particles) margin safety margin added to every box half-width (cuBQL uses 0.1*global_scale) outPairs[maxPairs][2] preallocated output; outCount is the (clamped) number found Returns the number of pairs found (may exceed maxPairs if the buffer is too small).

Definition at line 36 of file broadphase_arborx.hpp.

References PECLET_DEM_SANE_BOX.

Referenced by findCollisionsGrow().

◆ normalOfA()

bool peclet::dem::normalOfA ( const ContactC &  c)

Which body's surface a contact's normal belongs to.

A forward contact (A's shell point against B's SDF) carries B's outward normal; a two-way reverse contact (B's shell point against A's SDF, set_shell_detection('two_way')) carries the negated outward normal of A and marks normal.w = 1. The position phase delta-rotates a stored normal with its own body's predicted rotation.

Definition at line 54 of file contact_preprocessing.hpp.

References peclet::dem::ContactC::normal, and peclet::dem::F4::w.

Referenced by peclet::dem::PositionContactSweep::solveContact().

◆ wallContactId()

constexpr int peclet::dem::wallContactId ( int  wallIndex)
constexpr

Wall identity of a boundary contact (docs/contact_solve_framework.md §12 S6): a contact with a wall carries bodyB = -1 - wallIndex, where wallIndex runs over the analytic planes first ([0, numPlanes)) and then the SDF walls (numPlanes + w).

Every "is a wall" test is bodyB < 0; the index only separates the position units of one body against different walls. The manifold reduction key (pairKey) ignores it: all walls of a body still form ONE velocity manifold.

Definition at line 63 of file contact_preprocessing.hpp.

Referenced by detectBoundaryKokkos().

◆ wallIndexOf()

constexpr int peclet::dem::wallIndexOf ( int  bodyB)
constexpr

Definition at line 66 of file contact_preprocessing.hpp.

◆ pairKeyFromGids()

unsigned long long peclet::dem::pairKeyFromGids ( unsigned  a,
unsigned  b 
)

The one 64-bit pair identity every gid-keyed ledger uses (the XPBD persistent-contact and warm-start ledgers, the Hertz–Mindlin history): (min(a, b) << 32) | max(a, b), symmetric in (a, b) so a pair is one key whichever body is A.

a/b are whatever identity is stable for the run — the real index single-rank, the global id under MPI.

Definition at line 92 of file contact_preprocessing.hpp.

Referenced by hertzRebuildPairs(), and pairKeyOf().

◆ lowerBoundKey()

template<class KeysView >
int peclet::dem::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).

A hit is i < count && keys(i) == key; the index doubles as the ledger slot of the carried payload.

Definition at line 101 of file contact_preprocessing.hpp.

Referenced by hertzRebuildPairs(), and markPersistentManifoldsKokkos().

◆ pairKeyOf()

unsigned long long peclet::dem::pairKeyOf ( const ManifoldC &  m,
Kokkos::View< const int *, CpMem >  realIdx 
)

Persistent-contact detection for the gravity-gated restitution rule.

Key = pairKeyFromGids over (real body A, real body B); boundary manifolds (bodyB = -1: planes + SDF walls, merged per particle) use 0xFFFFFFFF for B. Flags each manifold whose pair already existed in the PREVIOUS substep (prevKeys sorted, device binary search). Real indices are stable within a single-GPU run, so the key identifies the physical pair across substeps.

Definition at line 118 of file contact_preprocessing.hpp.

References peclet::dem::ManifoldC::bodyA, peclet::dem::ManifoldC::bodyB, and pairKeyFromGids().

Referenced by markPersistentManifoldsKokkos().

◆ markPersistentManifoldsKokkos()

void peclet::dem::markPersistentManifoldsKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const int *, CpMem >  keyIdx,
Kokkos::View< const unsigned long long *, CpMem >  prevKeys,
int  prevCount,
Kokkos::View< unsigned long long *, CpMem >  outKeys,
Kokkos::View< unsigned char *, CpMem >  outFlags 
)
inline

keyIdx maps a body slot to the identity the pair key is built from: the REAL index map on the single-GPU path (stable within a run), or the GLOBAL particle id under MPI (stable across ranks, halo rebuilds and ownership migration — local slots are neither).

Definition at line 128 of file contact_preprocessing.hpp.

References lowerBoundKey(), peclet::dem::ManifoldC::num_points, and pairKeyOf().

Referenced by demSolveContacts().

◆ gatherWarmLambdaKokkos()

void peclet::dem::gatherWarmLambdaKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const int *, CpMem >  keyIdx,
Kokkos::View< const unsigned long long *, CpMem >  prevKeys,
Kokkos::View< const float *, CpMem >  prevLambda,
Kokkos::View< const float *[3], CpMem >  prevLambdaT,
Kokkos::View< const float *, CpMem >  prevPosImpulse,
Kokkos::View< const float *, CpMem >  prevRestBank,
Kokkos::View< const float *, CpMem >  prevRestVPeak,
int  prevCount,
Kokkos::View< unsigned long long *, CpMem >  outKeys,
Kokkos::View< float *, CpMem >  outWarm,
Kokkos::View< float *[3], CpMem >  outWarmT,
Kokkos::View< float *, CpMem >  outPosImpulse,
Kokkos::View< float *, CpMem >  outRestBank,
Kokkos::View< float *, CpMem >  outRestVPeak,
Kokkos::View< unsigned char *, CpMem >  outMatched = {},
bool  dedupTwins = true 
)
inline

Warm-start gather for the PGS velocity solve: per manifold, write its pair key and look up the previous substep's converged push impulse (0 for a new contact).

Periodic-ghost duplicate manifolds (realA > realB twin) get key ~0 and warm 0 – the canonical twin carries the impulse. keyIdx: see markPersistentManifoldsKokkos (realIdx keeps the periodic-dedup role; keyIdx builds the cross-substep pair identity).

Definition at line 158 of file contact_preprocessing.hpp.

Referenced by demSolveContacts().

◆ commitPairKeysLambdaKokkos()

void peclet::dem::commitPairKeysLambdaKokkos ( Kokkos::View< const unsigned long long *, CpMem >  keys,
Kokkos::View< const float *, CpMem >  lambda,
Kokkos::View< const float *[3], CpMem >  lambdaT,
Kokkos::View< const float *, CpMem >  restBank,
Kokkos::View< const float *, CpMem >  restVPeak,
Kokkos::View< unsigned long long *, CpMem >  prevKeys,
Kokkos::View< float *, CpMem >  prevLambda,
Kokkos::View< float *[3], CpMem >  prevLambdaT,
Kokkos::View< float *, CpMem >  prevRestBank,
Kokkos::View< float *, CpMem >  prevRestVPeak,
Kokkos::View< int *, CpMem >  perm,
int  numManifolds,
Kokkos::View< const int *, CpMem >  color = {},
Kokkos::View< int *, CpMem >  prevColor = {} 
)
inline

Save this substep's keys + converged impulses (normal AND tangential) and key-sort them for next substep's gather.

A permutation sort carries both value arrays through one key sort.

Definition at line 205 of file contact_preprocessing.hpp.

Referenced by demSolveContacts().

◆ commitPairKeysKokkos()

void peclet::dem::commitPairKeysKokkos ( Kokkos::View< const unsigned long long *, CpMem >  keys,
Kokkos::View< unsigned long long *, CpMem >  prevKeys,
int  numManifolds 
)
inline

Copy this substep's keys into prevKeys and sort them for next substep's binary search.

Definition at line 253 of file contact_preprocessing.hpp.

◆ updateGroundedLevelsKokkos()

void peclet::dem::updateGroundedLevelsKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const float *[3], CpMem >  posPred,
F3  gHat,
Kokkos::View< unsigned char *, CpMem >  grounded,
int  numReal,
int  sweeps,
int  decay 
)
inline

Guendelman support levels, warm-started: decay every body's level by decay, re-seed 255 at wall/plane contacts, then sweeps monotone propagation passes lower -> upper (255 -> 254 -> ...) through the manifold graph.

Warm start makes a few sweeps per substep track slowly-moving support fronts; the decay retires groundedness ~32 substeps after lift-off. Geometry-only (no persistence / velocity condition): grounded means "has a contact path down to the floor".

Definition at line 271 of file contact_preprocessing.hpp.

References peclet::dem::ManifoldC::bodyA, peclet::dem::ManifoldC::bodyB, dot3(), ldF3(), peclet::dem::ManifoldC::num_points, sub3(), peclet::dem::F3::x, peclet::dem::F3::y, and peclet::dem::F3::z.

Referenced by demSolveContacts().

◆ computeHeightLevelsKokkos()

void peclet::dem::computeHeightLevelsKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const float *[3], CpMem >  posPred,
F3  gHat,
Kokkos::View< int *, CpMem >  heights,
int  numReal 
)
inline

◆ buildColorBucketsKokkos()

void peclet::dem::buildColorBucketsKokkos ( Kokkos::View< const int *, CpMem >  colorOf,
int  n,
int  numColors,
Kokkos::View< int *, CpMem >  perm,
Kokkos::View< int *, CpMem >  cursor,
std::vector< int > &  offs 
)
inline

Dense colour buckets (numColors <= 64): perm[offs[c] .

. offs[c+1]) lists the item indices of colour c. Histogram + host prefix + scatter — two small kernels and one 64-int readback, amortized over every sweep that would otherwise scan ALL n items per colour per iteration (measured 25k multilevel: the per-colour full scans were 40% of GPU time and the step is host-submission-bound). Within a colour the scatter order is arbitrary — colour classes are body-disjoint, so the sweep result is bit-identical. Items with colour < 0 (inactive, dups, mask-saturation leftovers) are excluded, exactly like the scan-mode colour filter.

Definition at line 371 of file contact_preprocessing.hpp.

Referenced by buildCoarseBucketsKokkos(), and demSolveContacts().

◆ colorKey()

long long peclet::dem::colorKey ( int  idx)

splitmix32 finalizer: a well-mixed pseudo-random priority per edge index.

Random priorities make the Jones-Plassmann arbitration finish in O(log n) rounds w.h.p.; RAW indices are adversarial for lattice-ordered dense packs (monotone index chains -> one win per round -> O(chain) rounds).

Definition at line 407 of file contact_preprocessing.hpp.

◆ pairKey()

std::uint64_t peclet::dem::pairKey ( const ContactC &  c)

Canonical pair key: (min<<32)|max, or (idA<<32)|0xFFFFFFFF for a boundary (idB<0) contact.

Definition at line 417 of file contact_preprocessing.hpp.

References peclet::dem::ContactC::bodyA, and peclet::dem::ContactC::bodyB.

Referenced by unitKey().

◆ decodeKey()

void peclet::dem::decodeKey ( std::uint64_t  key,
int &  bodyA,
int &  bodyB 
)

Decode the canonical (bodyA, bodyB) from a pair key (bodyB = -1 for boundary).

Bit 63 (kNonOwnedPairBit) is masked: the identity on every key below 2^63.

Definition at line 472 of file contact_preprocessing.hpp.

◆ transformContact()

ManifoldC peclet::dem::transformContact ( const ContactC &  c)

Per-contact transform to a single-point manifold, aligned to the canonical pair.

Inactive contacts (dist > 0) contribute a zero manifold with num_points = 0. Mirrors ContactToManifold + TransformAndFilter from contact_preprocessing.cu.

Definition at line 481 of file contact_preprocessing.hpp.

References peclet::dem::ContactC::bodyA, peclet::dem::ContactC::bodyB, peclet::dem::ContactC::boundaryFriction, peclet::dem::ContactC::boundaryRestitution, peclet::dem::ContactC::boundaryVel, cross3(), peclet::dem::ContactC::dist, peclet::dem::ContactC::normal, peclet::dem::ContactC::rA, peclet::dem::ContactC::rB, peclet::dem::F4::x, peclet::dem::F4::y, and peclet::dem::F4::z.

◆ partitionContactsKokkos()

template<class Owns >
int peclet::dem::partitionContactsKokkos ( Kokkos::View< ContactC *, CpMem >  contacts,
int  n,
const Owns &  owns 
)
inline

Stable partition of contacts [0, n) by owns(contact): the owned ones first, [0, ncOwned), then the rest, each part in its old relative order (distributed step, docs/mpi_momentum_conservation.md §2.2).

One exclusive scan + one scatter into a scratch copy; with every contact owned (np = 1 on a closed domain) the contacts are unchanged bit for bit. Returns ncOwned.

Definition at line 524 of file contact_preprocessing.hpp.

◆ reduceContactsToManifoldsKokkos()

int peclet::dem::reduceContactsToManifoldsKokkos ( Kokkos::View< const ContactC *, CpMem >  contacts,
int  n,
Kokkos::View< ManifoldC *, CpMem >  outManifolds,
Kokkos::View< int, CpMem >  outCount,
Kokkos::View< int *, CpMem >  contactSlot = {},
int  numOwnedContacts = -1,
int *  numOwnedManifolds = nullptr 
)
inline

Reduce n contacts to manifolds (one per unique canonical pair).

outManifolds must hold at least the number of unique pairs; returns that count (also written to outCount).

Distributed step (docs/mpi_momentum_conservation.md §2.2): with numOwnedContacts >= 0 the contacts at index >= numOwnedContacts (not owned by this rank, after partitionContactsKokkos) get kNonOwnedPairBit in their key, so the sort puts the owned manifolds first, [0, nmOwned) in their old relative order, and the non-owned ones after them; *numOwnedManifolds receives nmOwned. The defaults reproduce the single-rank reduction exactly.

Definition at line 567 of file contact_preprocessing.hpp.

Referenced by demStep().

◆ unitKey()

std::uint64_t peclet::dem::unitKey ( const ContactC &  c)

Position-unit key (docs/contact_solve_framework.md §4.3, §12 S6): the canonical pair key for a body-body contact, and (bodyA << 32) | (unsigned)bodyB for a wall contact, which separates the walls of one body (bodyB = -1 - wallIndex, so the low word is 0xFFFFFFFF - wallIndex >= 2^31, never a body slot).

Wall 0 keeps pairKey's value; pairKey itself is unchanged.

Definition at line 685 of file contact_preprocessing.hpp.

References peclet::dem::ContactC::bodyA, peclet::dem::ContactC::bodyB, and pairKey().

Referenced by buildPositionUnitsKokkos().

◆ buildPositionUnitsKokkos()

int peclet::dem::buildPositionUnitsKokkos ( Kokkos::View< const ContactC *, CpMem >  contacts,
int  n,
Kokkos::View< int *, CpMem >  unitStart,
Kokkos::View< int *, CpMem >  unitContacts,
int  numManifolds = -1 
)
inline

Build the position units of contacts [0, n) (docs/contact_solve_framework.md §4.3, §12 S6): a unit is the set of contacts of one body pair, or of one body and one wall.

Two key sorts: by unitKey to group (leader = the segment's smallest contact index), then by (leader, contact index) for the canonical CSR. Returns the unit count; unitStart gets numUnits + 1 entries. numManifolds, when given, is the manifold count of the same contacts: the manifold key (pairKey, all walls of a body merged; the solved range [0, n) is all owned, so the ownership bit is constant on it) is never finer than the unit key, so numManifolds == n already proves every unit is one contact and the identity CSR is written without the key sort (a serial host sort on the host backends) – the same result the sort would find.

Definition at line 716 of file contact_preprocessing.hpp.

References unitKey().

Referenced by demSolveContacts().

◆ frictionBoundFromLambdaKokkos()

void peclet::dem::frictionBoundFromLambdaKokkos ( Kokkos::View< ContactC *, CpMem >  contacts,
int  numContacts,
Kokkos::View< const int *, CpMem >  contactSlot,
Kokkos::View< const ManifoldC *, CpMem >  manifolds,
Kokkos::View< const float *, CpMem >  lambdaAcc 
)
inline

PGS friction bound: overwrite each contact's friction_lambda_n with its manifold's converged PGS push impulse (lambdaAcc, shared equally over the manifold's contact points).

The legacy accumulateNormalImpulse bound derives from approach velocities, which the PGS warm start has already cancelled – without this the Coulomb bound is ~0 and friction is inert.

Definition at line 810 of file contact_preprocessing.hpp.

References peclet::dem::ManifoldC::num_points.

◆ commitPosImpulseKokkos()

void peclet::dem::commitPosImpulseKokkos ( Kokkos::View< const float *, CpMem >  posLambdaContact,
int  numContacts,
Kokkos::View< const int *, CpMem >  contactSlot,
Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const unsigned long long *, CpMem >  keys,
Kokkos::View< const unsigned long long *, CpMem >  prevKeysSorted,
int  prevCount,
float  dt,
Kokkos::View< float *, CpMem >  scratchManifold,
Kokkos::View< float *, CpMem >  prevPosImpulse 
)
inline

After the position solve: convert the per-contact positional lambdas into an impulse- equivalent per manifold (lambda_pos / dt has force units; x dt back to impulse => just lambda_pos * m_eff... the positional lambda already carries 1/w mass weighting, so the impulse equivalent over the substep is lambda_pos / dt * dt = lambda_pos / (w*...) – we store lambda_pos/dt * dt = lambda_pos scaled by 1/dt to velocity-impulse units) and write it into the sorted prev-store so next substep's warm gather can top up the Coulomb bound.

Definition at line 832 of file contact_preprocessing.hpp.

Referenced by demSolveContacts().

◆ add3()

◆ sub3()

◆ scale3()

◆ dot3()

◆ len3()

float peclet::dem::len3 ( F3  v)

Definition at line 41 of file dem_portable.hpp.

References dot3().

Referenced by hertzShapePairForcesKokkos().

◆ cross3v()

◆ cross3()

F4 peclet::dem::cross3 ( F4  a,
F4  b 
)

Definition at line 47 of file dem_portable.hpp.

References peclet::dem::F4::x, peclet::dem::F4::y, and peclet::dem::F4::z.

Referenced by transformContact().

◆ ldF3()

◆ ldF4()

template<class V >
F4 peclet::dem::ldF4 ( const V &  v,
int  i 
)

◆ rotateVector()

◆ invRotateVector()

◆ quatInverse()

F4 peclet::dem::quatInverse ( F4  q)

◆ quatMult()

F4 peclet::dem::quatMult ( F4  a,
F4  b 
)

◆ toCoreVec()

peclet::core::Vec3< float > peclet::dem::toCoreVec ( F3  p)

◆ sdfSphere()

float peclet::dem::sdfSphere ( F3  p,
F4  params 
)

Definition at line 107 of file dem_portable.hpp.

References toCoreVec(), and peclet::dem::F4::x.

Referenced by sdfEval().

◆ sdfHollowCylinder()

float peclet::dem::sdfHollowCylinder ( F3  p,
F4  params 
)

Definition at line 114 of file dem_portable.hpp.

References toCoreVec(), peclet::dem::F4::x, peclet::dem::F4::y, and peclet::dem::F4::z.

Referenced by sdfEval().

◆ sdfBox()

float peclet::dem::sdfBox ( F3  p,
F4  params 
)

Definition at line 119 of file dem_portable.hpp.

References toCoreVec(), peclet::dem::F4::x, peclet::dem::F4::y, and peclet::dem::F4::z.

Referenced by sdfEval().

◆ sdfEval()

float peclet::dem::sdfEval ( F3  p,
int  type,
F4  params 
)

Definition at line 123 of file dem_portable.hpp.

References BOX, HOLLOW_CYLINDER, sdfBox(), sdfHollowCylinder(), sdfSphere(), and SPHERE.

Referenced by sdfEvalShape().

◆ predictVelocityKokkos()

void peclet::dem::predictVelocityKokkos ( int  n,
V3  pos,
Vf  invMass,
V3  vel,
V4  quat,
V3  angVel,
V3  invInertia,
V3  posPred,
V4  quatPred,
V3  velPred,
V3  angVelPred,
V3  deltaPos,
V4  deltaQuat,
V3  deltaVel,
V3  deltaAngVel,
Vi  constraintCounts,
F3  gravity,
float  dt,
V3  extForce,
V3  extTorque 
)
inline

Predict velocity (gravity + gyroscopic precession), speculative position, and clear all deltas.

Definition at line 44 of file integration.hpp.

References add3(), cross3v(), invRotateVector(), ldF3(), ldF4(), rotateVector(), scale3(), peclet::dem::detail::st3(), peclet::dem::detail::st4(), peclet::dem::F3::x, peclet::dem::F3::y, and peclet::dem::F3::z.

Referenced by demStep().

◆ applyVelocityDeltasKokkos()

void peclet::dem::applyVelocityDeltasKokkos ( int  n,
V3  velPred,
V3  angVelPred,
V3  deltaVel,
V3  deltaAngVel 
)
inline

Add accumulated velocity/angular deltas onto the predicted velocity, then clear the delta buffers.

Definition at line 104 of file integration.hpp.

References add3(), ldF3(), and peclet::dem::detail::st3().

Referenced by demSolveContacts().

◆ applyVelocityAndPredictPositionKokkos()

void peclet::dem::applyVelocityAndPredictPositionKokkos ( int  n,
V3  pos,
Vf  invMass,
V3  vel,
V4  quat,
V3  velPred,
V3  angVelPred,
V3  posPred,
V4  quatPred,
V3  angVel,
float  dt 
)
inline

Re-integration: persist solved velocity, trapezoidal position predict, quaternion integrate.

Definition at line 119 of file integration.hpp.

References add3(), ldF3(), ldF4(), scale3(), peclet::dem::detail::st3(), peclet::dem::detail::st4(), peclet::dem::F4::w, peclet::dem::F3::x, peclet::dem::F4::x, peclet::dem::F3::y, peclet::dem::F4::y, peclet::dem::F3::z, and peclet::dem::F4::z.

Referenced by demSolveContacts().

◆ computeContactCountsKokkos()

void peclet::dem::computeContactCountsKokkos ( Kokkos::View< const ContactC *, CpMem >  contacts,
int  numContacts,
Vi  constraintCounts 
)
inline

Per-contact constraint-count pre-pass (active contacts, dist <= 0).

Definition at line 158 of file integration.hpp.

References peclet::dem::ContactC::bodyA, peclet::dem::ContactC::bodyB, and peclet::dem::ContactC::dist.

◆ applyUpdatesKokkos()

void peclet::dem::applyUpdatesKokkos ( int  n,
V3  posPred,
V3  velPred,
V3  deltaPos,
V3  deltaVel,
Vi  constraintCounts,
bool  averaged = true 
)
inline

Jacobi count-averaged apply of position/velocity deltas, then clear deltas + counts.

averaged = false (the mass-split 'jacobi' diagnostic, docs/contact_solve_framework.md §3.1): the deltas are already true-mass corrections against copies of mass m / count, so the apply is a plain add (factor 1).

Definition at line 178 of file integration.hpp.

References add3(), ldF3(), scale3(), and peclet::dem::detail::st3().

Referenced by demSolveContacts().

◆ finalCommitKokkos()

void peclet::dem::finalCommitKokkos ( int  n,
V3  pos,
Vf  invMass,
V3  posPred,
V4  quat,
V4  quatPred,
Domain  dom 
)
inline

◆ applyThermostatKokkos()

void peclet::dem::applyThermostatKokkos ( int  numReal,
V3  vel,
Vf  invMass,
V3  angVel,
V3  invInertia,
V4  quat,
double  kB,
double  tau,
double  Ttarget,
float  dt 
)
inline

◆ updateGrowthScalesKokkos()

void peclet::dem::updateGrowthScalesKokkos ( int  n,
Vf  scale,
Vf  targetScale,
float  factor 
)
inline

Growth mode: scale = target * factor (when active).

Definition at line 285 of file integration.hpp.

Referenced by demStep(), and peclet::dem::Simulation::setGrowthParams().

◆ writeLammpsDump()

void peclet::dem::writeLammpsDump ( const std::string &  filename,
int  step,
const std::vector< float > &  pos,
const std::vector< float > &  vel,
const std::vector< float > &  quat,
const std::vector< float > &  radii,
const float *  boxMin,
const float *  boxMax,
bool  pbcEnabled 
)
inline

Definition at line 23 of file io.hpp.

Referenced by peclet::dem::Simulation::exportLammps(), and NB_MODULE().

◆ writeSdfVti()

void peclet::dem::writeSdfVti ( const std::string &  filename,
const std::vector< float > &  grid,
int  rx,
int  ry,
int  rz,
const float *  minB,
const float *  maxB 
)
inline

Definition at line 86 of file io.hpp.

Referenced by peclet::dem::Simulation::exportSdf().

◆ loadF3()

F3 peclet::dem::loadF3 ( PosView  v,
int  i 
)

◆ loadF4()

F4 peclet::dem::loadF4 ( QuatView  v,
int  i 
)

Definition at line 114 of file narrowphase.hpp.

Referenced by detectBoundaryKokkos(), and hertzShapePairForcesKokkos().

◆ sampleGridSdf()

float peclet::dem::sampleGridSdf ( F3  p,
const ShapeDesc &  d,
GridView  grid 
)

Trilinearly sample an imported grid SDF at canonical point p.

The query is clamped into the sample lattice and the Euclidean distance from p to that clamped point is ADDED back (the kObject policy), so a probe outside the stored box gets a monotonically growing (positive) distance instead of a flat clamped value — the standard "clamp + residual" extension that keeps the far field well-signed and the central-difference normal sane near the grid boundary. Interior samples are exact trilinear. Requires nx,ny,nz >= 2 (the shape builder guarantees it).

RELOCATED (Layer 0 rung 3): the body is now peclet::core::geom::sampleGrid, which carries this and the wall variant below as ONE routine parameterised by GridExtension — the two used to be near-duplicate functions differing only in the residual's sign, which is exactly the kind of silent divergence that cost 71k grains once (see sampleWallSdf).

Definition at line 129 of file narrowphase.hpp.

References peclet::dem::ShapeDesc::grid, and toCoreVec().

Referenced by sdfEvalShape().

◆ sampleWallSdf()

float peclet::dem::sampleWallSdf ( F3  p,
const WallSdf &  w,
GridView  grid 
)

Trilinearly sample a static world-space wall SDF at world point p.

Positive in the void, negative in the wall. The off-grid extension SUBTRACTS the clamp residual (the kContainer policy) — the OPPOSITE of sampleGridSdf's object convention — because a wall SDF is a CONTAINER: the void is bounded and everything beyond the stored box is wall-side, so a probe outside the box must read an ever more negative (deeper-in-the-wall) distance. Adding the residual here (the old behaviour) made a grain pushed past the box boundary — e.g. squeezed through the floor plane, whose zero level sits exactly on the grid's lower face — read "clear" and free-fall out of the simulation forever: a 180k glass-bead pile lost 71k grains through the distributor during settling. That is why the sign is now a NAMED POLICY on one shared routine (Layer 0 rung 3) instead of a one-character difference between two copied functions.

Definition at line 143 of file narrowphase.hpp.

References peclet::dem::WallSdf::grid, peclet::dem::WallSdf::nodeCount, peclet::dem::WallSdf::nodes, peclet::dem::WallSdf::shapeRoot, peclet::dem::WallSdf::sign, and toCoreVec().

Referenced by hertzBuildWallCandidatesKokkos(), and peclet::dem::Simulation::wallSdfAt().

◆ sdfEvalShape()

float peclet::dem::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 SDF.

grid may be an empty View when no grid shape is present.

Definition at line 155 of file narrowphase.hpp.

References peclet::dem::ShapeDesc::nodeCount, peclet::dem::ShapeDesc::nodes, peclet::dem::ShapeDesc::params, sampleGridSdf(), sdfEval(), SHAPE_GRID_SDF, SHAPE_SCENE, peclet::dem::ShapeDesc::shapeRoot, toCoreVec(), and peclet::dem::ShapeDesc::type.

Referenced by hertzShapePairForcesKokkos(), and sdfGradShape().

◆ sdfGradShape()

F3 peclet::dem::sdfGradShape ( F3  p,
const ShapeDesc &  d,
GridView  grid 
)

Outward gradient of a shape's SDF at canonical point p, NOT normalised (callers normalise, matching the previous finite-difference code).

EXACT closed form for the analytic kinds (Layer 1). The old path spent SIX extra sdfEvalShape calls per shell point on a central difference with a fixed eps = 1e-4 in canonical units, which is both slower and less accurate: 1e-4 is a real length in canonical space, so a shape feature or a per-particle scale far from 1 degrades the normal. A grid SDF has no closed form and keeps the central difference.

Definition at line 175 of file narrowphase.hpp.

References BOX, HOLLOW_CYLINDER, peclet::dem::ShapeDesc::nodeCount, peclet::dem::ShapeDesc::nodes, peclet::dem::ShapeDesc::params, sdfEvalShape(), SHAPE_SCENE, peclet::dem::ShapeDesc::shapeRoot, SPHERE, toCoreVec(), peclet::dem::ShapeDesc::type, peclet::dem::F3::x, peclet::dem::F4::x, peclet::dem::F3::y, peclet::dem::F4::y, peclet::dem::F3::z, and peclet::dem::F4::z.

Referenced by hertzShapePairForcesKokkos().

◆ detectContactsKokkos()

void peclet::dem::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{},
bool  twoWay = false 
)
inline

Pair point-shell vs SDF contacts.

pairs[numPairs][2] are (idA,idB) from the broad-phase; emits ContactC into outContacts guarded by atomic outCount (clamped to outContacts.extent(0)).

One-way (the default, twoWay == false): A's probes – its shell points, or its centre and radius for a sphere – are tested against B's SDF, and nothing else. A thin rim, wall or edge of B pressed into a face of A can then sit between A's shell points unseen (docs/contact_physics_followups.md F2: 0.0235 penetration on tubes while 0.001 was reported).

Two-way (twoWay == true, Simulation::setShellDetection("two_way"); the XPBD narrow phase only – the Hertz engine has its own pair law and would double-count the stiffness):

  • shell-shell pairs ALSO test B's shell points against A's SDF;
  • a sphere-shell pair uses ONLY the exact sphere probe, whichever index the sphere has: with A the shell and B the sphere, B's centre probes A's SDF instead of A's shell probing B's sphere (a sphere can press between shell points; its centre distance cannot miss);
  • sphere-sphere pairs, and a sphere A against a shell B, are unchanged. A reverse contact is emitted in the pair's CANONICAL orientation (bodyA = idA, bodyB = idB, normal = the outward normal of B, rA / rB the surface points on A / B with pA = pB + normal * dist), exactly the form of a forward contact, so the manifold reduction, the position units, the warm-start ledger and the MPI contact ownership all treat it as one more point of the same pair. The two directions are not merged: each is a separate constraint row of the pair (docs/solver_details.md, "Two-way shell detection", for the measurement).

Definition at line 235 of file narrowphase.hpp.

Referenced by narrowPhaseGrow().

◆ detectWallSdfKokkos()

void peclet::dem::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{},
int  wallIdBase = 0 
)
inline

Per-real-particle contacts against a static world-space wall SDF set (a drum barrel, hopper, vibrating tray, ...).

Mirrors detectBoundaryKokkos but the boundary is the wall's zero level set: each surface point (or the sphere centre) is tested against sampleWallSdf, the outward gradient is the contact normal, and every emitted contact carries the wall's rigid-body surface velocity at the contact point plus the wall's binary (particle–wall) restitution/friction — so the moving-wall terms flow through the manifold velocity solve and the per-contact friction sweep. bodyB = wallContactId(wallIdBase + wallIndex), wallIdBase = numPlanes (a boundary, like a plane); the wall surface point is stored in rB for the position solve's plane-linearised non-penetration constraint.

Definition at line 397 of file narrowphase.hpp.

Referenced by narrowPhaseGrow().

◆ detectBoundaryKokkos()

void peclet::dem::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 
)
inline

Per-real-particle contacts against explicit planes (point-shell shapes test each surface point; analytic spheres use centre-minus-radius).

bodyB = wallContactId(planeIndex); plane anchor in rB.

Definition at line 505 of file narrowphase.hpp.

References add3(), peclet::dem::ContactC::bodyA, dot3(), loadF3(), loadF4(), peclet::dem::PlaneP::normal, peclet::dem::ShapeDesc::numPoints, peclet::dem::ShapeDesc::params, peclet::dem::PlaneP::point, rotateVector(), scale3(), peclet::dem::ShapeDesc::shellOffset, sub3(), wallContactId(), peclet::dem::F3::x, peclet::dem::F4::x, peclet::dem::F3::y, and peclet::dem::F3::z.

Referenced by narrowPhaseGrow().

◆ generateSdfKokkos()

std::vector< float > peclet::dem::generateSdfKokkos ( int  rx,
int  ry,
int  rz,
F3  dmin,
F3  dmax,
int  numReal,
PosView  pos,
QuatView  quat,
ScalarF  scale,
ScalarI  shapeId,
Kokkos::View< const ShapeDesc *, CpMem >  shapes,
bool  px,
bool  py,
bool  pz,
GridView  sdfGrid = GridView{},
float  globalScale = 1.0f 
)
inline

globalScale multiplies every per-particle scale, exactly as the narrow-phase does (narrowphase.hpp: effScale = scale(i) * globalScale).

It defaults to 1 so existing callers are unchanged; passing the simulation's real value is what makes the exported field agree with the simulated geometry.

Definition at line 29 of file output_sdf.hpp.

Referenced by peclet::dem::Simulation::getSdfGrid().

◆ calculateGhostCapacity()

int peclet::dem::calculateGhostCapacity ( int  nReal,
Domain  dom,
float  skin 
)
inline

Padded particle-array capacity that leaves room for the periodic ghosts generateGhostsKokkos will emit: the real count plus an estimate of the boundary-shell ghost population, gated on which axes are actually periodic.

Faithful port of the CUDA Simulation::calculate_capacity (small/dense box -> full 26-neighbour headroom; large box -> a few times the boundary-shell volume fraction), with the periodicity gating added so a closed or partly-periodic box is not over-allocated. skin is the ghost band width at each periodic face (1.0*globalScale in the solver).

Definition at line 26 of file periodicity.hpp.

References peclet::dem::Domain::periodic_x, peclet::dem::Domain::periodic_y, peclet::dem::Domain::periodic_z, peclet::dem::Domain::size, peclet::dem::F3::x, peclet::dem::F3::y, and peclet::dem::F3::z.

Referenced by computeOverlapsKokkos(), and demStep().

◆ generateGhostsKokkos()

void peclet::dem::generateGhostsKokkos ( int  numReal,
int  capacity,
Domain  dom,
float  skin,
V3  pos,
Vf  invMass,
V3  posPred,
V3  vel,
V3  velPred,
V4  quat,
V4  quatPred,
V3  angVel,
V3  angVelPred,
Vf  scale,
Vi  shapeId,
Vi  realIndices,
Kokkos::View< int, CpMem >  topGhost,
Vi  gid = Vi{},
Kokkos::View< unsigned char *, CpMem >  materialId = {} 
)
inline

Generate periodic ghosts for particles [0,numReal).

topGhost must be pre-seeded to numReal (the first free slot); on return it holds the total particle count. Slots beyond capacity are dropped. skin is the ghost band width at each periodic face.

Definition at line 44 of file periodicity.hpp.

Referenced by computeOverlapsKokkos(), and demStep().

◆ genCylinderShell()

std::vector< F3 > peclet::dem::genCylinderShell ( float  radius,
float  height,
float  thickness,
float  spacing 
)
inline

Definition at line 27 of file shapes_portable.hpp.

References kPi.

Referenced by peclet::dem::ShapeRegistry::initializeShape().

◆ genBoxShell()

std::vector< F3 > peclet::dem::genBoxShell ( float  hx,
float  hy,
float  hz,
float  spacing 
)
inline

Definition at line 86 of file shapes_portable.hpp.

Referenced by peclet::dem::ShapeRegistry::initializeShape().

◆ freezeAsleepKokkos()

void peclet::dem::freezeAsleepKokkos ( int  numReal,
Kokkos::View< const unsigned char *, CpMem >  asleep,
Kokkos::View< const float *[3], CpMem >  pos,
Kokkos::View< const float *[4], CpMem >  quat,
Kokkos::View< float *[3], CpMem >  posPred,
Kokkos::View< float *[4], CpMem >  quatPred,
Kokkos::View< float *[3], CpMem >  velPred,
Kokkos::View< float *[3], CpMem >  angVelPred 
)
inline

After predictVelocity: re-freeze the currently-asleep bodies so gravity/prediction do not move them — velPred = angVelPred = 0, posPred = pos, quatPred = quat.

Definition at line 31 of file sleeping.hpp.

Referenced by demStep().

◆ buildInvMassEffKokkos()

void peclet::dem::buildInvMassEffKokkos ( int  numBodies,
Kokkos::View< const unsigned char *, CpMem >  asleep,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< float *, CpMem >  invMassEff,
float  sleeperFrac 
)
inline

Effective inverse mass for the solve: a sleeping body (real, or a periodic ghost whose real is asleep) is immovable — invMassEff = 0.

Everything else keeps its real invMass. Built over the full body-slot span (owned + ghosts); the driver's swap points every solve kernel at it.

Definition at line 58 of file sleeping.hpp.

Referenced by demStep().

◆ computeManifoldSleepKokkos()

void peclet::dem::computeManifoldSleepKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const unsigned char *, CpMem >  asleep,
Kokkos::View< unsigned char *, CpMem >  manifoldSleep 
)
inline

Per-manifold "both endpoints asleep" flag (a static wall, bodyB < 0, counts as asleep).

Such manifolds are excluded from colouring / sweeps / the multilevel hierarchy but STILL carry their frozen force network through the warm-start ledger (the caller never skips the ledger kernels).

Definition at line 74 of file sleeping.hpp.

References peclet::dem::ManifoldC::bodyA, peclet::dem::ManifoldC::bodyB, and peclet::dem::ManifoldC::num_points.

Referenced by demStep().

◆ computeContactSleepKokkos()

void peclet::dem::computeContactSleepKokkos ( Kokkos::View< const ContactC *, CpMem >  contacts,
int  numContacts,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const unsigned char *, CpMem >  asleep,
Kokkos::View< unsigned char *, CpMem >  contactSleep 
)
inline

Per-contact twin of computeManifoldSleepKokkos for the position colouring.

Definition at line 95 of file sleeping.hpp.

References peclet::dem::ContactC::bodyA, and peclet::dem::ContactC::bodyB.

Referenced by demStep().

◆ wakeDisturbedKokkos()

void peclet::dem::wakeDisturbedKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const float *[3], CpMem >  velPred,
float  wakeSpeed,
Kokkos::View< unsigned char *, CpMem >  asleep,
Kokkos::View< unsigned char *, CpMem >  sleepCounter,
Kokkos::View< unsigned char *, CpMem >  movingWall,
Kokkos::View< int *, CpMem >  curCount,
int  numReal 
)
inline

Wake pass (before the solve): a sleeper is woken when actually disturbed.

Over this substep's manifolds — (a) a manifold pairing it with an AWAKE body whose speed exceeds wakeSpeed wakes it; (c) a contact with a MOVING wall (wallVel != 0) wakes it (and flags it never-sleep this substep). Also tallies each real body's live contact count into curCount for the caller's contact-set-change wake rule (b). velPred is the post-predict / post-freeze velocity (awake bodies carry their real speed, sleepers carry 0).

Definition at line 117 of file sleeping.hpp.

References peclet::dem::ManifoldC::bodyA, peclet::dem::ManifoldC::bodyB, peclet::dem::ManifoldC::num_points, peclet::dem::ManifoldC::wallVel_sum, peclet::dem::F4::x, peclet::dem::F4::y, and peclet::dem::F4::z.

Referenced by demStep().

◆ wakeContactChangeKokkos()

void peclet::dem::wakeContactChangeKokkos ( int  numReal,
Kokkos::View< const int *, CpMem >  curCount,
Kokkos::View< int *, CpMem >  prevCount,
Kokkos::View< const unsigned char *, CpMem >  movingWall,
Kokkos::View< unsigned char *, CpMem >  asleep,
Kokkos::View< unsigned char *, CpMem >  sleepCounter,
bool  wakeOnChange 
)
inline

Contact-set-change wake rule (b): wake any still-asleep body whose live contact count differs from the stored one (a support gained or lost), then refresh the store.

Also clears the counter of a moving-wall body so it can never accumulate toward sleep.

Definition at line 176 of file sleeping.hpp.

Referenced by demStep().

◆ updateSleepKokkos()

void peclet::dem::updateSleepKokkos ( int  numReal,
Kokkos::View< const float *[3], CpMem >  vel,
Kokkos::View< const float *[3], CpMem >  angVel,
Kokkos::View< const float *, CpMem >  rad,
Kokkos::View< const unsigned char *, CpMem >  grounded,
Kokkos::View< const unsigned char *, CpMem >  movingWall,
Kokkos::View< unsigned char *, CpMem >  asleep,
Kokkos::View< unsigned char *, CpMem >  sleepCounter,
float  sleepSpeed,
int  K,
Kokkos::View< float *[3], CpMem >  velOut,
Kokkos::View< float *[3], CpMem >  angVelOut 
)
inline

Sleep detection (after the commit): an AWAKE, grounded body whose linear AND angular motion has stayed below sleepSpeed for K consecutive substeps is put to sleep (velocity + spin zeroed).

Any body at/above the threshold, ungrounded, or on a moving wall resets its counter. vel / angVel are the committed velocities of this substep.

Definition at line 205 of file sleeping.hpp.

Referenced by demStep().

◆ growCopyView()

template<class V >
void peclet::dem::growCopyView ( V &  v,
std::size_t  n,
const char *  label 
)
inline

Grow-only (re)allocation of a copy-machinery view to at least n entries.

Definition at line 47 of file solve_copies.hpp.

Referenced by buildCopyGroupsKokkos(), buildHubCopiesKokkos(), buildSolveViewsKokkos(), buildSolveViewsRankKKokkos(), demSolveContacts(), and demStep().

◆ velocityEdgeEndsKokkos()

void peclet::dem::velocityEdgeEndsKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  nm,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const int *, CpMem >  color,
Kokkos::View< int *, CpMem >  eA,
Kokkos::View< int *, CpMem >  eB 
)
inline

Velocity-phase colouring edges: the vertices realIdx(bodyA / bodyB) of each manifold (-1: a wall end, or both ends of an inactive (-2) manifold).

§4.4 step 1: active = colour != -2.

Definition at line 54 of file solve_copies.hpp.

References peclet::dem::ManifoldC::bodyA, and peclet::dem::ManifoldC::bodyB.

Referenced by demSolveContacts().

◆ positionEdgeEndsKokkos()

void peclet::dem::positionEdgeEndsKokkos ( Kokkos::View< const ContactC *, CpMem >  contacts,
const PosUnits &  units,
int  nu,
Kokkos::View< const int *, CpMem >  color,
Kokkos::View< int *, CpMem >  eA,
Kokkos::View< int *, CpMem >  eB 
)
inline

Position-phase colouring edges: one per unit, the leader contact's raw (bodyA, bodyB); active = the unit is not sleep-masked (colour != -2).

Definition at line 69 of file solve_copies.hpp.

References peclet::dem::ContactC::bodyA, peclet::dem::ContactC::bodyB, and peclet::dem::PosUnits::leader().

Referenced by demSolveContacts().

◆ vertexDegreeKokkos()

void peclet::dem::vertexDegreeKokkos ( Kokkos::View< const int *, CpMem >  eA,
Kokkos::View< const int *, CpMem >  eB,
int  nE,
int  nV,
Kokkos::View< int *, CpMem >  deg 
)
inline

Per-vertex active degree over [0, nV) (§4.4 step 1). Wall edges have one endpoint.

Definition at line 83 of file solve_copies.hpp.

Referenced by demSolveContacts().

◆ buildHubCopiesKokkos()

void peclet::dem::buildHubCopiesKokkos ( Kokkos::View< const int *, CpMem >  eA,
Kokkos::View< const int *, CpMem >  eB,
int  nE,
int  nV,
int  slotBase,
Kokkos::View< const int *, CpMem >  deg,
PhaseCopies &  H 
)
inline

Build one phase's hub copies (§4.4 steps 2, 3 and 5): every vertex with d > kHubEdgeBudget active edges gets s = ceil(d / 32) copies – its own slot plus s - 1 slots appended at [slotBase, slotBase + nCopies).

Its incident edges, listed as (hub, edge index[, end]) and sorted, go round-robin: the j-th to copy j mod s (copy 0 = the base slot); an edge between two hubs is assigned independently at each end. Writes the per-edge overrides slotA / slotB (-1 = none) and copyBase. Deterministic: the order is the sorted key, never thread order.

Definition at line 103 of file solve_copies.hpp.

References peclet::dem::PhaseCopies::copyBase, growCopyView(), peclet::dem::PhaseCopies::hubS, peclet::dem::PhaseCopies::hubVertex, kHubEdgeBudget, peclet::dem::PhaseCopies::maxHubDegree, peclet::dem::PhaseCopies::nCopies, peclet::dem::PhaseCopies::nHubs, peclet::dem::PhaseCopies::slotA, peclet::dem::PhaseCopies::slotB, and peclet::dem::PhaseCopies::slotBase.

Referenced by demSolveContacts().

◆ buildCopyGroupsKokkos()

void peclet::dem::buildCopyGroupsKokkos ( Kokkos::View< const int *, CpMem >  realIdx,
int  span,
int  numReal,
Kokkos::View< const int *, CpMem >  deg,
Kokkos::View< const float *[3], CpMem >  imageShift,
PhaseCopies &  H,
bool  identityBodies = false 
)
inline

Build the fold groups of a phase (§4.4 "local fold", §WO-4 item 2): every body with more than one slot among [0, span) (the phase's vertex slots) and the hub copies.

A slot's body is realIdx(slot) (a copy's is its hub vertex's), so in demStep's position phase a body's periodic images join its group; under MPI and in the velocity phase realIdx is the identity on the vertices and only hubs form groups. Members are sorted (body, slot), so the body's own slot – the smallest – comes first. k = the members with at least one active edge (a copy always has one). imageShift (empty = none) gives an image slot's periodic shift; a copy takes its hub vertex's.

identityBodies: every vertex slot is its own body (a copy's body is its hub vertex), so only hubs form groups. The velocity phase (vertices are realIdx slots; under MPI the §6.2 slot map makes the other slots of a body aliases, which must not join its group) and the distributed position phase (raw slots: every image slot is its own copy, reconciled at rank level, §13.3). On the single-rank velocity phase realIdx is the identity on [0, span), so it is the same grouping.

Definition at line 243 of file solve_copies.hpp.

References peclet::dem::PhaseCopies::copyBase, peclet::dem::PhaseCopies::groupActive, peclet::dem::PhaseCopies::groupK, peclet::dem::PhaseCopies::groupShift, peclet::dem::PhaseCopies::groupSlot, peclet::dem::PhaseCopies::groupStart, growCopyView(), peclet::dem::PhaseCopies::nCopies, peclet::dem::PhaseCopies::nGroups, peclet::dem::PhaseCopies::seedW, peclet::dem::PhaseCopies::seedX, peclet::dem::PhaseCopies::slotBase, and peclet::dem::F3::x.

Referenced by demSolveContacts().

◆ buildSolveViewsKokkos()

void peclet::dem::buildSolveViewsKokkos ( Particles &  P,
const PhaseCopies &  H,
int  nSlots,
Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< const float *[3], CpMem >  invInertia 
)
inline

Solve views of a phase (§4.5): invMassSolve / invInertiaSolve = the phase's masses on every slot in [0, nSlots), times k on every member of a group, and splitSlot = (k > 1).

Built only while the phase has groups.

Definition at line 336 of file solve_copies.hpp.

References peclet::dem::PhaseCopies::groupK, peclet::dem::PhaseCopies::groupSlot, peclet::dem::PhaseCopies::groupStart, growCopyView(), peclet::dem::Particles::invInertiaSolve, peclet::dem::Particles::invMassSolve, peclet::dem::PhaseCopies::nGroups, and peclet::dem::Particles::splitSlot.

Referenced by demSolveContacts().

◆ seedCopySlotStateKokkos()

void peclet::dem::seedCopySlotStateKokkos ( Particles &  P,
const PhaseCopies &  H 
)
inline

Seed the copy slots' read-only state from their hub vertex (§4.4 step 6): orientation, local inverse inertia and grounded level (velocity), predicted orientation (position).

Definition at line 381 of file solve_copies.hpp.

References peclet::dem::PhaseCopies::copyBase, peclet::dem::Particles::groundedLevel, peclet::dem::Particles::invInertia, peclet::dem::PhaseCopies::nCopies, peclet::dem::Particles::quat, peclet::dem::Particles::quatPred, and peclet::dem::PhaseCopies::slotBase.

Referenced by demSolveContacts().

◆ markCopySeedsKokkos()

void peclet::dem::markCopySeedsKokkos ( const PhaseCopies &  H,
Kokkos::View< float *[3], CpMem >  x,
Kokkos::View< float *[3], CpMem >  w,
Kokkos::View< float *, CpMem >  orphan,
Kokkos::View< float *, CpMem >  orphanPeak,
bool  shareFromBase = false 
)
inline

Mark the phase seed sigma of every group and re-seed its members from the base (§4.4 step 6, and after every rank sync): x(q) = sigma + shift(q); w likewise (velocity, no shift).

With orphan (Poisson, velocity) every member's account becomes the share B / k of the base's balance B, and the peak the base's. shareFromBase (rank-level M, §13.3): the base already holds the share B / k_global (set by the opening / rank sync), and every member copies it.

Definition at line 410 of file solve_copies.hpp.

References peclet::dem::PhaseCopies::groupK, peclet::dem::PhaseCopies::groupShift, peclet::dem::PhaseCopies::groupSlot, peclet::dem::PhaseCopies::groupStart, peclet::dem::PhaseCopies::nGroups, peclet::dem::PhaseCopies::seedW, and peclet::dem::PhaseCopies::seedX.

Referenced by demSolveContacts().

◆ foldCopiesKokkos()

void peclet::dem::foldCopiesKokkos ( CpExec  space,
const PhaseCopies &  H,
Kokkos::View< float *[3], CpMem >  x,
Kokkos::View< float *[3], CpMem >  w,
Kokkos::View< float *, CpMem >  orphan,
Kokkos::View< float *, CpMem >  orphanPeak,
Kokkos::View< float, CpMem >  consensus = {} 
)
inline

The local fold (§4.4), once per iteration after the sweep: per group T = sum over the ACTIVE members of (x(q) - sigma - shift(q)) (the M-consensus of §1.3: an inactive member carries no increment of its own, only the re-seeded consensus); the base takes sigma + T / k and every member is re-seeded to it (plus its shift).

w likewise. Orphan accounts (Poisson): every member takes (sum of the active members' accounts) / k, the peak their max. With consensus (§12 S14) the largest correction the fold applies to an active member's x, |T / k - (x(q) - sigma - shift(q))| (increment form: exactly 0 once the sweep stops moving the copies), is atomic-maxed into it; w, orphan and the result are unaffected.

Definition at line 463 of file solve_copies.hpp.

Referenced by demSolveContacts().

◆ reseedCopiesKokkos()

void peclet::dem::reseedCopiesKokkos ( CpExec  space,
const PhaseCopies &  H,
Kokkos::View< float *[3], CpMem >  x,
Kokkos::View< float *[3], CpMem >  w 
)
inline

Re-seed every member from its base, sigma unchanged (the multilevel pass, after the coarse cycle has moved the bases; §4.4 "copies re-seeded again after the coarse cycle").

Definition at line 543 of file solve_copies.hpp.

References peclet::dem::PhaseCopies::groupShift, peclet::dem::PhaseCopies::groupSlot, peclet::dem::PhaseCopies::groupStart, and peclet::dem::PhaseCopies::nGroups.

Referenced by demSolveContacts().

◆ unfoldOrphanKokkos()

void peclet::dem::unfoldOrphanKokkos ( const PhaseCopies &  H,
Kokkos::View< float *, CpMem >  orphan 
)
inline

Orphan accounts back from share form (before a rank sync and at the end of the velocity phase): the base holds the body's balance, the sum of its members' shares.

Definition at line 567 of file solve_copies.hpp.

References peclet::dem::PhaseCopies::groupActive, peclet::dem::PhaseCopies::groupSlot, peclet::dem::PhaseCopies::groupStart, and peclet::dem::PhaseCopies::nGroups.

Referenced by demSolveContacts().

◆ activityHitVelocityKokkos()

void peclet::dem::activityHitVelocityKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  nm,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const int *, CpMem >  color,
SlotOverride  ov,
Kokkos::View< unsigned char *, CpMem >  hit,
int  nSlots 
)
inline

Rank-level activity pass (docs/contact_solve_framework.md §13.3 C4; the distributed step only), velocity phase: hit(slot) = 1 for every slot that is an end – through the hub-copy overrides, else realIdx – of an owned manifold of colour >= 0 in the final colouring.

A same-value store, so the order of the writers does not matter.

Definition at line 589 of file solve_copies.hpp.

References peclet::dem::ManifoldC::bodyA, peclet::dem::ManifoldC::bodyB, peclet::dem::SlotOverride::slotA(), and peclet::dem::SlotOverride::slotB().

Referenced by demSolveContacts().

◆ activityHitPositionKokkos()

void peclet::dem::activityHitPositionKokkos ( Kokkos::View< const ContactC *, CpMem >  contacts,
const PosUnits &  units,
int  nu,
Kokkos::View< const int *, CpMem >  color,
SlotOverride  ov,
Kokkos::View< unsigned char *, CpMem >  hit,
int  nSlots 
)
inline

The same over the position phase's units (raw slots of the leader contact, through the overrides).

Definition at line 609 of file solve_copies.hpp.

References peclet::dem::ContactC::bodyA, peclet::dem::ContactC::bodyB, peclet::dem::PosUnits::leader(), peclet::dem::SlotOverride::slotA(), and peclet::dem::SlotOverride::slotB().

Referenced by demSolveContacts().

◆ finishActivityKokkos()

void peclet::dem::finishActivityKokkos ( Kokkos::View< const unsigned char *, CpMem >  hit,
int  nSlots,
PhaseCopies &  H,
Kokkos::View< int *, CpMem >  a 
)
inline

a(q) from the pass (§13.3): hit(q) for a slot outside any group; at a hub base the number of its group's members (base + copies) that were hit, which also refills groupActive and groupK (floored at 1, WO-4's local fold divisor).

Copy slots get 0 (they are never packed; their solve mass takes the base's k). a is written on [0, nSlots + H.nCopies).

Definition at line 631 of file solve_copies.hpp.

References peclet::dem::PhaseCopies::groupActive, peclet::dem::PhaseCopies::groupK, peclet::dem::PhaseCopies::groupSlot, peclet::dem::PhaseCopies::groupStart, peclet::dem::PhaseCopies::nCopies, and peclet::dem::PhaseCopies::nGroups.

Referenced by demSolveContacts().

◆ buildSolveViewsRankKKokkos()

void peclet::dem::buildSolveViewsRankKKokkos ( Particles &  P,
const PhaseCopies &  H,
int  nSlots,
Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< const float *[3], CpMem >  invInertia,
Kokkos::View< const int *, CpMem >  kRank 
)
inline

Solve views under rank-level M (§13.3, §4.5): invMassSolve / invInertiaSolve = k x the body's masses on every slot in [0, nSlots) (nSlots = slotBase + the phase's copies), with k the slot's global active copy count kRank (owner-computed, forwarded); a hub copy takes its base's k.

splitSlot = (k > 1). k <= 1 keeps the true masses bit for bit.

Definition at line 663 of file solve_copies.hpp.

References peclet::dem::PhaseCopies::copyBase, growCopyView(), peclet::dem::Particles::invInertiaSolve, peclet::dem::Particles::invMassSolve, peclet::dem::PhaseCopies::nCopies, peclet::dem::PhaseCopies::slotBase, and peclet::dem::Particles::splitSlot.

Referenced by demSolveContacts().

◆ buildInvMassCoarseKokkos()

void peclet::dem::buildInvMassCoarseKokkos ( Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< const int *, CpMem >  k,
Kokkos::View< const int *, CpMem >  a,
int  n,
Kokkos::View< float *, CpMem >  out 
)
inline

The multilevel coarse vertex's inverse mass (§13.2): invMass(q) (k(q) / max(1, a(q))), the ratio computed first so that k = a gives invMass bit for bit.

Definition at line 700 of file solve_copies.hpp.

Referenced by demSolveContacts().

◆ buildInvInertiaCoarseKokkos()

void peclet::dem::buildInvInertiaCoarseKokkos ( Kokkos::View< const float *[3], CpMem >  invInertia,
Kokkos::View< const int *, CpMem >  k,
Kokkos::View< const int *, CpMem >  a,
int  n,
Kokkos::View< float *[3], CpMem >  out 
)
inline

The multilevel coarse vertex's inverse inertia (docs/contact_physics_followups.md §2.5): the same fraction a / k of the body's inertia as its mass, invInertia(q) (k(q) / max(1, a(q))) per component, the ratio computed first exactly as buildInvMassCoarseKokkos (k = a gives invInertia bit for bit).

Definition at line 717 of file solve_copies.hpp.

Referenced by demSolveContacts().

◆ countInactiveAggregatedKokkos()

int peclet::dem::countInactiveAggregatedKokkos ( Kokkos::View< const int *, CpMem >  parent,
int  off,
int  nV,
int  nGroups1,
Kokkos::View< const int *, CpMem >  a 
)
inline

Debug-build check (§13.2, §13.5 WO-5 item 4): the vertices with a = 0 that sit in a level-1 multilevel group of >= 2 members (must be none: an inactive copy never joins a coarse edge).

Definition at line 733 of file solve_copies.hpp.

Referenced by demSolveContacts().

◆ countSplitBodiesKokkos()

int peclet::dem::countSplitBodiesKokkos ( const PhaseCopies &  H)
inline

Count the bodies whose group has k > 1 (split_stats).

Definition at line 753 of file solve_copies.hpp.

References peclet::dem::PhaseCopies::groupK, and peclet::dem::PhaseCopies::nGroups.

Referenced by demSolveContacts().

◆ countSplitAggregatedKokkos()

int peclet::dem::countSplitAggregatedKokkos ( Kokkos::View< const int *, CpMem >  parent,
int  off,
int  nV,
int  nGroups1,
Kokkos::View< const unsigned char *, CpMem >  split 
)
inline

split_stats.mlHubAggregated (§13.2's positive control): the vertices v in [0, nV) with split(v) != 0 (k > 1) whose level-1 multilevel group – parent(off + v), the first level's parent map – has at least two members.

Diagnostic only (reads the hierarchy, writes nothing the solve reads).

Definition at line 768 of file solve_copies.hpp.

Referenced by demSolveContacts().

◆ countLightHubsKokkos()

int peclet::dem::countLightHubsKokkos ( const PhaseCopies &  H,
Kokkos::View< const int *, CpMem >  eA,
Kokkos::View< const int *, CpMem >  eB,
int  nE,
int  nV,
Kokkos::View< const float *, CpMem >  invMass 
)
inline

Light hubs (split_stats, R-P3): velocity hubs whose mass is below 10x the mean mass of their partners over the hub's active edges.

Definition at line 790 of file solve_copies.hpp.

References peclet::dem::PhaseCopies::hubVertex, and peclet::dem::PhaseCopies::nHubs.

Referenced by demSolveContacts().

◆ readInt()

◆ readFloat()

float peclet::dem::readFloat ( Kokkos::View< float, CpMem >  v)
inline

Definition at line 58 of file solve_driver.hpp.

Referenced by demSolveContacts().

◆ fillGidBaseKokkos()

void peclet::dem::fillGidBaseKokkos ( Vi  gid,
int  n,
int  base 
)
inline

gid(i) = base + i over [0, n) — the per-rank global-id re-base of the distributed step (namespace scope: nvcc forbids KOKKOS_LAMBDA in member functions).

Definition at line 66 of file solve_driver.hpp.

◆ maxOwnedRadius()

float peclet::dem::maxOwnedRadius ( const Particles &  P)
inline

Largest effective particle radius over the owned set (= max scale × globalScale, growth included).

The ghost band + broadphase margin are sized off THIS, not globalScale directly, so they scale with the actual grain size — set particles in SI (radius = 1e-3, globalScale left at 1) and the halo layer follows automatically. For the usual convention (globalScale ≈ grain size, scale ≈ 1) it is numerically identical to the old 1.0*globalScale.

Definition at line 78 of file solve_driver.hpp.

References peclet::dem::Particles::baseRadius, peclet::dem::Particles::globalScale, peclet::dem::Particles::numReal, and peclet::dem::Particles::scale.

Referenced by computeOverlapsKokkos(), and demStep().

◆ fillWorldRadiiKokkos()

void peclet::dem::fillWorldRadiiKokkos ( Vf  scale,
Vf  rad,
float  gs,
float  bR,
int  n 
)
inline

World radii rad(i) = scale(i) * globalScale * baseRadius over [0, n): the owned span before a step, owned + ghosts after periodic ghost generation or a halo gather.

Every step driver (XPBD, overlap probe, force-based, their distributed forms) fills the radii through this one kernel. Fences the default instance: the callers' next launch on that instance is stream-ordered anyway, so the fence changes nothing about the result.

Definition at line 94 of file solve_driver.hpp.

Referenced by computeOverlapsKokkos(), demStep(), and demStepForce().

◆ findCollisionsGrow()

int peclet::dem::findCollisionsGrow ( Particles &  P,
float  margin 
)
inline

Broad phase with an automatically-grown pair buffer.

findCollisionsArborX guards its pair WRITES at maxPairs but returns the RAW candidate count, which can exceed P.pairs' capacity once a bed compacts (more neighbour pairs than the buffer holds — e.g. a fluidized bed driven denser by the CFD-DEM drag). Feeding that raw count straight into detectContactsKokkos as its loop bound makes the narrowphase read P.pairs out of bounds → cudaErrorIllegalAddress. Here we detect the overflow, reallocate P.pairs (with headroom so an oscillating count doesn't realloc every step) and re-run once so no candidate pair is silently dropped, then clamp defensively so the returned count is ALWAYS ≤ the buffer extent — the narrowphase can never walk off the end regardless.

Definition at line 112 of file solve_driver.hpp.

References peclet::dem::Particles::crad(), peclet::dem::Particles::domain, findCollisionsArborX(), peclet::dem::Particles::maxPairs, peclet::dem::Particles::numParticles, peclet::dem::Particles::numReal, peclet::dem::Particles::pairCount, peclet::dem::Particles::pairs, peclet::dem::Particles::posPred, peclet::dem::Domain::size, peclet::dem::F3::x, peclet::dem::F3::y, and peclet::dem::F3::z.

Referenced by computeOverlapsKokkos(), demStep(), findCollisionsVerlet(), and hertzRebuildPairs().

◆ findCollisionsVerlet()

int peclet::dem::findCollisionsVerlet ( Particles &  P,
float  margin,
float  maxRad 
)
inline

Verlet-cached impulse broadphase (single-GPU, non-periodic).

Reuse the last ArborX candidate list while no particle's predicted position has moved more than skin/2 since the build; the list is built at margin + skin, so every within-margin pair is still present between rebuilds — a SUPERSET, so the narrowphase yields identical contacts (order differs -> run-scatter, like the colouring/fused paths). Rebuilds on an invalid cache, growth beyond the skin, or the displacement bound. Composes with sleeping: a frozen bed has zero displacement, so it never rebuilds. The caller guarantees non-periodic (no per-step ghost-slot churn) and verletSkinFrac > 0.

Definition at line 134 of file solve_driver.hpp.

References findCollisionsGrow(), peclet::dem::Particles::impNumPairs, peclet::dem::Particles::impRefMaxRad, peclet::dem::Particles::impRefPos, peclet::dem::Particles::numReal, peclet::dem::Particles::posPred, and peclet::dem::Particles::verletSkinFrac.

Referenced by demStep().

◆ demIterCountSlot()

int * peclet::dem::demIterCountSlot ( Particles &  P,
int  slot 
)
inline

Diagnostics (docs/contact_solve_framework.md §12 S12): the device scalar a fused main loop writes its iteration count to (slot 0 velocity, 1 position), allocated on first use.

Definition at line 274 of file solve_driver.hpp.

References peclet::dem::Particles::iterCountDev.

Referenced by demSolveContacts().

◆ demReadIterCount()

int peclet::dem::demReadIterCount ( const Particles &  P,
int  slot 
)
inline

The count a fused main loop wrote (one small readback, only with P.iterCounters on); -1 with the counters off (no fence, no copy).

Definition at line 281 of file solve_driver.hpp.

References peclet::dem::Particles::iterCountDev, and peclet::dem::Particles::iterCounters.

Referenced by demSolveContacts().

◆ demSolveContacts()

template<class Hooks >
void peclet::dem::demSolveContacts ( Particles &  P,
int  nc,
int  nm,
int  nBodies,
Kokkos::View< const int *, CpMem >  keyIdx,
const Hooks &  hooks 
)
inline

One full velocity + position contact solve over the already-built contacts/manifolds (see file comment).

Runs between the narrow phase and finalCommit; the caller owns ghost construction, broad/narrow phase and the commit.

Definition at line 293 of file solve_driver.hpp.

References accumulateNormalImpulseKokkos(), peclet::dem::Particles::activityHit, activityHitPositionKokkos(), activityHitVelocityKokkos(), peclet::dem::Particles::angVel, peclet::dem::Particles::angVelPred, peclet::dem::Particles::aPos, applyUpdatesKokkos(), applyVelocityAndPredictPositionKokkos(), applyVelocityDeltasAveragedKokkos(), applyVelocityDeltasKokkos(), peclet::dem::Particles::asleep, peclet::dem::Particles::aVel, peclet::dem::Particles::baseRadius, peclet::dem::Particles::bodyColorMask, peclet::dem::Particles::bodyOrphan, peclet::dem::Particles::bodyOrphanVPeak, peclet::dem::Particles::bodyWinner, peclet::dem::Particles::bucketCursor, buildCoarseBucketsKokkos(), buildColorBucketsKokkos(), buildContactHierarchyKokkos(), buildCopyGroupsKokkos(), buildHubCopiesKokkos(), buildInvInertiaCoarseKokkos(), buildInvMassCoarseKokkos(), buildLevelColorBucketsKokkos(), buildPositionUnitsKokkos(), buildSolveViewsKokkos(), buildSolveViewsRankKKokkos(), peclet::dem::Particles::capacity, peclet::dem::RankK::color, colorContactsIncrementalKokkos(), colorContactsKokkos(), colorManifoldsIncrementalKokkos(), colorManifoldsKokkos(), commitContactColorKokkos(), commitPairKeysLambdaKokkos(), peclet::dem::Particles::commitPerm, commitPosImpulseKokkos(), computeHeightLevelsKokkos(), computePlaneLoadKokkos(), computeSideFlagsKokkos(), computeVn0Kokkos(), computeXGateKokkos(), peclet::dem::Particles::constraintCounts, peclet::dem::Particles::contactColor, peclet::dem::Particles::contactKeys, peclet::dem::Particles::contacts, peclet::dem::Particles::contactSleep, peclet::dem::Particles::contactSlot, countFrictionContactsKokkos(), countInactiveAggregatedKokkos(), countLightHubsKokkos(), countPositionJacobiKokkos(), countSplitAggregatedKokkos(), countSplitBodiesKokkos(), countUnfiredKokkos(), countVelocityJacobiKokkos(), peclet::dem::Particles::cudaGraphs, decayBodyOrphanKokkos(), peclet::dem::Particles::deltaAngVel, peclet::dem::Particles::deltaPos, peclet::dem::Particles::deltaQuat, peclet::dem::Particles::deltaVel, demFusedWanted(), demIterCountSlot(), demMakeFusedCtx(), demMakeMlFusedCtx(), demReadIterCount(), peclet::dem::SplitStats::driftMigrations, peclet::dem::Particles::dt, peclet::dem::Particles::ensureCapacity(), expandUnitColorsKokkos(), peclet::dem::Particles::extForceActive, peclet::dem::Particles::extTorqueActive, finishActivityKokkos(), foldCopiesKokkos(), peclet::dem::Particles::frictionDynamic, peclet::dem::Particles::fusedBar, peclet::dem::Particles::fusedSweeps, gatherWarmLambdaKokkos(), peclet::dem::Particles::gid, peclet::dem::Particles::globalScale, peclet::dem::Particles::graphCache, peclet::dem::Particles::gravity, peclet::dem::Particles::groundedLevel, growCopyView(), peclet::dem::Particles::growthRate, peclet::dem::Particles::heightLevel, peclet::dem::Particles::imageShift, peclet::dem::Particles::incrementalColoring, peclet::dem::Particles::invInertia, peclet::dem::Particles::invInertiaCoarse, peclet::dem::Particles::invInertiaSolve, peclet::dem::Particles::invMass, peclet::dem::Particles::invMassCoarse, peclet::dem::Particles::invMassSolve, peclet::dem::Particles::iterCounters, kColorUncolourable, peclet::dem::mldetail::kGateSlip, peclet::dem::Particles::kPos, kSplitOmegaVelocity, peclet::dem::Particles::kVel, peclet::dem::Particles::lambdaAcc, peclet::dem::Particles::lambdaT, peclet::dem::Particles::levelKey, peclet::dem::Particles::levelPerm, peclet::dem::SplitStats::lightHubs, makePGSManifoldSweep(), peclet::dem::Particles::manifoldColor, peclet::dem::Particles::manifoldPersistent, peclet::dem::Particles::manifolds, peclet::dem::Particles::manifoldSleep, markCopySeedsKokkos(), markPersistentManifoldsKokkos(), peclet::dem::Particles::maxApproach, peclet::dem::Particles::maxApproachQS, peclet::dem::FusedSweepCtx::maxBucket, peclet::dem::Particles::maxConsensus, peclet::dem::Particles::maxOverlap, peclet::dem::MlFusedCtx::maxWork, peclet::dem::Particles::mlAccD, peclet::dem::Particles::mlAngG, peclet::dem::Particles::mlAngG0, peclet::dem::Particles::mlBucketPerm, peclet::dem::Particles::mlColorPacked, peclet::dem::Particles::mlComOffG, peclet::dem::Particles::mlGrp, peclet::dem::SplitStats::mlHubAggregated, peclet::dem::Particles::mlInvIG, peclet::dem::Particles::mlInvMassG, peclet::dem::Particles::mlLast, peclet::dem::Particles::mlMassG, peclet::dem::Particles::mlMate, peclet::dem::Particles::mlOffsDev, peclet::dem::Particles::mlOriginG, peclet::dem::Particles::mlParent, peclet::dem::Particles::mlVelG, peclet::dem::Particles::mlVelG0, multilevelCoarseCycleKokkos(), peclet::dem::PhaseCopies::nCopies, peclet::dem::PhaseCopies::nGroups, peclet::dem::PhaseCopies::nHubs, peclet::dem::Particles::noAdaptiveStop, peclet::dem::Particles::MlLast::numBodies, peclet::dem::RankK::numColors, peclet::dem::Particles::MlLast::numGroups, peclet::dem::ContactHierarchy::numGroups, peclet::dem::Particles::MlLast::numLevels, peclet::dem::ContactHierarchy::numLevels, peclet::dem::Particles::MlLast::numManifolds, peclet::dem::Particles::numParticles, peclet::dem::Particles::numPosUnits, peclet::dem::Particles::numReal, peclet::dem::Particles::pairKeys, peclet::dem::Particles::MlLast::parentOff, peclet::dem::ContactHierarchy::parentOff, PECLET_DEM_GRAPH_LOOP, peclet::dem::Particles::planeFriction, peclet::dem::Particles::pos, peclet::dem::Particles::posCommitPerm, peclet::dem::Particles::posCopies, peclet::dem::SplitStats::posHubCopies, peclet::dem::Particles::posImpulse, peclet::dem::SplitStats::posItersUsed, positionEdgeEndsKokkos(), peclet::dem::Particles::positionIterations, peclet::dem::Particles::positionOmega, peclet::dem::Particles::posLambdaContact, peclet::dem::Particles::posLastFullColors, peclet::dem::Particles::posOffsDev, peclet::dem::Particles::posPerm, peclet::dem::Particles::posPred, peclet::dem::Particles::posPrevContactCount, peclet::dem::Particles::posResidual, peclet::dem::Particles::prevContactColor, peclet::dem::Particles::prevContactKeys, peclet::dem::Particles::prevLambda, peclet::dem::Particles::prevLambdaT, peclet::dem::Particles::prevManifoldColor, peclet::dem::Particles::prevMatched, peclet::dem::Particles::prevPairCount, peclet::dem::Particles::prevPairKeys, peclet::dem::Particles::prevPosImpulse, peclet::dem::Particles::prevRestBank, peclet::dem::Particles::prevRestVPeak, peclet::dem::Particles::quat, peclet::dem::Particles::quatPred, readFloat(), peclet::dem::Particles::realIndices, reseedCopiesKokkos(), peclet::dem::Particles::restBank, peclet::dem::Particles::restitutionModel, peclet::dem::Particles::restitutionNormal, peclet::dem::Particles::restitutionTangent, peclet::dem::Particles::restRel, peclet::dem::Particles::restVPeak, scatterOrphanBanksKokkos(), seedCopySlotStateKokkos(), peclet::dem::Particles::sideFlags, peclet::dem::Particles::sleepingEnabled, peclet::dem::PhaseCopies::slotA, peclet::dem::PhaseCopies::slotB, peclet::dem::PhaseCopies::slotBase, solveContactFrictionKokkos(), peclet::dem::Particles::solveEpoch, solvePositionColoredGSKokkos(), solvePositionKokkos(), solveVelocityColoredGSKokkos(), solveVelocityKokkos(), solveVelocityPGSBucketsKokkos(), solveVelocityPGSKokkos(), peclet::dem::SplitStats::splitBodiesPos, peclet::dem::SplitStats::splitBodiesVel, peclet::dem::Particles::splitSlot, peclet::dem::Particles::splitStats, peclet::dem::Particles::stabilizationMode, peclet::dem::RankK::syncInterval, peclet::dem::SplitStats::unfiredSplitContacts, unfoldOrphanKokkos(), peclet::dem::Particles::unitColor, peclet::dem::Particles::unitContacts, peclet::dem::Particles::unitStart, updateGroundedLevelsKokkos(), updateRestitutionBankKokkos(), peclet::dem::Particles::vel, peclet::dem::Particles::velCopies, peclet::dem::Particles::velCopiesLastSubstep, peclet::dem::SplitStats::velHubCopies, peclet::dem::SplitStats::velItersUsed, peclet::dem::Particles::velLastFullColors, peclet::dem::Particles::velMask, velocityEdgeEndsKokkos(), peclet::dem::Particles::velocityIterations, peclet::dem::Particles::velocityUseGS, peclet::dem::Particles::velOffsDev, peclet::dem::Particles::velPerm, peclet::dem::Particles::velPred, peclet::dem::Particles::vertexDegree, vertexDegreeKokkos(), peclet::dem::Particles::vn0, peclet::dem::Particles::vt0, peclet::dem::Particles::wallFrictionMax, warmStartApplyKokkos(), peclet::dem::F3::x, peclet::dem::Particles::xGate, peclet::dem::F3::y, and peclet::dem::F3::z.

Referenced by demStep().

◆ hertzRebuildPairs()

void peclet::dem::hertzRebuildPairs ( Particles &  P,
float  skin 
)
inline

Rebuild the Hertz cached pair list with margin skin, carrying the Mindlin shear history across by pair key (permutation sort of the OLD list, binary search from the NEW).

Keys are built from GLOBAL ids (identity == local index single-rank, so single-GPU behaviour is unchanged; under MPI ghost slots are not stable identities, gids are).

Definition at line 53 of file solve_driver_force.hpp.

References findCollisionsGrow(), peclet::dem::Particles::gid, peclet::dem::Particles::hertzDispMax, peclet::dem::Particles::hertzKeys, peclet::dem::Particles::hertzNumPairs, peclet::dem::Particles::hertzPrevCount, peclet::dem::Particles::hertzPrevKeys, peclet::dem::Particles::hertzPrevXi, peclet::dem::Particles::hertzRefPos, peclet::dem::Particles::hertzSnPair, peclet::dem::Particles::hertzXi, lowerBoundKey(), pairKeyFromGids(), peclet::dem::Particles::pairs, peclet::dem::Particles::pos, and peclet::dem::Particles::posPred.

Referenced by peclet::dem::HertzMindlinLaw::rebuild().

◆ hertzCommitHistory()

void peclet::dem::hertzCommitHistory ( Particles &  P)
inline

◆ zeroForceScratchKokkos()

void peclet::dem::zeroForceScratchKokkos ( V3  dv,
V3  dw,
int  lo,
int  hi 
)
inline

Zero the force/torque accumulator rows [lo, hi) — under MPI the pair kernels atomically accumulate onto ghost slots too (Newton's third law), but only owned rows are consumed and cleared by the integrator; without this the ghost rows grow without bound.

Definition at line 196 of file solve_driver_force.hpp.

◆ demStepForce()

◆ demStepHertz()

void peclet::dem::demStepHertz ( Particles &  P,
float  dt,
int  nsteps,
float  skinFrac 
)
inline

nsteps of the soft-sphere Hertz-Mindlin engine — the single-GPU instantiation of the force-based driver (kept as the historical entry point; see demStepForce).

Definition at line 327 of file solve_driver_force.hpp.

References demStepForce().

Referenced by peclet::dem::Simulation::stepHertz().

◆ contactArmsMid()

void peclet::dem::contactArmsMid ( const ContactC &  c,
F3 &  rA,
F3 &  rB 
)

The single application point of a body-body contact: both lever arms moved to the contact midpoint, rA - dist n / 2 and rB + dist n / 2, so x_A + rA = x_B + rB (the shift of transformContact, contact_preprocessing.hpp).

The narrow phase's two surface arms differ by dist n, which made every legacy friction impulse also apply the couple dist n x J_t (docs/contact_solve_framework.md §1.7, D2). Wall contacts keep their own arm.

Definition at line 33 of file solver_friction.hpp.

References peclet::dem::ContactC::dist, peclet::dem::ContactC::normal, peclet::dem::ContactC::rA, peclet::dem::ContactC::rB, peclet::dem::F4::x, peclet::dem::F4::y, and peclet::dem::F4::z.

Referenced by accumulateNormalImpulseKokkos(), and solveContactFrictionKokkos().

◆ accumulateNormalImpulseKokkos()

void peclet::dem::accumulateNormalImpulseKokkos ( Kokkos::View< ContactC *, CpMem >  contacts,
int  numContacts,
Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< const float *[3], CpMem >  invInertia,
Kokkos::View< const float *[3], CpMem >  velPred,
Kokkos::View< const float *[3], CpMem >  angVelPred,
Kokkos::View< const int *, CpMem >  realIdx,
float  growthRate,
bool  dedupTwins = true 
)
inline

Force-chain normal load, accumulated over the velocity iterations: contacts(idx).friction_lambda_n += approach / w_n.

Body-body AND wall (boundary) contacts — a grain buried under the bed thus gets the WEIGHT TRANSMITTED FROM ABOVE pressing it into the wall, not just its own one-shot gravity approach (computePlaneLoad). Without this a deep bed slips against a moving wall instead of being dragged up (a rotating drum stays flat).

Definition at line 45 of file solver_friction.hpp.

References add3(), peclet::dem::ContactC::bodyA, peclet::dem::ContactC::bodyB, peclet::dem::ContactC::boundaryVel, peclet::dem::detail::computeW(), contactArmsMid(), cross3v(), dot3(), ldF3(), peclet::dem::ContactC::normal, peclet::dem::ContactC::rA, scale3(), sub3(), peclet::dem::F4::x, peclet::dem::F4::y, and peclet::dem::F4::z.

Referenced by demSolveContacts().

◆ computePlaneLoadKokkos()

void peclet::dem::computePlaneLoadKokkos ( Kokkos::View< ContactC *, CpMem >  contacts,
int  numContacts,
Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< const float *[3], CpMem >  invInertia,
Kokkos::View< const float *[3], CpMem >  velPred,
Kokkos::View< const float *[3], CpMem >  angVelPred,
FrManifoldCounts  planeFriction 
)
inline

Plane (idB<0) one-shot loads.

Zeros planeFriction first (both columns), then sets the per-contact friction_lambda_n and the per-body max plane load (.x).

Definition at line 101 of file solver_friction.hpp.

References add3(), peclet::dem::ContactC::bodyA, peclet::dem::ContactC::bodyB, peclet::dem::ContactC::boundaryVel, peclet::dem::detail::computeW(), cross3v(), dot3(), ldF3(), peclet::dem::ContactC::normal, peclet::dem::ContactC::rA, sub3(), peclet::dem::F4::x, peclet::dem::F4::y, and peclet::dem::F4::z.

Referenced by demSolveContacts().

◆ countFrictionContactsKokkos()

void peclet::dem::countFrictionContactsKokkos ( Kokkos::View< const ContactC *, CpMem >  contacts,
int  numContacts,
Kokkos::View< const int *, CpMem >  realIdx,
FrManifoldCounts  planeFriction 
)
inline

Per-body active-contact count into planeFriction(:,1).

Definition at line 137 of file solver_friction.hpp.

References peclet::dem::ContactC::bodyA, peclet::dem::ContactC::bodyB, and peclet::dem::ContactC::friction_lambda_n.

Referenced by demSolveContacts().

◆ solveContactFrictionKokkos()

void peclet::dem::solveContactFrictionKokkos ( Kokkos::View< const ContactC *, CpMem >  contacts,
int  numContacts,
Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< const float *[3], CpMem >  invInertia,
Kokkos::View< const float *[4], CpMem >  quat,
Kokkos::View< const float *[3], CpMem >  velPred,
Kokkos::View< const float *[3], CpMem >  angVelPred,
Kokkos::View< const int *, CpMem >  realIdx,
FrManifoldCounts  planeFriction,
float  frictionDynamic,
Kokkos::View< float *[3], CpMem >  deltaVel,
Kokkos::View< float *[3], CpMem >  deltaAngVel 
)
inline

One count-averaged Coulomb friction sweep.

Zeros the delta arrays first, then accumulates the tangential impulse (clamped by mu*normal-load, divided by the larger contact count) into them.

The torque r x t is a WORLD-frame vector and invInertia is the body-frame principal inverse inertia, so both the effective mass and the angular velocity change go through the world inverse inertia R diag(invI) R^T of the phase's frozen orientation quat (R from the unit quaternion), exactly as every other velocity kernel does (detail::genInvMass, invRotateVector / rotateVector; docs/contact_solve_framework.md §12 S15). Applying the body-frame diagonal to the world torque directly broke angular momentum by up to 32 % of the applied torque on one ring-ring impulse. Isotropic inverse inertia (spheres) has no principal frame: its branch keeps the component-wise arithmetic verbatim, so sphere runs are byte-identical.

Definition at line 165 of file solver_friction.hpp.

References add3(), peclet::dem::ContactC::bodyA, peclet::dem::ContactC::bodyB, peclet::dem::ContactC::boundaryFriction, peclet::dem::ContactC::boundaryVel, contactArmsMid(), cross3v(), dot3(), peclet::dem::ContactC::friction_lambda_n, peclet::dem::detail::genInvMass(), invRotateVector(), ldF3(), peclet::dem::ContactC::normal, peclet::dem::ContactC::rA, peclet::dem::ContactC::rB, rotateVector(), scale3(), sub3(), peclet::dem::F3::x, peclet::dem::F4::x, peclet::dem::F3::y, peclet::dem::F4::y, peclet::dem::F3::z, and peclet::dem::F4::z.

Referenced by demSolveContacts().

◆ demFusedWanted()

bool peclet::dem::demFusedWanted ( bool  graphReplayAvailable,
int  mode 
)
inline

Fused-sweep policy.

mode is Particles::fusedSweeps (set_fused_sweeps): -1 auto, 0 off (launch path everywhere), 1 on (fused everywhere it applies). Auto = fused only where CUDA-graph replay is unavailable — the distributed step (ghost syncs inside the loops forbid capture) and set_cuda_graphs(False) runs. Measured on the 25k/100k Dosta beds (RTX 5080): graph replay beats the fused kernels by 2-4% on the solo GPU (pipelined graph-node transitions are cheaper than software grid barriers once the step is GPU-bound), while capture-less paths pay the full per-launch submission storm that fusion removes (25k multilevel: 17.4 -> 16.2 ms/step with graphs off). Either way the arithmetic is identical.

Definition at line 63 of file solver_fused.hpp.

Referenced by demSolveContacts().

◆ demMakeFusedCtx()

FusedSweepCtx peclet::dem::demMakeFusedCtx ( CpExec &  space,
const std::vector< int > &  offs,
Kokkos::View< int *, CpMem >  offsDev,
Kokkos::View< unsigned *, CpMem >  bar 
)
inline

Fill a FusedSweepCtx from buildColorBucketsKokkos's host offsets: async-upload them into the pooled device view and record the largest bucket.

The caller owns the policy decision (demFusedWanted); no-CUDA / empty / oversize => inactive ctx.

Definition at line 233 of file solver_fused.hpp.

References peclet::dem::FusedSweepCtx::bar, peclet::dem::FusedSweepCtx::maxBucket, and peclet::dem::FusedSweepCtx::offsDev.

Referenced by demSolveContacts().

◆ hertzPairEMu()

void peclet::dem::hertzPairEMu ( int  matA,
int  matB,
PairTableView  pairTable,
float  eGlobal,
float  muGlobal,
float &  e,
float &  mu 
)

Pairwise (e, mu) lookup shared with the narrowphase convention: pair table if present, global fallback otherwise.

Definition at line 27 of file solver_hertz.hpp.

References kMaxMaterials.

Referenced by hertzShapePairForcesKokkos().

◆ hertzBetaD()

float peclet::dem::hertzBetaD ( float  e)

Restitution -> damping ratio beta_d = ln e / sqrt(ln^2 e + pi^2) (e in (0, 1]; e <= 0 -> -1, the critically-damped limit used by LIGGGHTS for e -> 0).

Definition at line 41 of file solver_hertz.hpp.

Referenced by hertzForce(), and hertzShapePairForcesKokkos().

◆ hertzForce()

F3 peclet::dem::hertzForce ( float  delta,
F3  nhat,
F3  vrel,
float  rStar,
float  mStar,
float  eStar,
float  gStar,
float  e,
float  mu,
float  dt,
F3 &  xi 
)

One Hertz–Mindlin force evaluation for a sphere-sphere or sphere-wall contact.

Returns the force ON body A applied at the contact point (linear) and updates xi in place. nhat points from B to A (push direction on A). vrel = contact velocity of A relative to B.

Definition at line 53 of file solver_hertz.hpp.

References add3(), dot3(), hertzBetaD(), scale3(), and sub3().

◆ hertzSphereRadius()

float peclet::dem::hertzSphereRadius ( int  i,
Kokkos::View< const float *, CpMem >  rad,
Kokkos::View< const float *, CpMem >  scale,
ScalarI  shapeId,
Kokkos::View< const ShapeDesc *, CpMem >  shapes,
float  globalScale 
)

A body's Hertz sphere radius: a SPHERE shape's own radius, scale * globalScale * params.x (the radius the XPBD narrow phase probes with); otherwise the world reach radius rad(i).

rad(i) is scale * globalScale * the registry's LARGEST bounding radius (fillWorldRadiiKokkos), so in a mixture with tubes or boxes (circumscribed radius) it overstates a sphere; alone it is equal, computed in the same order, bit for bit.

Definition at line 91 of file solver_hertz.hpp.

References peclet::dem::ShapeDesc::params, SPHERE, peclet::dem::ShapeDesc::type, and peclet::dem::F4::x.

◆ hertzPairForcesKokkos()

void peclet::dem::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 
)
inline

Cached-pair forces: overlap from current positions; history resets when a cached pair is currently separated.

Forces/torques accumulate atomically (a body appears in many pairs).

Definition at line 106 of file solver_hertz.hpp.

Referenced by peclet::dem::HertzMindlinLaw::pairForces().

◆ hertzWallForcesKokkos()

void peclet::dem::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 = {} 
)
inline

SDF-wall forces (per particle x wall).

Gradient by central differences at half a grid cell. The wall's rigid surface velocity enters vrel; per-(particle, wall) history in xiWall.

Definition at line 165 of file solver_hertz.hpp.

Referenced by peclet::dem::HertzMindlinLaw::wallForces().

◆ hertzBuildWallCandidatesKokkos()

int peclet::dem::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 
)
inline

Build the wall candidate list: particles within (radius + skin) of any wall's zero level.

Between pair rebuilds nothing else can reach a wall (same Verlet-skin argument as the pair list), so the per-step wall pass only visits these slots. Encodes i * maxWalls + wallIdx.

Definition at line 352 of file solver_hertz.hpp.

References sampleWallSdf().

Referenced by peclet::dem::HertzMindlinLaw::rebuild().

◆ hertzIntegrateKokkos()

void peclet::dem::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 
)
inline

Symplectic-Euler kick-drift (MUSEN-style) + displacement tracking for the Verlet rebuild.

Spheres: isotropic inertia, angular update needs no orientation.

Definition at line 380 of file solver_hertz.hpp.

Referenced by demStepForce().

◆ hertzMaxDisp2Kokkos()

float peclet::dem::hertzMaxDisp2Kokkos ( int  numReal,
Kokkos::View< const float *[3], CpMem >  pos,
Kokkos::View< const float *[3], CpMem >  refPos 
)
inline

Max squared displacement since the last pair build (called only at rebuild checks – keeping this out of the integrate kernel avoids a per-step single-address atomic).

Definition at line 432 of file solver_hertz.hpp.

References dot3(), loadF3(), and sub3().

Referenced by demStepForce().

◆ hertzShapePairForcesKokkos()

void peclet::dem::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 
)
inline

Non-spherical pairs: per-point Hertz springs over A's point shell against B's SDF (the same one-sided sampling convention as the narrowphase), ONE Mindlin patch history per pair.

Per shell point k: penetration delta_k from B's SDF, its own Hertz normal spring + dashpot applied AT THE POINT (correct face/edge force distribution and torques); the tangential spring acts at the load-weighted patch centroid, bounded by mu * (sum of point spring forces). R* combines per-shape CONTACT radii (Hertz curvature is undefined at faces/edges – this is the standard point-spring approximation; spheres use their true radii via params.x).

Definition at line 456 of file solver_hertz.hpp.

References add3(), cross3v(), dot3(), hertzBetaD(), hertzPairEMu(), invRotateVector(), len3(), loadF3(), loadF4(), peclet::dem::ShapeDesc::numPoints, peclet::dem::ShapeDesc::params, rotateVector(), scale3(), sdfEvalShape(), sdfGradShape(), peclet::dem::ShapeDesc::shellOffset, SPHERE, sub3(), peclet::dem::ShapeDesc::type, and peclet::dem::F4::x.

Referenced by peclet::dem::HertzMindlinLaw::pairForces().

◆ buildMlLevelGeometryKokkos()

int peclet::dem::buildMlLevelGeometryKokkos ( const MlScratch &  S,
int  off,
int  ng,
int  numReal,
Kokkos::View< const float *[3], CpMem >  posPred,
Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< const float *[4], CpMem >  quat,
Kokkos::View< const float *[3], CpMem >  invIc 
)
inline

The per-level geometry of the rigid coarse space (docs/contact_physics_followups.md §2.3), over the composed body -> group map S.grp of the level just built (groups [off, off + ng)):

  1. O_g = fl(sum mu x / sum mu), sums in double (mu = effMass(invMassCoarse), x = posPred);
  2. c_g = fl(sum mu e / sum mu) with e = fl(x - O_g), double sums; d = fl(e - c_g); 3./4. I_g = sum [J_q + mu (|d|^2 1 - d d^T)] in double (J_q the member's world spin inertia);
  3. invI_g = I_g^-1 by the double adjugate and determinant, stored as 6 floats. A non-positive or non-finite determinant is a logic error: debug builds throw, release builds store invI_g = 0 (that group moves translation-only). massG / invMassG are not touched. Returns the number of singular groups (0 unless broken).

Definition at line 223 of file solver_multilevel.hpp.

References peclet::dem::MlScratch::accD, peclet::dem::MlScratch::comOffG, peclet::dem::mldetail::effMass(), peclet::dem::MlScratch::grp, peclet::dem::MlScratch::invIG, peclet::dem::mldetail::memberOffset(), peclet::dem::MlScratch::originG, peclet::dem::mldetail::rotateD(), peclet::dem::F3::x, peclet::dem::mldetail::D3::x, peclet::dem::F3::y, peclet::dem::mldetail::D3::y, peclet::dem::F3::z, and peclet::dem::mldetail::D3::z.

◆ buildContactHierarchyKokkos()

ContactHierarchy peclet::dem::buildContactHierarchyKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const int *, CpMem >  mColor,
Kokkos::View< const float *, CpMem >  vn0,
Kokkos::View< const float *[3], CpMem >  vt0,
Kokkos::View< const unsigned char *, CpMem >  persistent,
Kokkos::View< const float *[3], CpMem >  posPred,
F3  gHat,
Kokkos::View< const float *, CpMem >  invMass,
float  qsThr,
int  gateMask,
int  numReal,
MlScratch &  S,
Kokkos::View< long long *, CpMem >  winner,
Kokkos::View< std::uint64_t *, CpMem >  colorMask,
bool  excludeImmovable = false,
Kokkos::View< const unsigned char *, CpMem >  asleep = {},
const MlBodyViews &  body = {} 
)
inline

Build the aggregation hierarchy + per-level crossing-manifold colorings.

Eligibility for both matching and coarse solving: mldetail::eligible (active, non-dup, persistent, quasi-static, support-oriented). Walls (bodyB < 0) never merge but their manifolds ARE coarse contacts (the momentum sink). Returns the host hierarchy description.

Definition at line 347 of file solver_multilevel.hpp.

Referenced by demSolveContacts().

◆ buildCoarseBucketsKokkos()

void peclet::dem::buildCoarseBucketsKokkos ( const ContactHierarchy &  H,
MlScratch &  S,
int  numManifolds,
Kokkos::View< int *, CpMem >  colorScratch,
Kokkos::View< int *, CpMem >  perm,
Kokkos::View< int *, CpMem >  cursor,
std::vector< std::vector< int > > &  offs 
)
inline

One multilevel stabilization cycle over an already-built hierarchy: fine colored smoothing is the caller's business; this runs the coarse leg (fine -> coarse), updating velPred in place.

Eligibility is baked into the packed per-level colours (slot 63 = skip), and the composed body -> group map is rebuilt from identity each cycle (numReal-sized passes; cheap next to the sweeps). Dense per-(level, colour) buckets for the coarse cycle, built ONCE per hierarchy (the structure is static for the whole substep, so every sweep of every stabilization iteration reuses them instead of scanning ALL manifolds per colour — measured 40% of GPU time and the bulk of the launch-submission bound at 25k). perm segment for level l lives at [(l-1)*numManifolds, l*numManifolds). Colour slots >= numColors (kMlSlotSkip = uncoloured / non-crossing) are excluded — exactly the set the scan-mode colour filter never matched.

Definition at line 623 of file solver_multilevel.hpp.

References buildColorBucketsKokkos(), peclet::dem::MlScratch::colorPacked, peclet::dem::ContactHierarchy::numColors, and peclet::dem::ContactHierarchy::numLevels.

Referenced by demSolveContacts().

◆ makeMlCoarseSweep()

MlCoarseSweep peclet::dem::makeMlCoarseSweep ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
Kokkos::View< const int *, CpMem >  realIdx,
const MlScratch &  S,
Kokkos::View< float *, CpMem >  lambdaAcc,
Kokkos::View< float, CpMem >  maxApproach,
Kokkos::View< const float *, CpMem >  restRel,
const MlBodyViews &  body 
)
inline

The coarse sweep over the multilevel scratch + the body views (the rigid views stay empty when the lazy pools are).

Definition at line 825 of file solver_multilevel.hpp.

References peclet::dem::MlScratch::angG, peclet::dem::MlScratch::angG0, peclet::dem::MlScratch::comOffG, peclet::dem::MlScratch::grp, peclet::dem::MlScratch::invIG, peclet::dem::MlScratch::invMassG, peclet::dem::MlScratch::originG, and peclet::dem::MlScratch::velG.

◆ demMakeMlFusedCtx()

MlFusedCtx peclet::dem::demMakeMlFusedCtx ( CpExec &  space,
const ContactHierarchy &  H,
const std::vector< std::vector< int > > &  bkOffs,
int  numManifolds,
int  numReal,
int  coarseSweeps,
Kokkos::View< int *, CpMem >  offsDev,
Kokkos::View< unsigned *, CpMem >  bar 
)
inline

◆ multilevelCoarseCycleKokkos()

void peclet::dem::multilevelCoarseCycleKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< float *[3], CpMem >  velPred,
Kokkos::View< float *, CpMem >  lambdaAcc,
Kokkos::View< float, CpMem >  maxApproach,
int  numReal,
const ContactHierarchy &  H,
MlScratch &  S,
int  coarseSweeps,
const MlBodyViews &  body,
Kokkos::View< const float *, CpMem >  restRel = {},
const std::vector< std::vector< int > > *  bkOffs = nullptr,
Kokkos::View< const int *, CpMem >  bkPerm = {},
const MlFusedCtx *  fused = nullptr 
)
inline

Definition at line 1067 of file solver_multilevel.hpp.

Referenced by demSolveContacts().

◆ countPositionJacobiKokkos()

void peclet::dem::countPositionJacobiKokkos ( Kokkos::View< const ContactC *, CpMem >  contacts,
int  numContacts,
Kokkos::View< int *, CpMem >  counts 
)
inline

Mass-split Jacobi count pass (docs/contact_solve_framework.md §3.1, D3): per body slot, the number of contacts the Jacobi position solve visits (every contact; a wall side counts nothing).

Accumulates into counts, handed over zeroed (the apply clears it). Under MPI the caller then makes the counts global (syncContactCounts).

Definition at line 33 of file solver_position.hpp.

References peclet::dem::ContactC::bodyA, and peclet::dem::ContactC::bodyB.

Referenced by demSolveContacts().

◆ solvePositionKokkos()

void peclet::dem::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,
bool  massSplit = false 
)
inline

Accumulate XPBD position corrections for numContacts contacts.

massSplit (the 'jacobi' diagnostic, §3.1): constraintCounts holds the global per-body counts n of countPositionJacobiKokkos and is only read; each contact's correction is solved against copies of mass m / n (w~ = n_A w_A + n_B w_B, a wall side 0) and the TRUE-mass deltas are accumulated, to be applied with factor 1. Off: the solve bumps the counts and the caller averages the sum by 1/count – the pre-framework count-averaged Jacobi, kept ONLY for the kernel unit test against its serial reference; no production path calls it (the colour-saturation fallbacks were deleted with complete colouring, docs/contact_solve_framework.md §4.2 / §12 S3).

Definition at line 55 of file solver_position.hpp.

Referenced by demSolveContacts().

◆ unitAsleep()

bool peclet::dem::unitAsleep ( const PosUnits &  units,
const Kokkos::View< const unsigned char *, CpMem > &  sleepMask,
int  u 
)

A position unit is sleep-inactive iff all its contacts are sleep-masked (§4.3).

Definition at line 162 of file solver_position.hpp.

References peclet::dem::PosUnits::begin(), peclet::dem::PosUnits::contact(), and peclet::dem::PosUnits::end().

◆ colorContactsKokkos()

int peclet::dem::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 = {},
PosUnits  units = {},
SlotOverride  ov = {},
int  vertexSpan = 0 
)
inline

Greedy graph-colour the contacts (raw bodyA/bodyB): no two contacts sharing a body get the same colour.

Round-based max-index arbitration, identical machinery to colorManifoldsKokkos but over the per-contact graph with direct (non-realIdx) body slots; every contact is active. numBodies is the body-slot count (numParticles, incl. periodic ghosts, which the position solve corrects independently). Returns the number of colours used.

With units (docs/contact_solve_framework.md §4.3) the edges are position UNITS, not contacts: numContacts is then the unit count and cColor is per unit; a unit's edge is its leader contact's (bodyA, bodyB) with key colorKey(leader). A one-point unit is exactly today's edge.

Definition at line 180 of file solver_position.hpp.

Referenced by demSolveContacts().

◆ colorContactsIncrementalKokkos()

int peclet::dem::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 = {},
PosUnits  units = {} 
)
inline

Incremental (warm-started) contact colouring for the single-GPU PGS position solve.

Twin of colorManifoldsIncrementalKokkos, but over the per-contact graph: key every contact by its canonical pair, carry the previous substep's colour by that key (sorted ledger), seed the per-body masks, and re-arbitrate only the NEW (-1) contacts. A pair can own MORE than one contact (non-spherical multi-point patches) and periodic ghost slots churn, so the carried colours are CONFLICT-CHECKED while the mask is built (a repeated colour on a shared body forces a full recolour that substep) — the sphere / non-periodic bed hits the fast path, everything else stays correct by falling back. forceFull bypasses the carry. keysOut holds this substep's contact keys for the subsequent commit. Returns the number of colours used.

Definition at line 298 of file solver_position.hpp.

Referenced by demSolveContacts().

◆ expandUnitColorsKokkos()

void peclet::dem::expandUnitColorsKokkos ( const PosUnits &  units,
int  numUnits,
Kokkos::View< const int *, CpMem >  unitColor,
Kokkos::View< int *, CpMem >  contactColor 
)
inline

Copy each position unit's colour onto its contacts (contactColor(i) = unitColor(unit of i)).

Definition at line 458 of file solver_position.hpp.

References peclet::dem::PosUnits::begin(), peclet::dem::PosUnits::contact(), and peclet::dem::PosUnits::end().

Referenced by demSolveContacts().

◆ commitContactColorKokkos()

void peclet::dem::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 
)
inline

Commit this substep's per-contact (key, colour) sorted by key, for next substep's warm gather.

Definition at line 474 of file solver_position.hpp.

Referenced by demSolveContacts().

◆ solvePositionColoredGSKokkos()

bool peclet::dem::solvePositionColoredGSKokkos ( Kokkos::View< const ContactC *, CpMem >  contacts,
const PosUnits &  units,
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,
SlotOverride  ov = {},
Kokkos::View< float, CpMem >  posResidual = {},
float  omega = 1.0f 
)
inline

Colored Gauss–Seidel XPBD overlap solve: sweep the numColors colour classes in order, applying each contact's non-penetration correction directly to posPred (in place, translation only).

Same per-contact math as solvePositionKokkos — only the write-back differs (in-place RMW instead of atomic-accumulate + count-average). Race-free because a colour is an independent set of contacts. One outer call = one full sweep over all colours; the caller loops it positionIterations times. Dense-bucket mode (colorPerm/colorOffs from buildColorBucketsKokkos) covers only each colour's own contacts; the fused mode collapses the whole sweep into one kernel — both bit-identical.

Definition at line 636 of file solver_position.hpp.

Referenced by demSolveContacts().

◆ countVelocityJacobiKokkos()

void peclet::dem::countVelocityJacobiKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< int *, CpMem >  counts,
bool  dedupTwins = true 
)
inline

Mass-split Jacobi count pass (docs/contact_solve_framework.md §3.1, D3): per body (REAL index), the number of manifolds the Jacobi velocity solve visits – num_points > 0, one manifold per periodic pair (the solve's realA > realB dedup); a wall side counts nothing.

Accumulates into counts, which the caller hands over zeroed (the apply clears it), exactly as the solve's own count did. Under MPI the caller then makes the counts global (syncContactCounts). dedupTwins (docs/contact_solve_framework.md §4.2 item 2, §6.2): the single-rank periodic twin rule (skip realIdx(A) > realIdx(B)); off under MPI, where ownership already excludes the non-owned twin and the velocity slot map would make the two rules disagree.

Definition at line 46 of file solver_velocity.hpp.

References peclet::dem::ManifoldC::bodyA, peclet::dem::ManifoldC::bodyB, and peclet::dem::ManifoldC::num_points.

Referenced by demSolveContacts().

◆ solveVelocityKokkos()

void peclet::dem::solveVelocityKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< const float *[3], CpMem >  invInertia,
Kokkos::View< const float *[4], CpMem >  quat,
Kokkos::View< const float *[3], CpMem >  velPred,
Kokkos::View< const float *[3], CpMem >  angVelPred,
Kokkos::View< const int *, CpMem >  realIdx,
float  growthRate,
float  restitutionNormal,
float  restVelThreshold,
Kokkos::View< float *[3], CpMem >  deltaVel,
Kokkos::View< float *[3], CpMem >  deltaAngVel,
Kokkos::View< int *, CpMem >  velCounts,
Kokkos::View< const int *, CpMem >  onlyColor = {},
int  colorFilter = 0,
Kokkos::View< const unsigned char *, CpMem >  persistent = {},
Kokkos::View< const float *[3], CpMem >  posPred = {},
F3  gHat = {},
Kokkos::View< const unsigned char *, CpMem >  grounded = {},
bool  massSplit = false,
bool  dedupTwins = true 
)
inline

Accumulate normal-restitution velocity deltas for numManifolds manifolds.

massSplit (the 'jacobi' diagnostic, §3.1): velCounts holds the global per-body counts n of countVelocityJacobiKokkos and is only read; each manifold is solved against copies of mass m / n (w~ = n_A w_A + n_B w_B, a wall side 0) and the TRUE-mass deltas are accumulated, to be applied with factor 1. Off: the solve bumps velCounts and the caller count-averages the sum – the pre-framework count-averaged Jacobi, kept ONLY for the kernel unit test against its serial reference; no production path calls it (the colour-saturation fallbacks were deleted with complete colouring, docs/contact_solve_framework.md §4.2 / §12 S3).

Definition at line 76 of file solver_velocity.hpp.

Referenced by demSolveContacts().

◆ applyVelocityDeltasAveragedKokkos()

template<class V3 , class Vi >
void peclet::dem::applyVelocityDeltasAveragedKokkos ( int  n,
V3  velPred,
V3  angVelPred,
V3  deltaVel,
V3  deltaAngVel,
Vi  velCounts,
bool  averaged = true 
)
inline

Apply the accumulated velocity deltas AVERAGED by the per-body manifold count — the velocity- solve twin of the position solve's constraint-count averaging (applyUpdatesKokkos).

A body in a dense pile receives one full-strength impulse per touching manifold; the raw Jacobi SUM overshoots by ~the contact count and diverges hard for e ≳ 0.5 (a settled 180k glass-bead bed switched to e=0.8 grew |v| by ~5x per substep). A lone binary collision (count 1) is unchanged. averaged = false (the mass-split 'jacobi' diagnostic, §3.1): the deltas are already true-mass impulses against copies of mass m / count, so the apply is a plain add (factor 1).

Definition at line 279 of file solver_velocity.hpp.

Referenced by demSolveContacts().

◆ colorManifoldsKokkos()

int peclet::dem::colorManifoldsKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const int *, CpMem >  realIdx,
int  numReal,
Kokkos::View< int *, CpMem >  mColor,
Kokkos::View< long long *, CpMem >  bodyWinner,
Kokkos::View< std::uint64_t *, CpMem >  bodyMask,
int &  leftover,
Kokkos::View< const unsigned char *, CpMem >  sleepMask = {},
SlotOverride  ov = {},
bool  dedupTwins = true,
int  vertexSpan = 0 
)
inline

Greedy graph-colour the manifolds: no two manifolds sharing a real body get the same colour.

Round-based max-index (Jones–Plassmann) arbitration, no adjacency lists: each round every still- uncoloured manifold contends for its endpoint bodies via atomicMax(bodyWinner, idx); a manifold that wins BOTH endpoints has no uncoloured conflict this round, so it commits the lowest colour free at either endpoint (a per-body bitmask) — and, as the unique winner of those bodies, updates the masks race-free. Inactive manifolds (empty, or the periodic-dedup duplicate realA>realB) are tagged -2 and skipped by the solve. Runs once per step; the colouring is reused across the velocity sweeps. Returns the number of colours used (0 if no active manifolds).

Definition at line 323 of file solver_velocity.hpp.

Referenced by demSolveContacts().

◆ colorManifoldsIncrementalKokkos()

int peclet::dem::colorManifoldsIncrementalKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const int *, CpMem >  realIdx,
int  numReal,
Kokkos::View< const unsigned long long *, CpMem >  prevKeys,
Kokkos::View< const int *, CpMem >  prevColor,
int  prevCount,
Kokkos::View< int *, CpMem >  mColor,
Kokkos::View< long long *, CpMem >  bodyWinner,
Kokkos::View< std::uint64_t *, CpMem >  bodyMask,
int &  leftover,
bool  forceFull,
Kokkos::View< const unsigned char *, CpMem >  sleepMask = {},
bool  dedupTwins = true 
)
inline

Incremental (warm-started) manifold colouring for the single-GPU PGS path.

Carry each surviving pair's colour from the previous substep — matched by pair key against the sorted (prevPairKeys, prevColor) ledger committed alongside the warm impulses — seed the per-body colour masks from those carried colours, then run the SAME Jones-Plassmann arbitration ONLY over the NEW (colour == -1) manifolds. Result is bit-identical in FORM to colorManifoldsKokkos (a valid body-disjoint colouring + numColors); only the assignment differs.

VALIDITY (why the carried colours need no re-arbitration): within a single-GPU run realIndices are stable, so a surviving pair key maps to the same two real bodies. Last substep's colouring was valid, so any two survivors sharing a real body carried DIFFERENT colours — the survivor subset is still a valid colouring. A conflict edge can only appear via a NEW manifold, which is arbitrated against the frozen (carried) masks. Hence seeding the masks non-atomically-safe with atomic_or and running the rounds over the -1 set reproduces a valid colouring. forceFull bypasses the carry entirely (fresh substep / colour-count-creep recompaction) — identical output to colorManifoldsKokkos. Single-GPU only: under MPI migration a carried colour can cross into a neighbourhood it never arbitrated against, so the distributed path keeps the full recolour.

Definition at line 458 of file solver_velocity.hpp.

Referenced by demSolveContacts().

◆ computeVn0Kokkos()

void peclet::dem::computeVn0Kokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const float *[3], CpMem >  velPred,
Kokkos::View< const float *[3], CpMem >  angVelPred,
Kokkos::View< const int *, CpMem >  realIdx,
float  growthRate,
Kokkos::View< float *, CpMem >  vn0,
Kokkos::View< float *[3], CpMem >  vt0,
bool  dedupTwins = true 
)
inline

-— Warm-started projected Gauss-Seidel (PGS) velocity solve -— Nonsmooth contact dynamics (Moreau-Jean; Stewart-Trinkle LCP): per manifold an ACCUMULATED push impulse p >= 0 along the manifold normal, updated by colored GS sweeps and projected to p >= 0 (a contact may push, never pull; over-push is retracted in later sweeps).

At convergence the p's are the contact force network (x dt): a resting pile's chains carry exactly the weight above them, all velocities -> 0 – statics with no notion of "lower body" or gravity direction, momentum-conserving at every contact (the wall provides the reaction). Deep-pile convergence is bought by WARM STARTING: p is seeded from the previous substep's converged value (matched by pair key) and applied up front, so a static network is re-established in ~1 sweep per substep. Restitution enters as the target relative velocity -e*vn0 on the PRE-SOLVE approach vn0 (with the resting threshold), so fresh binary impacts reproduce the one-shot impulse exactly. Pre-solve approach velocity per manifold (the restitution bias), measured BEFORE the warm-start application. Same kinematics as the sweep kernel.

Definition at line 615 of file solver_velocity.hpp.

References add3(), peclet::dem::ManifoldC::bodyA, peclet::dem::ManifoldC::bodyB, cross3v(), dot3(), peclet::dem::detail::ld3(), peclet::dem::ManifoldC::normal_sum, peclet::dem::ManifoldC::num_points, peclet::dem::ManifoldC::rA_sum, peclet::dem::ManifoldC::rB_sum, scale3(), sub3(), peclet::dem::ManifoldC::torque_armA_sum, peclet::dem::ManifoldC::torque_armB_sum, peclet::dem::ManifoldC::wallVel_sum, peclet::dem::F3::x, peclet::dem::F4::x, peclet::dem::F4::y, and peclet::dem::F4::z.

Referenced by demSolveContacts().

◆ updateRestitutionBankKokkos()

void peclet::dem::updateRestitutionBankKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< const float *[3], CpMem >  invInertia,
Kokkos::View< const float *[4], CpMem >  quat,
Kokkos::View< const float *[3], CpMem >  velPred,
Kokkos::View< const float *[3], CpMem >  angVelPred,
Kokkos::View< const int *, CpMem >  realIdx,
float  growthRate,
float  restitutionNormal,
float  restVelThreshold,
Kokkos::View< const float *, CpMem >  vn0,
Kokkos::View< const float *, CpMem >  lambdaAcc,
Kokkos::View< const float *, CpMem >  restRel,
Kokkos::View< float *, CpMem >  restBank,
Kokkos::View< float *, CpMem >  restVPeak,
bool  dedupTwins = true 
)
inline

Event-level (Poisson) restitution bookkeeping, once per substep AFTER all velocity phases (restitutionModel == 1 only).

Per pair, two carried floats define the current impact EVENT:

  • restVPeak — the event's peak physical approach speed (> 0 = event active). Set/refreshed whenever the pre-solve approach is kinetic (v0til > vRest*lenN), decayed by 1/256 per substep, cleared once it ages below the resting threshold: a buried/absorbed event's bank EVAPORATES (energy went to heat) instead of popping the pile later. A resting pile never sets vPeak, so it never banks — the dense-pile energy-bomb guard.
  • restBank — the remaining OWED separation impulse: while the event is active the contact banks e x its applied normal-impulse FLUX every substep (pTot - pR, the momentum the chain actually transmitted; a per-substep kinetic gate would miss the co-moving compression plateau where vn0 ~ 0), minus the separation already delivered (pR — so a clean one-substep binary impact nets exactly 0), minus what the sweep's release channel injected (restRel).

Definition at line 685 of file solver_velocity.hpp.

References peclet::dem::ManifoldC::bodyA, peclet::dem::ManifoldC::bodyB, dot3(), peclet::dem::detail::genInvMass(), peclet::dem::detail::ld3(), peclet::dem::ManifoldC::normal_sum, peclet::dem::ManifoldC::num_points, peclet::dem::ManifoldC::rA_sum, peclet::dem::ManifoldC::rB_sum, peclet::dem::ManifoldC::restitution_sum, scale3(), sub3(), peclet::dem::ManifoldC::torque_armA_sum, peclet::dem::ManifoldC::torque_armB_sum, peclet::dem::ManifoldC::wallVel_sum, peclet::dem::F3::x, peclet::dem::F4::x, peclet::dem::F4::y, and peclet::dem::F4::z.

Referenced by demSolveContacts().

◆ decayBodyOrphanKokkos()

void peclet::dem::decayBodyOrphanKokkos ( Kokkos::View< float *, CpMem >  orphan,
Kokkos::View< float *, CpMem >  orphanVPeak,
int  numOwned,
float  restVelThreshold 
)
inline

Orphan-account aging, once per substep over the OWNED bodies: both the balance and the carried event peak decay 1/64 per substep (e-fold ~3 ms at dt = 5e-5 — long enough for the rebound payout, which completes within ~2 ms of turnaround, short enough that a BURIED impactor's slowly re-fed credit cannot keep fluidizing its crater: with the pair-state 1/256 decay the 100k Dosta plateau crept 0.007 deeper), and once the peak ages below the resting threshold the whole account evaporates — stranded credit on a settling body never pops the pile later.

Definition at line 784 of file solver_velocity.hpp.

Referenced by demSolveContacts().

◆ scatterOrphanBanksKokkos()

void peclet::dem::scatterOrphanBanksKokkos ( Kokkos::View< const unsigned long long *, CpMem >  prevKeys,
Kokkos::View< const float *, CpMem >  prevRestBank,
Kokkos::View< const float *, CpMem >  prevRestVPeak,
Kokkos::View< const unsigned char *, CpMem >  matched,
int  prevCount,
Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< float *, CpMem >  orphan,
Kokkos::View< float *, CpMem >  orphanVPeak,
Kokkos::View< const int *, CpMem >  gidSorted = {},
Kokkos::View< const int *, CpMem >  slotSorted = {},
int  ownedLimit = -1 
)
inline

Orphan transfer: previous-ledger entries NOT matched by any current manifold (their pair died this substep) credit their remaining owed budget to the endpoint BODIES, mass-weighted — the heavier endpoint keeps the larger share (energy of a later release scales J^2/2m, so the light grain is the dangerous store and the heavy impactor is both the safe one and the event's physical carrier); boundary pairs (wall endpoint) credit everything to the particle.

The carried event peak joins by max. Key components are keyIdx identities: REAL slots on the single-GPU path (direct index); global ids under MPI — resolved through the sorted (gidSorted, slotSorted) map over owned + ghost slots. Under MPI the pair's bank lives only in its owning rank's ledger (docs/mpi_momentum_conservation.md §4.6): a credit to a ghost endpoint is delivered to that body's owner by the velocity reverse at the next sync, and an endpoint that has left this rank's halo is not found (it gets the conservative half, and that half is dropped).

Definition at line 814 of file solver_velocity.hpp.

Referenced by demSolveContacts().

◆ restBankStatsKokkos()

std::tuple< double, float, int > peclet::dem::restBankStatsKokkos ( Kokkos::View< const float *, CpMem >  bank,
int  n 
)
inline

Poisson-restitution diagnostics: (sum, max, count>0) over the committed owed-impulse store (namespace scope: nvcc forbids KOKKOS_LAMBDA in member functions).

Definition at line 882 of file solver_velocity.hpp.

Referenced by peclet::dem::Simulation::restBankStats(), and peclet::dem::Simulation::restOrphanStats().

◆ computeSideFlagsKokkos()

void peclet::dem::computeSideFlagsKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const unsigned char *, CpMem >  persistent,
Kokkos::View< const unsigned char *, CpMem >  grounded,
Kokkos::View< const float *[3], CpMem >  posPred,
Kokkos::View< const float *[3], CpMem >  velPred,
F3  gHat,
float  riseThr,
Kokkos::View< unsigned char *, CpMem >  sideFlag,
Kokkos::View< const float *, CpMem >  vn0,
float  approachThr,
bool  dedupTwins = true 
)
inline

Decide each persistent contact's treatment ONCE per substep (before any impulse is applied): 0 = symmetric momentum-conserving PGS, 1 = one-sided with B as the held ground side, 2 = A held.

One-sided requires the ground side grounded (contact path to the floor) and not rising. Flagged contacts get their warm impulse ZEROED: their ground side is held externally (recursively down to the floor), so the correct per-substep impulse is only the ~m g dt refill, accumulated from zero – warm-applying last substep's chain impulse one-sidedly would inject the whole column weight as upward velocity (measured: instant crush + churn from the inconsistent ledger).

Definition at line 910 of file solver_velocity.hpp.

References peclet::dem::ManifoldC::bodyA, peclet::dem::ManifoldC::bodyB, dot3(), peclet::dem::detail::ld3(), ldF3(), peclet::dem::ManifoldC::num_points, sub3(), peclet::dem::F3::x, peclet::dem::F3::y, and peclet::dem::F3::z.

Referenced by demSolveContacts().

◆ warmStartApplyKokkos()

void peclet::dem::warmStartApplyKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< const float *[3], CpMem >  invInertia,
Kokkos::View< const float *[4], CpMem >  quat,
Kokkos::View< float *[3], CpMem >  velPred,
Kokkos::View< float *[3], CpMem >  angVelPred,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const float *, CpMem >  warmP,
Kokkos::View< float *[3], CpMem >  warmT,
bool  dedupTwins = true 
)
inline

◆ makePGSManifoldSweep()

PGSManifoldSweep peclet::dem::makePGSManifoldSweep ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< const float *[3], CpMem >  invInertia,
Kokkos::View< const float *[4], CpMem >  quat,
Kokkos::View< float *[3], CpMem >  velPred,
Kokkos::View< float *[3], CpMem >  angVelPred,
Kokkos::View< const int *, CpMem >  realIdx,
float  growthRate,
float  restitutionNormal,
float  restVelThreshold,
Kokkos::View< float, CpMem >  maxApproach,
Kokkos::View< float, CpMem >  maxApproachQS,
Kokkos::View< float *, CpMem >  lambdaAcc,
Kokkos::View< const float *, CpMem >  vn0,
Kokkos::View< const unsigned char *, CpMem >  sideFlag,
Kokkos::View< float *[3], CpMem >  lambdaT,
float  frictionDynamic,
Kokkos::View< const float *[3], CpMem >  vt0,
float  restitutionTangent,
Kokkos::View< const float *, CpMem >  posImpulse,
Kokkos::View< float *, CpMem >  restBank,
Kokkos::View< float *, CpMem >  restRel,
Kokkos::View< const float *, CpMem >  restVPeak,
Kokkos::View< float *, CpMem >  restOrphan,
Kokkos::View< const float *, CpMem >  restOrphanVPeak,
SlotOverride  ov = {} 
)
inline

Build the shared per-manifold sweep functor (the colored launch loop, the fused kernels and the fused multilevel iteration loop all run this one body).

maxApproachQS empty => aliased to maxApproach (the duplicate atomic_max is idempotent).

Definition at line 1489 of file solver_velocity.hpp.

Referenced by demSolveContacts().

◆ solveVelocityPGSKokkos()

bool peclet::dem::solveVelocityPGSKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const int *, CpMem >  mColor,
int  numColors,
Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< const float *[3], CpMem >  invInertia,
Kokkos::View< const float *[4], CpMem >  quat,
Kokkos::View< float *[3], CpMem >  velPred,
Kokkos::View< float *[3], CpMem >  angVelPred,
Kokkos::View< const int *, CpMem >  realIdx,
float  growthRate,
float  restitutionNormal,
float  restVelThreshold,
Kokkos::View< float, CpMem >  maxApproach,
Kokkos::View< float *, CpMem >  lambdaAcc,
Kokkos::View< const float *, CpMem >  vn0,
Kokkos::View< const unsigned char *, CpMem >  sideFlag,
Kokkos::View< float *[3], CpMem >  lambdaT,
float  frictionDynamic,
Kokkos::View< const float *[3], CpMem >  vt0 = {},
float  restitutionTangent = 0.0f,
Kokkos::View< const float *, CpMem >  posImpulse = {},
Kokkos::View< float, CpMem >  maxApproachQS = {},
Kokkos::View< float *, CpMem >  restBank = {},
Kokkos::View< float *, CpMem >  restRel = {},
Kokkos::View< const float *, CpMem >  restVPeak = {},
Kokkos::View< float *, CpMem >  restOrphan = {},
Kokkos::View< const float *, CpMem >  restOrphanVPeak = {},
Kokkos::View< const int *, CpMem >  colorPerm = {},
const std::vector< int > *  colorOffs = nullptr,
const FusedSweepCtx *  fused = nullptr,
const FusedLoopSpec *  loop = nullptr,
SlotOverride  ov = {} 
)
inline

Returns true when the sweep (or, with loop, the whole iteration loop) was submitted; false ONLY in loop mode when the fused path is unavailable — the caller then runs its own host-side iteration loop (which calls back in per-iteration mode).

Definition at line 1534 of file solver_velocity.hpp.

Referenced by demSolveContacts().

◆ buildLevelColorBucketsKokkos()

void peclet::dem::buildLevelColorBucketsKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const int *, CpMem >  mColor,
Kokkos::View< const int *, CpMem >  heights,
Kokkos::View< int *, CpMem >  keys,
Kokkos::View< int *, CpMem >  perm,
std::vector< std::pair< int, int > > &  buckets 
)
inline

Bucket the active coloured manifolds by (support level, colour) for the level-ordered ("multilevel") stabilization sweeps.

A manifold's level is the smaller of its bodies' height-from-floor BFS levels (wall/plane manifolds sit at their body's level, i.e. 0), clamped to 1023; ungrounded manifolds land in the last buckets. key = level*64 + colour; key-sorting a permutation groups each (level, colour) bucket contiguously, and the host bucket list (begin, end into the permutation) drives the ordered sweeps. Inactive and uncoloured manifolds (colour < 0: periodic dups, empty) are keyed out entirely. (Complete colouring leaves no active manifold uncoloured: docs/contact_solve_framework.md §4.2.)

Definition at line 1610 of file solver_velocity.hpp.

References peclet::dem::ManifoldC::bodyA, peclet::dem::ManifoldC::bodyB, and peclet::dem::ManifoldC::num_points.

Referenced by demSolveContacts().

◆ solveVelocityPGSBucketsKokkos()

void peclet::dem::solveVelocityPGSBucketsKokkos ( const PGSManifoldSweep &  f,
Kokkos::View< const int *, CpMem >  perm,
const std::vector< std::pair< int, int > > &  buckets,
bool  topDown 
)
inline

Level-ordered symmetric sweep: launch one PGS kernel per (level, colour) bucket, ascending (bottom-up: a chain's load drains towards the floor in ~one pass) or descending (top-down: the return pass carries rebound waves back up).

Same impulse math and lambda >= 0 projection as the colored sweep (shared PGSManifoldSweep) – fully symmetric, exact momentum conservation; the ordering only accelerates transport. Launches are stream-ordered, so the Gauss-Seidel dependency between buckets needs no per-bucket fence.

Definition at line 1662 of file solver_velocity.hpp.

References peclet::dem::PGSManifoldSweep::solveOne().

Referenced by demSolveContacts().

◆ xPopcount()

int peclet::dem::xPopcount ( unsigned long long  m)

Rank-level X (docs/contact_solve_framework.md §1.4): the holder of a body whose velocity activity mask has several bits (ranks) in sync interval t – the colour t mod C if it is in the mask, else the (t mod popcount)-th set bit (lowest = 0th).

Every rank evaluates the same mask and t, so all ranks agree on the holder without communication.

Definition at line 1681 of file solver_velocity.hpp.

Referenced by computeXGateKokkos(), peclet::dem::XGateSpec::fires(), and xHolder().

◆ xHolder()

int peclet::dem::xHolder ( unsigned long long  mask,
long long  t,
int  numColors 
)

Definition at line 1689 of file solver_velocity.hpp.

References xPopcount().

Referenced by computeXGateKokkos(), and peclet::dem::XGateSpec::fires().

◆ computeXGateKokkos()

void peclet::dem::computeXGateKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  nm,
Kokkos::View< const int *, CpMem >  realIdx,
Kokkos::View< const unsigned long long *, CpMem >  velMask,
Kokkos::View< unsigned char *, CpMem >  gate,
int  color,
int  numColors,
long long  t 
)
inline

The per-manifold fire gate of sync interval t: 1 iff this rank (colour color) holds every endpoint whose mask names two or more ranks.

Masks live at the canonical velocity slot realIdx(body), so every image and hub copy of one body on this rank shares the decision.

Definition at line 1726 of file solver_velocity.hpp.

References peclet::dem::ManifoldC::bodyA, peclet::dem::ManifoldC::bodyB, xHolder(), and xPopcount().

Referenced by demSolveContacts().

◆ countUnfiredKokkos()

long long peclet::dem::countUnfiredKokkos ( Kokkos::View< const unsigned char *, CpMem >  gate,
int  nm 
)
inline

Diagnostics (iterCounters): the number of gated-off manifolds of one sweep.

Definition at line 1747 of file solver_velocity.hpp.

Referenced by demSolveContacts().

◆ solveVelocityColoredGSKokkos()

void peclet::dem::solveVelocityColoredGSKokkos ( Kokkos::View< const ManifoldC *, CpMem >  manifolds,
int  numManifolds,
Kokkos::View< const int *, CpMem >  mColor,
int  numColors,
Kokkos::View< const float *, CpMem >  invMass,
Kokkos::View< const float *[3], CpMem >  invInertia,
Kokkos::View< const float *[4], CpMem >  quat,
Kokkos::View< float *[3], CpMem >  velPred,
Kokkos::View< float *[3], CpMem >  angVelPred,
Kokkos::View< const int *, CpMem >  realIdx,
float  growthRate,
float  restitutionNormal,
float  restVelThreshold,
Kokkos::View< float, CpMem >  maxApproach,
Kokkos::View< const unsigned char *, CpMem >  persistent = {},
Kokkos::View< const float *[3], CpMem >  posPred = {},
F3  gHat = {},
Kokkos::View< const unsigned char *, CpMem >  grounded = {},
SlotOverride  ov = {},
XGateSpec  xg = {} 
)
inline

Colored Gauss–Seidel normal-restitution solve: sweep the numColors colour classes in order, applying each manifold's impulse directly to velPred/angVelPred (in place).

Same per-manifold impulse math as solveVelocityKokkos (growth-velocity term, approach gate, resting-contact e=0 threshold, and the R·(invI_local·(Rᵀ J))·… world-space angular update) — only the write-back differs (in-place RMW instead of atomic-accumulate + count-average). Race-free because a colour is an independent set of manifolds. One outer call = one full sweep over all colours; the caller loops it velocityIterations times.

Definition at line 1762 of file solver_velocity.hpp.

Referenced by demSolveContacts().

◆ growContactBuffers()

void peclet::dem::growContactBuffers ( Particles &  P,
long  nBodies,
long  floorWant = 0 
)
inline

Grow every maxContacts-sized collision buffer to match the CURRENT particle capacity.

Sized from capacity, not numReal: periodic ghost slots take part in the narrow phase, so a buffer sized when capacity was smaller is an out-of-bounds write the moment the ghost band grows it. That is exactly what happened – demStep calls ensureCapacity every step (the ghost headroom is numReal + estGhosts + 4096), so a Simulation constructed with a small capacity had its contact buffers frozen at the construction size while the particle SoA grew by an order of magnitude around them. Found via peclet-examples/pall-ring-packing, where it showed up twice: boundary contacts silently dropped (scene-shape grains falling through an add_plane floor) and heap corruption inside step() at ~2000 steps.

nBodies is the count the setup-time bound is taken over (capacity, exactly as the original sizing did, so every existing run is byte-identical). floorWant is the per-step path: the narrow phase knows the contact count it actually needs and asks for it directly. A per-PARTICLE bound is not safe for a many-probe composed shape – a pair can contribute up to shellPoints contacts and a grain has several neighbours – and sizing from the ghost capacity is worse still, since calculateGhostCapacity's +4096 slots of slack alone ask for 13 million contacts (several GB across the ~40 buffers) with a 1625-probe particle. See narrowPhaseGrow.

Definition at line 42 of file step_solve.hpp.

References peclet::dem::Particles::commitPerm, peclet::dem::Particles::contactColor, peclet::dem::Particles::contactKeys, peclet::dem::Particles::contacts, peclet::dem::Particles::contactSleep, peclet::dem::Particles::contactSlot, peclet::dem::Particles::lambdaAcc, peclet::dem::Particles::lambdaT, peclet::dem::Particles::levelKey, peclet::dem::Particles::levelPerm, peclet::dem::Particles::manifoldColor, peclet::dem::Particles::manifoldPersistent, peclet::dem::Particles::manifolds, peclet::dem::Particles::manifoldSleep, peclet::dem::Particles::maxContacts, peclet::dem::Particles::mlColorPacked, peclet::dem::Particles::numWalls, peclet::dem::Particles::pairKeys, peclet::dem::Particles::posCommitPerm, peclet::dem::Particles::posImpulse, peclet::dem::Particles::posLambdaContact, peclet::dem::Particles::posPerm, peclet::dem::Particles::posPrevContactCount, peclet::dem::Particles::prevContactColor, peclet::dem::Particles::prevContactKeys, peclet::dem::Particles::prevLambda, peclet::dem::Particles::prevLambdaT, peclet::dem::Particles::prevManifoldColor, peclet::dem::Particles::prevMatched, peclet::dem::Particles::prevPairCount, peclet::dem::Particles::prevPairKeys, peclet::dem::Particles::prevPosImpulse, peclet::dem::Particles::prevRestBank, peclet::dem::Particles::prevRestVPeak, peclet::dem::Particles::restBank, peclet::dem::Particles::restRel, peclet::dem::Particles::restVPeak, peclet::dem::Particles::shellDetection, peclet::dem::Particles::shellPoints, peclet::dem::Particles::sideFlags, peclet::dem::Particles::unitColor, peclet::dem::Particles::unitContacts, peclet::dem::Particles::unitStart, peclet::dem::Particles::velPerm, peclet::dem::Particles::vn0, and peclet::dem::Particles::vt0.

Referenced by peclet::dem::ShapeRegistry::ensureContactCapacity(), and narrowPhaseGrow().

◆ narrowPhaseGrow()

int peclet::dem::narrowPhaseGrow ( Particles &  P,
int  np,
float  margin 
)
inline

Narrow phase with an automatically-grown contact buffer.

The three detect kernels guard their contact WRITES at the buffer extent, but P.contactCount is the RAW number of contacts found – and every consumer downstream (manifold reduction, the sleeping masks, the whole solve) uses that count as a loop bound over maxContacts-sized views. When the raw count exceeds the buffer, those loops walk off the end: "corrupted double-linked list" on a host backend, cudaErrorIllegalAddress on a device one. Exactly the failure mode findCollisionsGrow already handles for the broad-phase pair buffer, and handled the same way – detect the overflow, grow with headroom, re-run once so no contact is silently dropped, then clamp defensively so the returned count is ALWAYS <= the extent.

This is what actually bounds the contact buffer. Sizing it from the particle count (capacity or numParticles) was only ever a proxy: a pair can contribute up to shellPoints contacts and a grain has several neighbours, so no per-particle bound is safe for a many-probe composed shape. Found via peclet-examples/pall-ring-packing (48 rings x 1625 probes).

Definition at line 118 of file step_solve.hpp.

References peclet::dem::Particles::contactCount, peclet::dem::Particles::contacts, detectBoundaryKokkos(), detectContactsKokkos(), detectWallSdfKokkos(), peclet::dem::Particles::globalScale, growContactBuffers(), peclet::dem::Particles::materialId, peclet::dem::Particles::maxContacts, peclet::dem::Particles::maxOverlap, peclet::dem::Particles::numPlanes, peclet::dem::Particles::numReal, peclet::dem::Particles::numWalls, peclet::dem::Particles::pairMaterials, peclet::dem::Particles::pairs, peclet::dem::Particles::planes, peclet::dem::Particles::posPred, peclet::dem::Particles::quatPred, readInt(), peclet::dem::Particles::scale, peclet::dem::Particles::sdfGrid, peclet::dem::Particles::shapeId, peclet::dem::Particles::shapes, peclet::dem::Particles::shell, peclet::dem::Particles::shellDetection, peclet::dem::Particles::wallGrid, and peclet::dem::Particles::walls.

Referenced by computeOverlapsKokkos(), and demStep().

◆ demStep()

void peclet::dem::demStep ( Particles &  P)
inline

One full XPBD DEM substep over the particle SoA (mirrors simulation.cpp Simulation::step()).

Definition at line 144 of file step_solve.hpp.

References peclet::dem::Particles::angVel, peclet::dem::Particles::angVelPred, applyThermostatKokkos(), peclet::dem::Particles::asleep, peclet::dem::Particles::baseRadius, buildInvMassEffKokkos(), calculateGhostCapacity(), peclet::dem::Particles::capacity, computeContactSleepKokkos(), computeManifoldSleepKokkos(), peclet::dem::Particles::constraintCounts, peclet::dem::Particles::contactCount, peclet::dem::Particles::contacts, peclet::dem::Particles::contactSleep, peclet::dem::Particles::contactSlot, peclet::dem::Particles::deltaAngVel, peclet::dem::Particles::deltaPos, peclet::dem::Particles::deltaQuat, peclet::dem::Particles::deltaVel, demSolveContacts(), peclet::dem::Particles::domain, peclet::dem::Particles::dt, peclet::dem::Particles::ensureCapacity(), peclet::dem::Particles::extForce, peclet::dem::Particles::extForceActive, peclet::dem::Particles::extTorque, peclet::dem::Particles::extTorqueActive, fillWorldRadiiKokkos(), finalCommitKokkos(), findCollisionsGrow(), findCollisionsVerlet(), freezeAsleepKokkos(), generateGhostsKokkos(), peclet::dem::Particles::gid, peclet::dem::Particles::globalScale, peclet::dem::Particles::gravity, peclet::dem::Particles::groundedLevel, growCopyView(), peclet::dem::Particles::growthFactor, peclet::dem::Particles::growthRate, peclet::dem::Particles::imageShift, peclet::dem::Particles::invInertia, peclet::dem::Particles::invMass, peclet::dem::Particles::invMassEff, peclet::dem::Particles::manifoldCount, peclet::dem::Particles::manifolds, peclet::dem::Particles::manifoldSleep, peclet::dem::Particles::materialId, peclet::dem::Particles::maxOverlap, maxOwnedRadius(), narrowPhaseGrow(), peclet::dem::Particles::numParticles, peclet::dem::Particles::numReal, peclet::dem::Domain::periodic_x, peclet::dem::Domain::periodic_y, peclet::dem::Domain::periodic_z, peclet::dem::Particles::pos, peclet::dem::Particles::posPred, predictVelocityKokkos(), peclet::dem::Particles::quat, peclet::dem::Particles::quatPred, peclet::dem::Particles::rad, readInt(), peclet::dem::Particles::realIndices, reduceContactsToManifoldsKokkos(), peclet::dem::Particles::scale, peclet::dem::Particles::shapeId, peclet::dem::Particles::sleepCounter, peclet::dem::Particles::sleepCurCount, peclet::dem::Particles::sleepImmovableFrac, peclet::dem::Particles::sleepingEnabled, peclet::dem::Particles::sleepK, peclet::dem::Particles::sleepMovingWall, peclet::dem::Particles::sleepPrevCount, peclet::dem::Particles::sleepScale, peclet::dem::Particles::sleepWakeLostContact, peclet::dem::Particles::targetScale, peclet::dem::Particles::thermostatKB, peclet::dem::Particles::thermostatTau, peclet::dem::Particles::thermostatTemp, peclet::dem::Particles::topGhost, updateGrowthScalesKokkos(), updateSleepKokkos(), peclet::dem::Particles::vel, peclet::dem::Particles::velPred, peclet::dem::Particles::verletSkinFrac, wakeContactChangeKokkos(), wakeDisturbedKokkos(), peclet::dem::Particles::wakeScale, peclet::dem::F3::x, peclet::dem::F3::y, and peclet::dem::F3::z.

Referenced by peclet::dem::Simulation::relax(), and peclet::dem::Simulation::step().

◆ computeOverlapsKokkos()

Variable Documentation

◆ kLevelInf

constexpr int peclet::dem::kLevelInf = 1 << 28
inlineconstexpr

Height-from-floor BFS levels for the level-ordered ("multilevel") stabilization pass: 0 at a wall/plane contact, else 1 + min over supporting contacts, kLevelInf with no contact path to the floor.

Recomputed FRESH each time the pass triggers – no warm start and no decay: a stale-low height would mis-order the sweeps, and the pass runs rarely enough that the exact BFS (early exit once a sweep changes nothing) is affordable. The support-orientation test (up vs 0.3|dx|) matches updateGroundedLevelsKokkos; ghost-slot positions keep the pair geometry periodic-aware.

Definition at line 319 of file contact_preprocessing.hpp.

Referenced by computeHeightLevelsKokkos().

◆ kNonOwnedPairBit

constexpr std::uint64_t peclet::dem::kNonOwnedPairBit = 1ull << 63
inlineconstexpr

Distributed step: bit 63 of a manifold-reduction key marks a contact this rank sees but does not own (docs/mpi_momentum_conservation.md §2.2), so the sort puts the owned manifolds first.

Slots are < 2^31, so no single-rank key ever has it set.

Definition at line 429 of file contact_preprocessing.hpp.

◆ kColorPalette

constexpr int peclet::dem::kColorPalette = 64
inlineconstexpr

Colouring palette (docs/contact_solve_framework.md §4.2 item 1): the greedy takes the lowest colour free at both endpoints among bits 0..63 of the uint64 mask and never forces one.

An edge with no free colour is marked kColorUncolourable; it is neither coloured nor remaining, so the arbitration terminates, and it counts as leftover (after hub copies: an invariant violation).

Definition at line 435 of file contact_preprocessing.hpp.

◆ kColorUncolourable

constexpr int peclet::dem::kColorUncolourable = -3
inlineconstexpr

◆ kMaxMaterials

constexpr int peclet::dem::kMaxMaterials = 8
constexpr

Pair-material lookup: flat [K*K*2] table, entry ((a*K + b)*2) = restitution, +1 = friction.

Definition at line 99 of file narrowphase.hpp.

Referenced by hertzPairEMu(), and peclet::dem::Simulation::setPairMaterial().

◆ kPi

constexpr double peclet::dem::kPi = 3.14159265358979323846
inlineconstexpr

Definition at line 23 of file shapes_portable.hpp.

Referenced by genCylinderShell().

◆ kHubEdgeBudget

constexpr int peclet::dem::kHubEdgeBudget = 32
inlineconstexpr

Hub edge budget (§4.4): a colouring vertex with more active edges is split into ceil(d / kHubEdgeBudget) copies, so no copy carries more than 32 and the 64-colour greedy provably succeeds (§1.6).

Definition at line 32 of file solve_copies.hpp.

Referenced by buildHubCopiesKokkos().

◆ kSplitOmegaVelocity

constexpr float peclet::dem::kSplitOmegaVelocity = 1.0f
inlineconstexpr

Split relaxation (§4.5, §13.1): the PGS normal step on an edge touching a mass-split slot is scaled by kSplitOmegaVelocity before the clamp (1: no relaxation; the pre-designed lever of R-F2, legitimate there because the PGS normal multiplier is accumulated and its clamp can retract an overshoot).

The overlap projection is the accumulated, retractable form since WO-12 (projected SOR on each contact's net push at P.positionOmega = 1.5, solver_position.hpp); the non-accumulated POCS of §13.1 could not retract and was held at omega 1. At omega 1 each update of that old form was an exact projection in the split metric, the local fold and the rank reconciliation are exact projections onto the consensus subspaces, and cyclic projection onto these half-spaces and subspaces converges to a feasible point (Fejer-monotone; which point depends on the order, as for serial POCS).

Definition at line 43 of file solve_copies.hpp.

Referenced by demSolveContacts().

◆ kMlMaxLevels

constexpr int peclet::dem::kMlMaxLevels = 10
inlineconstexpr

6-bit colour slots per level in the packed word; slot value 63 = not crossing / not eligible at that level (skipped by the coarse sweeps).

Definition at line 49 of file solver_multilevel.hpp.

Referenced by demMakeMlFusedCtx().

◆ kMlSlotSkip

constexpr int peclet::dem::kMlSlotSkip = 63
inlineconstexpr

◆ kMlPendingSkipBit

constexpr long long peclet::dem::kMlPendingSkipBit = 1ll << 62
inlineconstexpr

Transient mark of the coarse colouring (docs/contact_solve_framework.md §4.4, "the multilevel coarse colouring"): an edge that won its groups but found no free colour keeps slot 63 and is skipped at this level.

Bit 62 lies above the 10 x 6 packed slot bits and is SET in every word (the build initialises colorPacked to ~0), so the mark is the bit CLEARED, and it is set again when the level's colouring ends – the packed word is bit for bit what it was without the mark.

Definition at line 56 of file solver_multilevel.hpp.