Bubbling Flask
Project 5: Simulate This

Assigned:Thursday, November 20, 2008
Due:Thursday, December 11, 2008 by 11:59pm

Introduction

The purpose of this project is to make a 2D simulator of the ideal gas law. For those of you who may have forgotten, the ideal gas law is a law in physics and chemistry that relates the pressure, volume, temperature, and number of gas particles together as follows:

PV = nRT

The symbols in this equation are as follows:

Now, what we want to do is create a GUI that simulates the movements of a number of particles in a container, in conformity with these laws. A very nice demonstration of this can be found here. This applet is where your TA stole, I mean, drew inspiration allowing him to formulate, this project.


Overview

Your job is to create a 2D simulation of an ideal gas. The user will be able to control the number of moles of He, Ne, and Ar atoms present in the simulation. Likewise, the user will be able to control the pressure, volume, and temperature of the simulation.

Here's a screenshot of the final product:


You will note that the slider corresponding to Pressure and the Temperature also have radio buttons next to them. Because the ideal gas law is an equation, the change of any variables must coincide with the change in at least one other variable. This radio button allows the user to decide if a change in any of the variables will be mirrored by a corresponding change in Pressure or Volume. With this in mind, let's look at the series of tasks necessary to make this project work.

  1. Particle Drawing
    The first step to work on is getting the three various particles to display on the screen and bounce off the walls. When this is working well, you will be able to move on to the next step, knowing that you will be able to test it.

  2. Particle Collisions
    Making the particles bounce off the walls is easy; making them bounce off each other is much harder.

  3. Creating the GUI
    With the particles colliding, it is necessary to create the rest of the GUI so that you can control the thermodynamics of the gas particles.

  4. Linking the GUI to the Particles
    Once you get the particles colliding properly and the GUI created, it is necessary to link them together.

  5. Threading the Interactions (Extra Credit)
    With a large number of particles (larger, in fact, than the maximum number of particles in this simulation), some amount of speedup and smoothness could be gained by dividing the space among a thread pool.

Particle Drawing

The first step you need to accomplish is the ability to draw particles. There are three different particles in this simulation, corresponding to Helium, Neon, and Argon atoms. We represent an entire mole of each gas with a single ball. The characteristics of each particle are as follows:

ElementWeight
(kg per mole)
RepresentationRadius (m)
Helium (He).0040026025
Neon (Ne).02017979
Argon (Ar).03994811

As mentioned before, each colored ball represents one mole of a given element, somehow packed into a perfectly round, solid ball. The image files you can use for the balls are helium.png, neon.png, and argon.png. The image for the plunger is plunger.png.

Basic Drawing

At this point, you'll need to construct a basic GUI. You can either subclass a JFrame or use one directly. The important question is, how are you going to draw the balls bouncing around?

There are some basic drawing primitives in Java, but first you need a canvas on which you can draw things. Surprisingly, your old friend JPanel can serve as just such a canvas. You will need to subclass JPanel to create your own class. The JPanel class has a method called paintComponent() which takes a parameter of type Graphics. This method is called every time the JPanel is redrawn within the GUI.

What you must do is override the paintComponent(Graphics g) method to do the drawing you want to do. The first thing you must do is let it do the drawing that it should do by default by calling super.paintComponent( g ). Then, you'll need to draw all the particles in the gas. To do so, you will want to use the drawImage() method on the Graphics object once for every particle, using the appropriate image.

It is highly recommended that you create something like a Particle superclass that the other particles descend from. Then, you can simply tell each particle in your list to draw itself. Bear in mind that you will need to maintain a list of particles for this project. For this purpose, you are encouraged to use the Java collections framework (classes like ArrayList or LinkedList).

Particles

Each particle has a mass (the molar mass specific to the element), a location, and a velocity. The location and velocity have x and y components that should be stored as doubles.

At this stage, you will want to give particles random locations and random velocities, just to test them out. Write some code that creates a particle at a random location but checks to be sure that the particle does not overlap with another particle.

Unlike the traditional Cartesian coordinate system, the point (0,0) starts in the upper left hand corner of a Java GUI. The x value increases to the right, and the y value increases downward. As long as your code is internally consistent, this will not matter much.

Simulation

From basic physics, we can treat the x and y components separately. You need to create a loop inside your main program to perform the simulation tasks on your list of particles. After updating the locations of all the particles, the simulation loop should sleep for 10ms. After waking up, the loop should measure the actual elapsed time since the last update (could be longer than 10) and use this to do the next round of updates.

