peclet-dem 1.1.0
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 V3 extTorque) {
49 CpExec space;
50 Kokkos::parallel_for(
51 "peclet::dem::predict_velocity", Kokkos::RangePolicy<CpExec>(space, 0, n),
52 KOKKOS_LAMBDA(int i) {
53 const float invM = invMass(i);
54 F3 v = ldF3(vel, i);
55 if (invM > 0.0f) {
56 v = add3(v, scale3(gravity, dt));
57 // external force (fluid drag, etc.): F=ma => dv = F*invMass*dt (extForce is a FORCE, so
58 // it scales with 1/mass; gravity above is already an acceleration).
59 v = add3(v, scale3(ldF3(extForce, i), invM * dt));
60 }
61 detail::st3(velPred, i, v);
62
63 F3 wpred = ldF3(angVel, i);
64 if (invM > 0.0f) {
65 const F3 invI = ldF3(invInertia, i);
66 if (invI.x > 0.0f || invI.y > 0.0f || invI.z > 0.0f) {
67 const F4 q = ldF4(quat, i);
68 F3 wb = invRotateVector(q, ldF3(angVel, i));
69 const F3 Ib{(invI.x > 1e-9f) ? 1.0f / invI.x : 0.0f,
70 (invI.y > 1e-9f) ? 1.0f / invI.y : 0.0f,
71 (invI.z > 1e-9f) ? 1.0f / invI.z : 0.0f};
72 const F3 Lb{Ib.x * wb.x, Ib.y * wb.y, Ib.z * wb.z};
73 const F3 wxL = cross3v(wb, Lb);
74 // Euler's equations in the body (principal) frame:
75 // I dw/dt = tau_body - w x (I w) => dw = invI (tau_body - w x I w) dt.
76 // The external torque arrives in the WORLD frame and is rotated in here, the same
77 // way the angular velocity above is.
78 const F3 tb = invRotateVector(q, ldF3(extTorque, i));
79 const F3 alpha{invI.x * (tb.x - wxL.x), invI.y * (tb.y - wxL.y),
80 invI.z * (tb.z - wxL.z)};
81 wb = add3(wb, scale3(alpha, dt));
82 wpred = rotateVector(q, wb);
83 }
84 }
85 detail::st3(angVelPred, i, wpred);
86
87 F3 x = ldF3(pos, i);
88 if (invM > 0.0f)
89 x = add3(x, scale3(v, dt));
90 detail::st3(posPred, i, x);
91 detail::st4(quatPred, i, ldF4(quat, i));
92
93 detail::st3(deltaVel, i, F3{0, 0, 0});
94 detail::st3(deltaAngVel, i, F3{0, 0, 0});
95 detail::st3(deltaPos, i, F3{0, 0, 0});
96 detail::st4(deltaQuat, i, F4{0, 0, 0, 0});
97 constraintCounts(i) = 0;
98 });
99 space.fence();
100}
101
104inline void applyVelocityDeltasKokkos(int n, V3 velPred, V3 angVelPred, V3 deltaVel,
105 V3 deltaAngVel) {
106 CpExec space;
107 Kokkos::parallel_for(
108 "peclet::dem::apply_velocity_deltas", Kokkos::RangePolicy<CpExec>(space, 0, n),
109 KOKKOS_LAMBDA(int i) {
110 detail::st3(velPred, i, add3(ldF3(velPred, i), ldF3(deltaVel, i)));
111 detail::st3(angVelPred, i, add3(ldF3(angVelPred, i), ldF3(deltaAngVel, i)));
112 detail::st3(deltaVel, i, F3{0, 0, 0});
113 detail::st3(deltaAngVel, i, F3{0, 0, 0});
114 });
115 space.fence();
116}
117
119inline void applyVelocityAndPredictPositionKokkos(int n, V3 pos, Vf invMass, V3 vel, V4 quat,
120 V3 velPred, V3 angVelPred, V3 posPred,
121 V4 quatPred, V3 angVel, float dt) {
122 CpExec space;
123 Kokkos::parallel_for(
124 "peclet::dem::apply_vel_predict_pos", Kokkos::RangePolicy<CpExec>(space, 0, n),
125 KOKKOS_LAMBDA(int i) {
126 const F3 vStart = ldF3(vel, i);
127 const F3 vFinal = ldF3(velPred, i); // already includes deltas
128 detail::st3(velPred, i, vFinal);
129 detail::st3(vel, i, vFinal); // persist v_{n+1}
130
131 const float invM = invMass(i);
132 F3 x = ldF3(pos, i);
133 if (invM > 0.0f)
134 x = add3(x, scale3(add3(vStart, vFinal), 0.5f * dt));
135 detail::st3(posPred, i, x);
136
137 const F4 q = ldF4(quat, i);
138 const F3 omega = ldF3(angVelPred, i);
139 detail::st3(angVel, i, omega);
140 F4 qp = q;
141 if (invM > 0.0f) {
142 qp.x += 0.5f * dt * (omega.x * q.w + omega.y * q.z - omega.z * q.y);
143 qp.y += 0.5f * dt * (omega.y * q.w + omega.z * q.x - omega.x * q.z);
144 qp.z += 0.5f * dt * (omega.z * q.w + omega.x * q.y - omega.y * q.x);
145 qp.w += 0.5f * dt * (-omega.x * q.x - omega.y * q.y - omega.z * q.z);
146 const float len = Kokkos::sqrt(qp.x * qp.x + qp.y * qp.y + qp.z * qp.z + qp.w * qp.w);
147 if (len > 1e-9f) {
148 const float s = 1.0f / len;
149 qp = F4{qp.x * s, qp.y * s, qp.z * s, qp.w * s};
150 }
151 }
152 detail::st4(quatPred, i, qp);
153 });
154 space.fence();
155}
156
158inline void computeContactCountsKokkos(Kokkos::View<const ContactC*, CpMem> contacts,
159 int numContacts, Vi constraintCounts) {
160 CpExec space;
161 Kokkos::parallel_for(
162 "peclet::dem::contact_counts", Kokkos::RangePolicy<CpExec>(space, 0, numContacts),
163 KOKKOS_LAMBDA(int i) {
164 const ContactC c = contacts(i);
165 if (c.dist > 0.0f)
166 return;
167 Kokkos::atomic_add(&constraintCounts(c.bodyA), 1);
168 if (c.bodyB >= 0)
169 Kokkos::atomic_add(&constraintCounts(c.bodyB), 1);
170 });
171 space.fence();
172}
173
178inline void applyUpdatesKokkos(int n, V3 posPred, V3 velPred, V3 deltaPos, V3 deltaVel,
179 Vi constraintCounts, bool averaged = true) {
180 CpExec space;
181 Kokkos::parallel_for(
182 "peclet::dem::apply_updates", Kokkos::RangePolicy<CpExec>(space, 0, n), KOKKOS_LAMBDA(int i) {
183 const int count = constraintCounts(i);
184 if (count <= 0)
185 return;
186 const float f = averaged ? 1.0f / static_cast<float>(count) : 1.0f;
187 detail::st3(posPred, i, add3(ldF3(posPred, i), scale3(ldF3(deltaPos, i), f)));
188 detail::st3(velPred, i, add3(ldF3(velPred, i), scale3(ldF3(deltaVel, i), f)));
189 detail::st3(deltaPos, i, F3{0, 0, 0});
190 detail::st3(deltaVel, i, F3{0, 0, 0});
191 constraintCounts(i) = 0;
192 });
193 space.fence();
194}
195
197inline void finalCommitKokkos(int n, V3 pos, Vf invMass, V3 posPred, V4 quat, V4 quatPred,
198 Domain dom) {
199 CpExec space;
200 Kokkos::parallel_for(
201 "peclet::dem::final_commit", Kokkos::RangePolicy<CpExec>(space, 0, n), KOKKOS_LAMBDA(int i) {
202 F3 p = ldF3(posPred, i);
203 if (dom.periodic_x) {
204 if (p.x < dom.min.x)
205 p.x += dom.size.x;
206 else if (p.x >= dom.max.x)
207 p.x -= dom.size.x;
208 }
209 if (dom.periodic_y) {
210 if (p.y < dom.min.y)
211 p.y += dom.size.y;
212 else if (p.y >= dom.max.y)
213 p.y -= dom.size.y;
214 }
215 if (dom.periodic_z) {
216 if (p.z < dom.min.z)
217 p.z += dom.size.z;
218 else if (p.z >= dom.max.z)
219 p.z -= dom.size.z;
220 }
221 detail::st3(pos, i, p);
222 detail::st4(quat, i, ldF4(quatPred, i));
223 });
224 space.fence();
225}
226
231struct KE2 {
232 double t = 0.0, r = 0.0;
233 KOKKOS_INLINE_FUNCTION KE2& operator+=(const KE2& o) {
234 t += o.t;
235 r += o.r;
236 return *this;
237 }
238};
239inline void applyThermostatKokkos(int numReal, V3 vel, Vf invMass, V3 angVel, V3 invInertia,
240 V4 quat, double kB, double tau, double Ttarget, float dt) {
241 CpExec space;
242 KE2 sum;
243 Kokkos::parallel_reduce(
244 "peclet::dem::thermo_energy", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
245 KOKKOS_LAMBDA(int i, KE2& acc) {
246 const float invM = invMass(i);
247 const F3 v = ldF3(vel, i);
248 if (invM > 0.0f)
249 acc.t += 0.5 * (1.0 / invM) * (v.x * v.x + v.y * v.y + v.z * v.z);
250 const F3 invI = ldF3(invInertia, i);
251 if (invI.x > 0.0f || invI.y > 0.0f || invI.z > 0.0f) {
252 const F3 wb = invRotateVector(ldF4(quat, i), ldF3(angVel, i));
253 const double Ix = (invI.x > 0.0f) ? 1.0 / invI.x : 0.0;
254 const double Iy = (invI.y > 0.0f) ? 1.0 / invI.y : 0.0;
255 const double Iz = (invI.z > 0.0f) ? 1.0 / invI.z : 0.0;
256 acc.r += 0.5 * (Ix * wb.x * wb.x + Iy * wb.y * wb.y + Iz * wb.z * wb.z);
257 }
258 },
259 sum);
260
261 const double ndof = 3.0 * numReal;
262 auto lambda = [&](double ke) {
263 if (ndof <= 0 || kB <= 0)
264 return 1.0f;
265 const double Tcur = (2.0 * ke) / (ndof * kB);
266 if (Tcur < 1e-6)
267 return 1.0f;
268 double f = 1.0 + (dt / tau) * (Ttarget / Tcur - 1.0);
269 if (f < 0.0)
270 f = 0.0;
271 return static_cast<float>(Kokkos::sqrt(f));
272 };
273 const float lt = lambda(sum.t), lr = lambda(sum.r);
274
275 Kokkos::parallel_for(
276 "peclet::dem::thermo_scale", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
277 KOKKOS_LAMBDA(int i) {
278 detail::st3(vel, i, scale3(ldF3(vel, i), lt));
279 detail::st3(angVel, i, scale3(ldF3(angVel, i), lr));
280 });
281 space.fence();
282}
283
285inline void updateGrowthScalesKokkos(int n, Vf scale, Vf targetScale, float factor) {
286 if (!(factor > 0.0f))
287 return;
288 CpExec space;
289 Kokkos::parallel_for(
290 "peclet::dem::growth", Kokkos::RangePolicy<CpExec>(space, 0, n),
291 KOKKOS_LAMBDA(int i) { scale(i) = targetScale(i) * factor; });
292 space.fence();
293}
294
295} // namespace peclet::dem
296
297#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)
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 updateGrowthScalesKokkos(int n, Vf scale, Vf targetScale, float factor)
Growth mode: scale = target * factor (when active).
F3 add3(F3 a, F3 b)
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 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 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)