/
lj.py
129 lines (114 loc) · 3.02 KB
/
lj.py
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
## lj.py: a Monte Carlo simulation of the Lennard-Jones fluid, in the NVT ensemble.
import random
import sys
import math
import copy
import os
# Simulation Parameters
N = 500
sigma = 1
epsilon = 1
trunc = 3*sigma
truncsq = trunc**2
steps = 100000
temp = 8.5e-1
density = 1.0e-3
L = (N/density)**(1.0/3.0)
halfL = L/2
particles = []
# Some helper functions
def wrap(particle):
'''Apply perodic boundary conditions.'''
if particle[0] > L:
particle[0] -= L
elif particle[0] < 0:
particle[0] += L
if particle[1] > L:
particle[1] -= L
elif particle[1] < 0:
particle[1] += L
if particle[2] > L:
particle[2] -= L
elif particle[2] < 0:
particle[2] += L
return particle
def distancesq(particle1, particle2):
'''Gets the squared distance between two particles, applying the minimum image convention.'''
# Calculate distances
dx = particle1[0]-particle2[0]
dy = particle1[1]-particle2[1]
dz = particle1[2]-particle2[2]
# Minimum image convention
if dx > halfL:
dx -= L
elif dx < -halfL:
dx += L
if dy > halfL:
dy -= L
elif dy < -halfL:
dy += L
if dz > halfL:
dz -= L
elif dz < -halfL:
dz += L
return dx**2+dy**2+dz**2
def energy(particles):
'''Gets the energy of the system'''
energy = 0
for particle1 in range(0, len(particles)-1):
for particle2 in range(particle1+1, len(particles)):
dist = distancesq(particles[particle1], particles[particle2])
if dist <= truncsq:
energy += 4*(1/dist**6)-(1/dist**3)
return energy
def particleEnergy(particle, particles, p):
'''Gets the energy of a single particle.'''
part_energy = 0
i = 0
for particle2 in particles:
if i != p:
dist = distancesq(particle, particle2)
if dist <= truncsq:
part_energy += 4*(1/dist**6)-(1/dist**3)
i += 1
return part_energy
def writeEnergy(step, en):
'''Writes the energy to a file.'''
with open('energy', 'a') as f:
f.write('{0} {1}\n'.format(step, en))
# Clear files if they already exist.
if os.path.exists('energy'):
os.remove('energy')
# Initialize the simulation box:
for particle in range(0, N):
x_coord = random.uniform(0, L)
y_coord = random.uniform(0, L)
z_coord = random.uniform(0, L)
particles.append([x_coord, y_coord, z_coord])
# Calculate initial energy
en = energy(particles)
# MC
for step in range(0, steps):
sys.stdout.write("\rStep: {0} Energy: {1}".format(step, en))
sys.stdout.flush()
# Choose a particle to move at random.
p = random.randint(0, N-1)
# Move particle and evaluate energy
this_particle = copy.deepcopy(particles[p])
prev_E = particleEnergy(this_particle, particles, p)
this_particle[0] += random.uniform(-1, 1)
this_particle[1] += random.uniform(-1, 1)
this_particle[2] += random.uniform(-1, 1)
this_particle = wrap(this_particle)
new_E = particleEnergy(this_particle, particles, p)
deltaE = new_E - prev_E
# Acceptance rule enforcing Boltzmann statistics
if deltaE < 0:
particles[p] = this_particle
en += deltaE
else:
rand = random.random()
if math.exp(-deltaE/temp) > rand:
particles[p] = this_particle
en += deltaE
writeEnergy(str(step), str(en))