15#include <Kokkos_Core.hpp>
27#include "peclet/core/common/view.hpp"
40inline int readInt(Kokkos::View<int, CpMem> v) {
42 Kokkos::deep_copy(h, v);
56 Kokkos::parallel_reduce(
57 "peclet::dem::max_scale", Kokkos::RangePolicy<CpExec>(0, P.
numReal),
58 KOKKOS_LAMBDA(
int i,
float& m) { m = sc(i) > m ? sc(i) : m; }, Kokkos::Max<float>(mx));
76 if (np >
static_cast<int>(P.
pairs.extent(0))) {
77 const int grown = np + np / 2 + 64;
78 Kokkos::realloc(Kokkos::WithoutInitializing, P.
pairs, grown);
83 return std::min(np,
static_cast<int>(P.
pairs.extent(0)));
103 const float margin = 0.1f * maxRad;
111 Kokkos::parallel_for(
112 "self", Kokkos::RangePolicy<CpExec>(space, 0, P.
numReal),
113 KOKKOS_LAMBDA(
int i) { ri(i) = i; });
118 const float ghostBand = maxRad;
132 Kokkos::parallel_for(
133 "rad", Kokkos::RangePolicy<CpExec>(space, 0, P.
numParticles),
134 KOKKOS_LAMBDA(
int i) { rad(i) = sc(i) * gs * bR; });
206 const float margin = 0.1f * maxRad;
212 Kokkos::parallel_for(
213 "self", Kokkos::RangePolicy<CpExec>(space, 0, P.
numReal),
214 KOKKOS_LAMBDA(
int i) { ri(i) = i; });
217 const float ghostBand = maxRad;
229 Kokkos::parallel_for(
230 "rad", Kokkos::RangePolicy<CpExec>(space, 0, P.
numParticles),
231 KOKKOS_LAMBDA(
int i) { rad(i) = sc(i) * gs * bR; });
268inline void demStepMpi(Particles& P, ParticleHalo& halo,
double rcut,
int syncEvery,
269 bool forwardRotation) {
273 if (P.growthFactor != -1.0f && P.growthRate != 0.0f) {
274 P.growthFactor *= std::exp(P.growthRate * P.dt);
275 if (P.growthFactor > 1.0f)
276 P.growthFactor = 1.0f;
278 if (P.growthFactor > 0.0f)
282 P.numParticles = P.numReal;
284 P.posPred, P.quatPred, P.velPred, P.angVelPred, P.deltaPos, P.deltaQuat,
285 P.deltaVel, P.deltaAngVel, P.constraintCounts, P.gravity, P.dt, P.extForce);
289 halo.gather(P, rcut);
294 float gs = P.globalScale, bR = P.baseRadius;
295 Kokkos::parallel_for(
296 "rad", Kokkos::RangePolicy<CpExec>(space, 0, P.numParticles),
297 KOKKOS_LAMBDA(
int i) { rad(i) = sc(i) * gs * bR; });
305 Kokkos::deep_copy(space, P.contactCount, 0);
306 Kokkos::deep_copy(space, P.maxOverlap, 0.0f);
308 P.globalScale, margin, P.contacts, P.contactCount, P.maxOverlap, P.sdfGrid);
309 detectBoundaryKokkos(P.numReal, P.numPlanes, P.posPred, P.quatPred, P.scale, P.shapeId, P.shapes,
310 P.shell, P.planes, P.globalScale, margin, P.contacts, P.contactCount,
313 detectWallSdfKokkos(P.numReal, P.numWalls, P.posPred, P.quatPred, P.scale, P.shapeId, P.shapes,
314 P.shell, P.walls, P.wallGrid, P.globalScale, margin, P.contacts,
315 P.contactCount, P.maxOverlap);
316 const int nc =
readInt(P.contactCount);
319 const int nm =
readInt(P.manifoldCount);
329 const bool friction = (P.frictionDynamic > 0.0f || P.wallFrictionMax > 0.0f);
334 for (
int it = 0; it < P.velocityIterations; ++it) {
337 P.angVelPred, P.realIndices, P.growthRate);
338 solveVelocityKokkos(P.manifolds, nm, P.invMass, P.invInertia, P.quat, P.velPred, P.angVelPred,
339 P.realIndices, P.growthRate, P.restitutionNormal, P.deltaVel,
342 if ((it + 1) % syncEvery == 0 || it == P.velocityIterations - 1) {
343 halo.forward(P.velPred);
345 halo.forward(P.angVelPred);
351 P.realIndices, P.planeFriction, P.frictionDynamic, P.deltaVel,
354 halo.forward(P.velPred);
356 halo.forward(P.angVelPred);
362 P.angVelPred, P.posPred, P.quatPred, P.angVel, P.dt);
363 halo.forwardPositions(P.posPred);
365 halo.forward4(P.quatPred);
369 for (
int it = 0; it < P.positionIterations; ++it) {
370 solvePositionKokkos(P.contacts, nc, P.invMass, P.posPred, P.quatPred, P.quat, P.invInertia,
371 P.deltaPos, P.deltaQuat, P.constraintCounts, P.maxOverlap);
374 if ((it + 1) % syncEvery == 0 || it == P.positionIterations - 1) {
375 halo.forwardPositions(P.posPred);
377 halo.forward4(P.quatPred);
382 finalCommitKokkos(P.numReal, P.pos, P.invMass, P.posPred, P.quat, P.quatPred, P.domain);
384 if (P.thermostatTau > 0.0f && P.dt > 0.0f)
386 P.thermostatKB, P.thermostatTau, P.thermostatTemp, P.dt);
388 P.numParticles = P.numReal;
397 P_.
allocate(capacity, capacity * 64, capacity * 16, 1, 1, 8);
403 r.erase(std::remove(r.begin(), r.end(),
this), r.end());
423 static std::vector<Simulation*> r;
434 baseRadius_ = radius;
436 F4 params{radius, 0, 0, 0};
437 std::vector<F3> shell;
440 params =
F4{radius, height, thickness, 0};
442 float min_dim = std::min(radius, thickness);
445 float spacing = std::min(radius * 0.3f, min_dim * 0.5f);
449 }
else if (shape_type ==
BOX) {
451 params =
F4{radius, radius, radius, 0};
452 float spacing = std::max(radius * 0.5f, 1e-3f);
453 shell =
genBoxShell(radius, radius, radius, spacing);
456 params =
F4{radius, 0, 0, 0};
460 const int nPts =
static_cast<int>(shell.size());
463 P_.
shell = Kokkos::View<float* [3], CpMem>(
"shell", nPts);
464 auto hs = Kokkos::create_mirror_view(P_.
shell);
465 for (
int i = 0; i < nPts; ++i) {
466 hs(i, 0) = shell[i].x;
467 hs(i, 1) = shell[i].y;
468 hs(i, 2) = shell[i].z;
470 Kokkos::deep_copy(P_.
shell, hs);
473 auto h = Kokkos::create_mirror_view(P_.
shapes);
474 h(0) =
ShapeDesc{shape_type, params, 0, nPts};
475 Kokkos::deep_copy(P_.
shapes, h);
478 float ix = 1.0f, iy = 1.0f, iz = 1.0f;
479 if (shape_type ==
SPHERE) {
480 if (baseRadius_ > 0.0f) {
481 float v = 2.5f / (baseRadius_ * baseRadius_);
485 float r_out = baseRadius_, r_in = baseRadius_ - thickness;
488 float term_r = r_out * r_out + r_in * r_in;
489 float I_zz = 0.5f * term_r;
490 float I_xx = (1.0f / 12.0f) * (3.0f * term_r + height * height);
497 }
else if (shape_type ==
BOX) {
498 float L = 2.0f * baseRadius_;
499 float I = (1.0f / 6.0f) *
L *
L;
501 ix = iy = iz = 1.0f / I;
504 defaultInvI_ =
F3{ix, iy, iz};
518 void setSdfShape(
const std::vector<float>& grid,
int nx,
int ny,
int nz,
F3 origin,
F3 spacing,
519 const std::vector<float>& shellFlat,
F3 invInertia,
float boundingRadius) {
520 if (nx < 2 || ny < 2 || nz < 2)
521 throw std::runtime_error(
"setSdfShape: grid dims must be >= 2 on each axis");
522 if (
static_cast<long>(nx) * ny * nz !=
static_cast<long>(grid.size()))
523 throw std::runtime_error(
"setSdfShape: grid.size() must equal nx*ny*nz");
524 const int nPts =
static_cast<int>(shellFlat.size() / 3);
526 throw std::runtime_error(
"setSdfShape: empty surface point shell");
529 P_.
sdfGrid = Kokkos::View<float*, CpMem>(
"sdfGrid", grid.size());
530 auto hg = Kokkos::create_mirror_view(P_.
sdfGrid);
531 for (
size_t i = 0; i < grid.size(); ++i)
533 Kokkos::deep_copy(P_.
sdfGrid, hg);
536 P_.
shell = Kokkos::View<float* [3], CpMem>(
"shell", nPts);
537 auto hs = Kokkos::create_mirror_view(P_.
shell);
538 for (
int i = 0; i < nPts; ++i) {
539 hs(i, 0) = shellFlat[3 * i];
540 hs(i, 1) = shellFlat[3 * i + 1];
541 hs(i, 2) = shellFlat[3 * i + 2];
543 Kokkos::deep_copy(P_.
shell, hs);
548 sd.params =
F4{boundingRadius, 0, 0, 0};
555 sd.gridOrigin = origin;
556 sd.gridInvSpacing =
F3{spacing.
x > 0 ? 1.0f / spacing.
x : 0.0f,
557 spacing.
y > 0 ? 1.0f / spacing.
y : 0.0f,
558 spacing.
z > 0 ? 1.0f / spacing.
z : 0.0f};
559 auto h = Kokkos::create_mirror_view(P_.
shapes);
561 Kokkos::deep_copy(P_.
shapes, h);
563 baseRadius_ = boundingRadius;
565 defaultInvI_ = invInertia;
567 ensureContactCapacity();
570 void setDomain(
float lx,
float ly,
float lz,
bool px,
bool py,
bool pz) {
571 P_.
domain =
Domain{
F3{0, 0, 0},
F3{lx, ly, lz},
F3{lx, ly, lz}, px, py, pz};
578 F3{mx.
x - mn.
x, mx.y - mn.
y, mx.z - mn.
z},
616 (void)restitution_tangent;
618 void addPlane(
float px,
float py,
float pz,
float nx,
float ny,
float nz) {
619 auto h = Kokkos::create_mirror_view(P_.
planes);
620 Kokkos::deep_copy(h, P_.
planes);
623 Kokkos::deep_copy(P_.
planes, h);
634 int addSdfWall(
const std::vector<float>& grid,
int nx,
int ny,
int nz,
F3 origin,
F3 spacing,
635 float restitution,
float friction) {
636 if (nx < 2 || ny < 2 || nz < 2)
637 throw std::runtime_error(
"addSdfWall: grid dims must be >= 2 on each axis");
638 if (
static_cast<long>(nx) * ny * nz !=
static_cast<long>(grid.size()))
639 throw std::runtime_error(
"addSdfWall: grid.size() must equal nx*ny*nz");
645 w.gridOffset =
static_cast<int>(wallGridHost_.size());
647 w.invSpacing =
F3{spacing.
x > 0 ? 1.0f / spacing.
x : 0.0f, spacing.
y > 0 ? 1.0f / spacing.
y : 0.0f,
648 spacing.
z > 0 ? 1.0f / spacing.
z : 0.0f};
649 w.restitution = restitution;
650 w.friction = friction;
651 const int idx =
static_cast<int>(wallsHost_.size());
652 wallsHost_.push_back(w);
653 wallGridHost_.insert(wallGridHost_.end(), grid.begin(), grid.end());
655 P_.
wallGrid = Kokkos::View<float*, CpMem>(
"wallGrid", wallGridHost_.size());
656 auto hg = Kokkos::create_mirror_view(P_.
wallGrid);
657 for (
size_t i = 0; i < wallGridHost_.size(); ++i)
658 hg(i) = wallGridHost_[i];
661 P_.
numWalls =
static_cast<int>(wallsHost_.size());
664 ensureContactCapacity();
673 if (wallIndex < 0 || wallIndex >=
static_cast<int>(wallsHost_.size()))
674 throw std::runtime_error(
"setWallVelocity: wall index out of range");
675 wallsHost_[wallIndex].linVel = linVel;
676 wallsHost_[wallIndex].angVel = angVel;
677 wallsHost_[wallIndex].center = center;
683 const int n =
static_cast<int>(xyz.size() / 3);
686 auto pos = Kokkos::create_mirror_view(P_.
pos);
687 auto q = Kokkos::create_mirror_view(P_.
quat);
688 auto im = Kokkos::create_mirror_view(P_.
invMass);
689 auto sc = Kokkos::create_mirror_view(P_.
scale);
690 auto ii = Kokkos::create_mirror_view(P_.
invInertia);
691 auto sid = Kokkos::create_mirror_view(P_.
shapeId);
692 auto vel = Kokkos::create_mirror_view(P_.
vel);
693 auto av = Kokkos::create_mirror_view(P_.
angVel);
694 for (
int i = 0; i < n; ++i) {
695 pos(i, 0) = xyz[3 * i];
696 pos(i, 1) = xyz[3 * i + 1];
697 pos(i, 2) = xyz[3 * i + 2];
705 ii(i, 0) = defaultInvI_.
x;
706 ii(i, 1) = defaultInvI_.
y;
707 ii(i, 2) = defaultInvI_.
z;
708 vel(i, 0) = vel(i, 1) = vel(i, 2) = 0;
709 av(i, 0) = av(i, 1) = av(i, 2) = 0;
711 Kokkos::deep_copy(P_.
pos, pos);
712 Kokkos::deep_copy(P_.
quat, q);
713 Kokkos::deep_copy(P_.
invMass, im);
714 Kokkos::deep_copy(P_.
scale, sc);
716 Kokkos::deep_copy(P_.
shapeId, sid);
717 Kokkos::deep_copy(P_.
vel, vel);
718 Kokkos::deep_copy(P_.
angVel, av);
722 auto sc = Kokkos::create_mirror_view(P_.
scale);
723 for (
int i = 0; i < P_.
numReal; ++i)
725 Kokkos::deep_copy(P_.
scale, sc);
731 auto tsc = Kokkos::create_mirror_view(P_.
targetScale);
732 for (
int i = 0; i < P_.
numReal && i < (int)s.size(); ++i)
738 auto vel = Kokkos::create_mirror_view(P_.
vel);
739 for (
int i = 0; i < P_.
numReal && 3 * i + 2 < (int)v.size(); ++i) {
740 vel(i, 0) = v[3 * i];
741 vel(i, 1) = v[3 * i + 1];
742 vel(i, 2) = v[3 * i + 2];
744 Kokkos::deep_copy(P_.
vel, vel);
749 auto ef = Kokkos::create_mirror_view(P_.
extForce);
750 for (
int i = 0; i < P_.
numReal && 3 * i + 2 < (int)f.size(); ++i) {
752 ef(i, 1) = f[3 * i + 1];
753 ef(i, 2) = f[3 * i + 2];
762 auto h = Kokkos::create_mirror_view(P_.
quat);
763 for (
int i = 0; i < P_.
numReal && 4 * i + 3 < (int)q.size(); ++i) {
765 h(i, 1) = q[4 * i + 1];
766 h(i, 2) = q[4 * i + 2];
767 h(i, 3) = q[4 * i + 3];
769 Kokkos::deep_copy(P_.
quat, h);
772 auto h = Kokkos::create_mirror_view(P_.
angVel);
773 for (
int i = 0; i < P_.
numReal && 3 * i + 2 < (int)w.size(); ++i) {
775 h(i, 1) = w[3 * i + 1];
776 h(i, 2) = w[3 * i + 2];
778 Kokkos::deep_copy(P_.
angVel, h);
781 auto h = Kokkos::create_mirror_view(P_.
invInertia);
782 for (
int i = 0; i < P_.
numReal && 3 * i + 2 < (int)ii.size(); ++i) {
784 h(i, 1) = ii[3 * i + 1];
785 h(i, 2) = ii[3 * i + 2];
790 auto h = Kokkos::create_mirror_view(P_.
invMass);
791 for (
int i = 0; i < P_.
numReal && i < (int)im.size(); ++i)
793 Kokkos::deep_copy(P_.
invMass, h);
796 return peclet::core::toVector(
797 Kokkos::subview(P_.
angVel, Kokkos::make_pair(0, P_.
numReal), Kokkos::ALL));
800 return peclet::core::toVector(
807 P_.
growthFactor = (new_factor > 0.0f) ? new_factor : 0.01f;
808 else if (new_factor > 0.0f)
819 auto im = Kokkos::create_mirror_view(P_.
invMass);
820 Kokkos::deep_copy(im, P_.
invMass);
821 std::vector<float> out(P_.
numReal);
822 for (
int i = 0; i < P_.
numReal; ++i)
823 out[i] = (im(i) > 0.0f) ? (1.0f / im(i)) : 0.0f;
828 return peclet::core::toVector(
829 Kokkos::subview(P_.
pos, Kokkos::make_pair(0, P_.
numReal), Kokkos::ALL));
832 return peclet::core::toVector(
833 Kokkos::subview(P_.
vel, Kokkos::make_pair(0, P_.
numReal), Kokkos::ALL));
836 return peclet::core::toVector(
837 Kokkos::subview(P_.
quat, Kokkos::make_pair(0, P_.
numReal), Kokkos::ALL));
840 return peclet::core::toVector(Kokkos::subview(P_.
scale, Kokkos::make_pair(0, P_.
numReal)));
857 auto sc = Kokkos::create_mirror_view(P_.
scale);
858 Kokkos::deep_copy(sc, P_.
scale);
859 std::vector<float> radii(P_.
numReal);
860 for (
int i = 0; i < P_.
numReal; ++i)
867 void exportSdf(
const std::string& filename,
int rx,
int ry,
int rz) {
868 const std::vector<float> grid =
getSdfGrid(rx, ry, rz);
877 void initMpi(std::tuple<double, double, double> origin, std::tuple<double, double, double> size,
878 std::tuple<long, long, long> gsize, std::tuple<bool, bool, bool> periodic,
880 halo_->initMpi({std::get<0>(origin), std::get<1>(origin), std::get<2>(origin)},
881 {std::get<0>(size), std::get<1>(size), std::get<2>(size)},
882 {std::get<0>(gsize), std::get<1>(gsize), std::get<2>(gsize)},
883 {std::get<0>(periodic), std::get<1>(periodic), std::get<2>(periodic)}, comm);
891 void enableMpiStep(
double rcut,
int sync_every = 1,
bool forward_rotation =
true,
892 int rebalance_every = 0,
double verlet_skin = 0.0) {
894 mpiSyncEvery_ = sync_every < 1 ? 1 : sync_every;
895 mpiForwardRotation_ = forward_rotation;
896 mpiRebalanceEvery_ = rebalance_every < 0 ? 0 : rebalance_every;
899 halo_->setVerletSkin(
static_cast<float>(verlet_skin));
902 long mpiRebuilds()
const {
return halo_->numRebuilds(); }
903 long mpiGathers()
const {
return halo_->numGathers(); }
906 int rebalance() {
return halo_->rebalance(P_); }
909 int migrateToWeights(
const std::vector<peclet::core::Real>& w) {
910 return halo_->migrateToWeights(P_, w);
912 void stepMpi(
int nsteps) {
913 const double rcut = (mpiRcut_ > 0.0) ? mpiRcut_ :
maxOwnedRadius(P_);
914 for (
int s = 0; s < nsteps; ++s) {
915 if (mpiRebalanceEvery_ > 0 && mpiStepCount_ % mpiRebalanceEvery_ == 0)
916 halo_->rebalance(P_);
917 demStepMpi(P_, *halo_, rcut, mpiSyncEvery_, mpiForwardRotation_);
921 int rank()
const {
return halo_->rank(); }
922 int numGhost()
const {
return halo_->numGhost(); }
929 rx, ry, rz, P_.
domain.
min, P_.
domain.
max, P_.
numReal, P_.
pos, P_.
quat, P_.
scale, P_.
shapeId,
950 auto pos = Kokkos::create_mirror_view(P_.
pos);
951 Kokkos::deep_copy(pos, P_.
pos);
952 auto sc = Kokkos::create_mirror_view(P_.
scale);
953 Kokkos::deep_copy(sc, P_.
scale);
954 auto vel = Kokkos::create_mirror_view(P_.
vel);
955 Kokkos::deep_copy(vel, P_.
vel);
958 std::ofstream out(filename);
960 throw std::runtime_error(
"Could not open file for writing: " + filename);
961 out <<
"<?xml version=\"1.0\"?>\n";
962 out <<
"<VTKFile type=\"PolyData\" version=\"0.1\" byte_order=\"LittleEndian\">\n";
963 out <<
" <PolyData>\n";
964 out <<
" <Piece NumberOfPoints=\"" << n <<
"\" NumberOfVerts=\"0\" "
965 <<
"NumberOfLines=\"0\" NumberOfStrips=\"0\" NumberOfPolys=\"0\">\n";
966 out <<
" <Points>\n";
967 out <<
" <DataArray type=\"Float32\" Name=\"Position\" NumberOfComponents=\"3\" "
968 "format=\"ascii\">\n";
969 for (
int i = 0; i < n; ++i)
970 out << pos(i, 0) <<
" " << pos(i, 1) <<
" " << pos(i, 2) <<
" ";
971 out <<
"\n </DataArray>\n";
972 out <<
" </Points>\n";
973 out <<
" <PointData Scalars=\"Radius\">\n";
974 out <<
" <DataArray type=\"Float32\" Name=\"Radius\" NumberOfComponents=\"1\" "
975 "format=\"ascii\">\n";
976 for (
int i = 0; i < n; ++i)
977 out << sc(i) * P_.
globalScale * baseRadius_ <<
" ";
978 out <<
"\n </DataArray>\n";
979 out <<
" <DataArray type=\"Float32\" Name=\"Velocity\" NumberOfComponents=\"3\" "
980 "format=\"ascii\">\n";
981 for (
int i = 0; i < n; ++i)
982 out << vel(i, 0) <<
" " << vel(i, 1) <<
" " << vel(i, 2) <<
" ";
983 out <<
"\n </DataArray>\n";
984 out <<
" </PointData>\n";
985 out <<
" </Piece>\n";
986 out <<
" </PolyData>\n";
987 out <<
"</VTKFile>\n";
989 std::printf(
"Exported VTP: %s\n", filename.c_str());
996 const int n = std::max<int>(1,
static_cast<int>(wallsHost_.size()));
997 if (
static_cast<int>(P_.
walls.extent(0)) < n)
998 P_.
walls = Kokkos::View<WallSdf*, CpMem>(
"walls", n);
999 auto h = Kokkos::create_mirror_view(P_.
walls);
1000 for (
size_t i = 0; i < wallsHost_.size(); ++i)
1001 h(i) = wallsHost_[i];
1002 Kokkos::deep_copy(P_.
walls, h);
1009 void ensureContactCapacity() {
1010 const int perParticle = std::max(16, shellPoints_);
1011 const long want =
static_cast<long>(P_.
capacity) * perParticle +
1015 P_.
contacts = Kokkos::View<ContactC*, CpMem>(
"contacts", want);
1016 P_.
manifolds = Kokkos::View<ManifoldC*, CpMem>(
"manifolds", want);
1021 float baseRadius_ = 1.0f;
1022 int shellPoints_ = 0;
1023 std::vector<WallSdf> wallsHost_;
1024 std::vector<float> wallGridHost_;
1025 F3 defaultInvI_{2.5f, 2.5f, 2.5f};
1026#ifdef PECLET_DEM_MPI
1027 std::unique_ptr<ParticleHalo> halo_ = std::make_unique<ParticleHalo>();
1028 double mpiRcut_ = 0.0;
1029 int mpiSyncEvery_ = 1;
1030 bool mpiForwardRotation_ =
true;
1031 int mpiRebalanceEvery_ = 0;
1032 long mpiStepCount_ = 0;
dem — portable (ArborX) broad-phase, the Kokkos-native replacement for the CUDA-only cuBQL broad-phas...
Host-facing facade with std::vector setters/getters (binding-agnostic).
void setQuaternions(const std::vector< float > &q)
std::vector< float > getMasses() const
void setThermostat(float temperature, float tau, float kB)
std::tuple< float, float, float > getDomainMax() const
std::vector< float > getInvInertia() const
std::vector< float > getVelocities() const
std::vector< float > getScales() const
void enablePeriodicity(bool x, bool y, bool z)
static std::vector< Simulation * > & registry()
void setSphereShape(float radius)
std::vector< float > getQuaternions() const
void setWallVelocity(int wallIndex, F3 linVel, F3 angVel, F3 center)
void exportSdf(const std::string &filename, int rx, int ry, int rz)
void writeVtp(const std::string &filename) const
void setGlobalScale(float s)
void addPlane(float px, float py, float pz, float nx, float ny, float nz)
void setVelocities(const std::vector< float > &v)
float growthFactor() const
std::tuple< float, float, float > getDomainMin() const
int addSdfWall(const std::vector< float > &grid, int nx, int ny, int nz, F3 origin, F3 spacing, float restitution, float friction)
void setPositions(const std::vector< float > &xyz)
void setInvInertia(const std::vector< float > &ii)
void setAngularVelocities(const std::vector< float > &w)
const V3 & externalForcesView() const
void clearExternalForces()
std::vector< float > getAngularVelocities() const
void setScalesUniform(float s)
std::vector< float > getSdfGrid(int rx, int ry, int rz)
void setDomain(float lx, float ly, float lz, bool px, bool py, bool pz)
void setSolverIterations(int pos, int vel)
void initializeShape(int shape_type, float radius, float height, float thickness)
const V3 & velocitiesView() const
void setExternalForces(const std::vector< float > &f)
std::vector< float > getPositions() const
void setMaterialParams(float restitution_normal, float restitution_tangent, float friction)
void exportLammps(const std::string &filename, int step) const
float getGrowthRate() const
void setDomainMinMax(F3 mn, F3 mx)
const V3 & positionsView() const
void setInvMass(const std::vector< float > &im)
void setSdfShape(const std::vector< float > &grid, int nx, int ny, int nz, F3 origin, F3 spacing, const std::vector< float > &shellFlat, F3 invInertia, float boundingRadius)
void setScales(const std::vector< float > &s)
void setGrowthParams(float rate, float new_factor)
void setGravity(float gx, float gy, float gz)
dem — portable (Kokkos) time integration kernels (integration.cu).
dem — portable (CUDA-free) I/O helpers for the Kokkos dem module: a LAMMPS dump writer and a scalar-S...
dem — portable (Kokkos) owner<->ghost particle halo for the distributed XPBD step.
std::vector< F3 > genCylinderShell(float radius, float height, float thickness, float spacing)
int reduceContactsToManifoldsKokkos(Kokkos::View< const ContactC *, CpMem > contacts, int n, Kokkos::View< ManifoldC *, CpMem > outManifolds, Kokkos::View< int, CpMem > outCount)
Reduce n contacts to manifolds (one per unique canonical pair).
void computePlaneLoadKokkos(Kokkos::View< ContactC *, CpMem > contacts, int numContacts, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[3], CpMem > velPred, Kokkos::View< const float *[3], CpMem > angVelPred, FrManifoldCounts planeFriction)
Plane (idB<0) one-shot loads.
float maxOwnedRadius(const Particles &P)
Largest effective particle radius over the owned set (= max scale × globalScale, growth included).
void accumulateNormalImpulseKokkos(Kokkos::View< ContactC *, CpMem > contacts, int numContacts, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[3], CpMem > velPred, Kokkos::View< const float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, float growthRate)
Force-chain normal load, accumulated over the velocity iterations: contacts(idx).friction_lambda_n +=...
void detectWallSdfKokkos(int numReal, int numWalls, PosView pos, QuatView quat, ScalarF scale, ScalarI shapeId, Kokkos::View< const ShapeDesc *, CpMem > shapes, ShellView shell, Kokkos::View< const WallSdf *, CpMem > walls, GridView wallGrid, float globalScale, float margin, Kokkos::View< ContactC *, CpMem > outContacts, Kokkos::View< int, CpMem > outCount, Kokkos::View< float, CpMem > maxOverlap)
Per-real-particle contacts against a static world-space wall SDF set (a drum barrel,...
void solveContactFrictionKokkos(Kokkos::View< const ContactC *, CpMem > contacts, int numContacts, Kokkos::View< const float *, CpMem > invMass, Kokkos::View< const float *[3], CpMem > invInertia, Kokkos::View< const float *[3], CpMem > velPred, Kokkos::View< const float *[3], CpMem > angVelPred, Kokkos::View< const int *, CpMem > realIdx, FrManifoldCounts planeFriction, float frictionDynamic, Kokkos::View< float *[3], CpMem > deltaVel, Kokkos::View< float *[3], CpMem > deltaAngVel)
One count-averaged Coulomb friction sweep.
Kokkos::View< float *[3], CpMem > V3
void detectContactsKokkos(Kokkos::View< const int *[2], CpMem > pairs, int numPairs, PosView pos, QuatView quat, ScalarF scale, ScalarI shapeId, Kokkos::View< const ShapeDesc *, CpMem > shapes, ShellView shell, float globalScale, float margin, Kokkos::View< ContactC *, CpMem > outContacts, Kokkos::View< int, CpMem > outCount, Kokkos::View< float, CpMem > maxOverlap, GridView sdfGrid=GridView{})
Pair point-shell vs SDF contacts.
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.
void detectBoundaryKokkos(int numReal, int numPlanes, PosView pos, QuatView quat, ScalarF scale, ScalarI shapeId, Kokkos::View< const ShapeDesc *, CpMem > shapes, ShellView shell, Kokkos::View< const PlaneP *, CpMem > planes, float globalScale, float margin, Kokkos::View< ContactC *, CpMem > outContacts, Kokkos::View< int, CpMem > outCount, Kokkos::View< float, CpMem > maxOverlap)
Per-real-particle contacts against explicit planes (point-shell shapes test each surface point; analy...
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.
void writeLammpsDump(const std::string &filename, int step, const std::vector< float > &pos, const std::vector< float > &vel, const std::vector< float > &quat, const std::vector< float > &radii, const float *boxMin, const float *boxMax, bool pbcEnabled)
int readInt(Kokkos::View< int, CpMem > v)
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.
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.
int findCollisionsGrow(Particles &P, float margin)
Broad phase with an automatically-grown pair buffer.
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.
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).
float computeOverlapsKokkos(Particles &P)
Max pair interpenetration on the committed state (faithful to CUDA Simulation::compute_overlaps): cop...
void demStep(Particles &P)
One full XPBD DEM substep over the particle SoA (mirrors simulation.cpp Simulation::step()).
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.
std::vector< float > generateSdfKokkos(int rx, int ry, int rz, F3 dmin, F3 dmax, int numReal, PosView pos, QuatView quat, ScalarF scale, ScalarI shapeId, Kokkos::View< const ShapeDesc *, CpMem > shapes, bool px, bool py, bool pz, GridView sdfGrid=GridView{})
std::vector< F3 > genBoxShell(float hx, float hy, float hz, float spacing)
void generateGhostsKokkos(int numReal, int capacity, Domain dom, float skin, V3 pos, Vf invMass, V3 posPred, V3 vel, V3 velPred, V4 quat, V4 quatPred, V3 angVel, V3 angVelPred, Vf scale, Vi shapeId, Vi realIndices, Kokkos::View< int, CpMem > topGhost)
Generate periodic ghosts for particles [0,numReal).
int findCollisionsArborX(PosV pos, RadV rad, int numParticles, int numReal, float margin, PairsV outPairs, CountV outCount, float boxCap=0.0f)
Emit candidate collision pairs (i<j) for real particles into outPairs/outCount.
void applyThermostatKokkos(int numReal, V3 vel, Vf invMass, V3 angVel, V3 invInertia, V4 quat, double kB, double tau, double Ttarget, float dt)
void countFrictionContactsKokkos(Kokkos::View< const ContactC *, CpMem > contacts, int numContacts, Kokkos::View< const int *, CpMem > realIdx, FrManifoldCounts planeFriction)
Per-body active-contact count into planeFriction(:,1).
Kokkos::DefaultExecutionSpace CpExec
void writeSdfVti(const std::string &filename, const std::vector< float > &grid, int rx, int ry, int rz, const float *minB, const float *maxB)
int calculateGhostCapacity(int nReal, Domain dom, float skin)
Padded particle-array capacity that leaves room for the periodic ghosts generateGhostsKokkos will emi...
dem — portable (Kokkos) narrow-phase: SDF point-shell collision + boundary planes.
dem — portable (Kokkos) SDF-grid reconstruction: the get_sdf_grid pipeline.
dem — portable (Kokkos) particle SoA container: the storage the dem flip pivots on.
dem — portable (Kokkos) periodic ghost generation (periodicity.cu / integration.cu).
dem — portable (host) surface-shell point generators for the analytic shapes.
dem — portable (Kokkos) Coulomb friction cluster (the single dissipative friction path).
dem — portable (Kokkos) XPBD position solve (pure overlap removal).
dem — portable (Kokkos) manifold velocity solve (normal restitution impulse).
Kokkos::View< WallSdf *, CpMem > walls
Kokkos::View< int, CpMem > manifoldCount
void ensureCapacity(int newCap)
Kokkos::View< ManifoldC *, CpMem > manifolds
Kokkos::View< int *[2], CpMem > pairs
Kokkos::View< int, CpMem > contactCount
Kokkos::View< ShapeDesc *, CpMem > shapes
Kokkos::View< int, CpMem > topGhost
Kokkos::View< float *, CpMem > sdfGrid
Kokkos::View< float, CpMem > maxOverlap
Kokkos::View< float *, CpMem > wallGrid
Kokkos::View< const float *, CpMem > crad() const
void allocate(int cap, int maxPairs_, int maxContacts_, int nShapes, int nShell, int nPlanes)
Kokkos::View< float *[2], CpMem > planeFriction
Kokkos::View< float *[3], CpMem > shell
Kokkos::View< ContactC *, CpMem > contacts
Kokkos::View< int, CpMem > pairCount
Kokkos::View< PlaneP *, CpMem > planes
Portable mirror of ShapeDescriptor (analytic fields + a flat-array point shell).
Static, world-space SDF container/geometry the particles collide against (a drum barrel,...