peclet-dem
Performance-portable XPBD Discrete Element Method (Kokkos + ArborX)
Loading...
Searching...
No Matches
integration.hpp
Go to the documentation of this file.
1
9#ifndef DEM_INTEGRATION_HPP
10#define DEM_INTEGRATION_HPP
11
12#include <Kokkos_Core.hpp>
13
14#include "contact_preprocessing.hpp" // ContactC, CpExec/CpMem
15#include "dem_portable.hpp"
16
17namespace peclet::dem {
18
23
24using V3 = Kokkos::View<float* [3], CpMem>;
25using V4 = Kokkos::View<float* [4], CpMem>;
26using Vf = Kokkos::View<float*, CpMem>;
27using Vi = Kokkos::View<int*, CpMem>;
28
29namespace detail {
30KOKKOS_INLINE_FUNCTION void st3(const V3& v, int i, F3 a) {
31 v(i, 0) = a.x;
32 v(i, 1) = a.y;
33 v(i, 2) = a.z;
34}
35KOKKOS_INLINE_FUNCTION void st4(const V4& v, int i, F4 a) {
36 v(i, 0) = a.x;
37 v(i, 1) = a.y;
38 v(i, 2) = a.z;
39 v(i, 3) = a.w;
40}
41} // namespace detail
42
44inline void predictVelocityKokkos(int n, V3 pos, Vf invMass, V3 vel, V4 quat, V3 angVel,
45 V3 invInertia, V3 posPred, V4 quatPred, V3 velPred, V3 angVelPred,
46 V3 deltaPos, V4 deltaQuat, V3 deltaVel, V3 deltaAngVel,
47 Vi constraintCounts, F3 gravity, float dt, V3 extForce) {
48 CpExec space;
49 Kokkos::parallel_for(
50 "peclet::dem::predict_velocity", Kokkos::RangePolicy<CpExec>(space, 0, n),
51 KOKKOS_LAMBDA(int i) {
52 const float invM = invMass(i);
53 F3 v = ldF3(vel, i);
54 if (invM > 0.0f) {
55 v = add3(v, scale3(gravity, dt));
56 // external force (fluid drag, etc.): F=ma => dv = F*invMass*dt (extForce is a FORCE, so it
57 // scales with 1/mass; gravity above is already an acceleration).
58 v = add3(v, scale3(ldF3(extForce, i), invM * dt));
59 }
60 detail::st3(velPred, i, v);
61
62 F3 wpred = ldF3(angVel, i);
63 if (invM > 0.0f) {
64 const F3 invI = ldF3(invInertia, i);
65 if (invI.x > 0.0f || invI.y > 0.0f || invI.z > 0.0f) {
66 const F4 q = ldF4(quat, i);
67 F3 wb = invRotateVector(q, ldF3(angVel, i));
68 const F3 Ib{(invI.x > 1e-9f) ? 1.0f / invI.x : 0.0f,
69 (invI.y > 1e-9f) ? 1.0f / invI.y : 0.0f,
70 (invI.z > 1e-9f) ? 1.0f / invI.z : 0.0f};
71 const F3 Lb{Ib.x * wb.x, Ib.y * wb.y, Ib.z * wb.z};
72 const F3 wxL = cross3v(wb, Lb);
73 const F3 alpha{-invI.x * wxL.x, -invI.y * wxL.y, -invI.z * wxL.z};
74 wb = add3(wb, scale3(alpha, dt));
75 wpred = rotateVector(q, wb);
76 }
77 }
78 detail::st3(angVelPred, i, wpred);
79
80 F3 x = ldF3(pos, i);
81 if (invM > 0.0f)
82 x = add3(x, scale3(v, dt));
83 detail::st3(posPred, i, x);
84 detail::st4(quatPred, i, ldF4(quat, i));
85
86 detail::st3(deltaVel, i, F3{0, 0, 0});
87 detail::st3(deltaAngVel, i, F3{0, 0, 0});
88 detail::st3(deltaPos, i, F3{0, 0, 0});
89 detail::st4(deltaQuat, i, F4{0, 0, 0, 0});
90 constraintCounts(i) = 0;
91 });
92 space.fence();
93}
94
97inline void applyVelocityDeltasKokkos(int n, V3 velPred, V3 angVelPred, V3 deltaVel,
98 V3 deltaAngVel) {
99 CpExec space;
100 Kokkos::parallel_for(
101 "peclet::dem::apply_velocity_deltas", Kokkos::RangePolicy<CpExec>(space, 0, n),
102 KOKKOS_LAMBDA(int i) {
103 detail::st3(velPred, i, add3(ldF3(velPred, i), ldF3(deltaVel, i)));
104 detail::st3(angVelPred, i, add3(ldF3(angVelPred, i), ldF3(deltaAngVel, i)));
105 detail::st3(deltaVel, i, F3{0, 0, 0});
106 detail::st3(deltaAngVel, i, F3{0, 0, 0});
107 });
108 space.fence();
109}
110
112inline void applyVelocityAndPredictPositionKokkos(int n, V3 pos, Vf invMass, V3 vel, V4 quat,
113 V3 velPred, V3 angVelPred, V3 posPred,
114 V4 quatPred, V3 angVel, float dt) {
115 CpExec space;
116 Kokkos::parallel_for(
117 "peclet::dem::apply_vel_predict_pos", Kokkos::RangePolicy<CpExec>(space, 0, n),
118 KOKKOS_LAMBDA(int i) {
119 const F3 vStart = ldF3(vel, i);
120 const F3 vFinal = ldF3(velPred, i); // already includes deltas
121 detail::st3(velPred, i, vFinal);
122 detail::st3(vel, i, vFinal); // persist v_{n+1}
123
124 const float invM = invMass(i);
125 F3 x = ldF3(pos, i);
126 if (invM > 0.0f)
127 x = add3(x, scale3(add3(vStart, vFinal), 0.5f * dt));
128 detail::st3(posPred, i, x);
129
130 const F4 q = ldF4(quat, i);
131 const F3 omega = ldF3(angVelPred, i);
132 detail::st3(angVel, i, omega);
133 F4 qp = q;
134 if (invM > 0.0f) {
135 qp.x += 0.5f * dt * (omega.x * q.w + omega.y * q.z - omega.z * q.y);
136 qp.y += 0.5f * dt * (omega.y * q.w + omega.z * q.x - omega.x * q.z);
137 qp.z += 0.5f * dt * (omega.z * q.w + omega.x * q.y - omega.y * q.x);
138 qp.w += 0.5f * dt * (-omega.x * q.x - omega.y * q.y - omega.z * q.z);
139 const float len = Kokkos::sqrt(qp.x * qp.x + qp.y * qp.y + qp.z * qp.z + qp.w * qp.w);
140 if (len > 1e-9f) {
141 const float s = 1.0f / len;
142 qp = F4{qp.x * s, qp.y * s, qp.z * s, qp.w * s};
143 }
144 }
145 detail::st4(quatPred, i, qp);
146 });
147 space.fence();
148}
149
151inline void computeContactCountsKokkos(Kokkos::View<const ContactC*, CpMem> contacts,
152 int numContacts, Vi constraintCounts) {
153 CpExec space;
154 Kokkos::parallel_for(
155 "peclet::dem::contact_counts", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
156 KOKKOS_LAMBDA(int i) {
157 const ContactC c = contacts(i);
158 if (c.dist > 0.0f)
159 return;
160 Kokkos::atomic_add(&constraintCounts(c.bodyA), 1);
161 if (c.bodyB >= 0)
162 Kokkos::atomic_add(&constraintCounts(c.bodyB), 1);
163 });
164 space.fence();
165}
166
168inline void applyUpdatesKokkos(int n, V3 posPred, V3 velPred, V3 deltaPos, V3 deltaVel,
169 Vi constraintCounts) {
170 CpExec space;
171 Kokkos::parallel_for(
172 "peclet::dem::apply_updates", Kokkos::RangePolicy<CpExec>(space, 0, n), KOKKOS_LAMBDA(int i) {
173 const int count = constraintCounts(i);
174 if (count <= 0)
175 return;
176 const float f = 1.0f / static_cast<float>(count);
177 detail::st3(posPred, i, add3(ldF3(posPred, i), scale3(ldF3(deltaPos, i), f)));
178 detail::st3(velPred, i, add3(ldF3(velPred, i), scale3(ldF3(deltaVel, i), f)));
179 detail::st3(deltaPos, i, F3{0, 0, 0});
180 detail::st3(deltaVel, i, F3{0, 0, 0});
181 constraintCounts(i) = 0;
182 });
183 space.fence();
184}
185
187inline void finalCommitKokkos(int n, V3 pos, Vf invMass, V3 posPred, V4 quat, V4 quatPred,
188 Domain dom) {
189 CpExec space;
190 Kokkos::parallel_for(
191 "peclet::dem::final_commit", Kokkos::RangePolicy<CpExec>(space, 0, n), KOKKOS_LAMBDA(int i) {
192 F3 p = ldF3(posPred, i);
193 if (dom.periodic_x) {
194 if (p.x < dom.min.x)
195 p.x += dom.size.x;
196 else if (p.x >= dom.max.x)
197 p.x -= dom.size.x;
198 }
199 if (dom.periodic_y) {
200 if (p.y < dom.min.y)
201 p.y += dom.size.y;
202 else if (p.y >= dom.max.y)
203 p.y -= dom.size.y;
204 }
205 if (dom.periodic_z) {
206 if (p.z < dom.min.z)
207 p.z += dom.size.z;
208 else if (p.z >= dom.max.z)
209 p.z -= dom.size.z;
210 }
211 detail::st3(pos, i, p);
212 detail::st4(quat, i, ldF4(quatPred, i));
213 });
214 space.fence();
215}
216
221struct KE2 {
222 double t = 0.0, r = 0.0;
223 KOKKOS_INLINE_FUNCTION KE2& operator+=(const KE2& o) {
224 t += o.t;
225 r += o.r;
226 return *this;
227 }
228};
229inline void applyThermostatKokkos(int numReal, V3 vel, Vf invMass, V3 angVel, V3 invInertia,
230 V4 quat, double kB, double tau, double Ttarget, float dt) {
231 CpExec space;
232 KE2 sum;
233 Kokkos::parallel_reduce(
234 "peclet::dem::thermo_energy", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
235 KOKKOS_LAMBDA(int i, KE2& acc) {
236 const float invM = invMass(i);
237 const F3 v = ldF3(vel, i);
238 if (invM > 0.0f)
239 acc.t += 0.5 * (1.0 / invM) * (v.x * v.x + v.y * v.y + v.z * v.z);
240 const F3 invI = ldF3(invInertia, i);
241 if (invI.x > 0.0f || invI.y > 0.0f || invI.z > 0.0f) {
242 const F3 wb = invRotateVector(ldF4(quat, i), ldF3(angVel, i));
243 const double Ix = (invI.x > 0.0f) ? 1.0 / invI.x : 0.0;
244 const double Iy = (invI.y > 0.0f) ? 1.0 / invI.y : 0.0;
245 const double Iz = (invI.z > 0.0f) ? 1.0 / invI.z : 0.0;
246 acc.r += 0.5 * (Ix * wb.x * wb.x + Iy * wb.y * wb.y + Iz * wb.z * wb.z);
247 }
248 },
249 sum);
250
251 const double ndof = 3.0 * numReal;
252 auto lambda = [&](double ke) {
253 if (ndof <= 0 || kB <= 0)
254 return 1.0f;
255 const double Tcur = (2.0 * ke) / (ndof * kB);
256 if (Tcur < 1e-6)
257 return 1.0f;
258 double f = 1.0 + (dt / tau) * (Ttarget / Tcur - 1.0);
259 if (f < 0.0)
260 f = 0.0;
261 return static_cast<float>(Kokkos::sqrt(f));
262 };
263 const float lt = lambda(sum.t), lr = lambda(sum.r);
264
265 Kokkos::parallel_for(
266 "peclet::dem::thermo_scale", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
267 KOKKOS_LAMBDA(int i) {
268 detail::st3(vel, i, scale3(ldF3(vel, i), lt));
269 detail::st3(angVel, i, scale3(ldF3(angVel, i), lr));
270 });
271 space.fence();
272}
273
275inline void updateGrowthScalesKokkos(int n, Vf scale, Vf targetScale, float factor) {
276 if (!(factor > 0.0f))
277 return;
278 CpExec space;
279 Kokkos::parallel_for(
280 "peclet::dem::growth", Kokkos::RangePolicy<CpExec>(space, 0, n),
281 KOKKOS_LAMBDA(int i) { scale(i) = targetScale(i) * factor; });
282 space.fence();
283}
284
285} // namespace peclet::dem
286
287#endif // DEM_INTEGRATION_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.
void st3(const V3 &v, int i, F3 a)
void st4(const V4 &v, int i, F4 a)
F3 cross3v(F3 a, F3 b)
Kokkos::View< float *, CpMem > Vf
Kokkos::View< int *, CpMem > Vi
Kokkos::View< float *[3], CpMem > V3
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.
F3 invRotateVector(F4 q, F3 v)
F3 ldF3(const V &v, int i)
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)
Predict velocity (gravity + gyroscopic precession), speculative position, and clear all deltas.
F3 rotateVector(F4 q, F3 v)
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.
F4 ldF4(const V &v, int i)
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.
Kokkos::View< float *[4], CpMem > V4
F3 scale3(F3 a, float s)
void applyUpdatesKokkos(int n, V3 posPred, V3 velPred, V3 deltaPos, V3 deltaVel, Vi constraintCounts)
Jacobi count-averaged apply of position/velocity deltas, then clear deltas + counts.
void updateGrowthScalesKokkos(int n, Vf scale, Vf targetScale, float factor)
Growth mode: scale = target * factor (when active).
F3 add3(F3 a, F3 b)
void computeContactCountsKokkos(Kokkos::View< const ContactC *, CpMem > contacts, int numContacts, Vi constraintCounts)
Per-contact constraint-count pre-pass (active contacts, dist <= 0).
void applyThermostatKokkos(int numReal, V3 vel, Vf invMass, V3 angVel, V3 invInertia, V4 quat, double kB, double tau, double Ttarget, float dt)
Kokkos::DefaultExecutionSpace CpExec
Portable mirror of ParticleSystem.cuh ContactConstraint (the fields this reduction touches).
Berendsen thermostat: reduce translational + rotational KE over real particles, compute the two scali...
KE2 & operator+=(const KE2 &o)