peclet-dem
Performance-portable XPBD Discrete Element Method (Kokkos + ArborX)
Loading...
Searching...
No Matches
output_sdf.hpp
Go to the documentation of this file.
1
12#ifndef DEM_OUTPUT_SDF_HPP
13#define DEM_OUTPUT_SDF_HPP
14
15#include <Kokkos_Core.hpp>
16#include <vector>
17
18#include "dem_portable.hpp" // F3/F4, invRotateVector, sdfSphere/sdfHollowCylinder, ShapeKind
19#include "narrowphase.hpp" // ShapeDesc, CpExec/CpMem, view aliases
20
21namespace peclet::dem {
22
23// Generate the SDF grid (flat, x-fastest: i = x + y*rx + z*rx*ry; negative inside solid) over
24// [min,max].
25inline std::vector<float> generateSdfKokkos(int rx, int ry, int rz, F3 dmin, F3 dmax, int numReal,
26 PosView pos, QuatView quat, ScalarF scale,
27 ScalarI shapeId,
28 Kokkos::View<const ShapeDesc*, CpMem> shapes, bool px,
29 bool py, bool pz, GridView sdfGrid = GridView{}) {
30 CpExec space;
31 const long total = (long)rx * ry * rz;
32 const F3 origin = dmin;
33 const F3 vox{(dmax.x - dmin.x) / rx, (dmax.y - dmin.y) / ry, (dmax.z - dmin.z) / rz};
34
35 Kokkos::View<float*, CpMem> grid("sdf_grid", total);
36 Kokkos::View<int*, CpMem> state("sdf_state", total);
37 Kokkos::deep_copy(grid, 100.0f);
38 Kokkos::deep_copy(state, 0);
39
40 // SPLAT: each particle writes the exact analytic signed distance over its AABB band (atomic-min).
41 Kokkos::parallel_for(
42 "peclet::dem::sdf::splat", Kokkos::RangePolicy<CpExec>(space, 0, numReal),
43 KOKKOS_LAMBDA(int i) {
44 const F3 pw = loadF3(pos, i);
45 const F4 q = loadF4(quat, i);
46 const float sc = scale(i);
47 const ShapeDesc shp = shapes(shapeId(i));
48 float rbound = 1.0f;
49 if (shp.type == HOLLOW_CYLINDER) {
50 const float r = shp.params.x, h = shp.params.y;
51 rbound = Kokkos::sqrt(r * r + (h * 0.5f) * (h * 0.5f));
52 } else if (shp.type == SPHERE)
53 rbound = shp.params.x;
54 else if (shp.type == SHAPE_GRID_SDF)
55 rbound = shp.params.x; // canonical bounding radius stored in params.x
56 rbound *= sc;
57 rbound *= 1.2f;
58
59 int minx = (int)Kokkos::floor((pw.x - rbound - origin.x) / vox.x),
60 maxx = (int)Kokkos::ceil((pw.x + rbound - origin.x) / vox.x);
61 int miny = (int)Kokkos::floor((pw.y - rbound - origin.y) / vox.y),
62 maxy = (int)Kokkos::ceil((pw.y + rbound - origin.y) / vox.y);
63 int minz = (int)Kokkos::floor((pw.z - rbound - origin.z) / vox.z),
64 maxz = (int)Kokkos::ceil((pw.z + rbound - origin.z) / vox.z);
65 if (!px) {
66 minx = minx < 0 ? 0 : minx;
67 maxx = maxx > rx - 1 ? rx - 1 : maxx;
68 }
69 if (!py) {
70 miny = miny < 0 ? 0 : miny;
71 maxy = maxy > ry - 1 ? ry - 1 : maxy;
72 }
73 if (!pz) {
74 minz = minz < 0 ? 0 : minz;
75 maxz = maxz > rz - 1 ? rz - 1 : maxz;
76 }
77
78 for (int z = minz; z <= maxz; ++z)
79 for (int y = miny; y <= maxy; ++y)
80 for (int x = minx; x <= maxx; ++x) {
81 const F3 vp{origin.x + (x + 0.5f) * vox.x, origin.y + (y + 0.5f) * vox.y,
82 origin.z + (z + 0.5f) * vox.z};
83 const F3 prel = sub3(vp, pw);
84 const F3 plocal = scale3(invRotateVector(q, prel), 1.0f / sc);
85 float distc = 100.0f;
86 if (shp.type == HOLLOW_CYLINDER)
87 distc = sdfHollowCylinder(plocal, shp.params);
88 else if (shp.type == SPHERE)
89 distc = sdfSphere(plocal, shp.params);
90 else if (shp.type == SHAPE_GRID_SDF)
91 distc = sampleGridSdf(plocal, shp, sdfGrid);
92 const float dist = distc * sc;
93 const int wx = (x % rx + rx) % rx, wy = (y % ry + ry) % ry, wz = (z % rz + rz) % rz;
94 if (!px && (x < 0 || x >= rx))
95 continue;
96 if (!py && (y < 0 || y >= ry))
97 continue;
98 if (!pz && (z < 0 || z >= rz))
99 continue;
100 const long vi = (long)wz * rx * ry + (long)wy * rx + wx;
101 Kokkos::atomic_min(&grid(vi), dist);
102 if (dist < rbound)
103 state(vi) = 1;
104 }
105 });
106 space.fence();
107
108 // EIKONAL: Jacobi fast-iterative-method (ping-pong), max(res)*4 iterations, periodic wrap.
109 Kokkos::View<float*, CpMem> grid2("sdf_grid2", total);
110 const int iters = Kokkos::max(Kokkos::max(rx, ry), rz) * 4;
111 const float h = vox.x;
112 Kokkos::View<float*, CpMem> in = grid, out = grid2;
113 for (int it = 0; it < iters; ++it) {
114 Kokkos::View<float*, CpMem> din = in, dout = out;
115 Kokkos::View<int*, CpMem> st = state;
116 Kokkos::parallel_for(
117 "peclet::dem::sdf::eikonal", Kokkos::RangePolicy<CpExec>(space, 0, total),
118 KOKKOS_LAMBDA(long idx) {
119 if (st(idx) == 1) {
120 dout(idx) = din(idx);
121 return;
122 }
123 const long sy = rx, sz = (long)rx * ry;
124 const int z = (int)(idx / sz);
125 const long rem = idx % sz;
126 const int y = (int)(rem / sy), x = (int)(rem % sy);
127 float xm = 100.0f, xp = 100.0f, ym = 100.0f, yp = 100.0f, zm = 100.0f, zp = 100.0f;
128 if (x > 0)
129 xm = din(idx - 1);
130 else if (px)
131 xm = din(idx - 1 + rx);
132 if (x < rx - 1)
133 xp = din(idx + 1);
134 else if (px)
135 xp = din(idx + 1 - rx);
136 if (y > 0)
137 ym = din(idx - sy);
138 else if (py)
139 ym = din((long)z * sz + (long)(ry - 1) * sy + x);
140 if (y < ry - 1)
141 yp = din(idx + sy);
142 else if (py)
143 yp = din((long)z * sz + x);
144 if (z > 0)
145 zm = din(idx - sz);
146 else if (pz)
147 zm = din((long)(rz - 1) * sz + (long)y * sy + x);
148 if (z < rz - 1)
149 zp = din(idx + sz);
150 else if (pz)
151 zp = din((long)y * sy + x);
152 float a = Kokkos::fmin(xm, xp), b = Kokkos::fmin(ym, yp), c = Kokkos::fmin(zm, zp);
153 if (a > b) {
154 float t = a;
155 a = b;
156 b = t;
157 }
158 if (b > c) {
159 float t = b;
160 b = c;
161 c = t;
162 }
163 if (a > b) {
164 float t = a;
165 a = b;
166 b = t;
167 }
168 float u = 100.0f, u1 = a + h;
169 if (u1 <= b)
170 u = u1;
171 else {
172 const float d2 = 2.0f * h * h - (a - b) * (a - b);
173 float u2 = (d2 < 0.0f) ? 100.0f : (a + b + Kokkos::sqrt(d2)) * 0.5f;
174 if (u2 <= c)
175 u = u2;
176 else {
177 const float s = a + b + c, qd = s * s - 3.0f * (a * a + b * b + c * c - h * h);
178 u = (qd < 0.0f) ? 100.0f : (s + Kokkos::sqrt(qd)) / 3.0f;
179 }
180 }
181 dout(idx) = u;
182 });
183 space.fence();
184 auto tmp = in;
185 in = out;
186 out = tmp;
187 }
188
189 std::vector<float> h_grid(total);
190 auto hv = Kokkos::create_mirror_view(in);
191 Kokkos::deep_copy(hv, in);
192 for (long i = 0; i < total; ++i)
193 h_grid[i] = hv(i);
194 return h_grid;
195}
196
197} // namespace peclet::dem
198
199#endif // DEM_OUTPUT_SDF_HPP
dem — portable POD types + math + analytic SDFs shared by the Kokkos kernel ports.
float sdfHollowCylinder(F3 p, F4 params)
Kokkos::View< const float *, CpMem > GridView
F3 invRotateVector(F4 q, F3 v)
Kokkos::View< const int *, CpMem > ScalarI
F3 loadF3(PosView v, int i)
F3 sub3(F3 a, F3 b)
F4 loadF4(QuatView v, int i)
F3 scale3(F3 a, float s)
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{})
Kokkos::View< const float *[4], CpMem > QuatView
float sampleGridSdf(F3 p, const ShapeDesc &d, GridView grid)
Trilinearly sample an imported grid SDF at canonical point p.
Kokkos::View< const float *[3], CpMem > PosView
float sdfSphere(F3 p, F4 params)
Kokkos::View< const float *, CpMem > ScalarF
Kokkos::DefaultExecutionSpace CpExec
dem — portable (Kokkos) narrow-phase: SDF point-shell collision + boundary planes.
static const double dmin[3]