peclet-dem 1.1.0
Performance-portable XPBD Discrete Element Method (Kokkos + ArborX)
Loading...
Searching...
No Matches
solve_copies.hpp
Go to the documentation of this file.
1
7#ifndef DEM_SOLVE_COPIES_HPP
8#define DEM_SOLVE_COPIES_HPP
9
10#include <cstddef>
11#include <cstdint>
12#include <Kokkos_Core.hpp>
13#include <Kokkos_Sort.hpp>
14#include <string>
15
16#include "contact_preprocessing.hpp" // ManifoldC, ContactC, PosUnits, SlotOverride
17#include "particles.hpp" // Particles, PhaseCopies
18
19namespace peclet::dem {
20
21// ================================ contact-solve copies ================================
22// docs/contact_solve_framework.md §4.4 (hub copies), §4.5 (solve views, relaxation) and §WO-4
23// item 2 (demStep's periodic images in the position phase). A body updated through more than one
24// slot in a phase is a fold GROUP: its slots (the base, hub copies, images) are solved with the
25// mass-split inverse masses k x invMass, and after every sweep the group is folded -- the base
26// takes sigma + (sum of the members' increments) / k and every member is re-seeded to it (plus its
27// periodic shift). sigma is the phase seed, re-marked after every rank sync.
28
32inline constexpr int kHubEdgeBudget = 32;
43inline constexpr float kSplitOmegaVelocity = 1.0f;
44
46template <class V>
47inline void growCopyView(V& v, std::size_t n, const char* label) {
48 if (v.extent(0) < n)
49 v = V(Kokkos::view_alloc(Kokkos::WithoutInitializing, std::string(label)), n + n / 2 + 64);
50}
51
54inline void velocityEdgeEndsKokkos(Kokkos::View<const ManifoldC*, CpMem> manifolds, int nm,
55 Kokkos::View<const int*, CpMem> realIdx,
56 Kokkos::View<const int*, CpMem> color,
57 Kokkos::View<int*, CpMem> eA, Kokkos::View<int*, CpMem> eB) {
58 Kokkos::parallel_for(
59 "peclet::dem::copies_vel_ends", Kokkos::RangePolicy<CpExec>(0, nm), KOKKOS_LAMBDA(int e) {
60 const ManifoldC m = manifolds(e);
61 const bool act = color(e) != -2;
62 eA(e) = act ? realIdx(m.bodyA) : -1;
63 eB(e) = (act && m.bodyB >= 0) ? realIdx(m.bodyB) : -1;
64 });
65}
66
69inline void positionEdgeEndsKokkos(Kokkos::View<const ContactC*, CpMem> contacts,
70 const PosUnits& units, int nu,
71 Kokkos::View<const int*, CpMem> color,
72 Kokkos::View<int*, CpMem> eA, Kokkos::View<int*, CpMem> eB) {
73 Kokkos::parallel_for(
74 "peclet::dem::copies_pos_ends", Kokkos::RangePolicy<CpExec>(0, nu), KOKKOS_LAMBDA(int u) {
75 const ContactC c = contacts(units.leader(u));
76 const bool act = color(u) != -2;
77 eA(u) = act ? c.bodyA : -1;
78 eB(u) = (act && c.bodyB >= 0) ? c.bodyB : -1;
79 });
80}
81
83inline void vertexDegreeKokkos(Kokkos::View<const int*, CpMem> eA,
84 Kokkos::View<const int*, CpMem> eB, int nE, int nV,
85 Kokkos::View<int*, CpMem> deg) {
86 Kokkos::deep_copy(Kokkos::subview(deg, Kokkos::pair<int, int>(0, nV)), 0);
87 Kokkos::parallel_for(
88 "peclet::dem::copies_degree", Kokkos::RangePolicy<CpExec>(0, nE), KOKKOS_LAMBDA(int e) {
89 if (eA(e) >= 0)
90 Kokkos::atomic_add(&deg(eA(e)), 1);
91 if (eB(e) >= 0)
92 Kokkos::atomic_add(&deg(eB(e)), 1);
93 });
94 Kokkos::fence();
95}
96
103inline void buildHubCopiesKokkos(Kokkos::View<const int*, CpMem> eA,
104 Kokkos::View<const int*, CpMem> eB, int nE, int nV, int slotBase,
105 Kokkos::View<const int*, CpMem> deg, PhaseCopies& H) {
106 CpExec space;
107 using Kokkos::view_alloc;
108 using Kokkos::WithoutInitializing;
109 H.nHubs = 0;
110 H.nCopies = 0;
111 H.slotBase = slotBase;
112 H.maxHubDegree = 0;
113 growCopyView(H.slotA, static_cast<std::size_t>(nE), "peclet::dem::copies_slotA");
114 growCopyView(H.slotB, static_cast<std::size_t>(nE), "peclet::dem::copies_slotB");
115 Kokkos::deep_copy(space, Kokkos::subview(H.slotA, Kokkos::pair<int, int>(0, nE)), -1);
116 Kokkos::deep_copy(space, Kokkos::subview(H.slotB, Kokkos::pair<int, int>(0, nE)), -1);
117 Kokkos::View<int*, CpMem> hubId(
118 view_alloc(space, "peclet::dem::copies_hubid", WithoutInitializing), nV);
119 int nHubs = 0;
120 Kokkos::parallel_scan(
121 "peclet::dem::copies_hub_scan", Kokkos::RangePolicy<CpExec>(space, 0, nV),
122 KOKKOS_LAMBDA(int v, int& run, const bool final) {
123#if defined(PECLET_DEM_TEST_MUTANT) && PECLET_DEM_TEST_MUTANT == 3
124 const bool hub = false; // G13 mutant 3: no hub copies
125#else
126 const bool hub = deg(v) > kHubEdgeBudget;
127#endif
128 if (final)
129 hubId(v) = hub ? run : -1;
130 if (hub)
131 ++run;
132 },
133 nHubs);
134 space.fence();
135 H.nHubs = nHubs;
136 if (nHubs == 0)
137 return;
138 growCopyView(H.hubVertex, static_cast<std::size_t>(nHubs), "peclet::dem::copies_hubv");
139 growCopyView(H.hubS, static_cast<std::size_t>(nHubs), "peclet::dem::copies_hubs");
140 auto hv = H.hubVertex;
141 auto hs = H.hubS;
142 Kokkos::parallel_for(
143 "peclet::dem::copies_hub_list", Kokkos::RangePolicy<CpExec>(space, 0, nV),
144 KOKKOS_LAMBDA(int v) {
145 const int h = hubId(v);
146 if (h < 0)
147 return;
148 hv(h) = v;
149 hs(h) = (deg(v) + kHubEdgeBudget - 1) / kHubEdgeBudget;
150 });
151 Kokkos::View<int*, CpMem> copyOff(
152 view_alloc(space, "peclet::dem::copies_off", WithoutInitializing), nHubs);
153 Kokkos::View<int*, CpMem> degOff(
154 view_alloc(space, "peclet::dem::copies_doff", WithoutInitializing), nHubs);
155 int nCopies = 0, nEntries = 0, maxDeg = 0;
156 Kokkos::parallel_scan(
157 "peclet::dem::copies_off_scan", Kokkos::RangePolicy<CpExec>(space, 0, nHubs),
158 KOKKOS_LAMBDA(int h, int& run, const bool final) {
159 if (final)
160 copyOff(h) = run;
161 run += hs(h) - 1;
162 },
163 nCopies);
164 Kokkos::parallel_scan(
165 "peclet::dem::copies_doff_scan", Kokkos::RangePolicy<CpExec>(space, 0, nHubs),
166 KOKKOS_LAMBDA(int h, int& run, const bool final) {
167 if (final)
168 degOff(h) = run;
169 run += deg(hv(h));
170 },
171 nEntries);
172 Kokkos::parallel_reduce(
173 "peclet::dem::copies_maxdeg", Kokkos::RangePolicy<CpExec>(space, 0, nHubs),
174 KOKKOS_LAMBDA(int h, int& m) {
175 if (deg(hv(h)) > m)
176 m = deg(hv(h));
177 },
178 Kokkos::Max<int>(maxDeg));
179 space.fence();
180 H.nCopies = nCopies;
181 H.maxHubDegree = maxDeg;
182 // (hub, edge, end) keys: the hub in the high bits, then the edge index, then the end (0 = A,
183 // 1 = B), so each hub's edges sort by edge index -- canonical (manifolds and units are sorted by
184 // pair key).
185 Kokkos::View<std::uint64_t*, CpMem> keys(
186 view_alloc(space, "peclet::dem::copies_keys", WithoutInitializing), nEntries);
187 Kokkos::View<int, CpMem> cursor("peclet::dem::copies_cursor");
188 Kokkos::parallel_for(
189 "peclet::dem::copies_entries", Kokkos::RangePolicy<CpExec>(space, 0, nE),
190 KOKKOS_LAMBDA(int e) {
191 const int ends[2] = {eA(e), eB(e)};
192 for (int end = 0; end < 2; ++end) {
193 const int v = ends[end];
194 if (v < 0 || hubId(v) < 0)
195 continue;
196 const int p = Kokkos::atomic_fetch_add(&cursor(), 1);
197 keys(p) = (static_cast<std::uint64_t>(hubId(v)) << 33) |
198 (static_cast<std::uint64_t>(static_cast<unsigned>(e)) << 1) |
199 static_cast<std::uint64_t>(end);
200 }
201 });
202 Kokkos::sort(space, keys);
203 growCopyView(H.copyBase, static_cast<std::size_t>(nCopies > 0 ? nCopies : 1),
204 "peclet::dem::copies_base");
205 auto sA = H.slotA;
206 auto sB = H.slotB;
207 auto cb = H.copyBase;
208 Kokkos::parallel_for(
209 "peclet::dem::copies_assign", Kokkos::RangePolicy<CpExec>(space, 0, nEntries),
210 KOKKOS_LAMBDA(int p) {
211 const std::uint64_t k = keys(p);
212 const int h = static_cast<int>(k >> 33);
213 const int e = static_cast<int>((k >> 1) & 0xFFFFFFFFull);
214 const int end = static_cast<int>(k & 1ull);
215 const int s = hs(h);
216 const int c = (p - degOff(h)) % s;
217 const int slot = (c == 0) ? hv(h) : slotBase + copyOff(h) + c - 1;
218 if (end == 0)
219 sA(e) = slot;
220 else
221 sB(e) = slot;
222 if (c > 0)
223 cb(copyOff(h) + c - 1) = hv(h);
224 });
225 space.fence();
226}
227
243inline void buildCopyGroupsKokkos(Kokkos::View<const int*, CpMem> realIdx, int span, int numReal,
244 Kokkos::View<const int*, CpMem> deg,
245 Kokkos::View<const float* [3], CpMem> imageShift, PhaseCopies& H,
246 bool identityBodies = false) {
247 CpExec space;
248 using Kokkos::view_alloc;
249 using Kokkos::WithoutInitializing;
250 const int nC = H.nCopies, base = H.slotBase, nQ = span + nC;
251 H.nGroups = 0;
252 if (nQ <= 0)
253 return;
254 auto cb = H.copyBase;
255 Kokkos::View<int*, CpMem> cnt("peclet::dem::copies_cnt", span);
256 Kokkos::parallel_for(
257 "peclet::dem::copies_grp_cnt", Kokkos::RangePolicy<CpExec>(space, 0, nQ),
258 KOKKOS_LAMBDA(int qi) {
259 const int o = identityBodies ? ((qi < span) ? qi : cb(qi - span))
260 : ((qi < span) ? realIdx(qi) : realIdx(cb(qi - span)));
261 Kokkos::atomic_add(&cnt(o), 1);
262 });
263 Kokkos::View<std::uint64_t*, CpMem> keys(
264 view_alloc(space, "peclet::dem::copies_gkeys", WithoutInitializing), nQ);
265 int nMem = 0;
266 Kokkos::parallel_reduce(
267 "peclet::dem::copies_grp_keys", Kokkos::RangePolicy<CpExec>(space, 0, nQ),
268 KOKKOS_LAMBDA(int qi, int& acc) {
269 const int q = (qi < span) ? qi : base + (qi - span);
270 const int o = identityBodies ? ((qi < span) ? qi : cb(qi - span))
271 : ((qi < span) ? realIdx(qi) : realIdx(cb(qi - span)));
272 if (cnt(o) >= 2) {
273 keys(qi) = (static_cast<std::uint64_t>(static_cast<unsigned>(o)) << 32) |
274 static_cast<unsigned>(q);
275 acc += 1;
276 } else {
277 keys(qi) = ~std::uint64_t(0);
278 }
279 },
280 nMem);
281 space.fence();
282 if (nMem == 0)
283 return;
284 Kokkos::sort(space, keys);
285 growCopyView(H.groupStart, static_cast<std::size_t>(nMem) + 1, "peclet::dem::copies_gstart");
286 growCopyView(H.groupSlot, static_cast<std::size_t>(nMem), "peclet::dem::copies_gslot");
287 growCopyView(H.groupShift, static_cast<std::size_t>(nMem), "peclet::dem::copies_gshift");
288 growCopyView(H.groupActive, static_cast<std::size_t>(nMem), "peclet::dem::copies_gact");
289 auto gs = H.groupStart;
290 auto gq = H.groupSlot;
291 auto gsh = H.groupShift;
292 auto ga = H.groupActive;
293 const bool haveShift = imageShift.extent(0) > 0;
294 int nG = 0;
295 Kokkos::parallel_scan(
296 "peclet::dem::copies_grp_scan", Kokkos::RangePolicy<CpExec>(space, 0, nMem),
297 KOKKOS_LAMBDA(int p, int& run, const bool final) {
298 const bool isNew = (p == 0) || ((keys(p) >> 32) != (keys(p - 1) >> 32));
299 if (final) {
300 const int q = static_cast<int>(keys(p) & 0xFFFFFFFFull);
301 gq(p) = q;
302 const int b = (q < base) ? q : cb(q - base); // an image's (or its copy's) image slot
303 F3 sh{0.0f, 0.0f, 0.0f};
304 if (haveShift && b >= numReal && b < span)
305 sh = F3{imageShift(b, 0), imageShift(b, 1), imageShift(b, 2)};
306 gsh(p, 0) = sh.x;
307 gsh(p, 1) = sh.y;
308 gsh(p, 2) = sh.z;
309 ga(p) = (q >= base || deg(q) > 0) ? 1 : 0; // a copy always has an edge
310 if (isNew)
311 gs(run) = p;
312 }
313 if (isNew)
314 ++run;
315 },
316 nG);
317 Kokkos::deep_copy(space, Kokkos::subview(gs, nG), nMem);
318 growCopyView(H.groupK, static_cast<std::size_t>(nG), "peclet::dem::copies_gk");
319 growCopyView(H.seedX, static_cast<std::size_t>(nG), "peclet::dem::copies_seedx");
320 growCopyView(H.seedW, static_cast<std::size_t>(nG), "peclet::dem::copies_seedw");
321 auto gk = H.groupK;
322 Kokkos::parallel_for(
323 "peclet::dem::copies_grp_k", Kokkos::RangePolicy<CpExec>(space, 0, nG), KOKKOS_LAMBDA(int g) {
324 int k = 0;
325 for (int p = gs(g); p < gs(g + 1); ++p)
326 k += ga(p);
327 gk(g) = k > 0 ? k : 1;
328 });
329 space.fence();
330 H.nGroups = nG;
331}
332
336inline void buildSolveViewsKokkos(Particles& P, const PhaseCopies& H, int nSlots,
337 Kokkos::View<const float*, CpMem> invMass,
338 Kokkos::View<const float* [3], CpMem> invInertia) {
339 CpExec space;
340 growCopyView(P.invMassSolve, static_cast<std::size_t>(nSlots), "peclet::dem::invMassSolve");
341 growCopyView(P.invInertiaSolve, static_cast<std::size_t>(nSlots), "peclet::dem::invInertiaSolve");
342 growCopyView(P.splitSlot, static_cast<std::size_t>(nSlots), "peclet::dem::splitSlot");
343 auto ims = P.invMassSolve;
344 auto iis = P.invInertiaSolve;
345 auto spl = P.splitSlot;
346 Kokkos::parallel_for(
347 "peclet::dem::copies_solve_views", Kokkos::RangePolicy<CpExec>(space, 0, nSlots),
348 KOKKOS_LAMBDA(int q) {
349 const bool own = q < static_cast<int>(invMass.extent(0));
350 ims(q) = own ? invMass(q) : 0.0f;
351 for (int c = 0; c < 3; ++c)
352 iis(q, c) = own ? invInertia(q, c) : 0.0f;
353 spl(q) = 0;
354 });
355 auto gs = H.groupStart;
356 auto gq = H.groupSlot;
357 auto gk = H.groupK;
358 Kokkos::parallel_for(
359 "peclet::dem::copies_solve_split", Kokkos::RangePolicy<CpExec>(space, 0, H.nGroups),
360 KOKKOS_LAMBDA(int g) {
361 const int b = gq(gs(g));
362 const int k = gk(g);
363 const float kf = static_cast<float>(k);
364 const float m = kf * invMass(b);
365 const float i0 = kf * invInertia(b, 0), i1 = kf * invInertia(b, 1),
366 i2 = kf * invInertia(b, 2);
367 for (int p = gs(g); p < gs(g + 1); ++p) {
368 const int q = gq(p);
369 ims(q) = m;
370 iis(q, 0) = i0;
371 iis(q, 1) = i1;
372 iis(q, 2) = i2;
373 spl(q) = k > 1 ? 1 : 0;
374 }
375 });
376 space.fence();
377}
378
382 if (H.nCopies <= 0)
383 return;
384 auto cb = H.copyBase;
385 const int base = H.slotBase;
386 auto quat = P.quat;
387 auto quatPred = P.quatPred;
388 auto invI = P.invInertia;
389 auto grounded = P.groundedLevel;
390 Kokkos::parallel_for(
391 "peclet::dem::copies_seed_state", Kokkos::RangePolicy<CpExec>(0, H.nCopies),
392 KOKKOS_LAMBDA(int c) {
393 const int q = base + c, b = cb(c);
394 for (int j = 0; j < 4; ++j) {
395 quat(q, j) = quat(b, j);
396 quatPred(q, j) = quatPred(b, j);
397 }
398 for (int j = 0; j < 3; ++j)
399 invI(q, j) = invI(b, j);
400 grounded(q) = grounded(b);
401 });
402 Kokkos::fence();
403}
404
410inline void markCopySeedsKokkos(const PhaseCopies& H, Kokkos::View<float* [3], CpMem> x,
411 Kokkos::View<float* [3], CpMem> w,
412 Kokkos::View<float*, CpMem> orphan,
413 Kokkos::View<float*, CpMem> orphanPeak,
414 bool shareFromBase = false) {
415 if (H.nGroups <= 0)
416 return;
417 auto gs = H.groupStart;
418 auto gq = H.groupSlot;
419 auto gsh = H.groupShift;
420 auto gk = H.groupK;
421 auto sx = H.seedX;
422 auto sw = H.seedW;
423 const bool haveW = w.extent(0) > 0;
424 const bool haveO = orphan.extent(0) > 0;
425 Kokkos::parallel_for(
426 "peclet::dem::copies_mark", Kokkos::RangePolicy<CpExec>(0, H.nGroups), KOKKOS_LAMBDA(int g) {
427 const int b = gq(gs(g));
428 for (int c = 0; c < 3; ++c) {
429 sx(g, c) = x(b, c);
430 if (haveW)
431 sw(g, c) = w(b, c);
432 }
433 float share = 0.0f, pk = 0.0f;
434 if (haveO) {
435 share = shareFromBase ? orphan(b) : orphan(b) / static_cast<float>(gk(g));
436 pk = orphanPeak(b);
437 }
438 for (int p = gs(g); p < gs(g + 1); ++p) {
439 const int q = gq(p);
440 if (q != b) {
441 for (int c = 0; c < 3; ++c) {
442 x(q, c) = sx(g, c) + gsh(p, c);
443 if (haveW)
444 w(q, c) = sw(g, c);
445 }
446 }
447 if (haveO) {
448 orphan(q) = share;
449 orphanPeak(q) = pk;
450 }
451 }
452 });
453}
454
463inline void foldCopiesKokkos(CpExec space, const PhaseCopies& H, Kokkos::View<float* [3], CpMem> x,
464 Kokkos::View<float* [3], CpMem> w, Kokkos::View<float*, CpMem> orphan,
465 Kokkos::View<float*, CpMem> orphanPeak,
466 Kokkos::View<float, CpMem> consensus = {}) {
467 if (H.nGroups <= 0)
468 return;
469 auto gs = H.groupStart;
470 auto gq = H.groupSlot;
471 auto gsh = H.groupShift;
472 auto gk = H.groupK;
473 auto ga = H.groupActive;
474 auto sx = H.seedX;
475 auto sw = H.seedW;
476 const bool haveW = w.extent(0) > 0;
477 const bool haveO = orphan.extent(0) > 0;
478 const bool haveC = consensus.data() != nullptr;
479 Kokkos::parallel_for(
480 "peclet::dem::copies_fold", Kokkos::RangePolicy<CpExec>(space, 0, H.nGroups),
481 KOKKOS_LAMBDA(int g) {
482 const float kf = static_cast<float>(gk(g));
483 float tx[3] = {0.0f, 0.0f, 0.0f}, tw[3] = {0.0f, 0.0f, 0.0f};
484 float oSum = 0.0f, oPk = 0.0f;
485 for (int p = gs(g); p < gs(g + 1); ++p) {
486 if (!ga(p))
487 continue;
488 const int q = gq(p);
489 for (int c = 0; c < 3; ++c) {
490 tx[c] += x(q, c) - (sx(g, c) + gsh(p, c));
491 if (haveW)
492 tw[c] += w(q, c) - sw(g, c);
493 }
494 if (haveO) {
495 oSum += orphan(q);
496 oPk = Kokkos::fmax(oPk, orphanPeak(q));
497 }
498 }
499 float nx[3], nw[3];
500 for (int c = 0; c < 3; ++c) {
501#if defined(PECLET_DEM_TEST_MUTANT) && PECLET_DEM_TEST_MUTANT == 2
502 nx[c] = sx(g, c) + tx[c]; // G13 mutant 2: raw sum of split-mass increments (no 1/k)
503 nw[c] = haveW ? sw(g, c) + tw[c] : 0.0f;
504#else
505 nx[c] = sx(g, c) + tx[c] / kf;
506 nw[c] = haveW ? sw(g, c) + tw[c] / kf : 0.0f;
507#endif
508 }
509 const float share = oSum / kf;
510 if (haveC) {
511 float cmax = 0.0f;
512 for (int p = gs(g); p < gs(g + 1); ++p) {
513 if (!ga(p))
514 continue;
515 const int q = gq(p);
516 float s2 = 0.0f;
517 for (int c = 0; c < 3; ++c) {
518 const float e = tx[c] / kf - (x(q, c) - (sx(g, c) + gsh(p, c)));
519 s2 += e * e;
520 }
521 cmax = Kokkos::fmax(cmax, Kokkos::sqrt(s2));
522 }
523 if (cmax > 0.0f)
524 Kokkos::atomic_max(&consensus(), cmax);
525 }
526 for (int p = gs(g); p < gs(g + 1); ++p) {
527 const int q = gq(p);
528 for (int c = 0; c < 3; ++c) {
529 x(q, c) = nx[c] + gsh(p, c);
530 if (haveW)
531 w(q, c) = nw[c];
532 }
533 if (haveO) {
534 orphan(q) = share;
535 orphanPeak(q) = oPk;
536 }
537 }
538 });
539}
540
543inline void reseedCopiesKokkos(CpExec space, const PhaseCopies& H,
544 Kokkos::View<float* [3], CpMem> x,
545 Kokkos::View<float* [3], CpMem> w) {
546 if (H.nGroups <= 0)
547 return;
548 auto gs = H.groupStart;
549 auto gq = H.groupSlot;
550 auto gsh = H.groupShift;
551 Kokkos::parallel_for(
552 "peclet::dem::copies_reseed", Kokkos::RangePolicy<CpExec>(space, 0, H.nGroups),
553 KOKKOS_LAMBDA(int g) {
554 const int b = gq(gs(g));
555 for (int p = gs(g) + 1; p < gs(g + 1); ++p) {
556 const int q = gq(p);
557 for (int c = 0; c < 3; ++c) {
558 x(q, c) = x(b, c) + gsh(p, c);
559 w(q, c) = w(b, c);
560 }
561 }
562 });
563}
564
567inline void unfoldOrphanKokkos(const PhaseCopies& H, Kokkos::View<float*, CpMem> orphan) {
568 if (H.nGroups <= 0 || orphan.extent(0) == 0)
569 return;
570 auto gs = H.groupStart;
571 auto gq = H.groupSlot;
572 auto ga = H.groupActive;
573 Kokkos::parallel_for(
574 "peclet::dem::copies_unfold", Kokkos::RangePolicy<CpExec>(0, H.nGroups),
575 KOKKOS_LAMBDA(int g) {
576 float s = 0.0f;
577 for (int p = gs(g); p < gs(g + 1); ++p)
578 if (ga(p))
579 s += orphan(gq(p));
580 orphan(gq(gs(g))) = s;
581 });
582 Kokkos::fence();
583}
584
589inline void activityHitVelocityKokkos(Kokkos::View<const ManifoldC*, CpMem> manifolds, int nm,
590 Kokkos::View<const int*, CpMem> realIdx,
591 Kokkos::View<const int*, CpMem> color, SlotOverride ov,
592 Kokkos::View<unsigned char*, CpMem> hit, int nSlots) {
593 CpExec space;
594 Kokkos::deep_copy(space, Kokkos::subview(hit, Kokkos::pair<int, int>(0, nSlots)),
595 static_cast<unsigned char>(0));
596 Kokkos::parallel_for(
597 "peclet::dem::activity_vel", Kokkos::RangePolicy<CpExec>(space, 0, nm), KOKKOS_LAMBDA(int e) {
598 if (color(e) < 0)
599 return;
600 const ManifoldC m = manifolds(e);
601 Kokkos::atomic_store(&hit(ov.slotA(e, realIdx(m.bodyA))), static_cast<unsigned char>(1));
602 if (m.bodyB >= 0)
603 Kokkos::atomic_store(&hit(ov.slotB(e, realIdx(m.bodyB))), static_cast<unsigned char>(1));
604 });
605 space.fence();
606}
609inline void activityHitPositionKokkos(Kokkos::View<const ContactC*, CpMem> contacts,
610 const PosUnits& units, int nu,
611 Kokkos::View<const int*, CpMem> color, SlotOverride ov,
612 Kokkos::View<unsigned char*, CpMem> hit, int nSlots) {
613 CpExec space;
614 Kokkos::deep_copy(space, Kokkos::subview(hit, Kokkos::pair<int, int>(0, nSlots)),
615 static_cast<unsigned char>(0));
616 Kokkos::parallel_for(
617 "peclet::dem::activity_pos", Kokkos::RangePolicy<CpExec>(space, 0, nu), KOKKOS_LAMBDA(int u) {
618 if (color(u) < 0)
619 return;
620 const ContactC c = contacts(units.leader(u));
621 Kokkos::atomic_store(&hit(ov.slotA(u, c.bodyA)), static_cast<unsigned char>(1));
622 if (c.bodyB >= 0)
623 Kokkos::atomic_store(&hit(ov.slotB(u, c.bodyB)), static_cast<unsigned char>(1));
624 });
625 space.fence();
626}
631inline void finishActivityKokkos(Kokkos::View<const unsigned char*, CpMem> hit, int nSlots,
632 PhaseCopies& H, Kokkos::View<int*, CpMem> a) {
633 CpExec space;
634 const int nAll = nSlots + (H.nGroups > 0 ? H.nCopies : 0);
635 Kokkos::parallel_for(
636 "peclet::dem::activity_a", Kokkos::RangePolicy<CpExec>(space, 0, nAll),
637 KOKKOS_LAMBDA(int q) { a(q) = q < nSlots ? static_cast<int>(hit(q)) : 0; });
638 if (H.nGroups > 0) {
639 auto gs = H.groupStart;
640 auto gq = H.groupSlot;
641 auto ga = H.groupActive;
642 auto gk = H.groupK;
643 Kokkos::parallel_for(
644 "peclet::dem::activity_groups", Kokkos::RangePolicy<CpExec>(space, 0, H.nGroups),
645 KOKKOS_LAMBDA(int g) {
646 int n = 0;
647 for (int p = gs(g); p < gs(g + 1); ++p) {
648 const unsigned char h = hit(gq(p));
649 ga(p) = h;
650 n += h;
651 }
652 gk(g) = n > 0 ? n : 1;
653 a(gq(gs(g))) = n;
654 });
655 }
656 space.fence();
657}
658
663inline void buildSolveViewsRankKKokkos(Particles& P, const PhaseCopies& H, int nSlots,
664 Kokkos::View<const float*, CpMem> invMass,
665 Kokkos::View<const float* [3], CpMem> invInertia,
666 Kokkos::View<const int*, CpMem> kRank) {
667 CpExec space;
668 growCopyView(P.invMassSolve, static_cast<std::size_t>(nSlots), "peclet::dem::invMassSolve");
669 growCopyView(P.invInertiaSolve, static_cast<std::size_t>(nSlots), "peclet::dem::invInertiaSolve");
670 growCopyView(P.splitSlot, static_cast<std::size_t>(nSlots), "peclet::dem::splitSlot");
671 auto ims = P.invMassSolve;
672 auto iis = P.invInertiaSolve;
673 auto spl = P.splitSlot;
674 auto cb = H.copyBase;
675 const int base = H.slotBase;
676 const bool haveCopies = H.nCopies > 0;
677 Kokkos::parallel_for(
678 "peclet::dem::rankk_solve_views", Kokkos::RangePolicy<CpExec>(space, 0, nSlots),
679 KOKKOS_LAMBDA(int q) {
680 const int b = (haveCopies && q >= base) ? cb(q - base) : q;
681 const int k = kRank(b);
682 if (k > 1) {
683 const float kf = static_cast<float>(k);
684 ims(q) = kf * invMass(b);
685 for (int c = 0; c < 3; ++c)
686 iis(q, c) = kf * invInertia(b, c);
687 spl(q) = 1;
688 } else {
689 ims(q) = invMass(b);
690 for (int c = 0; c < 3; ++c)
691 iis(q, c) = invInertia(b, c);
692 spl(q) = 0;
693 }
694 });
695 space.fence();
696}
697
700inline void buildInvMassCoarseKokkos(Kokkos::View<const float*, CpMem> invMass,
701 Kokkos::View<const int*, CpMem> k,
702 Kokkos::View<const int*, CpMem> a, int n,
703 Kokkos::View<float*, CpMem> out) {
704 Kokkos::parallel_for(
705 "peclet::dem::inv_mass_coarse", Kokkos::RangePolicy<CpExec>(0, n), KOKKOS_LAMBDA(int q) {
706 const int aq = a(q) > 1 ? a(q) : 1;
707 const float ratio = static_cast<float>(k(q)) / static_cast<float>(aq);
708 out(q) = invMass(q) * ratio;
709 });
710 Kokkos::fence();
711}
712
717inline void buildInvInertiaCoarseKokkos(Kokkos::View<const float* [3], CpMem> invInertia,
718 Kokkos::View<const int*, CpMem> k,
719 Kokkos::View<const int*, CpMem> a, int n,
720 Kokkos::View<float* [3], CpMem> out) {
721 Kokkos::parallel_for(
722 "peclet::dem::inv_inertia_coarse", Kokkos::RangePolicy<CpExec>(0, n), KOKKOS_LAMBDA(int q) {
723 const int aq = a(q) > 1 ? a(q) : 1;
724 const float ratio = static_cast<float>(k(q)) / static_cast<float>(aq);
725 for (int c = 0; c < 3; ++c)
726 out(q, c) = invInertia(q, c) * ratio;
727 });
728 Kokkos::fence();
729}
730
733inline int countInactiveAggregatedKokkos(Kokkos::View<const int*, CpMem> parent, int off, int nV,
734 int nGroups1, Kokkos::View<const int*, CpMem> a) {
735 if (nV <= 0 || nGroups1 <= 0)
736 return 0;
737 Kokkos::View<int*, CpMem> cnt("peclet::dem::ml_inact_cnt", nGroups1);
738 Kokkos::parallel_for(
739 "peclet::dem::ml_inact_hist", Kokkos::RangePolicy<CpExec>(0, nV),
740 KOKKOS_LAMBDA(int v) { Kokkos::atomic_add(&cnt(parent(off + v)), 1); });
741 int n = 0;
742 Kokkos::parallel_reduce(
743 "peclet::dem::ml_inact_count", Kokkos::RangePolicy<CpExec>(0, nV),
744 KOKKOS_LAMBDA(int v, int& acc) {
745 if (a(v) == 0 && cnt(parent(off + v)) >= 2)
746 acc += 1;
747 },
748 n);
749 return n;
750}
751
754 if (H.nGroups <= 0)
755 return 0;
756 auto gk = H.groupK;
757 int n = 0;
758 Kokkos::parallel_reduce(
759 "peclet::dem::copies_count_split", Kokkos::RangePolicy<CpExec>(0, H.nGroups),
760 KOKKOS_LAMBDA(int g, int& acc) { acc += gk(g) > 1 ? 1 : 0; }, n);
761 return n;
762}
763
768inline int countSplitAggregatedKokkos(Kokkos::View<const int*, CpMem> parent, int off, int nV,
769 int nGroups1,
770 Kokkos::View<const unsigned char*, CpMem> split) {
771 if (nV <= 0 || nGroups1 <= 0 || split.extent(0) < static_cast<std::size_t>(nV))
772 return 0;
773 Kokkos::View<int*, CpMem> cnt("peclet::dem::ml_agg_cnt", nGroups1);
774 Kokkos::parallel_for(
775 "peclet::dem::ml_agg_hist", Kokkos::RangePolicy<CpExec>(0, nV),
776 KOKKOS_LAMBDA(int v) { Kokkos::atomic_add(&cnt(parent(off + v)), 1); });
777 int n = 0;
778 Kokkos::parallel_reduce(
779 "peclet::dem::ml_agg_count", Kokkos::RangePolicy<CpExec>(0, nV),
780 KOKKOS_LAMBDA(int v, int& acc) {
781 if (split(v) != 0 && cnt(parent(off + v)) >= 2)
782 acc += 1;
783 },
784 n);
785 return n;
786}
787
790inline int countLightHubsKokkos(const PhaseCopies& H, Kokkos::View<const int*, CpMem> eA,
791 Kokkos::View<const int*, CpMem> eB, int nE, int nV,
792 Kokkos::View<const float*, CpMem> invMass) {
793 if (H.nHubs <= 0)
794 return 0;
795 const int nH = H.nHubs;
796 auto hv = H.hubVertex;
797 Kokkos::View<int*, CpMem> hubOf("peclet::dem::lh_hubof", nV);
798 Kokkos::deep_copy(hubOf, -1);
799 Kokkos::parallel_for(
800 "peclet::dem::lh_map", Kokkos::RangePolicy<CpExec>(0, nH),
801 KOKKOS_LAMBDA(int h) { hubOf(hv(h)) = h; });
802 Kokkos::View<double*, CpMem> msum("peclet::dem::lh_msum", nH);
803 Kokkos::View<int*, CpMem> mcnt("peclet::dem::lh_mcnt", nH);
804 Kokkos::parallel_for(
805 "peclet::dem::lh_accum", Kokkos::RangePolicy<CpExec>(0, nE), KOKKOS_LAMBDA(int e) {
806 const int a = eA(e), b = eB(e);
807 if (a < 0 || b < 0)
808 return;
809 const int ends[2][2] = {{a, b}, {b, a}};
810 for (int j = 0; j < 2; ++j) {
811 const int h = hubOf(ends[j][0]);
812 const float im = invMass(ends[j][1]);
813 if (h >= 0 && im > 0.0f) {
814 Kokkos::atomic_add(&msum(h), 1.0 / static_cast<double>(im));
815 Kokkos::atomic_add(&mcnt(h), 1);
816 }
817 }
818 });
819 int n = 0;
820 Kokkos::parallel_reduce(
821 "peclet::dem::lh_count", Kokkos::RangePolicy<CpExec>(0, nH),
822 KOKKOS_LAMBDA(int h, int& acc) {
823 const float im = invMass(hv(h));
824 if (mcnt(h) == 0 || im <= 0.0f)
825 return;
826 const double mHub = 1.0 / static_cast<double>(im);
827 if (mHub < 10.0 * msum(h) / static_cast<double>(mcnt(h)))
828 acc += 1;
829 },
830 n);
831 return n;
832}
833
834} // namespace peclet::dem
835
836#endif // DEM_SOLVE_COPIES_HPP
dem — portable (Kokkos) contact->manifold reduction, replacing the thrust-based reduce_contacts_to_ma...
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),...
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 edg...
int countSplitBodiesKokkos(const PhaseCopies &H)
Count the bodies whose group has k > 1 (split_stats).
constexpr int kHubEdgeBudget
Hub edge budget (§4.4): a colouring vertex with more active edges is split into ceil(d / kHubEdgeBudg...
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 ...
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 multileve...
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))),...
CpExec::memory_space CpMem
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 u...
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 grou...
constexpr float kSplitOmegaVelocity
Split relaxation (§4.5, §13.1): the PGS normal step on an edge touching a mass-split slot is scaled b...
void seedCopySlotStateKokkos(Particles &P, const PhaseCopies &H)
Seed the copy slots' read-only state from their hub vertex (§4.4 step 6): orientation,...
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 b...
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 [...
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....
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 slo...
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...
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....
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 ...
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 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 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,...
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 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 ...
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) !...
Kokkos::DefaultExecutionSpace CpExec
dem — portable (Kokkos) particle SoA container: the storage the dem flip pivots on.
Portable mirror of ParticleSystem.cuh ContactConstraint (the fields this reduction touches).
Portable mirror of ManifoldConstraint.
Kokkos::View< float *[3], CpMem > invInertiaSolve
Kokkos::View< unsigned char *, CpMem > splitSlot
Kokkos::View< float *, CpMem > invMassSolve
Kokkos::View< unsigned char *, CpMem > groundedLevel
One solve phase's body copies (docs/contact_solve_framework.md §4.4, §4.5, §WO-4 item 2): the hub cop...
Definition particles.hpp:44
Kokkos::View< float *[3], CpMem > seedX
Definition particles.hpp:53
Kokkos::View< int *, CpMem > hubS
Definition particles.hpp:49
Kokkos::View< float *[3], CpMem > seedW
Definition particles.hpp:53
Kokkos::View< int *, CpMem > slotB
Definition particles.hpp:47
Kokkos::View< float *[3], CpMem > groupShift
Definition particles.hpp:51
Kokkos::View< int *, CpMem > hubVertex
Definition particles.hpp:49
Kokkos::View< int *, CpMem > copyBase
Definition particles.hpp:48
Kokkos::View< int *, CpMem > slotA
Definition particles.hpp:47
Kokkos::View< int *, CpMem > groupK
Definition particles.hpp:50
Kokkos::View< int *, CpMem > groupStart
Definition particles.hpp:50
Kokkos::View< unsigned char *, CpMem > groupActive
Definition particles.hpp:52
Kokkos::View< int *, CpMem > groupSlot
Definition particles.hpp:50
The position units of a contact list as a CSR (§4.3): unit u owns the contacts list(start(u)) .
Per-edge slot overrides of a phase's hub copies (docs/contact_solve_framework.md §4....
int slotA(int e, int def) const
int slotB(int e, int def) const