/*

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

// 96 really

Credits and references

*/ 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); } }