/*
Potts model
Hamiltonian
The Potts model has the Hamiltonian
Ham[s] = -sum_{} J delta_{s_i s_j} - sum_i H delta_{s_i 0}
where the spins, s_i, are integer variables taking values 0,...,smax-1.
Comments
- It is not possible to achieve optimal performance, simplicity, and generalityh simultaneously.
- The lattice with periodic boundary conditions corresponds to a weighted graph. This graph is stored as a set of "adjacency lists" nn[i]
- The "refresh interval" is not a number in seconds; rather, it controls the number of spin flips to perform between display refreshes.
- The Metropolis algorithm is not ergodic at J/T=0; Glauber is.
- Warning: The current implementation of Metropolis doesn't satisfy detailed balance---you would have to a backward sweep.
- The Wolff algorithm only works for H=0.
- The geometric algorithm maintains M constant, ignoring H.
- Geometric+Metropolis simulates at constant H, but can be a bit slow.
- Certain parameters can be changed on the fly.
- Other parameters require restarting the simulation. (smax, xmax, ymax, lattice)
Credits and references
- R. B. Potts, Proceedings of the Cambridge Philosophical Society 48, pp106 (1952).
- Wikipedia entry
- This applet by Yen Lee Loh (2005-11-16)
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*; // for changelistener
import javax.swing.JSpinner.NumberEditor;
import java.text.DecimalFormat;
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// Unfortunately, Java does not permit "typedef"s, so I will make do with byte.
public class potts extends JApplet
implements Runnable,ComponentListener,MouseListener,ChangeListener,ActionListener {
// Enums don't work in my formalism
//public enum ALGORITHM {ALG_METROPOLIS, ALG_GLAUBER};
//public enum LATTICE {LATTICE_SQUARE, LATTICE_TRIANGULAR};
final int SPIN_INACTIVE=-128;
final int LATTICE_SQUARE=0, LATTICE_TRIANGULAR=1,LATTICE_HONEYCOMB=2,LATTICE_KAGOME=3,LATTICE_TKL=4;
final String namesOfAlgorithms[] = {"Metropolis", "Glauber", "Wolff", "Geometric", "Met+Geometric"};
final String namesOfLattices[] = {"Square", "Triangular", "Honeycomb", "Kagome", "TKL"};
//---------- GUI stuff
Container appletPane,framePane,framePane2;
Image imageInApplet,imageInFrame,imageInFrame2;
ImageIcon iconInApplet,iconInFrame,iconInFrame2;
JFrame frame1,frame2;
JSpinner spnrT,spnrxmax,spnrymax,spnrsmax,spnrdelay,spnrrefresh,spnrH,spnrK,spnrKab;
JButton buttonPolarize,buttonRandomize;
JLabel labelInApplet,labelInFrame,labelInFrame2,labelMSPF,labelMSPI,label1;
JComboBox comboAlgorithm,comboLattice;
JCheckBox chkboxRunning,chkboxCellBorders,chkboxShowBonds;
JPanel panelW;
GridBagConstraints gbc;
Color colorTable[];
Thread thread1;
boolean boolEraseImage=true;
boolean boolRestart=true;
boolean boolGetRuntimePars=false;
int delay,refresh;
double mctime; // This is a joke. Don't take it seriously.
//---------- Core stuff
int smax,xmax,ymax,imax,bmax;
double kk[]; // Exchange couplings
byte ss[]; // Potts spins
int cc[]; // Cluster flags
int nn[]; // Neighbor list (adjacency list)
int xxi[]; // x-coordinate of vertex i of graph (for GCA and rendering)
int yyi[]; // y-coordinate of vertex i
int sublat[]; // sublattice index of vertex i
int lSubLat; // Number of sublattices (2 for SQLATT, 3 for TRILATT, 9 for TKL)
int axx,axy,ayx,ayy; // Supercell vectors (in triangular coord sys)
double ww[]; // Probability list for Glauber algorithm
int iqueue[]; // Queue for Wolff and GCA
int counter=1;
double temp,beta,pstop,field,exch,exchKab;
int ncluster;
//====================================================================
// Set up Potts model
//====================================================================
// Performance-critical methods (private final methods)
private int ixy (int x, int y) {return x+xmax*y;}
private int ixySafe (int x, int y) {return ((x+xmax)%xmax)+xmax*((y+ymax)%ymax);}
private int ixyb (int x, int y, int b) {return b+bmax*(x+xmax*y);}
private int iib (int i, int b) {return b+bmax*i;}
private int irand (int i) {
return (int)(Math.random()*i);
}
private double drand_gauss () {
double x,y,u,t,xx,yy;
do {x=Math.random()*2-1; y=Math.random()*2-1; u=x*x + y*y;} while (u>1);
t = Math.sqrt(-2*Math.log(u)/u); xx=t*x;// yy=t*y;
return xx;
}
void initPotts() {
int x,y,i,j,b,cx,cy,xo,yo;
//========== FIRST GET THESE PARS! YOU NEED THEM BADLY!
getSetupPars();
//---------- Set up the neighborlist
switch (comboLattice.getSelectedIndex()) {
case LATTICE_SQUARE:
bmax = 4; imax = xmax*ymax;
xxi=new int[imax]; yyi=new int[imax]; ss=new byte[imax];
nn=new int[imax*bmax]; kk=new double[imax*bmax];
axx = ayy = 1; axy = ayx = 0;
for (y=0; y pstop) {
ss[j] = sprimej; // Flip j
cc[j] = counter; // Mark j as part of cluster
iqueue[++ltail] = j;// Push j on queue
}
}
}
}
ncluster = lhead+1; // Return the cluster size!
}
//====================================================================
// Geometric cluster algorithm (Heringa-Bloete)
// This requires a self-inverse symmetry operation in real space.
// We use inversions plus translations: x'=xoffset-x, y'=yoffset-y.
// For C++ convenience, let xmax<=xoffset<2*xmax, and similarly for yoffset.
// pstop = exp(S[s_i,s_j] + S[s_i',s_j'] - S[s_i',s_j] - S[s_i,s_j'])
//
// Warning: For swapping, the a^=b^=a^=b trick doesn't work in Java.
// Note: For efficiency, jprime should be pushed to avoid having to recalc it.
//====================================================================
private final int iInversion(int i, int xoffset, int yoffset) {
// Given site i, return its image i' under a geometric inversion.
// The following code works for both 90deg and 120deg coordinate systems.
return ixy((xoffset - xxi[i])%xmax, (yoffset - yyi[i])%ymax);
}
void geometric() {
int i,j,b,lhead,ltail,xoffset,yoffset,iprime,jprime,cx,cy;
byte s;
if (--counter==0) {counter=0x7FFFFFFE-1; for (i=0; i pstop) {
s=ss[j];ss[j]=ss[jprime]; ss[jprime]=s; // Swap s[j] and s[j']
cc[j] = counter; // Mark j as part of cluster
cc[jprime] = counter; // Mark j' as part of cluster
iqueue[++ltail] = j; // Push j on queue
}
}
}
}
ncluster = lhead+1; // Return the cluster size!
}
//&&&&&&&&&&&&&&&& Get parameters from HTML, and from spinners
int getParamInt(String parname, int def) {
int i=def;
try {i=Integer.parseInt(getParameter(parname));} catch (Exception e) {}
return i;
}
void getSetupPars() { // Essential parameters such as system size
smax = Integer.parseInt(spnrsmax.getValue().toString());
xmax = Integer.parseInt(spnrxmax.getValue().toString());
ymax = Integer.parseInt(spnrymax.getValue().toString());
}
void getRuntimePars() {
temp = Double.parseDouble(spnrT.getValue().toString());
exch = Double.parseDouble(spnrK.getValue().toString());
exchKab = Double.parseDouble(spnrKab.getValue().toString());
field = Double.parseDouble(spnrH.getValue().toString());
refresh = Integer.parseInt(spnrrefresh.getValue().toString());
delay = Integer.parseInt(spnrdelay.getValue().toString());
beta = 1d/temp;
initCouplingArray(); // must do this if exch changes
}
//&&&&&&&&&&&&&&&& Override JApplet.init() etc
//public void stop() {frame1.dispose();} // I think I gotit right
public void destroy() {frame1.dispose();}
public void init() {
//---------- Read parameters from HTML
xmax = getParamInt("XMAX", 64);
ymax = getParamInt("YMAX", 64);
smax = getParamInt("SMAX", 2);
delay = getParamInt("DELAY", 1);
temp = 1.0; exch = 1.0; exchKab=0.1; field = 0.0;
//---------- Set up applet, frame, and panels
appletPane = getContentPane();
setSize(200,32); appletPane.add(new JLabel ("See popup window"));
JFrame frameTemp = new JFrame();
frameTemp.setDefaultLookAndFeelDecorated (true);
frame1 = new JFrame("Potts Model");
frame1.setSize(640, 480);
//frame1.setFont (new Font("MONOSPACED", Font.PLAIN, 24));
framePane = frame1.getContentPane();
imageInFrame = createImage(256,256);
iconInFrame = new ImageIcon();
iconInFrame.setImage(imageInFrame);
labelInFrame = new JLabel(iconInFrame);
labelInFrame.setDoubleBuffered(true); //This helps to eliminate flicker
labelInFrame.setOpaque(true); //No need for JFrameInFrame to redraw itself
framePane.add(labelInFrame);
frame1.setIgnoreRepaint(false);//Controls need to draw themselves ...
framePane.addComponentListener(this);
framePane.addMouseListener(this);
gbc = new GridBagConstraints();
panelW = new JPanel();
framePane.add(panelW, BorderLayout.LINE_START);
frame2 = new JFrame("Sublattice magnetizations");
frame2.setSize(640, 256);
frame2.setLocation(0, 480);
frame2.setUndecorated(true);
framePane2 = frame2.getContentPane();
imageInFrame2 = createImage(256,256);
iconInFrame2 = new ImageIcon();
iconInFrame2.setImage(imageInFrame2);
labelInFrame2 = new JLabel(iconInFrame2);
//labelInFrame2.setDoubleBuffered(true); //This helps to eliminate flicker
//labelInFrame2.setOpaque(true);
framePane2.add(labelInFrame2);
//---------- Add spinners
panelW.setLayout(new GridBagLayout());
spnrsmax = new JSpinner(new SpinnerNumberModel(smax, 1, 10, 1));
spnrxmax = new JSpinner(new SpinnerNumberModel(xmax, 1, 1024, 1));
spnrymax = new JSpinner(new SpinnerNumberModel(ymax, 1, 1024, 1));
spnrT = new JSpinner(new SpinnerNumberModel(temp, 0.000001d, 1000d, 0.01d ));
spnrK = new JSpinner(new SpinnerNumberModel(exch, -1000d, 1000d, 0.01d ));
spnrKab = new JSpinner(new SpinnerNumberModel(exchKab, -1000d, 1000d, 0.01d ));
spnrH = new JSpinner(new SpinnerNumberModel(field, -1000d, 1000d, 0.01d));
spnrrefresh = new JSpinner(new SpinnerNumberModel(100, 1, 5000, 1));
spnrdelay = new JSpinner(new SpinnerNumberModel(delay, 0, 1000, 1));
gbc.anchor = GridBagConstraints.EAST;
addLabeledSpinner (spnrsmax, " Spin-type smax ", 's');
addLabeledSpinner (spnrxmax, " Size xmax ", 'x');
addLabeledSpinner (spnrymax, " Size ymax ", 'y');
addLabeledSpinner (spnrK, " Exchange Jaa ", 'J');
addLabeledSpinner (spnrKab, " Exchange Jab ", 'b');
addLabeledSpinner (spnrH, " Field H ", 'H');
addLabeledSpinner (spnrT, " Temperature T ", 'T');
addLabeledSpinner (spnrrefresh, " Refresh interval ", 'r');
addLabeledSpinner (spnrdelay, " Delay ", 'd');
spnrT.setEditor(new NumberEditor(spnrT, "###0.#####")); //Float
spnrK.setEditor(new NumberEditor(spnrK, "###0.#####")); //Float
spnrKab.setEditor(new NumberEditor(spnrKab, "###0.#####")); //Float
spnrH.setEditor(new NumberEditor(spnrH, "###0.#####")); //Float
//---------- Add algorithm combo box
comboAlgorithm = new JComboBox(namesOfAlgorithms);
label1 = new JLabel ("Algorithm");
label1.setDisplayedMnemonic('A');
label1.setLabelFor(comboAlgorithm);
gbc.gridwidth = GridBagConstraints.RELATIVE;
gbc.fill = GridBagConstraints.NONE;
gbc.weightx = 0.0;
panelW.add(label1, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1.0;
panelW.add(comboAlgorithm, gbc); // The way I coded, no need listener for combobox
//---------- Add lattice combo box
comboLattice = new JComboBox(namesOfLattices);
label1 = new JLabel ("Lattice");
label1.setDisplayedMnemonic('L');
label1.setLabelFor(comboLattice);
gbc.gridwidth = GridBagConstraints.RELATIVE;
gbc.fill = GridBagConstraints.NONE;
gbc.weightx = 0.0;
panelW.add(label1, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1.0;
comboLattice.setSelectedIndex(4);
panelW.add(comboLattice, gbc); // The way I coded, no need listener for combobox
comboLattice.addActionListener(this); // oh but this one triggers restart
//---------- Add checkboxes
chkboxRunning = new JCheckBox ("Running", true);
chkboxCellBorders = new JCheckBox ("Cell borders", true);
chkboxShowBonds = new JCheckBox ("Show bonds", true);
panelW.add(chkboxRunning, gbc);
panelW.add(chkboxCellBorders, gbc);
panelW.add(chkboxShowBonds, gbc);
//---------- Add polarize and randomize buttons
gbc.gridwidth = GridBagConstraints.RELATIVE;
panelW.add(buttonPolarize = new JButton("Polarize"), gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
panelW.add(buttonRandomize = new JButton("Randomize"), gbc);
buttonPolarize.addActionListener(this);
buttonRandomize.addActionListener(this);
//---------- Add performance labels
panelW.add(labelMSPF = new JLabel(" "), gbc);
panelW.add(labelMSPI = new JLabel(" "), gbc);
gbc.fill = GridBagConstraints.VERTICAL; gbc.weighty = 1.0;
//panelW.add(new JLabel(""), gbc); // filler
//---------- Init system and start simulation
frame1.setVisible(true);
frame2.setVisible(true);
initPotts();
thread1=new Thread(this);
thread1.start();
}
void addLabeledSpinner (JSpinner spnr, String labeltext, char mnemonic) {
JLabel label1 = new JLabel(labeltext);
if (mnemonic!=' ') label1.setDisplayedMnemonic(mnemonic);
label1.setLabelFor(spnr);
spnr.setEditor(new NumberEditor(spnr, "###0")); //No commas!
gbc.gridwidth = GridBagConstraints.RELATIVE;
gbc.fill = GridBagConstraints.NONE;
gbc.weightx = 0.0;
panelW.add(label1, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1.0;
panelW.add(spnr, gbc);
spnr.addChangeListener(this);
}
//&&&&&&&&&&&&&&&&
public void run() {
long timeNow,timeElapsed,timeLast=System.currentTimeMillis();
float mspf,mspi;
long framesElapsed=0;
try {
while (true) {
//----- Restart entire simulation if necessary ----
if (boolRestart) {
initPotts(); // initPotts calls getSetupPars and... getRuntimePars
boolRestart=false;
boolEraseImage=true;
}
else if (boolGetRuntimePars) {
getRuntimePars();
boolGetRuntimePars=false;
}
if (!chkboxRunning.isSelected()) {
Thread.sleep (250); // sleep for 250 ms
continue;
}
//----- Metropolis or Wolff ----
switch (comboAlgorithm.getSelectedIndex()) {
case 0:
// Metropolis
// n is the number of iterations
for (int n=1+(int)((500d*refresh)/(xmax*ymax)); n>0; n--) metropolis();
break;
case 1:
// Glauber
for (int n=1+(int)((500d*refresh)/(xmax*ymax)); n>0; n--) glauber();
break;
case 2:
// Wolff
// Assume that flipping 1 spin takes 1 unit of time.
// In order for the Wolff simulation to have a steady refresh rate
// regardless of temperature, we need to aim for roughly the same number
// of spin flips between refreshes, n.
for (int n=1000*refresh; n>0; n-=ncluster) wolff();
break;
case 3:
// Geometric cluster algorithm
for (int n=1000*refresh; n>0; n-=ncluster) geometric();
break;
case 4:
// Geometric+Metropolis hybrid
// don't bother interleaving
for (int n=1+(int)((250d*refresh)/(xmax*ymax)); n>0; n--) metropolis();
for (int n=500*refresh; n>0; n-=ncluster) geometric();
break;
default:
// not implemented!
}
updateImageInFrame(); labelInFrame.repaint();
updateImageInFrame2(); labelInFrame2.repaint();
boolEraseImage=false;
//----- Timing routines ----
timeNow = System.currentTimeMillis();
timeElapsed = timeNow - timeLast;
framesElapsed++;
if (timeElapsed >= 1000) {
mspf = (float)(timeElapsed)/(framesElapsed);
mspi = mspf/refresh;
labelMSPF.setText ("ms per frame: " +(int)mspf);
labelMSPI.setText ("ms per iter: " +(int)mspi);
timeLast = timeNow;
framesElapsed = 0;
}
}
} catch (Exception e) {
System.out.println("Caught exception! Thread exiting");
}
}
//&&&&&&&&&&&&&&&& Implement ComponentListener
public void componentHidden(ComponentEvent e){}
public void componentMoved(ComponentEvent e){}
public void componentShown(ComponentEvent e) {}
public void componentResized(ComponentEvent e) {
int width,height;
if (e.getSource()==framePane) {
width=labelInFrame.getSize().width; height=labelInFrame.getSize().height;
if (width==0||height==0) return;
imageInFrame=createImage(width,height);
iconInFrame.setImage(imageInFrame);
}
}
//&&&&&&&&&&&&&&&& Implement MouseListener
public void mouseEntered (MouseEvent e) {}
public void mouseExited (MouseEvent e) {}
public void mousePressed (MouseEvent e) {}
public void mouseReleased (MouseEvent e) {}
public void mouseClicked (MouseEvent e) {}
//&&&&&&&&&&&&&&&& Implement ChangeListener
public void stateChanged(ChangeEvent e) {
Object esrc = e.getSource();
if (esrc==spnrxmax || esrc==spnrymax || esrc==spnrsmax
|| esrc==comboLattice) { // is this necessary?
boolRestart=true;
}
else {
boolGetRuntimePars=true;
}
}
//&&&&&&&&&&&&&&&& Implement ActionListener
public void actionPerformed(ActionEvent e) {
int i;
Object esrc = e.getSource();
if (esrc==comboLattice) { boolRestart=true; }
else if (esrc==buttonPolarize) {initSpins(1d);}
else if (esrc==buttonRandomize) {initSpins(1d/smax);}
}
//&&&&&&&&&&&&&&&& This code is the RENDERING CODE!
void updateImageInFrame() {
int width,height;
int X,Y,x,y;
Graphics g;
int cellborder;
if (imageInFrame==null) return;
g=imageInFrame.getGraphics();
width=imageInFrame.getWidth(null);
height=imageInFrame.getHeight(null);
if (boolEraseImage) { // let caller reset this flag
g.setColor(Color.white); g.fillRect(0,0,width,height);
}
cellborder = chkboxCellBorders.isSelected()?1:0;
int iLattice = comboLattice.getSelectedIndex();
if (iLattice==LATTICE_SQUARE) {
int xscal=width/xmax;
int yscal=height/ymax;
xscal = yscal = Math.max(1+cellborder, Math.min(xscal,yscal));
int Yorg = height - yscal;
for (x=0; x64||Y-Y2<-64||Y-Y2>64) continue; //avoid mess
if (kk[ixyb(x,y,b)] < 0.5)
g.setColor(Color.gray);
else
g.setColor(Color.black);
g.drawLine (X+xscal2, Y+yscal2, X2+xscal2, Y2+yscal2);
}
}
}
}
}
}
void updateImageInFrame2() {
int width,height;
int width2,height2;
int X,Y,x,y;
int yorg;
double xscal,yscal;
Graphics g;
if (imageInFrame2==null) return;
width=labelInFrame2.getSize().width;
height=labelInFrame2.getSize().height;
width2=imageInFrame2.getWidth(null);
height2=imageInFrame2.getHeight(null);
// if width 0... return
if (width!=width2 || height!=height2) {
imageInFrame2=createImage(width,height);
iconInFrame2.setImage(imageInFrame2);
boolEraseImage = true;
}
g=imageInFrame2.getGraphics();
// move into new method called GetSublattMagnetisation
xscal = (double)width/3000;
yscal = (double)(height-1) / ((double)imax/2);
yorg = height-1;
switch (comboLattice.getSelectedIndex()) {
case LATTICE_SQUARE: {
int sublatt;
int mm[] = new int[2];
for (sublatt=0; sublatt<2; sublatt++) {
mm[sublatt] = 0;
for (y=0; y=width) {mctime=0; boolEraseImage=true;} //ugly hack
break;
}
case LATTICE_TRIANGULAR: {
int sublatt;
int mm[] = new int[3];
for (sublatt=0; sublatt<3; sublatt++) {
mm[sublatt] = 0;
for (y=0; y=width-1) {mctime=0; boolEraseImage=true; } //ugly hack
break;
}
}
if (boolEraseImage) { //again: ugly hack
g.setColor(Color.white); g.fillRect(0,0,width,height);
g.setColor(Color.black); g.drawRect(0,0,width,height);
g.setColor(Color.gray); g.drawLine(0,height-height/smax,width,height-height/smax);
mctime = 0.0d;
}
}
private Color hsvColor(float h, float s, float v) { // h,s,v in [0,1]
int ir,ig,ib;
int iq;
double fr,fg,fb,p,q,x1,x2,x3;
h *= 6; v *= 255;
q = iq = (int)Math.floor(h);
p = h - q; // p is the fractional part of h
x1 = v * (1 - s);
x2 = v * (1 - s*p); //--- only one of x2 and x3 need be computed
x3 = v * (1 - s*(1-p));
iq = (iq+600)%6; // iq is the integer part of h, modulo 6
switch(iq) {
case 0: fr=v ; fg=x3; fb=x1; break;
case 1: fr=x2; fg=v ; fb=x1; break;
case 2: fr=x1; fg=v ; fb=x3; break;
case 3: fr=x1; fg=x2; fb=v ; break;
case 4: fr=x3; fg=x1; fb=v ; break;
default: fr=v ; fg=x1; fb=x2;
}
ir = (int)fr;
ig = (int)fg;
ib = (int)fb;
return new Color(((ir<<8)+ig<<8)+ib);
}
}