At this stage, updating the particles is very simple. You take the x and y components of the location of each particle and, respectively, add the x and y components of the velocity, multiplied by the elapsed time. Let L be the location of the particle, v be the velocity of the particle, and t be the elapsed time, then:

Lx = Lx + vxt
Ly = Ly + vyt

The units of the GUI are assumed to be meters, and the velocities are assumed to be in m/s.

Note that, if a particle collides with the left or the right wall, the x component of its velocity is reversed. Likewise, if a particle collides with the top or bottom wall, the y component of its velocity is reversed. When a collision occurs, it is wise to reset the location of the particle so that it is no longer colliding. Otherwise, the collision may incorrectly be detected repeatedly.


Particle Collisions

Once you have the particles bouncing off the walls, it is time to make them collide with each other. Unfortunately, the physics behind this interaction are more complicated.

From a coding perspective, you will probably want to check every particle in your list against every particle that comes later in the list. Beware of causing double collisions. As with the walls, it is useful to "uncollide" the balls after they have collided to prevent them from becoming stuck together.

Elastic Collisions

From a physics perspective, these balls undergo what is known as an elastic collision. This means that both the momentum and the kinetic energy of the balls are conserved after the collision.

As described in the Wikipedia article, we can explain the collision mathematically in a single dimension with a few equations. Recall that the kinetic energy of an object is (1/2)mv2 where m is the object's mass and v is its velocity. Likewise, mv gives the momentum of an object.

Given two objects with masses m1 and m2, initial velocities u1 and u2, and post-collision velocities v1 and v2, solving the equations needed to keep both the momentum and kinetic energy equal after the collision gives the following equations:

v1 = (u1(m1 - m2) + 2m2u2) / (m1 + m2)
v2 = (u2(m2 - m1) + 2m1u1) / (m1 + m2)

Complications

These equations describe things very simply in one dimension, but we need to deal with two. As it turns out, when two balls have a collision, only the components of their velocities which are parallel to the collision are affected by the equation above. Those components of the velocities of the balls that are tangent to the collision are unaffected. Below, a picture gives an illustration of this phenomenon.

Collision Vectors

Let's assume that the red ball is traveling in the positive x direction. Thus, the yellow arrow gives the total velocity of the red ball. For simplicity, we can assume that the green ball is stationary. As you can see, the blue arrow shows the portion of the velocity which is directly involved in the collision. Only this part changes. The orange arrow is tangential to the collision and is unaffected.

The angle θ gives the angle between the point of the collision and the x-axis. From basic trigonometry, the portion of the x velocity that is affected in the collision is proportional to cos(θ). The portion of the collision that is tangent is proportional to sin(θ).

It may come as some surprise that the analysis for the y component of the velocity of the red ball reverses the sign for the tangential velocity. Thus, the portion of the y velocity that is affected in the collision is proportional to sin(θ), and the portion of the collision that is tangent is proportional to -cos(θ).

With the one-dimensional collision velocities in hand, you can use the elastic collision equations given above to find out how much each is affected. Finally, the x component of the velocity vector for the ball is simply the tangent velocity multiplied by sin(θ) plus the post-collision velocity multiplied by cos(θ). The y component of the velocity vector for the ball is the tangent velocity multiplied by -cos(θ) plus the post-collision velocity multiplied by sin(θ).

You can calculate the value of θ by finding the arctangent of slope of the line connecting the centers of the two balls. Note that you only need to calculate θ once. It doesn't matter which ball you use as the "starting" ball as long as you are consistent throughout.


Creating the GUI

By now you should have a simple JFrame with a panel inside it displaying balls beautifully bouncing around. We want to add controls to the GUI, and so that's the next step.

The first thing that I want to say is that my GUI is a guideline. There are opportunities for creativity here as long as you provide at least the same functionality of my GUI. Also, this is your most significant chance to work on a GUI in this class, and so you should try to keep the GUI attractive and easy to use.

In order to be in conformity with my example, your JPanel displaying the bouncing balls should be 800 x 800 pixels (Sorry, Mr. Bartol). Unfortunately, this display size will eventually be tied to the physics we will discuss in the next section.

Sliders

The first thing you might notice about my GUI is the large number of sliders running down the left-hand side. These are all widgets of type JSlider. They are intended to make entering numeric data within a given range a simple process. A wonderful tutorial on JSliders can be found here.

