22// it under the terms of the GNU General Public License as published by
33// the Free Software Foundation, either version 3 of the License, or
44// (at your option) any later version.
5-
65// This program is distributed in the hope that it will be useful,
76// but WITHOUT ANY WARRANTY; without even the implied warranty of
87// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
98// GNU General Public License for more details.
10-
119// You should have received a copy of the GNU General Public License
1210// along with this program. If not, see <http://www.gnu.org/licenses/>.
1311
1412#pragma once
1513#include " ../struct/particle.h"
1614#include < vector>
15+ #include < cmath>
16+ #include " dt/softening.h"
1717
1818struct Octree {
1919 // monopole
@@ -25,7 +25,7 @@ struct Octree {
2525 real size; // half-width
2626 bool leaf = true ;
2727 Particle* body = nullptr ;
28- Octree* child[8 ] = {nullptr };
28+ Octree* child[8 ] = { nullptr };
2929
3030 // symmetric quadrupole tensor (6 independent components)
3131 real Qxx = 0 , Qyy = 0 , Qzz = 0 ;
@@ -36,11 +36,25 @@ struct Octree {
3636 x (X), y(Y), z(Z), size(S) {}
3737
3838 ~Octree () {
39- for (auto c : child) delete c;
39+ for (auto c : child) {
40+ delete c;
41+ }
4042 }
4143
4244 int index (const Particle& p) const {
43- return (p.x > x) * 1 + (p.y > y) * 2 + (p.z > z) * 4 ;
45+ return (p.x > x) * 1
46+ + (p.y > y) * 2
47+ + (p.z > z) * 4 ;
48+ }
49+
50+ Octree* createChild (int idx) {
51+ real hs = size * real (0.5 );
52+ return new Octree (
53+ x + ((idx & 1 ) ? hs : -hs),
54+ y + ((idx & 2 ) ? hs : -hs),
55+ z + ((idx & 4 ) ? hs : -hs),
56+ hs
57+ );
4458 }
4559
4660 void insert (Particle* p) {
@@ -54,27 +68,19 @@ struct Octree {
5468 Particle* old = body;
5569 body = nullptr ;
5670 int idx = index (*old);
57- if (!child[idx])
71+ if (!child[idx]) {
5872 child[idx] = createChild (idx);
73+ }
5974 child[idx]->insert (old);
6075 }
6176
6277 int idx = index (*p);
63- if (!child[idx])
78+ if (!child[idx]) {
6479 child[idx] = createChild (idx);
80+ }
6581 child[idx]->insert (p);
6682 }
6783
68- Octree* createChild (int idx) {
69- real hs = size * real (0.5 );
70- return new Octree (
71- x + ((idx & 1 ) ? hs : -hs),
72- y + ((idx & 2 ) ? hs : -hs),
73- z + ((idx & 4 ) ? hs : -hs),
74- hs
75- );
76- }
77-
7884 void computeMass () {
7985 if (leaf) {
8086 if (body) {
@@ -126,22 +132,26 @@ struct Octree {
126132 real rx = c->cx - cx;
127133 real ry = c->cy - cy;
128134 real rz = c->cz - cz;
129- real r2 = rx* rx + ry* ry + rz* rz;
135+ real r2 = rx * rx + ry * ry + rz * rz;
130136 real mchild = c->m ;
131137
132- Qxx += mchild * (3 *rx* rx - r2);
133- Qyy += mchild * (3 *ry* ry - r2);
134- Qzz += mchild * (3 *rz* rz - r2);
138+ Qxx += mchild * (3 * rx * rx - r2);
139+ Qyy += mchild * (3 * ry * ry - r2);
140+ Qzz += mchild * (3 * rz * rz - r2);
135141
136- Qxy += mchild * (3 *rx* ry);
137- Qxz += mchild * (3 *rx* rz);
138- Qyz += mchild * (3 *ry* rz);
142+ Qxy += mchild * (3 * rx * ry);
143+ Qxz += mchild * (3 * rx * rz);
144+ Qyz += mchild * (3 * ry * rz);
139145 }
140146 }
141147};
142148
143- inline void bhAccel (Octree* node, const Particle& p, real theta,
144- real& ax, real& ay, real& az)
149+ inline void bhAccel (Octree* node,
150+ const Particle& p,
151+ real theta,
152+ real& ax,
153+ real& ay,
154+ real& az)
145155{
146156 if (!node || node->m == 0 )
147157 return ;
@@ -150,21 +160,20 @@ inline void bhAccel(Octree* node, const Particle& p, real theta,
150160 if (node->leaf && node->body == &p)
151161 return ;
152162
153- constexpr real G = real (1.0 );
163+ constexpr real G = real (1.0 );
154164
155165 // Adaptive softening
156- real eps = node->size * real ( 0.01 );
166+ real eps = nextSoftening ( node->size , node-> m , dist );
157167
158168 real dx = node->cx - p.x ;
159169 real dy = node->cy - p.y ;
160170 real dz = node->cz - p.z ;
161171
162- real r2_soft = dx* dx + dy* dy + dz* dz + eps* eps;
172+ real r2_soft = dx * dx + dy * dy + dz * dz + eps * eps;
163173 real dist = std::sqrt (r2_soft);
164174
165175 // BH acceptance criterion
166- if (node->leaf || (node->size / dist) < theta)
167- {
176+ if (node->leaf || (node->size / dist) < theta) {
168177 // Monopole
169178 real invDist = real (1.0 ) / dist;
170179 real invDist2 = invDist * invDist;
@@ -180,7 +189,7 @@ inline void bhAccel(Octree* node, const Particle& p, real theta,
180189 real rx = dx;
181190 real ry = dy;
182191 real rz = dz;
183- real r2 = rx* rx + ry* ry + rz* rz + real (1e-12 ); // avoid zero
192+ real r2 = rx * rx + ry * ry + rz * rz + real (1e-12 ); // avoid zero
184193 real r = std::sqrt (r2);
185194 real invr = real (1.0 ) / r;
186195 real invr2 = invr * invr;
@@ -219,7 +228,71 @@ inline void bhAccel(Octree* node, const Particle& p, real theta,
219228 }
220229
221230 // Recurse
222- for (int i = 0 ; i < 8 ; i++)
223- if (node->child [i])
231+ for (int i = 0 ; i < 8 ; ++i) {
232+ if (node->child [i]) {
224233 bhAccel (node->child [i], p, theta, ax, ay, az);
225- }
234+ }
235+ }
236+ }
237+
238+ // Barnes–Hut solver façade, nbody-style, no namespace.
239+ struct BarnesHut {
240+ real theta;
241+ real root_x, root_y, root_z;
242+ real root_size;
243+ Octree* root = nullptr ;
244+
245+ BarnesHut (real theta_,
246+ real cx,
247+ real cy,
248+ real cz,
249+ real halfSize)
250+ : theta(theta_)
251+ , root_x(cx)
252+ , root_y(cy)
253+ , root_z(cz)
254+ , root_size(halfSize) {}
255+
256+ ~BarnesHut () {
257+ delete root;
258+ }
259+
260+ // Build tree from particles (positions must already be set).
261+ void build (std::vector<Particle>& particles) {
262+ delete root;
263+ root = new Octree (root_x, root_y, root_z, root_size);
264+
265+ for (auto & p : particles) {
266+ root->insert (&p);
267+ }
268+ root->computeMass ();
269+ }
270+
271+ // Compute self-gravity accelerations in-place on particles.
272+ // You can adapt this to your own accel storage.
273+ void evalSelfGravity (std::vector<Particle>& particles) const {
274+ if (!root) return ;
275+
276+ for (auto & p : particles) {
277+ real ax = 0 ;
278+ real ay = 0 ;
279+ real az = 0 ;
280+ bhAccel (root, p, theta, ax, ay, az);
281+
282+ // store back however your engine does it
283+ p.ax = ax;
284+ p.ay = ay;
285+ p.az = az;
286+ }
287+ }
288+
289+ // Single-point query, like SPH's evalAcceleration.
290+ void evalAtPoint (const Particle& probe,
291+ real& ax,
292+ real& ay,
293+ real& az) const {
294+ ax = ay = az = 0 ;
295+ if (!root) return ;
296+ bhAccel (root, probe, theta, ax, ay, az);
297+ }
298+ };
0 commit comments