/* 
Loesungsvorschlag fuer 
              :  Uebung 1 
zur Vorlesung :  Moderne numerische Methoden der Festkoerperphysik

(c) Eberhard Jakobi, 2007 
*/

// includes
#include <iostream>
#include <string>
#include <fstream>
#include <unistd.h>
#include <cmath>

class Potential {
	public:
		double left() { return -3.; }
		double right() { return 3.; }
		double operator() (double fx) {
			if (fx<-3. || fx>3.) {
				return std::numeric_limits<double>::max();
			} else {
				return
				-(std::pow(fx,2)+.3)*std::exp(-std::pow((fx-.1),2));
			}
		}
};

bool inline metropolis(double beta, double E_n, double E_c) {
	if (E_n<E_c) return true;
	double r = (double)(rand())/((double)(RAND_MAX));
	if (std::exp(-beta*(E_n-E_c))>r) return true; else return false;
}

class Move {
	private:
		Potential& pot;

	public:
		enum Observable { MeanPosition, MeanEnergy };

		Move(Potential& _pot,size_t _max_iter, Observable _observable):pot(_pot) {
			max_iter = _max_iter;
			observable = _observable;
			warm_up_sweeps = max_iter/100;
			max_iter += warm_up_sweeps;
		}
		double operator() (double fx, std::ostream& x_out, std::ostream& E_out, double s=-1.) {
			size_t iter = 0;
			size_t N = 0;
			beta = 1./fx;
			
			// average
			double E1 = 0.;
			double x1 = 0.;
			
			double x_akt = 0.;
			double E_akt = pot(x_akt);

			size_t accepted = 0;

			if (s<0.) { s = 1.+(fx-0.005)*10.; }

			while(iter++ < max_iter) {
				double r = rand()/((double)(RAND_MAX));

				double x_neu = x_akt + (r-.5)*s;
				double E_neu = pot(x_neu);

				if (metropolis(beta,E_neu,E_akt)) {
					x_akt = x_neu;
					E_akt = E_neu;					
					if (iter>warm_up_sweeps) accepted++;
				}

				if (iter>warm_up_sweeps) {
					N++;
					E1+=E_akt;
					x1+=x_akt;

					E_out << E_akt << "\n";
					x_out << x_akt << "\n";
				}
			}
			// average
			x1 /= (double)N;
			E1 /= (double)N;

			// return observable
			switch(observable) {
				case MeanPosition:
					return x1;
				case MeanEnergy:
					return E1;
				default:
					std::cerr << "ERROR : unknown selected.\n";
					return 0.;
			}
		}
	private:
		double beta;
		double x_c;
		double s;
		size_t max_iter, warm_up_sweeps;
		Observable observable;
};


int main(int argc, char *argv[]) {
	std::cout << "# Metropolis-Tester.\n";
	Potential pot;

	const size_t max_samples = 1000;
	std::ofstream pot_file("Potential.out");
	for (size_t i = 0; i < max_samples; i++) {
		double x = pot.left()+(pot.right()-pot.left())*i/(double)max_samples;
		pot_file << x << "\t" << pot(x) << "\n";
	}
	pot_file.close();
	
	std::cout << "Starting ... \n";
	Move::Observable ob = Move::MeanPosition;
	Move move(pot,20000,ob);

	std::ofstream x1_file("T1.00_s0.50_20k.x");
	std::ofstream E1_file("T1.00_s0.50_20k.E");
	move(1.0,x1_file,E1_file,0.5);
	x1_file.close(); E1_file.close();

	std::ofstream x2_file("T1.00_s0.30_20k.x");
	std::ofstream E2_file("T1.00_s0.30_20k.E");
	move(1.0,x2_file,E2_file,0.3);
	x2_file.close(); E2_file.close();

	std::ofstream x3_file("T1.00_s0.10_20k.x");
	std::ofstream E3_file("T1.00_s0.10_20k.E");
	move(1.0,x3_file,E3_file,0.1);
	x3_file.close(); E3_file.close();

	std::ofstream x4_file("T0.10_s0.10_20k.x");
	std::ofstream E4_file("T0.10_s0.10_20k.E");
	move(0.1,x4_file,E4_file,0.1);
	x4_file.close(); E4_file.close();

	std::ofstream x5_file("T0.003_s0.10_20k.x");
	std::ofstream E5_file("T0.003_s0.10_20k.E");
	move(0.003,x5_file,E5_file,0.1);
	x5_file.close(); E5_file.close();
	
	std::cout << "single runs finished\n";

	std::ofstream T_file("T_log_1000k.x");
	ob = Move::MeanPosition;
	Move move2(pot,2000000,ob);
	std::cout << "Temperature run ... \n";
	for (size_t i = 200; i > 0; i--) {
		double temperature = std::exp(-(double)i*0.05);
		std::ofstream x_file("/dev/null");
		std::ofstream E_file("/dev/null"); 
 		if (i/30 == 0) std::cout << "." << std::flush;
		T_file << temperature << "\t" << move2(temperature,x_file,E_file,-1.0) << "\n";
		x_file.close(); E_file.close();
	}
	T_file.close();

	std::cout << "\n ... finished\n";
}



syntax highlighted by Code2HTML, v. 0.9.1