Skip to content

Commit 3c3dea6

Browse files
Updated to barnes-hut w/ Higher-order multipoles
1 parent 0751add commit 3c3dea6

1 file changed

Lines changed: 96 additions & 17 deletions

File tree

src/gravity/octree.h

Lines changed: 96 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,22 @@
1515
#include "../struct/particle.h"
1616
#include <vector>
1717

18-
19-
// =========================
20-
// Octree definition
21-
// =========================
2218
struct Octree {
19+
// monopole
2320
real cx, cy, cz; // center of mass
2421
real m; // total mass
22+
23+
// node geometry
2524
real x, y, z; // center of node
2625
real size; // half-width
2726
bool leaf = true;
2827
Particle* body = nullptr;
2928
Octree* child[8] = {nullptr};
3029

30+
// symmetric quadrupole tensor (6 independent components)
31+
real Qxx = 0, Qyy = 0, Qzz = 0;
32+
real Qxy = 0, Qxz = 0, Qyz = 0;
33+
3134
Octree(real X, real Y, real Z, real S)
3235
: cx(0), cy(0), cz(0), m(0),
3336
x(X), y(Y), z(Z), size(S) {}
@@ -75,21 +78,31 @@ struct Octree {
7578
void computeMass() {
7679
if (leaf) {
7780
if (body) {
78-
m = body->m;
81+
m = body->m;
7982
cx = body->x;
8083
cy = body->y;
8184
cz = body->z;
85+
} else {
86+
m = 0;
87+
cx = cy = cz = 0;
8288
}
89+
90+
// single particle → no internal quadrupole
91+
Qxx = Qyy = Qzz = 0;
92+
Qxy = Qxz = Qyz = 0;
8393
return;
8494
}
8595

8696
m = 0;
8797
cx = cy = cz = 0;
8898

99+
// first: recurse and accumulate mass + COM
89100
for (auto c : child) {
90101
if (!c) continue;
91102
c->computeMass();
92-
m += c->m;
103+
if (c->m == 0) continue;
104+
105+
m += c->m;
93106
cx += c->cx * c->m;
94107
cy += c->cy * c->m;
95108
cz += c->cz * c->m;
@@ -99,6 +112,30 @@ struct Octree {
99112
cx /= m;
100113
cy /= m;
101114
cz /= m;
115+
} else {
116+
cx = cy = cz = 0;
117+
}
118+
119+
// second: build quadrupole from children treated as point masses
120+
Qxx = Qyy = Qzz = 0;
121+
Qxy = Qxz = Qyz = 0;
122+
123+
for (auto c : child) {
124+
if (!c || c->m == 0) continue;
125+
126+
real rx = c->cx - cx;
127+
real ry = c->cy - cy;
128+
real rz = c->cz - cz;
129+
real r2 = rx*rx + ry*ry + rz*rz;
130+
real mchild = c->m;
131+
132+
Qxx += mchild * (3*rx*rx - r2);
133+
Qyy += mchild * (3*ry*ry - r2);
134+
Qzz += mchild * (3*rz*rz - r2);
135+
136+
Qxy += mchild * (3*rx*ry);
137+
Qxz += mchild * (3*rx*rz);
138+
Qyz += mchild * (3*ry*rz);
102139
}
103140
}
104141
};
@@ -113,34 +150,76 @@ inline void bhAccel(Octree* node, const Particle& p, real theta,
113150
if (node->leaf && node->body == &p)
114151
return;
115152

116-
constexpr real G = real(1.0); // temporarily 1 instead of meter units for galaxies to run efficiently
153+
constexpr real G = real(1.0);
117154

118155
// Adaptive softening
119-
real eps = node->size * real(0.01);
156+
real eps = node->size * real(0.01);
120157

121158
real dx = node->cx - p.x;
122159
real dy = node->cy - p.y;
123160
real dz = node->cz - p.z;
124161

125-
real distSq = dx*dx + dy*dy + dz*dz + eps*eps;
126-
real dist = std::sqrt(distSq);
162+
real r2_soft = dx*dx + dy*dy + dz*dz + eps*eps;
163+
real dist = std::sqrt(r2_soft);
127164

128-
// Correct Barnes–Hut acceptance criterion
165+
// BH acceptance criterion
129166
if (node->leaf || (node->size / dist) < theta)
130167
{
131-
real invDist = std::sqrt(real(1.0) / distSq);
132-
real invDist3 = invDist * invDist * invDist;
168+
// Monopole
169+
real invDist = real(1.0) / dist;
170+
real invDist2 = invDist * invDist;
171+
real invDist3 = invDist * invDist2;
133172

134173
real fac = G * node->m * invDist3;
135174

136-
ax += dx * fac;
137-
ay += dy * fac;
138-
az += dz * fac;
175+
real ax_m = dx * fac;
176+
real ay_m = dy * fac;
177+
real az_m = dz * fac;
178+
179+
// Quadrupole (use non-softened r^2 for shape; still approximate)
180+
real rx = dx;
181+
real ry = dy;
182+
real rz = dz;
183+
real r2 = rx*rx + ry*ry + rz*rz + real(1e-12); // avoid zero
184+
real r = std::sqrt(r2);
185+
real invr = real(1.0) / r;
186+
real invr2 = invr * invr;
187+
real invr3 = invr * invr2;
188+
real invr5 = invr3 * invr2;
189+
real invr7 = invr5 * invr2;
190+
191+
// q = r_i Q_ij r_j
192+
real q =
193+
node->Qxx * rx * rx +
194+
node->Qyy * ry * ry +
195+
node->Qzz * rz * rz +
196+
2 * (node->Qxy * rx * ry +
197+
node->Qxz * rx * rz +
198+
node->Qyz * ry * rz);
199+
200+
// ∇q = 2 Q r
201+
real Qrx = 2 * (node->Qxx * rx + node->Qxy * ry + node->Qxz * rz);
202+
real Qry = 2 * (node->Qxy * rx + node->Qyy * ry + node->Qyz * rz);
203+
real Qrz = 2 * (node->Qxz * rx + node->Qyz * ry + node->Qzz * rz);
204+
205+
// ∇(r^-5) = -5 r^-7 r
206+
real grad_r5_x = -5 * invr7 * rx;
207+
real grad_r5_y = -5 * invr7 * ry;
208+
real grad_r5_z = -5 * invr7 * rz;
209+
210+
// a_Q = (G/2) [ (∇q) r^-5 + q ∇(r^-5) ]
211+
real ax_q = (G * real(0.5)) * (Qrx * invr5 + q * grad_r5_x);
212+
real ay_q = (G * real(0.5)) * (Qry * invr5 + q * grad_r5_y);
213+
real az_q = (G * real(0.5)) * (Qrz * invr5 + q * grad_r5_z);
214+
215+
ax += ax_m + ax_q;
216+
ay += ay_m + ay_q;
217+
az += az_m + az_q;
139218
return;
140219
}
141220

142221
// Recurse
143222
for (int i = 0; i < 8; i++)
144223
if (node->child[i])
145224
bhAccel(node->child[i], p, theta, ax, ay, az);
146-
}
225+
}

0 commit comments

Comments
 (0)