peclet-dem 1.1.0
Performance-portable XPBD Discrete Element Method (Kokkos + ArborX)
Loading...
Searching...
No Matches
dem_portable.hpp
Go to the documentation of this file.
1
8#ifndef DEM_PORTABLE_HPP
9#define DEM_PORTABLE_HPP
10
11#include <Kokkos_Core.hpp>
12#include <Kokkos_MathematicalFunctions.hpp>
13
14// Kokkos_Core.hpp above defines KOKKOS_VERSION, so core's PECLET_HD resolves to
15// KOKKOS_INLINE_FUNCTION and the leaf formulas are device-callable on every backend.
16#include "peclet/core/geom/primitives.hpp"
17
18namespace peclet::dem {
19
20// Portable mirrors of CUDA float3/float4 (POD, trivially copyable).
21struct F3 {
22 float x, y, z;
23};
24struct F4 {
25 float x, y, z, w;
26};
27
28// --- vector ops ---
29KOKKOS_INLINE_FUNCTION F3 add3(F3 a, F3 b) {
30 return F3{a.x + b.x, a.y + b.y, a.z + b.z};
31}
32KOKKOS_INLINE_FUNCTION F3 sub3(F3 a, F3 b) {
33 return F3{a.x - b.x, a.y - b.y, a.z - b.z};
34}
35KOKKOS_INLINE_FUNCTION F3 scale3(F3 a, float s) {
36 return F3{a.x * s, a.y * s, a.z * s};
37}
38KOKKOS_INLINE_FUNCTION float dot3(F3 a, F3 b) {
39 return a.x * b.x + a.y * b.y + a.z * b.z;
40}
41KOKKOS_INLINE_FUNCTION float len3(F3 v) {
42 return Kokkos::sqrt(dot3(v, v));
43}
44KOKKOS_INLINE_FUNCTION F3 cross3v(F3 a, F3 b) {
45 return F3{a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
46}
47KOKKOS_INLINE_FUNCTION F4 cross3(F4 a, F4 b) {
48 return F4{a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x, 0.0f};
49}
50
51// Load an F3 / F4 from a multidimensional Kokkos View row (templated => no per-header duplication).
52template <class V>
53KOKKOS_INLINE_FUNCTION F3 ldF3(const V& v, int i) {
54 return F3{v(i, 0), v(i, 1), v(i, 2)};
55}
56template <class V>
57KOKKOS_INLINE_FUNCTION F4 ldF4(const V& v, int i) {
58 return F4{v(i, 0), v(i, 1), v(i, 2), v(i, 3)};
59}
60
61// --- quaternion rotate (q = {x,y,z,w}) — copy of math_utils.cuh rotate_vector/inv_rotate_vector
62// ---
63KOKKOS_INLINE_FUNCTION F3 rotateVector(F4 q, F3 v) {
64 const F3 qv{q.x, q.y, q.z};
65 const F3 t = scale3(cross3v(qv, v), 2.0f);
66 return add3(add3(v, scale3(t, q.w)), cross3v(qv, t));
67}
68KOKKOS_INLINE_FUNCTION F3 invRotateVector(F4 q, F3 v) {
69 return rotateVector(F4{-q.x, -q.y, -q.z, q.w}, v);
70}
71KOKKOS_INLINE_FUNCTION F4 quatInverse(F4 q) {
72 return F4{-q.x, -q.y, -q.z, q.w};
73}
74KOKKOS_INLINE_FUNCTION F4 quatMult(F4 a, F4 b) {
75 return F4{
76 a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y, a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
77 a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w, a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z};
78}
79
80// --- analytic SDFs (canonical/unit space) ---
81//
82// RELOCATED (suite/docs/ANALYTIC_SDF_GEOMETRY.md, Layer 0 rung 3): the formula bodies now live in
83// peclet::core::geom::prim (core/include/peclet/core/geom/primitives.hpp), which transcribed them
84// from THIS file operation-for-operation. The wrappers below keep dem's exact signatures and
85// parameter packing, so every call site is untouched and the results are bit-identical -- verified
86// by a raw-bit capture over 76000 evaluations on host-openmp and CUDA, before vs after.
87//
88// The enum values are also core's ShapeKind values (core's kGrid/kSphere/kHollowCylinder/kBox
89// = 0/1/2/3 were chosen to match), so a shape id means the same thing on both sides.
92 SPHERE = 1,
94 BOX = 3,
95 // A COMPOSED analytic shape: a core geom node tree (CSG of the full leaf vocabulary), evaluated
96 // exactly in canonical body space -- the particle sibling of the Layer-1 analytic walls. The
97 // canonical frame MUST be the principal inertia frame (the solver's diagonal-inertia model);
98 // SceneBuilder::principal_frame / addReframed produces exactly that, no resampling.
100};
101
103KOKKOS_INLINE_FUNCTION peclet::core::Vec3<float> toCoreVec(F3 p) {
104 return peclet::core::Vec3<float>{p.x, p.y, p.z};
105}
106
107KOKKOS_INLINE_FUNCTION float sdfSphere(F3 p, F4 params) {
108 return peclet::core::geom::prim::Sphere<float>{params.x}.eval(toCoreVec(p));
109}
110
111// params = (rOuter, height, thickness); tube about the y axis. core's HollowCylinder is the
112// distance-exact form -- NOT core's HollowCylinderShell, which is the max-of-halfspaces variant
113// that core/voro use and which is a different function (see the design note's TRAP section).
114KOKKOS_INLINE_FUNCTION float sdfHollowCylinder(F3 p, F4 params) {
115 return peclet::core::geom::prim::HollowCylinder<float>{params.x, params.y, params.z}.eval(
116 toCoreVec(p));
117}
118
119KOKKOS_INLINE_FUNCTION float sdfBox(F3 p, F4 params) {
120 return peclet::core::geom::prim::Box<float>{params.x, params.y, params.z}.eval(toCoreVec(p));
121}
122
123KOKKOS_INLINE_FUNCTION float sdfEval(F3 p, int type, F4 params) {
124 if (type == SPHERE)
125 return sdfSphere(p, params);
126 if (type == HOLLOW_CYLINDER)
127 return sdfHollowCylinder(p, params);
128 if (type == BOX)
129 return sdfBox(p, params);
130 return 1e9f; // grid SDF (texture) not yet ported
131}
132
133} // namespace peclet::dem
134
135#endif // DEM_PORTABLE_HPP
F4 quatMult(F4 a, F4 b)
F4 quatInverse(F4 q)
float sdfEval(F3 p, int type, F4 params)
float sdfHollowCylinder(F3 p, F4 params)
F3 cross3v(F3 a, F3 b)
float sdfBox(F3 p, F4 params)
F3 invRotateVector(F4 q, F3 v)
F3 ldF3(const V &v, int i)
F3 rotateVector(F4 q, F3 v)
float dot3(F3 a, F3 b)
peclet::core::Vec3< float > toCoreVec(F3 p)
F3 -> core's Real-templated point type. Layout-identical; this is a type change, not a copy.
F3 sub3(F3 a, F3 b)
F4 ldF4(const V &v, int i)
F3 scale3(F3 a, float s)
float len3(F3 v)
F3 add3(F3 a, F3 b)
float sdfSphere(F3 p, F4 params)
F4 cross3(F4 a, F4 b)