36#ifndef PECLET_CORE_GEOM_BODY_PROPERTIES_HPP
37#define PECLET_CORE_GEOM_BODY_PROPERTIES_HPP
58namespace body_detail {
65template <
class Real,
class Phi>
71 for (
int it = 0;
it < 40; ++
it) {
81 return Real(0.5) * (a + b);
86 m1 +=
Real(0.5) * (b * b - a * a);
87 m2 += (b * b * b - a * a * a) /
Real(3);
91 for (
int i = 1;
i <=
NS; ++
i) {
115 for (
int i = 0;
i < 3; ++
i)
116 for (
int j = 0;
j < 3; ++
j) {
121 Real off = std::fabs(a[0][1]) + std::fabs(a[0][2]) + std::fabs(a[1][2]);
124 for (
int p = 0; p < 2; ++p)
125 for (
int q = p + 1;
q < 3; ++
q) {
126 if (a[p][
q] ==
Real(0))
128 const Real theta = (a[
q][
q] - a[p][p]) / (
Real(2) * a[p][
q]);
130 (std::fabs(theta) + std::sqrt(theta * theta +
Real(1)));
132 for (
int k = 0;
k < 3; ++
k) {
137 for (
int k = 0;
k < 3; ++
k) {
142 for (
int k = 0;
k < 3; ++
k) {
149 for (
int i = 0;
i < 3; ++
i)
152 for (
int i = 0;
i < 2; ++
i)
153 for (
int j =
i + 1;
j < 3; ++
j)
155 std::swap(w[
i], w[
j]);
156 for (
int k = 0;
k < 3; ++
k)
157 std::swap(
V[
k][
i],
V[
k][
j]);
166 const Real scale = std::max(std::max(std::fabs(w[0]), std::fabs(w[2])),
Real(1e-30));
176 for (
int r = 0; r < 3; ++r) {
178 for (
int c =
i; c <
j; ++c)
179 rn[r] +=
V[r][c] *
V[r][c];
184 for (
int r = 0; r < 3; ++r) {
186 for (
int u = 0; u <
ns; ++u)
193 for (
int u = 0; u <
ns; ++u)
194 for (
int v = u + 1; v <
ns; ++v)
196 std::swap(
sel[u],
sel[v]);
199 for (
int r = 0; r <
k; ++r)
200 for (
int c = 0; c <
k; ++c)
201 M[r][c] =
V[
sel[c]][
i + r];
204 for (
int r = 0; r <
k; ++r)
205 for (
int c = 0; c <
k; ++c)
206 for (
int t = 0;
t <
k; ++
t)
207 MtM[r][c] += M[
t][r] * M[
t][c];
208 for (
int r =
k; r < 3; ++r)
213 for (
int r = 0; r < 3; ++r)
214 for (
int c = 0; c < 3; ++c)
215 for (
int t = 0;
t < 3; ++
t)
218 for (
int r = 0; r <
k; ++r)
219 for (
int c = 0; c <
k; ++c)
220 for (
int t = 0;
t <
k; ++
t)
221 U[r][c] += M[r][
t] *
inv[
t][c];
224 for (
int r = 0; r < 3; ++r)
225 for (
int c = 0; c <
k; ++c) {
227 for (
int t = 0;
t <
k; ++
t)
228 NV[r][c] +=
V[r][
i +
t] *
U[
t][c];
230 for (
int r = 0; r < 3; ++r)
231 for (
int c = 0; c <
k; ++c)
232 V[r][
i + c] =
NV[r][c];
242 const Real tr =
R[0][0] +
R[1][1] +
R[2][2];
245 q.w =
Real(0.25) * s;
246 q.x = (
R[2][1] -
R[1][2]) / s;
247 q.y = (
R[0][2] -
R[2][0]) / s;
248 q.z = (
R[1][0] -
R[0][1]) / s;
249 }
else if (
R[0][0] >
R[1][1] &&
R[0][0] >
R[2][2]) {
251 q.w = (
R[2][1] -
R[1][2]) / s;
252 q.x =
Real(0.25) * s;
253 q.y = (
R[0][1] +
R[1][0]) / s;
254 q.z = (
R[0][2] +
R[2][0]) / s;
255 }
else if (
R[1][1] >
R[2][2]) {
257 q.w = (
R[0][2] -
R[2][0]) / s;
258 q.x = (
R[0][1] +
R[1][0]) / s;
259 q.y =
Real(0.25) * s;
260 q.z = (
R[1][2] +
R[2][1]) / s;
263 q.w = (
R[1][0] -
R[0][1]) / s;
264 q.x = (
R[0][2] +
R[2][0]) / s;
265 q.y = (
R[1][2] +
R[2][1]) / s;
266 q.z =
Real(0.25) * s;
281template <
class Real,
class Phi>
284 namespace bd = body_detail;
286 throw std::invalid_argument(
"bodyProperties: empty box");
288 throw std::invalid_argument(
"bodyProperties: n must be >= 1");
294 Real V = 0, M[3] = {0, 0, 0},
S[3][3] = {};
296 for (
int ci = 0;
ci < n; ++
ci)
297 for (
int cj = 0;
cj < n; ++
cj)
298 for (
int ck = 0;
ck < n; ++
ck) {
311 auto ab = [](
Real v) {
return v <
Real(0) ? -v : v; };
317 const int t1 = (
hA + 1) % 3,
t2 = (
hA + 2) % 3;
322 quad_detail::gaussLegendre01<Real>(order,
qi,
ti,
wi);
325 quad_detail::gaussLegendre01<Real>(order,
qj,
tj,
wj);
327 p0[t1] +=
ti *
H[t1];
329 Real d[3] = {0, 0, 0};
344 M[t1] +=
W *
tw[t1] *
L0;
347 S[t1][t1] +=
W *
tw[t1] *
tw[t1] *
L0;
356 S[1][0] =
S[0][1] +=
S[1][0];
357 S[2][0] =
S[0][2] +=
S[2][0];
358 S[2][1] =
S[1][2] +=
S[2][1];
364 throw std::runtime_error(
365 "bodyProperties: the field encloses no volume in this box -- check the sign convention "
366 "(negative inside) and that the box contains the solid");
368 const Real c[3] = {
bp.com.x,
bp.com.y,
bp.com.z};
371 for (
int a = 0; a < 3; ++a)
372 for (
int b = 0; b < 3; ++b)
373 cs[a][b] =
S[a][b] -
V * c[a] * c[b];
375 for (
int a = 0; a < 3; ++a)
376 for (
int b = 0; b < 3; ++b)
380 bd::eigSym3(
bp.inertia, w,
R);
381 bd::resolveDegenerateFrame(w,
R);
383 const Real det =
R[0][0] * (
R[1][1] *
R[2][2] -
R[1][2] *
R[2][1]) -
384 R[0][1] * (
R[1][0] *
R[2][2] -
R[1][2] *
R[2][0]) +
385 R[0][2] * (
R[1][0] *
R[2][1] -
R[1][1] *
R[2][0]);
387 for (
int r = 0; r < 3; ++r)
389 for (
int k = 0;
k < 3; ++
k)
390 bp.principal[
k] = w[
k];
391 for (
int r = 0; r < 3; ++r)
392 for (
int cc = 0;
cc < 3; ++
cc)
394 bp.quat = bd::quatFromRotation(
R);
void interiorMoments(const Phi &phi, Vec3< Real > p0, Vec3< Real > d, int nseg, Real &m0, Real &m1, Real &m2)
Moments of the NEGATIVE set {phi < 0} along p0 + s*d, s in [0,1], in the PARAMETER s: m0 = ∫ 1 ds,...
void eigSym3(const Real A[3][3], Real w[3], Real V[3][3])
Jacobi eigensolver for a symmetric 3x3: A = V diag(w) V^T, V orthonormal columns.
Quat< Real > quatFromRotation(const Real R[3][3])
Rotation matrix (columns orthonormal, det +1) -> quaternion (x, y, z, w), Shepperd's method.
void resolveDegenerateFrame(Real w[3], Real V[3][3], Real relTol=Real(1e-2))
Within each cluster of (near-)equal eigenvalues, rotate the eigenbasis onto the input axes with the l...
PECLET_HD int gaussCount(int n)
BodyProperties< Real > bodyProperties(const Phi &phi, Vec3< Real > lo, Vec3< Real > hi, int n=32, int order=5, int nseg=8, Real density=Real(1))
Mass properties of {phi < 0} over the box [lo, hi], composite implicit quadrature: an n^3 cell grid; ...
Kokkos::View< T *, MemSpace > View
1D device array.
PECLET_HD Vec3< Real > scale(Vec3< Real > a, Real s)
double Real
Default host floating type. Device kernels may use float; conversions happen at the boundary.
Real mass
density * volume
Vec3< Real > com
centre of mass, input frame
Real principal[3]
principal moments; principal[k] pairs with column k of R
Real inertia[3][3]
full tensor about the COM, input frame, at the given density
Quat< Real > quat
the same rotation, p_input = com + rotate(quat, p_body)
Real rotation[3][3]
columns = principal axes in the input frame