/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2010-2012 University of Bonn. All rights reserved.
 * 
 *
 *   This file is part of MoleCuilder.
 *
 *    MoleCuilder is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    MoleCuilder is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with MoleCuilder.  If not, see .
 */
/*
 * GaussianThermostat.cpp
 *
 *  Created on: Aug 18, 2010
 *      Author: crueger
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include "GaussianThermostat.hpp"
#include "Atom/AtomSet.hpp"
#include "CodePatterns/Log.hpp"
#include "config.hpp"
#include "Element/element.hpp"
#include "Helpers/defs.hpp"
#include "LinearAlgebra/Vector.hpp"
#include "Parser/PcpParser_helper.hpp"
#include "World.hpp"
#include 
GaussianThermostat::GaussianThermostat(int _ScaleTempStep) :
  E(0),G(0),
  ScaleTempStep(_ScaleTempStep)
{}
GaussianThermostat::GaussianThermostat() :
  E(0),G(0),
  ScaleTempStep(25)
{}
GaussianThermostat::~GaussianThermostat()
{}
const char *ThermostatTraits::name = "Gaussian";
std::string ThermostatTraits::getName(){
  return ThermostatTraits::name;
}
Thermostat *ThermostatTraits::make(class ConfigFileBuffer * const fb){
  int ScaleTempStep;
  const int verbose = 0;
  ParseForParameter(verbose,fb,"Thermostat", 0, 2, 1, int_type, &ScaleTempStep, 1, critical); // read collision rate
  return new class GaussianThermostat(ScaleTempStep);
}
double GaussianThermostat::scaleAtoms(unsigned int step,double ActualTemp,ATOMSET(std::list) atoms){
  return doScaleAtoms(step,ActualTemp,atoms.begin(),atoms.end());
}
double GaussianThermostat::scaleAtoms(unsigned int step,double ActualTemp,ATOMSET(std::vector) atoms){
  return doScaleAtoms(step,ActualTemp,atoms.begin(),atoms.end());
}
double GaussianThermostat::scaleAtoms(unsigned int step,double ActualTemp,ATOMSET(std::set) atoms){
  return doScaleAtoms(step,ActualTemp,atoms.begin(),atoms.end());
}
template 
double GaussianThermostat::doScaleAtoms(unsigned int step,double ActualTemp,ForwardIterator begin, ForwardIterator end){
  LOG(2,  "Applying Gaussian thermostat...");
  init(step,begin,end);
  double G_over_E = G/E;
  LOG(1, "Gaussian Least Constraint constant is " << G_over_E << ".");
  double ekin =0;
  for(ForwardIterator iter=begin;iter!=end;++iter){
    Vector U = (*iter)->getAtomicVelocityAtStep(step);
    if ((*iter)->getFixedIon() == 0) {// even FixedIon moves, only not by other's forces
      U += World::getInstance().getConfig()->Deltat * G_over_E * U;
      ekin += (*iter)->getType()->getMass() * U.NormSquared();
    }
    (*iter)->setAtomicVelocityAtStep(step, U);
  }
  return ekin;
}
template 
void GaussianThermostat::init(unsigned int step,ForwardIterator begin, ForwardIterator end){
  E=0;
  G=0;
  for(ForwardIterator iter=begin;iter!=end;++iter){
    const Vector &U = (*iter)->getAtomicVelocityAtStep(step);
    const Vector &F = (*iter)->getAtomicForceAtStep(step);
    if ((*iter)->getFixedIon() == 0){ // even FixedIon moves, only not by other's forces
      G += U.ScalarProduct(F);
      E += U.NormSquared()*(*iter)->getType()->getMass();
    }
  }
}
double GaussianThermostat::getE() const{
  return E;
}
double GaussianThermostat::getG() const{
  return G;
}
std::string GaussianThermostat::name(){
  return ThermostatTraits::name;
}
std::string GaussianThermostat::writeParams(){
  std::stringstream sstr;
  sstr << ScaleTempStep;
  return sstr.str();
}