/*  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.multicanonical;

import java.util.*;
import java.awt.*; import javax.swing.*;
import fr.ensea.Localizer;
import fr.ensea.montecarlo.*;
import fr.ensea.montecarlo.event.*;

/**
 * This class manages the Wang-Landau algorithm thread (since the algorithm and the UI are executed 
 * in separate threads). In particular, this class allows one to configure either a temperature sweep or
 * a single-temperature simulation, to pause/stop/restart the simulation, to set the "sleep" delay between
 * MC steps, and arranges to repaint the UI asynchronously at appropriate times. 
 * Any particular algorithm (e.g. Metropolis, Wolff, ...) may be plugged into it.
 */
public class WLSimulationThread extends AbstractSimulationThread {

	private WangLandau algorithm;
	private double[] kTList; // for reweighting
	private int iteration;
	private double maxFlatness;
	
	/**
	 * Init a new Wang-Landau simulatin thread.
	 * Reweighting will be carried out from kTstart to kTend in kTsteps.
	 * @param dE histogram bin width
	 * @param weight initial weight for updating S(E), i.e. S(E)+=weight; then weight/=2 after each iteration.
	 * @param maxFlatness maximum histogram flatness below which the update weight is divided by 2. 
	 */
	public WLSimulationThread(WLApplet controller, double kTstart, double kTend, int kTsteps, double dE, double weight, double maxFlatness){
		super(controller);
		this.algorithm = controller.getLattice().createWangLandau(controller.getSamplesBag(),dE,weight);// par défaut
		iteration=0;
		this.maxFlatness = maxFlatness;
		setTemperatureRange(kTstart, kTend, kTsteps);
	}
	
	public WLDOSHistogram getWLDOSHistogram(){
		return algorithm.wlDosHistogram;
	}

	// N/A for WL algo (local-update only)
	public boolean setAlgorithm(String algo){
		return false;
	}

	public String toString(){
		String s = "WLSimulationThread:\n";
		return s;
	}
	
	public double getMaxFlatness(){
		return maxFlatness;
	}
	
	public void setMaxFlatness(double f){
		this.maxFlatness = f;
	}

	public void setWeight(double w){
		algorithm.wlDosHistogram.setWeight(w);
		controller.getControlPane().setRunName("WL-weight="+Double.toString(w));
		((WLApplet)controller).getSimulationCustomizer().updateWeightJTextField(w);
	}
	
	// --- acceptance rates ---
	public String getCountersRateJLabel(){
		return algorithm.getCountersRateJLabel();
	}

	public void reinitCounters(){
		algorithm.reinitCounters();
	}

	// --- implementation of abstract methods for iteration management ---
	protected  boolean configureNextRun(){
		//controller.getSamplesBag().resetSamplesGraphs();
		double w = algorithm.wlDosHistogram.getWeight();
		controller.getControlPane().setRunName(Localizer.get("WLweight")+"="+Double.toString(w));
		return true;
	}
	
	protected  void sweep(){
		this.algorithm.sweep();
	}

	protected  boolean finalizeRun(){
		final int sz =  controller.getSamplesBag().size();
		if (sz==0 || sz%nbMCSteps !=0) return false; 
		WLDOSHistogram h = algorithm.wlDosHistogram;
		h.normalize();
		double flatness = h.flatness();
		double energyGS = controller.getLattice().getGroundstateEnergy();
		double minEnergy = h.xAxis().binLowerEdge(0);
		if (minEnergy <= energyGS){
			if (flatness < maxFlatness) {
				((WLApplet)controller).getThermalAveragesSet().storeSimulationResults(this); // also backup histogram 
				controller.showStatus(Localizer.get("GroundstateReached")+"/"+Localizer.get("HistogramFlatness")+"="+flatness+" < "+maxFlatness);
				iteration++;
				// reset histogram
				h.reset();
				// update weight
				double oldWeight = h.getWeight();
				setWeight(oldWeight/2.0);
				controller.getSamplesBag().resetSamplesGraphs();
				return true;
			}
			else 
				controller.showStatus(Localizer.get("GroundstateReached")+"/"+Localizer.get("HistogramFlatness")+"="+flatness+" > "+maxFlatness);
		}
		return false;
	}

	// --------------- UI -----------------
	public JComboBox createAlgoChoice(){
		return null;
	}
	
	// --------------- reweighting parameters ---
	public double getKTStart(){
		return kTList[0];
	}

	public double getKTEnd(){
		return kTList[kTList.length-1];
	}

	public int getKTSteps(){
		return kTList.length;
	}

	public double[] getKTList(){
		return kTList;
	}

	/**
	 * Configures a temperature sweep.
	 */
	public void setTemperatureRange(double kTstart, double kTend, int kTsteps){
		if (kTsteps<2) kTsteps=2;
		kTList = new double[kTsteps];
		double deltakT = (kTend-kTstart)/(kTsteps-1);
		for (int i=0; i<kTsteps; i++){
			kTList[i] = kTstart + i * deltakT;
		}
	}

}

