/* Monte-Carlo simulation code for statistical physics Copyright (C) 2001-2005 Sylvain Reynal Département de Physique Ecole Nationale Supérieure de l'Electronique et de ses Applications (ENSEA) 6, avenue du Ponceau, F-95014 CERGY CEDEX et Laboratoire de Physique Théorique et Modélisation (LPTM) Université de Cergy-Pontoise - Site de Neuville F-95031 CERGY CEDEX Tel : 00 +33 130 736 245 Fax : 00 +33 130 736 667 e-mail : reynal@ensea.fr web page : http://www.ensea.fr/staff/reynal/ */ package fr.ensea.montecarlo; import java.util.*; import java.awt.*; import fr.ensea.Localizer; import fr.ensea.montecarlo.*; import fr.ensea.montecarlo.event.*; /** * This class is the abstract class for simulation thread management (since the algorithm and the UI are executed * in separate threads). In particular, this class allows one to pause/stop/restart the simulation, to set the "sleep" delay between * MC steps, arranges to repaint the UI asynchronously at appropriate times, and dispatch Thread events to * registered listeners. */ public abstract class AbstractSimulationThread implements Runnable { protected AbstractApplet controller; protected boolean alive, paused; protected long sleepDelay=100; protected boolean isSuspendRedrawViewer; protected Thread activeThread; protected int nbMCSteps; /** * a list of listeners to be notified signals during algorithm run-time */ protected Vector listenerList = new Vector(); public AbstractSimulationThread(AbstractApplet controller){ this.controller=controller; isSuspendRedrawViewer = false; nbMCSteps=Integer.MAX_VALUE; } public abstract boolean setAlgorithm(String algo); /** * Set the number of MC steps to be simulated * @param n set to a negative number to simulated an infinite number of steps */ public void setNumberMCSteps(int n){ if (n<1) this.nbMCSteps = Integer.MAX_VALUE; else this.nbMCSteps = n; } public int getNumberMCSSteps(){ return this.nbMCSteps; } /** * Returns the counter rate (e.g. acceptance rate, cluster size) in human-readable form */ public abstract String getCountersRateLabel(); public abstract void reinitCounters(); /** * If "s" is true, stops triggering repaint event for the lattice viewer. */ public void suspendRedrawViewer(boolean s){ this.isSuspendRedrawViewer = s; } public boolean isSuspendRedrawViewer(){ return this.isSuspendRedrawViewer; } /** * Implements the run() method of interface Runnable. We iterate over the temperature list (which is reduced * to a single element if the simulation is a single-temperature one), testing for the "alive" and "pause" flags * at each iteration. */ public void run(){ alive=true; paused=false; fireSignal(new SignalEvent(this, SignalEvent.STARTED)); while (configureNextRun()){ //while(alive && controller.getSamplesBag().size() < nbMCSteps){ while(true){ sweep(); controller.updateGUI(); // update graphs + labels if (!this.isSuspendRedrawViewer){ controller.updateLatticeViewer(); try { Thread.sleep(sleepDelay); } catch (InterruptedException ex){} } if (!alive) break; while(paused){ try { Thread.sleep(1000); } catch (InterruptedException ex){} } if (finalizeRun()) break; if (!alive) break; } //finalizeRun(); if (!alive) break; // break for() loop 'cause we killed it inside the while() loop ! } alive =false; fireSignal(new SignalEvent(this, SignalEvent.COMPLETED)); System.out.println("Runnable COMPLETED"); } protected abstract boolean configureNextRun(); protected abstract void sweep(); /** * Invoked at the end of the run() loop. Return true if we can switch to the next run (either the next * temperature, or the next WL iteration). */ protected abstract boolean finalizeRun(); public void kill(){ if (activeThread != null){ alive = false; try { activeThread.join(); // wait until dead activeThread=null; } catch (InterruptedException e){ e.printStackTrace(); } } alive = false; paused=false; System.out.println("Algorithm killed"); } /** N/A if this thread is dead */ public void pause(){ if (alive) { paused=true; fireSignal(new SignalEvent(this, SignalEvent.SUSPENDED)); } } /** N/A if this thread is dead */ public void resume(){ if (alive) { paused=false; fireSignal(new SignalEvent(this, SignalEvent.RESUMED)); } } public void start(){ kill(); activeThread = new Thread(this); activeThread.start(); } public void setSleepDelay(long millis){ this.sleepDelay = millis; } public long getSleepDelay(){ return this.sleepDelay; } public boolean isAlive(){ if (activeThread != null) return activeThread.isAlive(); return alive; } public boolean isPaused(){ return isAlive() & paused; // a dead thread can't be paused ! } public boolean isRunning(){ return isAlive() & !paused; } public boolean isDead(){ return !isAlive(); } // -------------- listeners methods ------------------------------ /** * registers a SignalListener to receive SignalEvent's during algorithm run-time * note : this method tries to mimic javax.swing.EventListener as much as possible, * seeing that swing classes ain't accessible easily from an applet. */ public void addSignalListener(SignalListener listener){ if (listener!=null) listenerList.addElement(listener); } public void removeSignalListener(SignalListener listener){ if (listener!=null) listenerList.removeElement(listener); } /** * fires the given DrawingEvent to all registered DrawingListener's * this usually takes place at the very beginnig of the algorithm, then at the end of each step processed, finally when * the algorithm has finished. */ public void fireSignal(SignalEvent event){ for(Enumeration e = listenerList.elements(); e.hasMoreElements();){ ((SignalListener)(e.nextElement())).signalBroadcast(event); } } // --------------- UI ----------------- public abstract Choice createAlgoChoice(); }