Source Code for Program "Ising" (Visual Python version)

For use with An Introduction to Thermal Physics by Daniel V. Schroeder.

This code is written in Python, using the Visual module (VPython) developed by David Scherer and Bruce Sherwood. Although VPython is intended mostly for 3D graphics, it can also be used to draw a two-dimensional lattice of squares such as this.

If you don't like Visual Python, click here for a list of versions of the Ising program in other languages (including a Python version that uses the Tkinter graphics module).


# vising.py
# Simulates the two-dimensional Ising model using the Metropolis algorithm
# This version uses Visual Python, www.vpython.org
# By Dan Schroeder, Weber State University, January 2013
# Modified for VPython 6 compatibility (with help from Bruce Sherwood), July 2013

import visual, visual.controls, numpy, random, math

size = 50                            # number of sites in a lattice row (change if desired)
s = numpy.ones((size, size), int)    # 2D array of dipoles (1=up, -1=down)
T = 2.27                             # temperature in natural units (adjusted by GUI slider)
running = False                      # will be true when simulation is running

# Set up the main graphics window (see www.vpython.org/contents/docs/visual/display.html):
windowSize = 500                           # size in screen pixels
visual.scene.title = "Ising Model"
visual.scene.x = 50                        # move window away from left edge of screen
visual.scene.width = windowSize
visual.scene.height = windowSize + 20      # height in pixels includes title bar; 20 is a guess
visual.scene.fov = 0.01                    # small field of view eliminates 3D perspective
visual.scene.userzoom = visual.scene.userspin = False    # no changing the view!

# Set up the GUI controls (see www.vpython.org/contents/docs/visual/controls.html):
cWindow = visual.controls.controls(title="Control", x=windowSize+60, y=0, width=150, height=visual.scene.height)
pButton = visual.controls.button(pos=(0,65), width=40, height=23, text="Run", action=lambda: runPause())
visual.label(display=cWindow.display, text="Temperature:", box=False, opacity=0, pos=(0,35))
tLabel = visual.label(display=cWindow.display, text="2.27", box=False, opacity=0, pos=(0,25))
tSlider = visual.controls.slider(pos=(0,-80), axis=(0,1,0), length=90, min=0.01, max=10.0, value=2.27, action=lambda: tSliderAdjust())

# Function to run or pause the simulation:
def runPause():
    global running
    running = not running
    pButton.text = "Pause" if running else "Resume"

# Function to update temperature readout when slider is moved:
def tSliderAdjust():
    global T
    T = tSlider.value
    tLabel.text = "%1.2f" % T        # display temperature to two decimal places

# We represent the square 2D array of lattice sites using a vpython "points"
# object, which is basically a 1D array of squares (or circles) placed in the
# 3D space.  The code below arranges them on a grid in the xy plane.
thePoints = visual.points(size=1, size_units="world", shape="square")    # can also try "round"
for i in range(size):
    for j in range(size):
        thePoints.append(pos=(i-size/2+0.5, j-size/2+0.5, 0))

# Function to color the "point" representing site (i,j):
def colorSquare(i, j):
    thePoints.color[i*size + j] = (0.5,0,1) if s[i,j]==1 else (1,1,1)    # purple and white

# Initialize the lattice to a random array, and draw it as we go:
for i in range(size):
    for j in range(size):
        s[i,j] = 1 if random.random()<0.5 else -1
        colorSquare(i,j)

# Function to calculate energy change upon hypothetical flip (with pbc):
def deltaE(i,j):
    leftS = s[size-1,j] if i==0 else s[i-1,j]
    rightS = s[0,j] if i==size-1 else s[i+1,j]
    topS = s[i,size-1] if j==0 else s[i,j-1]
    bottomS = s[i,0] if j==size-1 else s[i,j+1]
    return 2.0 * s[i,j] * (leftS + rightS + topS + bottomS)

# Main simulation loop:
while True:
    if running:
        visual.rate(1000000)             # handle events and run really fast
        i = int(random.random()*size)    # choose a random row and column
        j = int(random.random()*size)
        eDiff = deltaE(i,j)
        if eDiff <= 0 or random.random() < math.exp(-eDiff/T):    # Metropolis!
            s[i,j] = -s[i,j]
            colorSquare(i, j)
    else:
        visual.rate(20)        # don't eat processor time when paused

Last modified on July 23, 2013.