Molecular Dynamics Simulation of Hard Spheres — Priority Queue In Action With Java
By: Sabin Sharma
Tags: Programming, Java, Simulation, Coding, Science

A tad bit of high school physics is required. As we know velocity is the rate of change of its position with respect to time. So time taken for an object to reach certain distance i.e distance over velocity. Same is applied in the above mentioned function.

public void bounceOffVerticalWall() {

vx = -vx;

count++;

}



public void bounceOffHorizontalWall() {

vy = -vy;

count++;

}

These two function just sets its axis to negative whenever they touch vertical or horizontal walls respectively.

public double timeToHit(Particle that) {

if (this == that) return Double.POSITIVE_INFINITY;

double dx = that.rx - this.rx;

double dy = that.ry - this.ry;

double dvx = that.vx - this.vx;

double dvy = that.vy - this.vy;

double dvdr = dx * dvx + dy * dvy;

if (dvdr > 0) return Double.POSITIVE_INFINITY;

double dvdv = dvx * dvx + dvy * dvy;

if (dvdv == 0) return Double.POSITIVE_INFINITY;

double drdr = dx * dx + dy * dy;

double sigma = this.radius + that.radius;

double d = (dvdr * dvdr) - dvdv * (drdr - sigma * sigma);

if (d < 0) return Double.POSITIVE_INFINITY;

return -(dvdr + Math.sqrt(d)) / dvdv;

}

Here comes the heavy lifting. This function calculates the time taken to collide with a given particle. The mathematical analogy can be found at Princeton’s java assignment page.

As newtons second law of motion states that the acceleration of an object depends on the mass of the object and the amount of force applied to it. So, when two object collide there are some resultant of that action but here in this simulation only the change in velocity and direction are visible but more can be added onto this algorithm. There are no different logic in the above mentioned code they are just mathematical formulas to calculate the time taken for a collision to occur.

public void bounceOff(Particle that) {

double dx = that.rx - this.rx;

double dy = that.ry - this.ry;

double dvx = that.vx - this.vx;

double dvy = that.vy - this.vy;

double dvdr = dx * dvx + dy * dvy;

double dist = this.radius + that.radius;

double J = 2 * this.mass * that.mass * dvdr / ((this.mass + that.mass) * dist);

double Jx = J * dx / dist;

double Jy = J * dy / dist;

this.vx += Jx / this.mass;

this.vy += Jy / this.mass;

that.vx -= Jx / that.mass;

that.vy -= Jy / that.mass;

this.count++;

that.count++;

}

After, we have done predicting the collision with a given object . Now, all we need to do is calculate the resultant of the collision. Since, no particle exhibits the same property after a collision so the respective properties must be updated. These update process is carried out using some formulas shown below.

Event Problem’s Solution

The main motivation behind creating Event class is to check if the two particle collides relative to time differences. So to tackle this problem we will have to take help of java’s Comparable interface, which allows us to implement compareTo method to calculate the difference in time which we can use in future to arrange nodes or events in priority queue. As we know priority queue can be of either of max type or min type. Meaning when we delete an item within ordered priority queue it removes the maximum or minimum node relative to the type we use. So, the main motivation is to delete minimum node or node representing present time step so that events that occurs in future are examined and operated later. Hence, saving a lot of computation.

Now, we need a class that implements Comparable interface. Then, we define what should be the metric to order the Event class within the queue in compareTo method. If you are not familiar with comparable interface I suggest you to give a quick look onto that. Moreover, a event class must contain two particles : its current time step and the number of collision met by each particles.

The reason behind maintaining the number of collision is that we predict the potential collision before hand .If the particle collides with another particle before reaching the predicted time step then this event is considered as invalid .The reason for that is the particle it needed to collide with has already made a collision with another particle and has changed its velocity and direction.

public Event(double t, Particle a, Particle b) {

time = t;

this.a = a;

this.b = b;

if (a != null) countA = a.count();

else countA = -1;

if (b != null) countB = b.count();

else countB = -1;

}

Initially, we maintain a collision count in two different variables and later on we compare if the event is valid or invalid.

