BHTree
/**
* BHTree.java
* <p>
* Represents a quadtree for the Barnes-Hut algorithm.
* <p>
* Dependencies: Body.java Quad.java
*
* @author chindesaurus
* @version 1.00
*/
public class BHTree {
// threshold value
private final double Theta = 0.5;
private Body body; // body or aggregate body stored in this node
private Quad quad; // square region that the tree represents
private BHTree NW; // tree representing northwest quadrant
private BHTree NE; // tree representing northeast quadrant
private BHTree SW; // tree representing southwest quadrant
private BHTree SE; // tree representing southeast quadrant
/**
* Constructor: creates a new Barnes-Hut tree with no bodies.
* Each BHTree represents a quadrant and an aggregate body
* that represents all bodies inside the quadrant.
*
* @param q the quadrant this node is contained within
*/
public BHTree(Quad q) {
this.quad = q;
this.body = null;
this.NW = null;
this.NE = null;
this.SW = null;
this.SE = null;
}
/**
* Adds the Body b to the invoking Barnes-Hut tree.
*/
public void insert(Body b) {
// if this node does not contain a body, put the new body b here
if (body == null) {
body = b;
return;
}
// internal node
if (!isExternal()) {
// update the center-of-mass and total mass
body = body.plus(b);
// recursively insert Body b into the appropriate quadrant
putBody(b);
}
// external node
else {
// subdivide the region further by creating four children
NW = new BHTree(quad.NW());
NE = new BHTree(quad.NE());
SE = new BHTree(quad.SE());
SW = new BHTree(quad.SW());
// recursively insert both this body and Body b into the appropriate quadrant
putBody(this.body);
putBody(b);
// update the center-of-mass and total mass
body = body.plus(b);
}
}
/**
* Inserts a body into the appropriate quadrant.
*/
private void putBody(Body b) {
if (b.in(quad.NW()))
NW.insert(b);
else if (b.in(quad.NE()))
NE.insert(b);
else if (b.in(quad.SE()))
SE.insert(b);
else if (b.in(quad.SW()))
SW.insert(b);
}
/**
* Returns true iff this tree node is external.
*/
private boolean isExternal() {
// a node is external iff all four children are null
return (NW == null && NE == null && SW == null && SE == null);
}
/**
* Approximates the net force acting on Body b from all bodies
* in the invoking Barnes-Hut tree, and updates b's force accordingly.
*/
public void updateForce(Body b) {
if (body == null || b.equals(body))
return;
// if the current node is external, update net force acting on b
if (isExternal())
b.addForce(body);
// for internal nodes
else {
// width of region represented by internal node
double s = quad.length();
// distance between Body b and this node's center-of-mass
double d = body.distanceTo(b);
// compare ratio (s / d) to threshold value Theta
if ((s / d) < Theta)
b.addForce(body); // b is far away
// recurse on each of current node's children
else {
NW.updateForce(b);
NE.updateForce(b);
SW.updateForce(b);
SE.updateForce(b);
}
}
}
/**
* Returns a string representation of the Barnes-Hut tree
* in which spaces represent external nodes, and asterisks
* represent internal nodes.
*
* @return a string representation of this quadtree
*/
public String toString() {
if (isExternal())
return " " + body + "\n";
else
return "*" + body + "\n" + NW + NE + SW + SE;
}
}
Body
/**
* Body.java
*
* Represents a Body (a point mass) and its position,
* velocity, mass, color, and the net force acting upon it.
*
* @author chindesaurus
* @version 1.00
*/
import java.awt.Color;
public class Body {
// gravitational constant
private static final double G = 6.67e-11;
private double rx, ry; // position
private double vx, vy; // velocity
private double fx, fy; // force
private double mass; // mass
private Color color; // color
/**
* Constructor: creates and initializes a new Body.
*
* @param rx the x-position of this new body
* @param ry the y-position of this new body
* @param vx the x-velocity of this new body
* @param vy the y-velocity of this new body
* @param mass the mass of this new body
* @param color the color of this new body (RGB)
*/
public Body(double rx, double ry, double vx, double vy, double mass, Color color) {
this.rx = rx;
this.ry = ry;
this.vx = vx;
this.vy = vy;
this.mass = mass;
this.color = color;
}
/**
* Updates the velocity and position of the invoking Body
* using leapfrom method, with timestep dt.
*
* @param dt the timestep for this simulation
*/
public void update(double dt) {
vx += dt * fx / mass;
vy += dt * fy / mass;
rx += dt * vx;
ry += dt * vy;
}
/**
* Returns the Euclidean distance between the invoking Body and b.
*
* @param b the body from which to determine the distance
* @return the distance between this and Body b
*/
public double distanceTo(Body b) {
double dx = rx - b.rx;
double dy = ry - b.ry;
return Math.sqrt(dx*dx + dy*dy);
}
/**
* Resets the force (both x- and y-components) of the invoking Body to 0.
*/
public void resetForce() {
fx = 0.0;
fy = 0.0;
}
/**
* Computes the net force acting between the invoking body and b, and
* adds this to the net force acting on the invoking Body.
*
* @param b the body whose net force on this body to calculate
*/
public void addForce(Body b) {
Body a = this;
double EPS = 3E4; // softening parameter
double dx = b.rx - a.rx;
double dy = b.ry - a.ry;
double dist = Math.sqrt(dx*dx + dy*dy);
double F = (G * a.mass * b.mass) / (dist*dist + EPS*EPS);
a.fx += F * dx / dist;
a.fy += F * dy / dist;
}
/**
* Draws the invoking Body.
*/
public void draw() {
StdDraw.setPenColor(color);
StdDraw.point(rx, ry);
}
/**
* Returns a string representation of this body formatted nicely.
*
* @return a formatted string containing this body's x- and y- positions,
* velocities, and mass
*/
public String toString() {
return String.format("%10.3E %10.3E %10.3E %10.3E %10.3E", rx, ry, vx, vy, mass);
}
/**
* Returns true if the body is in quadrant q, else false.
*
* @param q the Quad to check
* @return true iff body is in Quad q, else false
*/
public boolean in(Quad q) {
return q.contains(this.rx, this.ry);
}
/**
* Returns a new Body object that represents the center-of-mass
* of the invoking body and b.
*
* @param b the body to aggregate with this Body
* @return a Body object representing an aggregate of this
* and b, having this and b's center of gravity and
* combined mass
*/
public Body plus(Body b) {
Body a = this;
double m = a.mass + b.mass;
double x = (a.rx * a.mass + b.rx * b.mass) / m;
double y = (a.ry * a.mass + b.ry * b.mass) / m;
return new Body(x, y, a.vx, b.vx, m, a.color);
}
}
NBodyBH
/**
* NBodyBH.java
* <p>
* Reads in a universe of N bodies from stdin, and performs an
* N-Body simulation in O(N log N) using the Barnes-Hut algorithm.
* <p>
* Compilation: javac NBodyBH.java
* Execution: java NBodyBH < inputs/[filename].txt
* Dependencies: BHTree.java Body.java Quad.java StdDraw.java
* Input files: ./inputs/*.txt
*
* @author chindesaurus
* @version 1.00
*/
import java.awt.Color;
public class NBodyBH {
public static void main(String[] args) {
final double dt = 0.1; // time quantum
int N = 10000; // number of particles
double radius = 1000; // radius of universe
// turn on animation mode and rescale coordinate system
StdDraw.show(0);
StdDraw.setXscale(-radius, +radius);
StdDraw.setYscale(-radius, +radius);
// read in and initialize bodies
Body[] bodies = new Body[N]; // array of N bodies
for (int i = 0; i < N; i++) {
double px = radius * 0.5 * (Math.random() - 1);
double py = radius * 0.5 * (Math.random() - 1);
Color color = new Color(225, 225, 0);
bodies[i] = new Body(px, py, 2, 2, 1, color);
}
// simulate the universe
for (double t = 0.0; true; t = t + dt) {
Quad quad = new Quad(0, 0, radius * 2);
BHTree tree = new BHTree(quad);
// build the Barnes-Hut tree
for (int i = 0; i < N; i++)
if (bodies[i].in(quad))
tree.insert(bodies[i]);
// update the forces, positions, velocities, and accelerations
for (int i = 0; i < N; i++) {
bodies[i].resetForce();
tree.updateForce(bodies[i]);
bodies[i].update(dt);
}
// draw the bodies
StdDraw.clear(StdDraw.BLACK);
for (int i = 0; i < N; i++)
bodies[i].draw();
StdDraw.show(10);
}
}
}
Quad
/**
* Quad.java
*
* Represents quadrants for the Barnes-Hut algorithm.
*
* Dependencies: StdDraw.java
*
* @author chindesaurus
* @version 1.00
*/
public class Quad {
private double xmid;
private double ymid;
private double length;
/**
* Constructor: creates a new Quad with the given
* parameters (assume it is square).
*
* @param xmid x-coordinate of center of quadrant
* @param ymid y-coordinate of center of quadrant
* @param length the side length of the quadrant
*/
public Quad(double xmid, double y