peclet-dem
Performance-portable XPBD Discrete Element Method (Kokkos + ArborX)
Loading...
Searching...
No Matches
solver_position.hpp
Go to the documentation of this file.
1
10#ifndef DEM_SOLVER_POSITION_HPP
11#define DEM_SOLVER_POSITION_HPP
12
13#include <Kokkos_Core.hpp>
14
15#include "contact_preprocessing.hpp" // ContactC, CpExec/CpMem
16#include "dem_portable.hpp"
17
18namespace peclet::dem {
19
20namespace detail {
21KOKKOS_INLINE_FUNCTION float computeW(F3 r, F3 dir, float invM, F3 invI) {
22 const F3 rn = cross3v(r, dir);
23 return invM + rn.x * rn.x * invI.x + rn.y * rn.y * invI.y + rn.z * rn.z * invI.z;
24}
25KOKKOS_INLINE_FUNCTION F4 deltaQuat(F3 dTheta, F4 q) {
26 return F4{0.5f * (dTheta.x * q.w + dTheta.y * q.z - dTheta.z * q.y),
27 0.5f * (dTheta.y * q.w + dTheta.z * q.x - dTheta.x * q.z),
28 0.5f * (dTheta.z * q.w + dTheta.x * q.y - dTheta.y * q.x),
29 0.5f * (-dTheta.x * q.x - dTheta.y * q.y - dTheta.z * q.z)};
30}
31} // namespace detail
32
34inline void solvePositionKokkos(Kokkos::View<const ContactC*, CpMem> contacts, int numContacts,
35 Kokkos::View<const float*, CpMem> invMass,
36 Kokkos::View<const float* [3], CpMem> posPred,
37 Kokkos::View<const float* [4], CpMem> quatPred,
38 Kokkos::View<const float* [4], CpMem> quatStatic,
39 Kokkos::View<const float* [3], CpMem> invInertia,
40 Kokkos::View<float* [3], CpMem> deltaPos,
41 Kokkos::View<float* [4], CpMem> deltaQuat,
42 Kokkos::View<int*, CpMem> constraintCounts,
43 Kokkos::View<float, CpMem> maxOverlap) {
44 using detail::computeW;
45 CpExec space;
46 Kokkos::parallel_for(
47 "peclet::dem::solve_position", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
48 KOKKOS_LAMBDA(int idx) {
49 const ContactC c = contacts(idx);
50 const int idA = c.bodyA, idB = c.bodyB;
51 const float invMassA = invMass(idA);
52 const float invMassB = (idB >= 0) ? invMass(idB) : 0.0f;
53
54 const F3 pA = ldF3(posPred, idA);
55 const F4 qA = ldF4(quatPred, idA);
56 F3 pB{0, 0, 0};
57 F4 qB{0, 0, 0, 1};
58 if (idB >= 0) {
59 pB = ldF3(posPred, idB);
60 qB = ldF4(quatPred, idB);
61 }
62
63 // Delta-rotate the stored lever arms / normal from the static frame to the predicted one.
64 const F4 qAdelta = quatMult(qA, quatInverse(ldF4(quatStatic, idA)));
65 F3 rA = rotateVector(qAdelta, F3{c.rA.x, c.rA.y, c.rA.z});
66 F3 rB{c.rB.x, c.rB.y, c.rB.z};
67 F3 n{c.normal.x, c.normal.y, c.normal.z};
68 if (idB >= 0) {
69 const F4 qBdelta = quatMult(qB, quatInverse(ldF4(quatStatic, idB)));
70 rB = rotateVector(qBdelta, rB);
71 n = rotateVector(qBdelta, n);
72 }
73
74 float C;
75 if (idB < 0) {
76 n = F3{c.normal.x, c.normal.y, c.normal.z}; // wall normal is static
77 const F3 pAsurf = add3(pA, rA);
78 C = dot3(sub3(pAsurf, F3{c.rB.x, c.rB.y, c.rB.z}), n);
79 } else {
80 const F3 pAc = add3(pA, rA);
81 const F3 pBc = add3(pB, rB);
82 C = dot3(sub3(pAc, pBc), n);
83 }
84 if (C >= 0.0f)
85 return;
86
87 const F3 invIA = ldF3(invInertia, idA);
88 const F3 invIB = (idB >= 0) ? ldF3(invInertia, idB) : F3{0, 0, 0};
89 const float wTotal = computeW(rA, n, invMassA, invIA) + computeW(rB, n, invMassB, invIB);
90 if (wTotal < 1e-6f)
91 return;
92
93 const float dLambda = -C / wTotal;
94
95 // Linear + angular correction on A.
96 Kokkos::atomic_add(&deltaPos(idA, 0), n.x * dLambda * invMassA);
97 Kokkos::atomic_add(&deltaPos(idA, 1), n.y * dLambda * invMassA);
98 Kokkos::atomic_add(&deltaPos(idA, 2), n.z * dLambda * invMassA);
99 {
100 const F3 rn = cross3v(rA, n);
101 const F3 dTheta{rn.x * invIA.x * dLambda, rn.y * invIA.y * dLambda,
102 rn.z * invIA.z * dLambda};
103 const F4 dq = detail::deltaQuat(dTheta, qA);
104 Kokkos::atomic_add(&deltaQuat(idA, 0), dq.x);
105 Kokkos::atomic_add(&deltaQuat(idA, 1), dq.y);
106 Kokkos::atomic_add(&deltaQuat(idA, 2), dq.z);
107 Kokkos::atomic_add(&deltaQuat(idA, 3), dq.w);
108 }
109 if (idB >= 0) {
110 Kokkos::atomic_add(&deltaPos(idB, 0), -n.x * dLambda * invMassB);
111 Kokkos::atomic_add(&deltaPos(idB, 1), -n.y * dLambda * invMassB);
112 Kokkos::atomic_add(&deltaPos(idB, 2), -n.z * dLambda * invMassB);
113 const F3 rn = cross3v(rB, n);
114 const F3 dTheta{-rn.x * invIB.x * dLambda, -rn.y * invIB.y * dLambda,
115 -rn.z * invIB.z * dLambda};
116 const F4 dq = detail::deltaQuat(dTheta, qB);
117 Kokkos::atomic_add(&deltaQuat(idB, 0), dq.x);
118 Kokkos::atomic_add(&deltaQuat(idB, 1), dq.y);
119 Kokkos::atomic_add(&deltaQuat(idB, 2), dq.z);
120 Kokkos::atomic_add(&deltaQuat(idB, 3), dq.w);
121 Kokkos::atomic_add(&constraintCounts(idB), 1);
122 }
123 Kokkos::atomic_add(&constraintCounts(idA), 1);
124
125 if (C < 0.0f)
126 Kokkos::atomic_max(&maxOverlap(), -C);
127 });
128 space.fence();
129}
130
131} // namespace peclet::dem
132
133#endif // DEM_SOLVER_POSITION_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.
F4 deltaQuat(F3 dTheta, F4 q)
float computeW(F3 r, F3 dir, float invM, F3 invI)
F4 quatMult(F4 a, F4 b)
F4 quatInverse(F4 q)
F3 cross3v(F3 a, F3 b)
F3 ldF3(const V &v, int i)
F3 rotateVector(F4 q, F3 v)
float dot3(F3 a, F3 b)
F3 sub3(F3 a, F3 b)
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)
Accumulate XPBD position corrections for numContacts contacts.
CpExec::memory_space CpMem
F4 ldF4(const V &v, int i)
F3 add3(F3 a, F3 b)
Kokkos::DefaultExecutionSpace CpExec
Portable mirror of ParticleSystem.cuh ContactConstraint (the fields this reduction touches).