@@ -146,6 +146,7 @@ struct Octree {
146146 }
147147};
148148
149+
149150inline void bhAccel (Octree* node,
150151 const Particle& p,
151152 real theta,
@@ -162,20 +163,26 @@ inline void bhAccel(Octree* node,
162163
163164 constexpr real G = real (1.0 );
164165
165- // Adaptive softening
166- real eps = nextSoftening (node->size , node->m , dist);
167-
166+ // Geometric separation
168167 real dx = node->cx - p.x ;
169168 real dy = node->cy - p.y ;
170169 real dz = node->cz - p.z ;
171170
172- real r2_soft = dx * dx + dy * dy + dz * dz + eps * eps;
173- real dist = std::sqrt (r2_soft);
171+ // Physical distance (unsmoothed)
172+ real r2 = dx*dx + dy*dy + dz*dz;
173+ real dist = std::sqrt (r2 + real (1e-20 )); // tiny floor to avoid NaN
174+
175+ // Adaptive softening (returns epsilon)
176+ real eps = nextSoftening (node->size , node->m , dist);
177+
178+ // Softened distance for force
179+ real r2_soft = r2 + eps*eps;
180+ real dist_soft = std::sqrt (r2_soft);
174181
175- // BH acceptance criterion
182+ // BH acceptance criterion (use geometric distance)
176183 if (node->leaf || (node->size / dist) < theta) {
177- // Monopole
178- real invDist = real (1.0 ) / dist ;
184+ // Monopole term with softened distance
185+ real invDist = real (1.0 ) / dist_soft ;
179186 real invDist2 = invDist * invDist;
180187 real invDist3 = invDist * invDist2;
181188
@@ -185,13 +192,13 @@ inline void bhAccel(Octree* node,
185192 real ay_m = dy * fac;
186193 real az_m = dz * fac;
187194
188- // Quadrupole ( use non-softened r^2 for shape; still approximate)
195+ // Quadrupole: use geometric r (no softening) for shape
189196 real rx = dx;
190197 real ry = dy;
191198 real rz = dz;
192- real r2 = rx * rx + ry * ry + rz * rz + real (1e-12 ); // avoid zero
193- real r = std::sqrt (r2 );
194- real invr = real (1.0 ) / r ;
199+ real r2_q = rx* rx + ry* ry + rz* rz + real (1e-12 ); // avoid zero
200+ real r_q = std::sqrt (r2_q );
201+ real invr = real (1.0 ) / r_q ;
195202 real invr2 = invr * invr;
196203 real invr3 = invr * invr2;
197204 real invr5 = invr3 * invr2;
@@ -233,65 +240,4 @@ inline void bhAccel(Octree* node,
233240 bhAccel (node->child [i], p, theta, ax, ay, az);
234241 }
235242 }
236- }
237-
238-
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-
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- p.ax = ax;
283- p.ay = ay;
284- p.az = az;
285- }
286- }
287-
288-
289- void evalAtPoint (const Particle& probe,
290- real& ax,
291- real& ay,
292- real& az) const {
293- ax = ay = az = 0 ;
294- if (!root) return ;
295- bhAccel (root, probe, theta, ax, ay, az);
296- }
297- };
243+ }
0 commit comments