$35
VP5 Equipartition of energy [class and module]
Introduction
For a system in thermal equilibrium, every degree of freedom of every particle has the same expectation value of energy, hence the name equipartition of energy.
In the figure, we have 20 artificial molecules, each composed of two atoms, O (red ball)
and C (blue ball), and they are connected by a bond (white cylinder). Each molecule can have translational motion, vibrational motion, and rotational motion. The translational motion of the center of mass of the molecule has 3 degrees of freedom (f = 3), corresponding to motion in x, y, and z and is associated with kinetic energy of the center of mass, com_K. The vibrational motion (red arrows) has f = 2 and half of it (f = 1) is associated with vibrational kinetic energy v_K and half of it (f = 1) vibrational potential energy v_P. The rotational motion (grey arrows) has f = 2, corresponding to the 2 rotational motions with their axes perpendicular to the bond direction, and is associated with the rotational kinetic energy r_K. For rotational
motion, f = 2 instead of f = 3, because the rotation with the axis parallel to the bond direction is not discernable. Since for each available degree of freedom, the expected energy is the same, therefore statistically, if we add up the energies of the different types of motions respectively for all molecules and averaged over time, the ratio between them will be avg_com_K : avg_v_K : avg_v_P : avg_r_K = 3 : 1 : 1 : 2. (Notice: Here we do not consider quantum effect, therefore all 3 types of the motions will be available). In this homework, we will simulate that.
In addition, we will learn to use python’s module feature to finish the homework in a more systematical way.
Python provides many useful modules, such as “numpy” and “vpython”. For example, every time you write ‘from vpython import *’, you can use everything provided by the module ‘vpython’, such as canvas( ), sphere( ), and etc. We can also write our own module. One of the reason is that this module is very useful and can be used again and again in the future. The other reason is that we do not want the main program to look very complicated, so we separate the program into layers, allowing each layer to handle different things. In this homework, we provide two template files, diatomic.py, which is the module file to handle each individual CO molecule and their collisions, and VP8.py, which is the main program. You need to put these two files into the same file folder. When the module is run for the first time, a diatomic.pyc will be generated and stored in the subfolder “__pycache__”.
the template file of the diatomic module diatomic.py:
from vpython import *
size, m_o, m_c, k_bond = 31E-12, 16.0/6E23, 12.0/6E23, 18600.0 # These numbers are all made up d = 2.5*size
dt = 1E-16
class CO_molecule: def __init__(self, pos, axis): self.O = sphere(pos = pos, radius = size, color = color.red) self.C = sphere(pos = pos+axis, radius = size, color = color.blue)
self.bond = cylinder(pos = pos, axis = axis, radius = size/2.0, color = color.white) self.O.m = m_o self.C.m = m_c self.O.v = vector(0, 0, 0) self.C.v = vector(0, 0, 0)
self.bond.k = k_bond
def bond_force_on_O(self): # return bond force acted on the O atom return self.bond.k*(mag(self.bond.axis)-d)*norm(self.bond.axis)
def time_lapse(self, dt): # by bond's force, calculate a, v and pos of C and O, and bond's pos and axis after dt self.C.a = - self.bond_force_on_O() / self.C.m self.O.a = self.bond_force_on_O() / self.O.m self.C.v += self.C.a * dt self.O.v += self.O.a * dt self.C.pos += self.C.v * dt self.O.pos += self.O.v * dt self.bond.axis = self.C.pos - self.O.pos self.bond.pos = self.O.pos
def com(self): # return position of center of mass return vector(0, 0, 0)
def com_v(self): # return velocity of center of mass return vector(0, 0, 0)
def v_P(self): # return potential energy of the bond for the vibration motion return 0
def v_K(self): # return kinetic energy of the vibration motion return 0
def r_K(self): # return kinetic energy of the rotational motion return 0
def com_K(self): #return kinetic energy of the translational motion of the center of mass return 0
def collision(a1, a2):
v1prime = a1.v - 2 * a2.m/(a1.m+a2.m) *(a1.pos-a2.pos) * dot (a1.v-a2.v, a1.pos-a2.pos) / mag(a1.pos-a2.pos)**2 v2prime = a2.v - 2 * a1.m/(a1.m+a2.m) *(a2.pos-a1.pos) * dot (a2.v-a1.v, a2.pos-a1.pos) / mag(a2.pos-a1.pos)**2 return v1prime, v2prime
if __name__ == '__main__': a = CO_molecule(pos=vector(0, 0, 0), axis = vector(2.6*size, 0, 0)) a.O.v = vector(1.0, 1.0, 0)
a.C.v = vector(2.0, -1.0, 0)
a.time_lapse(dt)
print(a.bond_force_on_O(), a.com(), a.com_v(), a.v_P(), a.v_K(), a.r_K(), a.com_K())
Here, some usage of python syntax new to you is explained in the following:
class CO_molecule:
We declare a class called CO_molecule, which does not inherit from any previous known class, thus no parentheses behind it. For a new class, usually the first method is __init__(self, ….), before and behind init are double underlines. def __init__(self, pos, axis):
……
is to initialize the object when CO_molecule( pos = …, axis=…) is called to create an object. Here, it first creates two sphere object for O atom and C atom and creates a cylinder for the bond. It then assigns the basic parameters for the CO molecule.
Then we write all other methods to complete the class. These include def bond_force_on_O(self): # return bond force acted on the O atom return self.bond.k*(mag(self.bond.axis)-d)*norm(self.bond.axis)
to calculate the force acting by the bond on the O atom by assuming the bond works like a spring, and def time_lapse(self, dt): # by bond's force, calculate a, v and pos of C and O, and bond's pos and axis after dt
……
to calculate both C and O atoms’ acceleration and then to calculate both atoms’ velocities and positions, bond’s position and axis, for a short time dt.
III. Homework Part 1:
It is your job to finish the rest methods for class CO_molecule. com( ) is to return the position vector of the center of the mass of the molecule. com_v( ) is to the velocity vector of the center of mass. v_P( ) is to return the potential energy stored in the bond similar to the potential energy stored in a spring. v_K( ) is to return the kinetic energy associated to the two atoms’ motion relative to the center of mass and parallel to the bond axis. r_K( ) is to return the kinetic energy associated to the two atoms’ motion relative to the center of mass and perpendicular to the bond axis. com_K( ) is to return of kinetic energy of the center of mass.
After you finish writing all the methods in CO_molecule class, you should check its correctness before proceeding to the main program. If this file, ‘diatomic.py’ is executed by itself as the main program, the lines under if __name__ == '__main__': will be executed. If this file is imported from other program as a module, the lines under if __name__ == '__main__': will not be executed. This is a convenient way to test the correctness of this module. This way of coding has an advantage. One can separate a large program into several modules with smaller sections of codes, which are more easily to debug. It means that we can write smaller sections of codes and debug them without wasting time on other modules.
After you complete diatomic.py correctly, you can test your module’s correctness by the following code:
if __name__ == '__main__': a = CO_molecule(pos=vector(0, 0, 0), axis = vector(2.6*size, 0, 0)) a.O.v = vector(1.0, 1.0, 0)
a.C.v = vector(2.0, -1.0, 0)
a.time_lapse(dt)
print(a.bond_force_on_O(), a.com(), a.com_v(), a.v_P(), a.v_K(), a.r_K(), a.com_K())
This will generate a, an object from class CO_molecule, with the given pos and axis. It will then assign the velocities for a‘s two atoms O and C. It then runs a.time_lapse(dt) to let a to proceed a short time dt. Then, if your codes are correctly written, you will have the following printed results:
<5.76609e-08, -1.43079e-13, 0> <3.4543e-11, 1.42857e-17, 0> <1.42857, 0.142857, 0>
8.937585694598954e-20 1.4028593914919891e-24 2.2857114754936572e-23 4.8095238095238086e-23
Homework Part 2: the template file of the main program VP8.py
from vpython import *
from diatomic import *
N = 20 # 20 molecules
L = ((24.4E-3/(6E23))*N)**(1/3.0)/50 # 2L is the length of the cubic container box, the number is made up
m = 14E-3/6E23 # average mass of O and C
k, T = 1.38E-23, 298.0 # some constants to set up the initial speed initial_v = (3*k*T/m)**0.5 # some constant
scene = canvas(width = 400, height =400, align = 'left', background = vec(1, 1, 1))
container = box(length = 2*L, height = 2*L, width = 2*L, opacity=0.4, color = color.yellow ) energies = graph(width = 600, align = 'left', ymin=0) c_avg_com_K = gcurve(color = color.green) c_avg_v_P = gcurve(color = color.red) c_avg_v_K = gcurve(color = color.purple) c_avg_r_K = gcurve(color = color.blue)
COs=[]
for i in range(N): # initialize the 20 CO molecules
O_pos = vec(random()-0.5, random()-0.5, random()-0.5)*L # random() yields a random number between 0 and 1
CO = CO_molecule(pos=O_pos, axis = vector(1.0*d, 0, 0)) # generate one CO molecule
CO.C.v = vector(initial_v*random(), initial_v*random(), initial_v*random()) # set up the initial velocity of C randomly
CO.O.v = vector(initial_v*random(), initial_v*random(), initial_v*random()) # set up the initial velocity of O randomly
COs.append(CO) # store this molecule into list COs
times = 0 # number of loops that has been run
dt = 5E-16 t = 0 while True:
rate(3000) for CO in COs:
CO.time_lapse(dt)
for i in range(N-1): # the first N-1 molecules
for j in range(i+1,N): # from i+1 to the last molecules, to avoid double checking
pass ## change this to check and handle the collisions between the atoms of different molecules
for CO in COs:
pass ## change this to check and handle the collision of the atoms of all molecules on all 6 walls
## sum com_K, v_K, v_P, and r_K for all molecules, respectively, to get total_com_K, total_v_K, total_v_P, total_r_K at the ## current moment
## calculate avg_com_K to be the time average of total_com_K since the beginning of the simulation, and do the same ## for others.
## plot avg_com_K, avg_v_K, avg_v_P, and avg_r_K
In this main program, all the codes are explained by the corresponding comments. Read them carefully. You need complete all those marked by ##. If your program works well, you will observe something similar to the following figure, in which avg_com_K : avg_v_K : avg_v_P : avg_r_K = 3 (green) : 1 (red) : 1(purple) : 2(blue).