-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtute-gas.html
More file actions
184 lines (179 loc) · 19.4 KB
/
Copy pathtute-gas.html
File metadata and controls
184 lines (179 loc) · 19.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
<h1 id="using-vpython-to-simulate-a-gas">Using VPython to Simulate a Gas</h1>
<p><strong>Sally Lloyd and Stephen Roberts</strong></p>
<p>This tutorial follows on from the <a href="tute-bounce.html">bouncing ball tutorial</a> which introduces VPython as a computational environment and develops a program to simulate a ball bouncing in a box. You should start with the previous tutorial before starting on this tutorial as this tutorial is based on the solution obtained from the previous tutorial.</p>
<p>A clean version of the bouncing ball program can be obtained from the link below. <a href="bounce2.py">Example bouncing ball program</a></p>
<p>Start IDLE by clicking on the snake icon on the panel at the bottom of your desktop. A window labelled ’Untitled’ should appear. This is the window in which you will type your program. Open your bouncing ball program, or cut and paste the <a href="bounce2.py">clean version with 6 walls</a> into VIDLE. Check to see if it is working. You see a ball bouncing in a box.</p>
<h2 id="simulating-particle-motion-in-a-gas">Simulating particle motion in a gas</h2>
<p>Now you will extend the bouncing ball program so that it simulates the motion of atoms in a gas. There will need to be many atoms in the box and they should bounce off each other as well as off the sides of the box. We will need to set the initial distribution of atom positions and velocities, but these distributions may evolve as the simulation progresses. The velocity distribution of the atoms in the simulation will be compared with the expected Maxwellian velocity distribution.</p>
<h2 id="random-numbers">Random numbers</h2>
<p>You can make the velocity of the ball different each time the program is run by using a random number generator. The random number generator is not part of standard python and needs to be imported from the random module. The random module contains random number generator for several different distributions. You can import just a uniform distribution random number generator by inserting the line</p>
<pre><code>from random import uniform</code></pre>
<p>at the beginning of the program; in the "Import Library(s)" section of the code.</p>
<p>The function <code>uniform(-1,1)</code> will give a random number between -1 and 1. You can set a random ball velocity by replacing the line setting the ball’s velocity with:</p>
<pre><code>ball.velocity=maxv*vector(uniform(-1,1),uniform(-1,1),uniform(-1,1))</code></pre>
<h2 id="multiple-balls-using-lists">Multiple balls: Using lists</h2>
<p>To simulate a gas we will need many particles inside the box. One easy way of dealing with many particles is to put them into a list.</p>
<p>Replace the "Create Ball(s)'' section of code with the following code (it should be instead of the code defining the ball and its velocity):</p>
<pre><code>##########################################
# Create Ball(s)
##########################################
no_particles=10
ball_radius=0.4
maxpos=side-.5*thk-ball_radius
maxv=1.0
ball_list=[]
for i in range(no_particles):
ball=sphere(color=color.green,radius=ball_radius)
ball.pos=maxpos*vector(uniform(-1,1),uniform(-1,1),uniform(-1,1))
ball.velocity=maxv*vector(uniform(-1,1),uniform(-1,1),uniform(-1,1))
ball_list.append(ball)</code></pre>
<p>The construction of a <code>for</code> loop in python is slightly different from many other programming languages which cycle through a set of numbers. Python iterates through a list. To get the more usual counted for loop you use <code>range</code> to create a list of integers between 0 and <code>no_particles-1</code>.</p>
<p>This gives a list called <code>ball_list</code> containing 10 balls. Any type of object can go into a list. In fact a list can be made up of several different types of object. If you run the code now you will see 10 balls within the box but the “Time Loop” section of code only moves one of the balls (the last one created in the <code>for</code> loop, which is called <code>ball</code>).</p>
<p>To move all the balls in a list you use another <code>for</code> loop. In python a <code>for</code> loop cycles through the elements of a list. We need to loop through all the balls in the list <code>balls</code>. The loop to move all the balls in the list would look like:</p>
<pre><code>while (1==1):
# Set number of times loop is repeated per second
rate(100)
######################################
# Loop over list of particles
# Move and check wall collisions
######################################
for ball in ball_list:
# Move ball(s)
ball.pos = ball.pos + ball.velocity*timestep
#check for collisions with the walls
. . .</code></pre>
<p>Here you <strong>only</strong> have to add in the following code and get the indentation right.</p>
<pre><code>######################################
# Loop over list of particles
# Move and check wall collisions
######################################
for ball in ball_list:</code></pre>
<p>The code within the for loop is indented by two levels since it is also inside the while loop (with code inside the if indented an additional level). You can indent a whole section by selecting it and then choosing "indent section" from the format menu. (Try running the program now to see 10 balls bouncing within your box)</p>
<p>Maybe you would like to play with increasing the number of balls, or changing the radii of the balls, or the colours or what ever.</p>
<h2 id="particle-interactions">Particle interactions</h2>
<p>You should now have a program that shows many particles bouncing around inside a box. At the moment the balls are simply passing through each other. For this to be a realistic model of a gas, the particles need to interact in some fashion.</p>
<p>Different "interaction potentials" could be used depending on the type of molecule making up the gas. For an ideal gas an appropriate interaction is "hard sphere collisions" — the balls bounce off each other the same way they bounce off the walls.</p>
<h3 id="detecting-a-collision">Detecting a collision</h3>
<p>At each time step we need to check whether any particles have collided. For each pair of particles we need to check whether the distance between them is less than the sum of their radii. For example add the following lines to the end of your program, after you have updated the position of all the particles. The indentation should place it within the time <code>while</code> loop but outside the wall collision <code>for</code> loop.</p>
<pre><code> ######################################
# Ball Collision Detection
######################################
for ball in ball_list:
for otherball in ball_list:
if not ball == otherball:
distance=mag(otherball.pos-ball.pos)
if distance<(ball.radius+otherball.radius):
print 'collision' , distance</code></pre>
<p><code>mag</code> is a function that returns the size (magnitude) of a vector.</p>
<p>Run your program. Notice that every time a pair of balls collide in the display window the word ’collision’ is printed twice to the output window. This is because the loop goes through every pair of balls twice. To check each pair only once the second loop should only check against balls with higher index than the first. To do this replace the ball collision detection code with:</p>
<pre><code> ######################################
# Ball Collision Detection
######################################
for i in range(no_particles):
for j in range(i+1,no_particles):
distance=mag(ball_list[i].pos-ball_list[j].pos)
if distance<(ball_list[i].radius+ball_list[j].radius):
print 'collision', distance</code></pre>
<p>Now when you run the program, "collision" should be written once for each collision.</p>
<h3 id="exchanging-momentum-more-vector-calculations">Exchanging momentum: more vector calculations</h3>
<p>When two objects collide elastically (without losing energy) in one dimension they swap momentum. If their masses are equal this means they should swap velocity. Since we are modelling the interaction between particles as hard spheres, with no friction, the force at impact is only along the line joining the centres of the balls. The balls exchange momentum in this direction, leaving the perpendicular velocity components unchanged. The result is similar to the way billiard balls collide. The section of code that detects a collision and swaps the velocity component of the two balls in the collision direction looks like:</p>
<pre><code> ######################################
# Ball Collision Detection
######################################
#loop through all pairs
for i in range(no_particles):
for j in range(i+1,no_particles):
distance=mag(ball_list[i].pos-ball_list[j].pos)
#check collision
if distance<(ball_list[i].radius+ball_list[j].radius):
#unit vector in collision direction
direction=norm(ball_list[j].pos-ball_list[i].pos)
vi=dot(ball_list[i].velocity,direction)
vj=dot(ball_list[j].velocity,direction)
#impact velocity
exchange=vj-vi
#exchange momentum
ball_list[i].velocity=ball_list[i].velocity + exchange*direction
ball_list[j].velocity=ball_list[j].velocity - exchange*direction</code></pre>
<p>The function <code>norm</code> returns a vector with the same direction as the input but with its magnitude normalised to one. The function <code>dot</code> returns the dot product of the two input vectors, in this case the component of the ball’s velocity that is in the collision direction.</p>
<p>You should now have a program that shows ten spheres bouncing around inside a box and bouncing off each other. You might find that balls get stuck together, particularly if three or more balls collide at the same time. This problem is reduced by making the time step smaller. We can also make the collisions more accurate, and stop balls sticking together, by adjusting the position of the balls after a collision, in much the same way we did for collisions with the sides of the box.</p>
<hr />
<pre><code> #adjust position
overlap=2*ball_radius-distance
ball_list[i].pos=ball_list[i].pos - overlap*direction
ball_list[j].pos=ball_list[j].pos + overlap*direction</code></pre>
<hr />
<p><a href="manybounce1.py">Example solution</a></p>
<h2 id="program-speed">Program speed</h2>
<p>The program you have written shows a simple model of the behaviour of particles within a gas. Play around with changing the number and size of particles and their average velocities. As you add more particles the speed of the program slows down significantly. Doubling the number of particles increases the time to run the program by about four times. This is because the number of steps to check for collisions increases as the square of the number of particles. Many computational science models which are based on interacting particles have this <span class="math"><em>N</em><sup>2</sup></span> speed dependence. Models with large numbers of particles need high speed computers and efficient programs.</p>
<p>Increasing the speed of a simulation program can make up a large part of the work of a computational scientist. Tactics for improving the efficiency of a program include:</p>
<ul>
<li><p>removing unnecessary calculations (eg the calculation of the distance between particles involves calculating a square root which is quite slow. The comparison of particle separation for detecting collisions could be made just as easily with the square of the distance which is much quicker to calculate.)</p></li>
<li><p>Saving results of calculations instead of repeating them.</p></li>
<li><p>Using more efficient data constructions (eg the elements of lists in python are not necessarily all the same data type. This makes them flexible for many different programming tasks but the program takes longer to access this data. Python arrays have each element the same data type and are faster to work with.)</p></li>
<li><p>Improved mathematical algorithms which allow larger step sizes for the same accuracy.</p></li>
<li><p>Minimise the number of particles needed by the model</p></li>
<li><p>Include only the most important interactions.</p></li>
</ul>
<p><a href="quickbounce1.py">Here</a> is a faster version of the ideal gas simulation program.</p>
<h2 id="extracting-useful-information">Extracting useful information</h2>
<p>The visualisation of a model can be useful in itself to get insight into its behaviour. But the program might be more useful as a computational science ’experiment’ if we could extract from it predictions of gas behaviour that could be measured in a physical experiment or were relevant in some real world application. Alternatively we could use the results to confirm some theory about ideal gasses.</p>
<p>To do either of these we need to extract the sort of information that can normally be measured since you don’t get to watch the particles of a real gas bouncing around.</p>
<p>Examples of some measurements we could extract from our simulation:</p>
<ul>
<li><p>measure how the pressure on the walls varies with gas temperature and density</p></li>
<li><p>distribution of particle velocities</p></li>
<li><p>collision frequency</p></li>
</ul>
<h1 id="velocity-distribution-using-graphs">Velocity distribution: using graphs</h1>
<p>We gave our particles a uniform initial velocity distribution for each direction. A statistical analysis suggests that the "most likely" velocity distribution for each component will have more low energy particles and a long tail of high energy particles. Does our gas evolve to this "most likely" velocity distribution.</p>
<h2 id="graphing-in-visual-python">Graphing in visual python</h2>
<p>We will need the graphing functions contained in the visual.graph module, so insert a line at the beginning of your program</p>
<pre><code>from visual.graph import *</code></pre>
<p>Several types of graph are available. We will use a histogram that displays how many particles fit into each velocity range. This graph is set up before the main time while loop with:</p>
<pre><code>graphwindow=gdisplay(xtitle='v_x',ytitle='N',ymax=no_particles/2)
velocity_dist=ghistogram(bins=arange(0,2*maxv,maxv/5))</code></pre>
<p><code>velocity_dist</code> defines a histogram with a set of ten velocity "bins" into which the list of particle velocities will be sorted. You might want to adjust the number of bins depending on the number of particles in your gas. The graph will be displayed in a new graphing window.</p>
<p>To plot the distribution of particle speeds in the x direction you would form a list and use this list as data for the histogram</p>
<pre><code> v_list=[]
for ball in ball_list:
v_list.append(abs(ball.velocity.x))
velocity_dist.plot(data=v_list)</code></pre>
<p>You can put these lines within the while loop and see the velocity distribution evolve. Unless you have many particles in your gas the velocity distribution will jump around a lot.</p>
<p>You can reduce this problem by averaging the distribution over time. Change the code which defines <code>velocity_dist</code> using:</p>
<pre><code>velocity_dist=ghistogram(bins=arange(0,2*maxv,maxv/5),accumulate=1,average=1)</code></pre>
<p>(You may find that displaying the graph slows down the simulation considerably. If this is a problem, turn your while loop into a for loop so that it runs for a limited time and put all the graphing parts of the program after the loop finishes.)</p>
<p>If you are using the quick version of the code the velocity is stored as in <code>p_array</code>, which can be fed directly to the histogram as:</p>
<pre><code> velocity_dist.plot(data=abs(p_array[:,0]))</code></pre>
<h2 id="compare-with-expected-result">Compare with expected result</h2>
<p>The expected "most likely" velocity distribution can be plotted for comparison as a curve set up with:</p>
<pre><code>expected_distribution=gcurve(color=color.green)
for vx in arange(0,2*maxv,maxv/20):
expected_distribution.plot(pos = (vx,.27*no_particles*exp(-vx**2/maxv**2*3/2)))</code></pre>
<p>(plot only once - so just before while loop. You may need to rescale this graph if you used a different number of velocity bins)</p>
<p><a href="quickbouncepdist.py">Here</a> is an example of graphing with the quick program.</p>
<p><a href="manybouncepdist.py">Here</a> is an example of graphing with the slower program.</p>
<p>Some questions that you could use this program to investigate:</p>
<ul>
<li><p>Is the initial velocity distribution different from the final velocity distribution?</p></li>
<li><p>Does the final velocity distribution match the expected result?</p></li>
<li><p>Does changing the initial velocity distribution effect the final distribution?</p></li>
<li><p>Consider the ways this simulation is different from a real gas. What changes might you need to make to model a real gas more closely? How might this effect the behaviour of the simulation?</p></li>
</ul>
<h1 id="more-complicated-simulations">More complicated simulations</h1>
<p>You could extend this simulation to investigate more complex situations.</p>
<ul>
<li><p>Mixing of two ideal gasses (diffusion)(eg look at <a href="quicktwogas.py">quicktwogas</a>)</p></li>
<li><p>Heat conduction</p></li>
<li><p>Other interaction potentials including some attraction may give phase change to solid or liquid phases</p></li>
<li><p>Include effects of earth’s gravity to get height variations in pressure and temperature - or better simulation of liquid/gas interface</p></li>
<li><p>Velocity distribution when a gas includes molecules of different masses</p></li>
<li><p>Movable or elastic walls (eg look at <a href="quickbounce2.html">quickbounce2</a>)</p></li>
</ul>
<h1 id="other-situations-that-can-be-modelled-using-similar-techniques">Other situations that can be modelled using similar techniques</h1>
<p>The technique of following individual particles, calculating their accelerations due to their position and the position of other particles around them can be used to simulate a wide variety of situations.</p>
<ul>
<li><p>Molecular dynamics and computational chemistry can determine the shape of molecules, their resonant frequencies and their potential reactions. The most difficult part is to determine the interaction potentials which can depend on relative positions of more than two particles, and also on angles between them.</p></li>
<li><p>Gravitational interactions determine the orbits of planets in the solar system. Precise calculations of these orbits determine whether the solar system is stable or chaotic, whether orbits may change drastically at some future time, give clues as to how the solar system form, and whether any asteroid is likely to hit the earth. With large objects like planets their shape and rotation may need to be taken into account.</p></li>
<li><p>Animal behaviour. Animals use the positions of other animals and objects around them to decide how they should move in order to achieve their goals. The goals might include getting food, fleeing predators, staying in a group and avoiding collisions. This simulations apply to people and traffic flow and can be used to design adequate escape routes from burning buildings and sinking ships, road systems that minimise congestions, or to suggest driving techniques that avoid collisions.</p></li>
</ul>