#!/usr/bin/env python
import sys
from numpy import array, zeros, exp, arange, sum, ravel
from random import randint, random
class Lattice:
def __init__(self, J=-1.0, S=1.0, L=10, T=1.0):
self.J = J
self.S = S
self.dim = L
self.lattice = array(map(lambda x: x+1, zeros((self.dim, self.dim))))
self.kT = 1.0*T
self.eTot = 0.0
# The possible values for the change in energy when
# flipping a spin.
# All up, the middle one is shifted to down: dE = +8.
# One do, the middle one is shifted to down: dE = +4.
# [...]
# All do, the middle one is shifted to down: dE = -8.
self.dE = [+8, +4, 0, -4, -8]
#self.dE = [-8, -4, 0, +4, +8]
self.dEB = array(self.dE)
# Precalculate the corresponding Boltzmann factor
# for the given temperature.
self.dEB = exp(-self.dEB/self.kT)
# Convenience lists for the properties to be sampled.
self.E = []
self.M = []
# Convenience methods.
def getLattice(self):
return self.lattice
def flip(self, i, j):
self.lattice[i][j] *= -1
def getNeighbours(self, r, c):
# r: row, c: column
# (r, c) is the position of the spin of which
# the positions of the neighbours are returned.
# Periodic boundaries:
# if m == 0, then the neighbour is the element
# in the last row, i.e. the lattice is
# on a torus.
up = [(r-1)%self.dim, c]
le = [r, (c-1)%self.dim]
ri = [r, (c+1)%self.dim]
do = [(r+1)%self.dim, c]
return [up, le, ri, do]
# Called upon initialization of the simulation.
def calculateInitialTotalEnergy(self):
eIni = 0.0
# Run over the lattice and over neighbours of spin at
# site 'i,j'.
for i in range(self.dim):
for j in range(self.dim):
for n in self.getNeighbours(i,j):
eIni += self.J*self.lattice[i][j]*self.lattice[n[0]][n[1]]
# Devide by 2 to eliminate double counting.
self.eTot = -eIni/2.0
def calculateEnergyChangeOfFlip(self,i,j):
# Calculating dE depending on number of neighbours which are
# already down.
# By convention, initially all spins up and J>0.
# 'counter' counts how many spins are of like orientation.
counter = 0
for n in self.getNeighbours(i,j):
# We flipped the i,j spin. Now we check its environment.
# E.g. for all neighbour spins up, and the i,j spin down,
# 'if' never evaluates to 'True'. We end up with '+8' in
# dE.
if self.getLattice()[n[0]][n[1]] == self.getLattice()[i][j]:
counter += 1
# Returns the 'counter' too so the call to the 'index' function
# can be avoided.
return counter, self.dE[counter]
def run(self, step):
# Define random site to flip.
k, l = randint(0,self.dim-1), randint(0,self.dim-1)
self.flip(k, l)
# Obtain the index of the energy value in the prestored
# energy array, and the change in energy for the
# prospective flip.
ind, dE = self.calculateEnergyChangeOfFlip(k,l)
if dE <= 0.0:
if step % 10000 == 0:
print "%05i: Accepted. dE = %0.2f" % (step, dE)
# Only add change in energy to the total energy.
self.eTot += dE
else:
# Uniform distributed random number.
x = random()
# Obtain the corresponding, precalculated Boltzmann
# factor, avoiding the call to the 'index' builtin
# function.
if self.dEB[ind] >= x:
if step % 10000 == 0:
print "%05i: " % step +\
"kT: %2.1f, " % self.kT +\
"Accepted Bf: %3.2f, " % self.dEB[ind] +\
"%1.4f" % x
# Adding the change in energy.
self.eTot += dE
else:
# The step is being undone.
if step % 10000 == 0:
print "%05i: " % step +\
"kT: %2.1f, " % self.kT +\
"Not Acce. Bf: %3.2f, " % self.dEB[ind] +\
"%1.4f" % x
# Set the spin state back.
self.flip(k,l)
# Append the total energy of the temperature.
self.E.append(self.eTot)
# Append the total magnetization to 'self.M'.
self.M.append(sum(self.lattice))
def heat(self, steps):
E = array(self.E)
E2 = E*E
return 1.0/self.dim*1.0/self.dim*\
(self.J/self.kT)*(self.J/self.kT)*\
(1.0/steps*sum(E2) - (1.0/steps*sum(E))*(1.0/steps*sum(E)))
def magnet(self, steps):
M = array(self.M)
M /= (steps*self.dim*self.dim)
return M
def susz(self, steps):
M = array(self.M)
M /= (self.dim*self.dim)
#M /= M[0]
M2 = M*M
return 1.0/self.dim*1.0/self.dim*\
(self.J/self.kT)*\
(1.0/steps*sum(M2) - (1.0/steps*sum(M))*(1.0/steps*sum(M)))
if __name__ == '__main__':
J = 1.0; S = 1.0; L = 20
nsteps=20000
heat = []
magn = []
susz = []
tempRange = arange(1.5, 4.0, 0.1)
for t in tempRange:
print "*"*50, t
lat = Lattice(J, S, L, t)
lat.calculateInitialTotalEnergy()
for step in range(nsteps):
lat.run(step)
heat.append(lat.heat(nsteps))
#magn.append(sum(lat.magn(nsteps)))
magn.append(sum(lat.magnet(nsteps)))
susz.append(lat.susz(nsteps))
print heat
import pylab
print "Simulation done."
pylab.plot(tempRange, heat, 'bo')
pylab.savefig('heat.png')
print "Save heat done."
pylab.clf()
pylab.plot(tempRange, magn, 'bo')
pylab.savefig('magn.png')
print "Save magn done."
pylab.clf()
pylab.plot(tempRange, susz, 'bo')
pylab.savefig('susz.png')
print "Save susz done."
"""
"""