Each JSlider allows you to specify minimum, maximum, and starting values. Also, you can specify how many major and minor tick marks are displayed. The values I used for my sliders are as follows:

SliderMinimum ValueMaximum ValueStarting ValueMajor TicksMinor Ticks
Pressure0100001002000500
Temperature5050030015010
Volume10064064010818
Helium01500305
Neon0500102
Argon025051
Slowdown110131

Radio Buttons

If you look closely, you will see what are called radio buttons next to the Pressure and Temperature sliders. As a consequence of the ideal gas law, an increase in one of the sliders must be accompanied by an increase or decrease in one of the others. To simplify matters, there are two possible choices for the slider that reacts to the change of other sliders: Pressure and Temperature. When one of these is selected, its slider will be disabled and not updated. However, the value that corresponds to the slider should be updated to reflect the changes made on other sliders.

These widgets are of type JRadioButton. Note that you can add these buttons to a ButtonGroup. Doing so makes it so that selecting one will automatically deselect the others.

Layout

Of course, a number of layout managers are used to make this GUI work. The basis for everything is the BorderLayout that comes by default with a JFrame. My implementation of the right column uses many nested JPanels, combining both a GridLayout and many BoxLayouts. The BoxLayout layout manager is useful for laying out items in a vertical line or in a horizontal line. Here is a nice tutorial on how to use them.

You may also gather that a number of JLabels have been sprinkled throughout the GUI in order to label the sliders and inform the user about changes in values.

Borders

For purely aesthetic purposes, I included a number of borders in my JPanels. Borders can be used to decorate the outer edges of a JPanel in Java. The standard technique for creating them uses something called the factory design pattern. For practical purposes, this means that you will often call a static method on the BorderFactory class to create a border. Here is a great tutorial about borders in Java.


Linking the GUI to the Particles

Once your GUI looks the way you want it to, you'll need to tie the functionality of the sliders to the simulation of the particles.

Internally, we recommend that you keep the pressure, temperature, and volume stored in the fundamental units of Pa, K, and m3, respectively. Doing so will make your calculations easier and mean that you will not have to convert the value of R to other units. However, the pressure slider displays units of kPa, and the volume slider displays L.

The basic principle is simple. When you change the pressure, temperature, or volume slider, you must update value of either pressure or temperature, depending on which has the selected radio button. Doing so, of course, is a simple matter of solving the equation PV = nRT.

The volume slider has an additional issue. You must change the plunger drawing appropriately so that the amount of space on the screen corresponds to the right amount of volume. The minimum volume (100 L) is one in which 125 pixels remain below the plunger. This is one of the situations in which physics has been interpreted somewhat loosely. As stated earlier, each pixel in the screen is supposed to represent a full meter. Thus, the view of the screen is 800 m x 800 m = 640000 m2. However, the maximum volume is 640 L = 0.64 m3. Thus, the depth of the container must be 1 μm = 0.000001 m. Clearly, this is ridiculous, as the balls are supposed to have radiuses in the neighborhood of 10 m. So, the balls much be considered incredibly thin discs. Regardless, the setup was chosen so that the balls would have reasonable speeds at reasonable temperatures and pressures. A real gas simulation might have more scientific merit, but the units would be incredibly small, and the particles would move much faster than the human eye can see.

Particles

The Helium, Neon, and Argon sliders correspond to the number of particles of each available (where each particle represents a mole of the real element). When you add a particle, you should make sure that it does not change the average kinetic energy of the system.

To do this, there are two interesting equations for energy from physics:

E = (1/2)mv2

E = (3/2)kT

The first of these equations gives kinetic energy for a moving body. The second describes the average kinetic energy of particles at a given temperature. We can combine them to figure out how fast the particles should be moving.

Whenever you add a new particle, you should set its energy to be the correct average energy, thus keeping the average of all the particles the same. Note that E = (3/2)kT is misleading, because that is the individual particle energy. Since each one of our particles represents a mole of particles, the correct equation is E = (3/2)RT. With appropriate mathematical manipulations, you should be able to determine the magnitude of the velocity a given new particle should have. When you create a new particle, choose a random angle between 0 and 2π and create the x and y components using this magnitude and the cosine and sine of this angle, respectively.

