In [ ]:
from vpython import *
from random import random
scene.width = 600
scene.height = 600
scene.range = 2.4
# Bruce Sherwood
N = 3 # N by N by N array of atoms
k = 1
m = 1
spacing = 1
atom_radius = 0.3*spacing
L0 = spacing-1.8*atom_radius
V0 = pi*(0.5*atom_radius)**2*L0 # initial volume of spring
scene.center = 0.5*(N-1)*vector(1,1,1)
dt = 0.04*(2*pi*sqrt(m/k))
axes = [vector(1,0,0), vector(0,1,0), vector(0,0,1)]
scene.caption = """A model of a solid represented as atoms connected by interatomic bonds.
Right button drag or Ctrl-drag to rotate "camera" to view scene.
To zoom, drag with middle button or Alt/Option depressed, or use scroll wheel.
On a two-button mouse, middle is left + right.
Touch screen: pinch/extend to zoom, swipe or two-finger rotate."""
class crystal:
def atomAt(self, np):
if (np.x>=0 and np.y>=0 and np.z>=0 and np.x<N and np.y<N and np.z<N):
return self.atoms[int(np.x + np.y*N + np.z*N*N)]
# Otherwise create an invisible wall and return it
w = box()
w.visible = False # comment out to see the true model
w.radius = atom_radius
w.pos = np*spacing
w.momentum = vector(0,0,0)
return w
def __init__(self, N, atom_radius, spacing, momentumRange ):
self.atoms = []
self.springs = []
# Create N^3 atoms in a grid
for z in range(N):
for y in range(N):
for x in range(N):
atom = sphere()
atom.pos = vector(x,y,z)*spacing
atom.radius = atom_radius
atom.color = vector(0,0.58,0.69)
# vec.random() available in GlowScript but not in classic VPython
px = 2*random()-1 # ranges from -1 to +1
py = 2*random()-1
pz = 2*random()-1
atom.momentum = momentumRange*vector(px,py,pz)
self.atoms.append( atom )
# Create a grid of springs linking each atom to the adjacent atoms
# in each dimension, or to invisible walls where no atom is adjacent
for d in range(3):
for z in range(-1,N):
for y in range(-1,N):
for x in range(-1,N):
atom = self.atomAt(vector(x,y,z))
neighbor = self.atomAt(vector(x,y,z)+axes[d])
if (atom.visible or neighbor.visible):
spring = helix()
spring.visible = atom.visible and neighbor.visible
spring.thickness = 0.05
spring.radius = 0.5*atom_radius
spring.length = spacing
spring.up = vector(1,1,1) # prevent fibrillation of vertical springs
spring.atoms = [ atom, neighbor ]
spring.color = vector(1,0.5,0)
self.springs.append(spring)
c = crystal(N, atom_radius, spacing, 0.1*spacing*sqrt(k/m))
while True:
rate(60)
for atom in c.atoms:
atom.pos = atom.pos + atom.momentum/m*dt
for spring in c.springs:
spring.axis = spring.atoms[1].pos - spring.atoms[0].pos
L = mag(spring.axis)
spring.axis = spring.axis.norm()
spring.pos = spring.atoms[0].pos+0.5*atom_radius*spring.axis
Ls = L-1*atom_radius
spring.length = Ls
Fdt = spring.axis * (k*dt * (1-spacing/L))
spring.atoms[0].momentum = spring.atoms[0].momentum + Fdt
spring.atoms[1].momentum = spring.atoms[1].momentum - Fdt
In [ ]:
In [ ]: