peclet-dem
Performance-portable XPBD Discrete Element Method (Kokkos + ArborX)
Loading...
Searching...
No Matches
solver_velocity.hpp
Go to the documentation of this file.
1
9#ifndef DEM_SOLVER_VELOCITY_HPP
10#define DEM_SOLVER_VELOCITY_HPP
11
12#include <Kokkos_Core.hpp>
13
14#include "contact_preprocessing.hpp" // ManifoldC, CpExec/CpMem
15#include "dem_portable.hpp"
16
17namespace peclet::dem {
18
19namespace detail {
20KOKKOS_INLINE_FUNCTION F3 ld3(Kokkos::View<const float* [3], CpMem> v, int i) {
21 return F3{v(i, 0), v(i, 1), v(i, 2)};
22}
23// v^T I_world^-1 v with I_world^-1 = R I_local^-1 R^T -> (R^T v) diag(invI_local) (R^T v).
24KOKKOS_INLINE_FUNCTION float genInvMass(F3 tau, F3 invIlocal, F4 q) {
25 const F3 t = invRotateVector(q, tau);
26 return t.x * t.x * invIlocal.x + t.y * t.y * invIlocal.y + t.z * t.z * invIlocal.z;
27}
28} // namespace detail
29
31inline void solveVelocityKokkos(Kokkos::View<const ManifoldC*, CpMem> manifolds, int numManifolds,
32 Kokkos::View<const float*, CpMem> invMass,
33 Kokkos::View<const float* [3], CpMem> invInertia,
34 Kokkos::View<const float* [4], CpMem> quat,
35 Kokkos::View<const float* [3], CpMem> velPred,
36 Kokkos::View<const float* [3], CpMem> angVelPred,
37 Kokkos::View<const int*, CpMem> realIdx, float growthRate,
38 float restitutionNormal, Kokkos::View<float* [3], CpMem> deltaVel,
39 Kokkos::View<float* [3], CpMem> deltaAngVel) {
41 using detail::ld3;
42 CpExec space;
43 Kokkos::parallel_for(
44 "peclet::dem::solve_velocity", Kokkos::RangePolicy<CpExec>(space, 0, numManifolds),
45 KOKKOS_LAMBDA(int idx) {
46 const ManifoldC m = manifolds(idx);
47 if (m.num_points <= 0)
48 return;
49
50 const int idA = m.bodyA, idB = m.bodyB;
51 const int realA = realIdx(idA);
52 int realB = idB;
53 if (idB >= 0) {
54 realB = realIdx(idB);
55 if (realA > realB)
56 return; // periodic dedup
57 }
58
59 const float invMassA = invMass(realA);
60 const float invMassB = (idB >= 0) ? invMass(realB) : 0.0f;
61 const F3 invIA = ld3(invInertia, realA);
62 const F3 invIB = (idB >= 0) ? ld3(invInertia, realB) : F3{0, 0, 0};
63 const F4 qA = F4{quat(realA, 0), quat(realA, 1), quat(realA, 2), quat(realA, 3)};
64 const F4 qB = (idB >= 0)
65 ? F4{quat(realB, 0), quat(realB, 1), quat(realB, 2), quat(realB, 3)}
66 : F4{0, 0, 0, 1};
67
68 const F3 vA = ld3(velPred, realA), wA = ld3(angVelPred, realA);
69 F3 vB{0, 0, 0}, wB{0, 0, 0};
70 if (idB >= 0) {
71 vB = ld3(velPred, realB);
72 wB = ld3(angVelPred, realB);
73 }
74
75 const F3 Nsum{m.normal_sum.x, m.normal_sum.y, m.normal_sum.z};
78
79 const float invN = 1.0f / static_cast<float>(m.num_points);
80
81 // Moving-wall boundary (idB<0): the static "body B" carries the wall's surface velocity
82 // (count-averaged over the contact patch) so restitution is against the wall's motion, and
83 // its own restitution (a < 0 average keeps the global material — planes, body-body).
84 float restitution = restitutionNormal;
85 if (idB < 0) {
86 vB = scale3(F3{m.wallVel_sum.x, m.wallVel_sum.y, m.wallVel_sum.z}, invN);
87 const float ra = m.restitution_sum * invN;
88 if (ra >= 0.0f)
89 restitution = ra;
90 }
91 const F3 rAavg = scale3(F3{m.rA_sum.x, m.rA_sum.y, m.rA_sum.z}, invN);
92 const F3 rBavg = scale3(F3{m.rB_sum.x, m.rB_sum.y, m.rB_sum.z}, invN);
93
94 const float lenN = Kokkos::sqrt(dot3(Nsum, Nsum));
95 if (lenN < 1e-9f)
96 return;
97
98 // Separation vector for the approaching-sign gate + growth velocity. For a BOUNDARY (idB<0),
99 // rBavg is the ABSOLUTE wall contact point (kept absolute for the position solve's plane
100 // linearisation), NOT a body-relative lever — so rAavg - rBavg would depend on where the
101 // contact sits in world space, flipping `alignment` (and thus the approaching test) around a
102 // curved wall / a wall far from the origin and injecting energy (grains "jump" on the way
103 // down a rotating drum). The grain's own contact lever rAavg is the meaningful relative
104 // vector (dot(Nsum, rAavg) = radius > 0, a consistent convention). Body-body keeps rAavg-rBavg.
105 const F3 diffCenters = (idB < 0) ? rAavg : sub3(rAavg, rBavg);
106 const F3 vGrowth = scale3(diffCenters, growthRate);
107
108 float vn = dot3(vA, Nsum) + dot3(wA, TauA) + dot3(vB, F3{-Nsum.x, -Nsum.y, -Nsum.z}) +
109 dot3(wB, TauB);
110 vn += dot3(vGrowth, Nsum);
111
112 const float alignment = dot3(Nsum, diffCenters);
113 if (alignment > 0.0f) {
114 if (vn < 0.0f)
115 return; // sphere convention: approaching if vn>0
116 } else {
117 if (vn > 0.0f)
118 return; // inverted convention
119 }
120
121 const float Nsq = dot3(Nsum, Nsum);
122 const float wA_n = Nsq * invMassA + genInvMass(TauA, invIA, qA);
123 const float wB_n = Nsq * invMassB + genInvMass(TauB, invIB, qB);
124 const float wTotal = wA_n + wB_n;
125 if (wTotal <= 0.0f)
126 return;
127
128 const float lambda = (-restitution * vn - vn) / wTotal;
129
130 const F3 Jlin = scale3(Nsum, lambda);
131 const F3 JangA = scale3(TauA, lambda);
132 const F3 JangB = scale3(TauB, lambda);
133
134 // Linear delta on A.
135 Kokkos::atomic_add(&deltaVel(realA, 0), Jlin.x * invMassA);
136 Kokkos::atomic_add(&deltaVel(realA, 1), Jlin.y * invMassA);
137 Kokkos::atomic_add(&deltaVel(realA, 2), Jlin.z * invMassA);
138 // Angular delta on A: dw_world = R (invI_local * (R^T Jang)).
139 {
140 const F3 Jl = invRotateVector(qA, JangA);
141 const F3 dwl{Jl.x * invIA.x, Jl.y * invIA.y, Jl.z * invIA.z};
142 const F3 dww = rotateVector(qA, dwl);
143 Kokkos::atomic_add(&deltaAngVel(realA, 0), dww.x);
144 Kokkos::atomic_add(&deltaAngVel(realA, 1), dww.y);
145 Kokkos::atomic_add(&deltaAngVel(realA, 2), dww.z);
146 }
147 if (idB >= 0) {
148 Kokkos::atomic_add(&deltaVel(realB, 0), -Jlin.x * invMassB);
149 Kokkos::atomic_add(&deltaVel(realB, 1), -Jlin.y * invMassB);
150 Kokkos::atomic_add(&deltaVel(realB, 2), -Jlin.z * invMassB);
151 const F3 Jl = invRotateVector(qB, JangB);
152 const F3 dwl{Jl.x * invIB.x, Jl.y * invIB.y, Jl.z * invIB.z};
153 const F3 dww = rotateVector(qB, dwl);
154 Kokkos::atomic_add(&deltaAngVel(realB, 0), dww.x);
155 Kokkos::atomic_add(&deltaAngVel(realB, 1), dww.y);
156 Kokkos::atomic_add(&deltaAngVel(realB, 2), dww.z);
157 }
158 });
159 space.fence();
160}
161
162} // namespace peclet::dem
163
164#endif // DEM_SOLVER_VELOCITY_HPP
dem — portable (Kokkos) contact->manifold reduction, replacing the thrust-based reduce_contacts_to_ma...
dem — portable POD types + math + analytic SDFs shared by the Kokkos kernel ports.
float genInvMass(F3 tau, F3 invIlocal, F4 q)
F3 ld3(Kokkos::View< const float *[3], CpMem > v, int i)
F3 invRotateVector(F4 q, F3 v)
F3 rotateVector(F4 q, F3 v)
float dot3(F3 a, F3 b)
F3 sub3(F3 a, F3 b)
CpExec::memory_space CpMem
F3 scale3(F3 a, float s)
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, Kokkos::View< float *[3], CpMem > deltaVel, Kokkos::View< float *[3], CpMem > deltaAngVel)
Accumulate normal-restitution velocity deltas for numManifolds manifolds.
Kokkos::DefaultExecutionSpace CpExec
Portable mirror of ManifoldConstraint.