Removing a particle is more complicated. Remove a particle of the appropriate kind from your simulation. Then, you must adjust the energy of all the remaining particles to remain consistent with the temperature. To do so, you will need to appropriately scale the speed of each one so that its energy increases or decreases proportionally to the increase or decrease of energy needed in the system. Be careful when doing so: Remember that energy is related to the square of the velocity, and so the change in velocity will not be a simple multiplicative scale up or down.

The positive side is that you need a method that will increase or decrease energy anyway, since changing the temperature (either directly or indirectly) will require the same thing to be done. Note that my GUI displays the energy calculated by summing up the kinetic energy of all the particles as well as the average kinetic energy computed from the temperature. Although these numbers should always be about the same (ignoring rounding errors), they are useful for debugging.

Slowdown

At high temperatures, the particles can begin to move very quickly. So that an observer can better appreciate the experiment, we have introduced a slowdown slider to slow the movement of the particles down. Slowdown is a number between 1 and 10. At the point where the particle locations are updated by multiplying their velocities by the elapsed time, the elapsed time is first divided by the slowdown value. Very easy to implement.

Illegal Values

Some combinations of slider positions may create illegal values (above the maximum or below the minimum) for either temperature or pressure. Should a slider movement create such a problem, you must reset the slider to the closest minor tick setting that is within legal limits.

This requirement is a difficult one because it involves detecting the problem and then undoing the slider movement. You may wish to use exception handling for this purpose.


Threading the Interactions (Extra Credit)

With a larger number of smaller particles, this problem would be a very interesting one to use threads on. As it stands, it is difficult to get speedup considering the small number of total particles and the relatively simple mathematical model with which they interact.

Nevertheless, the challenge of threading this application is still very great. A simple version of threading could divide up the balls in the list when computing collisions. A much more interesting threading model would divide the two dimensional space into grid cells. Each cell is 100 pixels wide and at least 100 pixels tall. At maximum volume, you would have 48 grid cells. Each cell could be handled (mostly) independently by a thread. For those particles close to the edges, you might have to check other grid cells for collisions. To avoid deadlock, a thread servicing a grid cell should only check for collisions with grid cells that have a lower number than they do (or vice versa). Whenever collision checking is being done for a particular grid cell, appropriate synchronization tools must be used.

All threads must wait until all the collisions have been computed. Then, the new locations can be updated. If any particles have moved to a new grid cell, this shift must happen carefully so as not to have a race condition. After the locations have been updated, the threads can once again compute collisions. Grid cells must be created or destroyed on the fly as volume changes. Naturally, thread pooling should be used so that a large or small number of threads can be used to deal with the grid cells and to avoid thread creation and destruction overhead.

You can earn extra credit points for any kind of thread implementation. Maximum points will only be earned for the grid cell thread pooling explained above. If you implement threading and wish to receive extra credit, you must submit a file called threading.txt which contains an explanation of the approach you used, justification for any synchronization you used, and timing information that shows the results of testing the average time needed to compute collisions for large numbers of particles.

You should probably include something in the GUI to increase or decrease the number of threads as well.


Hints


Submission

When you have tested your code and are satisfied with the results, you will need to turn in all of your Java source files, including at least Simulation.java. If you are doing the extra credit, you must turn in threading.txt as well.

To turn in your project, navigate to the directory containing your source files. Then type the following command:

turnin -v -c cs190m -p project5 *.java *.txt

You can submit your code as many times as you want. Submit early and often.


Grading

Your project must compile with the command javac Simulation.java and run with the command as specified above.

Grading will be done based on the following guidelines:

  1. Particle Simulations (30):

    30 points will be awarded for particles that exhibit proper elastic collisions and behave as they should.

  2. GUI Appearance (20):

    You can earn up to 20 points based on having the appropriate widgets to make your GUI function, provided that they are attractively laid out.

  3. GUI Funtionality (40):

    For fully implementing the GUI controls as specified, you can earn 40 points. Your GUI must handle corner cases and properly modulate the particle interactions within the system.

  4. Programming Style (10):

    As always, you should use good programming style and commenting in order to receive full credit. Every Java file should have a comment header at the top giving your name, the course, the project, the date, and a short explanation of the class(es) in the file.

  5. Threading (25 Extra):

    You can earn up to 25 points of extra credit for adding threading to the application as described.

Note: Submissions which do not compile will automatically score 0 points.

As always, be sure to uphold the high standards of academic honesty required by this course. Failure to do so can lead to consequences as dire as failing the course and expulsion from the school. If you have any questions about whether or not a given action is allowed, please ask your instructor or TA.