Reputation: 561
I'm trying to do simple simulation of ideal gas according to Clapeyron equation `pv=nkbT' using Metropolis Monte Carlo algorithm.This is very simple example,where I consider molecules in 2D with no interactions with each other and energy is equeal to E=pV wher V is area of circle containing all molecules.
My problem is that after very few monte carlo steps volume of my gas goes always to almost zero,no matter how many molecules or pressure I put.I can't figure out if I have bug in my code,or it is becouse I don't have any molecules interactions. All help will be much appriciated,here is my code
My results are shown in plot bellow,x-axis are monte carlo steps and y-axis is volume,i would expected as a result some none zero constant value of volume after greater number of steps.
import numpy as np
import random
import matplotlib.pyplot as plt
def centroid(arr):
length = arr.shape[0]
sum_x = sum([i.x for i in arr])
sum_y = sum([i.y for i in arr])
return sum_x/length, sum_y/length
class Molecule:
def __init__(self, xpos, ypos):
self.pos = (xpos, ypos)
self.x = xpos
self.y = ypos
class IdealGas:
# CONSTANTS
def __init__(self, n,full_radius, pressure, T, number_of_runs):
gas = []
for i in range(0, n):
gas.append(Molecule(random.random() * full_radius,
random.random() * full_radius))
self.gas = np.array(gas)
self.center = centroid(self.gas)
self.small_radius = full_radius/4.
self.pressure = pressure
self.kbT = 9.36E-18 * T
self.n = n
self.number_of_runs = number_of_runs
def update_pos(self):
random_molecule = np.random.choice(self.gas)
old_state_x = random_molecule.x
old_state_y = random_molecule.y
old_radius = np.linalg.norm(np.array([old_state_x,old_state_y])-np.array([self.center[0],self.center[1]]))
energy_old = np.pi * self.pressure * old_radius**2
random_molecule.x = old_state_x + (random.random() * self.small_radius) * np.random.choice([-1, 1])
random_molecule.y = old_state_y + (random.random() * self.small_radius) * np.random.choice([-1, 1])
new_radius = np.linalg.norm(np.array([random_molecule.x,random_molecule.y])-np.array([self.center[0],self.center[1]]))
energy_new = np.pi * self.pressure * new_radius**2
if energy_new - energy_old <= 0.0:
return random_molecule
elif np.exp((-1.0 * (energy_new - energy_old)) / self.kbT) > random.random():
return random_molecule
else:
random_molecule.x = old_state_x
random_molecule.y = old_state_y
return random_molecule
def monte_carlo_step(self):
gas = []
for molecule in range(0, self.n):
gas.append(self.update_pos())
self.gas = np.array(gas)
#self.center = centroid(self.gas)
return self.gas
def simulation(self):
volume = []
for run in range(self.number_of_runs):
step_gas = self.monte_carlo_step()
step_centroid = centroid(step_gas)
step_radius = max([np.linalg.norm(np.array([gas.x,gas.y])-np.array([step_centroid[0],step_centroid[1]]))
for gas in step_gas])
step_volume = np.pi * step_radius**2
volume.append(step_volume)
return volume
Gas = IdealGas(500, 50, 1000, 300, 100)
vol = Gas.simulation()
plt.plot(vol)
plt.show()
Upvotes: 0
Views: 529
Reputation: 757
You only allow your molecules to move if the new radius is inferior to the old radius:
if energy_new - energy_old <= 0.0:
is equivalent to:
np.pi * self.pressure * new_radius**2 <= np.pi * self.pressure * old_radius**2
that is:
abs(new_radius) <= abs(old_radius)
So all molecules goes to the centroïd.
To me your hypothesis are too strong: you fix temperature, pressure and number of molecules. According to the ideal gas equation, it means volumne v=nRT/p is constant too. If the volume can change, then pressure or temperature has to change. In your simulation, allowing pressure to change would mean that the product of pressure and volume is constant, so that energy is constant, so molecules can move freely in an arbitraty large volume.
By the way I think molecules should be initialized with:
(random.random() - 0.5) * full_radius
so that the occupy all the plane around zero.
Upvotes: 1