public boolean isValid() {

if (a != null && a.count() != countA) return false;

if (b != null && b.count() != countB) return false;

return true;

}

So, for an event to be valid both given particles A and B must not have made any collision with other particles. To validate the event we compare its collision count when it was initialized with the number of collision count when the event is being deleted from the queue. So, if they match then the object has not met any collision otherwise the event is invalidated and moved on to next event. We will see this process when we solve Collision System Problem.

Collision System’s Solution

This is the part where Particles and Events get tied up. As I mentioned earlier we will be using Min Priority Queue to maintain a sequence of events, and there are arrays of particles that will be randomly assigned.

The motivation behind the creation of collision system can be defined as follows:

Delete the impending event from the priority queue. Check if the event has been invalidated (if any of the particle has made a collision before) Move every other particles to time t on straight trajectory. Update the velocity of the colliding particle if it meets the validation criteria. Predict future particles wall and particle-particle collision and insert them onto the priority queue.

If we follow the above mentioned condition then we will have a Molecular Dynamics Simulation of Hard Spheres.

private static final double refresh = 0.2;

private MinPQ<Event> pq;

private double t = 0.0;

private Particle[] particles;

Here, refresh is just a fancy variable you can discard it if you wish to.

public CollisionSystem(Particle[] particles) {

this.particles = particles.clone();

}

We just clone the previously initialized particles just to save few lines.

private void predict(Particle a, double limit) {

if (a == null) return;

for (int i = 0; i < particles.length; i++) {

double dt = a.timeToHit(particles[i]);

if (t + dt <= limit) {

pq.insert(new Event(t + dt, a, particles[i]));

}

}

double dtX = a.timeToHitVerticalWall();

double dtY = a.timeToHitHorizontalWall();

if (t + dtX <= limit) pq.insert(new Event(t + dtX, a, null));

if (t + dtY <= limit) pq.insert(new Event(t + dtY, null, a));

}

Here, the predict method iterates over all the particles and inserts an Event with respect to the possibility of hitting another particle. Here I have used a limiting parameter namely limit which can be discarded. It is present here just to limit the simulation into arbitrary steps.

private void redraw(double limit) {

StdDraw.clear();

for (int i = 0; i < particles.length; i++) {

particles[i].draw();

}

StdDraw.show();

StdDraw.pause(20);

if (t < limit) {

pq.insert(new Event(t + 1.0 / refresh, null, null));

}

}

Nothing fancy going into this function. We just redraw every particle after clearing previously drawn sets of particles.

public void simulate(double limit) {

StdDraw.setCanvasSize(800, 800);

pq = new MinPQ<Event>();

for (int i = 0; i < particles.length; i++) predict(particles[i], limit);

pq.insert(new Event(0, null, null));



while (!pq.isEmpty()) {

Event event = pq.delMin();

if (!event.isValid()) continue;

Particle a = event.getA();

Particle b = event.getB();



for (int i = 0; i < particles.length; i++)

particles[i].move(event.getTime() - t);

t = event.getTime();



if (a != null && b != null) a.bounceOff(b);

else if (a != null && b == null) a.bounceOffVerticalWall();

else if (a == null && b != null) b.bounceOffHorizontalWall();

else if (a == null && b == null) redraw(limit);



predict(a, limit);

predict(b, limit);

}

}

Previously mentioned steps can be seen here in this simulate function. Initially, we predict straight line trajectory of every particle. We insert an event with 0,null ,null just to trigger the starting of the simulation. After that we delete minimum item in the priority queue , check if they are valid or not.

After we get a valid event we move every particle forward and check if any collision have occurred or not. Then , we check if both particles are present. If both are present , we call bounceOff function in order to mimic the collision which eventually updates the velocity of the particles. And if either of these two particles are null then they are bounced off x or y axes respectively. Finally, we predict the new straight line trajectory for both particles.

At the end, we have a complete Molecular Dynamics Simulation of Hard Spheres.

There are many more articles if you wanna get started into Machine Learning , or Build Your Own AI Powered game, or maybe overclock your NVIDIA GPU fan with simple GUI

Who Is Sabin Sharma?