From ef3303cbe1aabd9fe6fc8b4976ae359cf608e1d2 Mon Sep 17 00:00:00 2001
From: Giovanni Bussi <giovanni.bussi@gmail.com>
Date: Wed, 23 Jan 2013 13:43:57 +0100
Subject: [PATCH] Improved grids to allow free energy projections

---
 src/tools/Grid.cpp | 164 +++++++++++++++++++++++++++++++++++++++++++--
 src/tools/Grid.h   |  53 +++++++++++++--
 2 files changed, 208 insertions(+), 9 deletions(-)

diff --git a/src/tools/Grid.cpp b/src/tools/Grid.cpp
index f2c4d240d..ce769baaf 100644
--- a/src/tools/Grid.cpp
+++ b/src/tools/Grid.cpp
@@ -41,6 +41,42 @@ Grid::Grid(const std::string& funcl, std::vector<Value*> args, const vector<std:
  plumed_massert(args.size()==gmin.size(),"grid dimensions in input do not match number of arguments");
  plumed_massert(args.size()==nbin.size(),"grid dimensions in input do not match number of arguments");
  plumed_massert(args.size()==gmax.size(),"grid dimensions in input do not match number of arguments");
+ unsigned dim=gmax.size(); 
+ std::vector<std::string> names; 
+ std::vector<bool> isperiodic; 
+ std::vector<string> pmin,pmax; 
+ names.resize( dim );
+ isperiodic.resize( dim );
+ pmin.resize( dim );
+ pmax.resize( dim );
+ for(unsigned int i=0;i<dim;++i){
+  names[i]=args[i]->getName();
+  if( args[i]->isPeriodic() ){
+      isperiodic[i]=true; 
+      args[i]->getDomain( pmin[i], pmax[i] );
+  } else {
+      isperiodic[i]=false;
+      pmin[i]="0.";
+      pmax[i]="0.";
+  }
+ }
+ // this is a value-independent initializator
+ Init(funcl,names,gmin,gmax,nbin,dospline,usederiv,doclear,isperiodic,pmin,pmax);
+}
+
+Grid::Grid(const std::string& funcl, const std::vector<string> &names, const std::vector<std::string> & gmin, 
+           const vector<std::string> & gmax, const std::vector<unsigned> & nbin, bool dospline, bool usederiv, bool doclear, const std::vector<bool> &isperiodic, const std::vector<std::string> &pmin, const std::vector<std::string> &pmax ){
+ // this calls the initializator
+ Init(funcl,names,gmin,gmax,nbin,dospline,usederiv,doclear,isperiodic,pmin,pmax);
+}
+
+void Grid::Init(const std::string& funcl, const std::vector<std::string> &names, const vector<std::string> & gmin, 
+           const std::vector<std::string> & gmax, const std::vector<unsigned> & nbin, bool dospline, bool usederiv, bool doclear, 
+     	   const std::vector<bool> &isperiodic, const std::vector<std::string> &pmin, const std::vector<std::string> &pmax ){
+// various checks
+ plumed_massert(names.size()==gmin.size(),"grid dimensions in input do not match number of arguments");
+ plumed_massert(names.size()==nbin.size(),"grid dimensions in input do not match number of arguments");
+ plumed_massert(names.size()==gmax.size(),"grid dimensions in input do not match number of arguments");
  dimension_=gmax.size(); 
  str_min_=gmin; str_max_=gmax; 
  argnames.resize( dimension_ );
@@ -48,16 +84,17 @@ Grid::Grid(const std::string& funcl, std::vector<Value*> args, const vector<std:
  max_.resize( dimension_ );
  pbc_.resize( dimension_ );
  for(unsigned int i=0;i<dimension_;++i){
-  argnames[i]=args[i]->getName();
-  if( args[i]->isPeriodic() ){
+  argnames[i]=names[i];
+  if( isperiodic[i] ){
       pbc_[i]=true; 
-      args[i]->getDomain( str_min_[i], str_max_[i] );
+      str_min_[i]=pmin[i];
+      str_max_[i]=pmax[i];
   } else {
       pbc_[i]=false;
   }
+  Tools::convert(str_min_[i],min_[i]); 
+  Tools::convert(str_max_[i],max_[i]); 
   funcname=funcl;
-  Tools::convert( str_min_[i], min_[i] );
-  Tools::convert( str_max_[i], max_[i] );
   plumed_massert(max_[i]>min_[i],"maximum in grid must be larger than minimum");
   plumed_massert(nbin[i]>0,"number of grid points must be greater than zero");
  }
@@ -112,6 +149,11 @@ vector<unsigned> Grid::getNbin() const {
  return nbin_;
 }
 
+vector<string> Grid::getArgNames() const {
+ return argnames;
+}
+
+
 unsigned Grid::getSize() const {
  return maxsize_;
 }
@@ -430,6 +472,17 @@ void Grid::scaleAllValuesAndDerivatives( const double& scalef ){
   }
 }
 
+void Grid::applyFunctionAllValuesAndDerivatives( double (*func)(double val), double (*funcder)(double valder) ){
+  if(usederiv_){
+     for(unsigned i=0;i<grid_.size();++i){
+         grid_[i]=func(grid_[i]);
+         for(unsigned j=0;j<dimension_;++j) der_[i][j]=funcder(der_[i][j]);
+     }
+  } else {
+     for(unsigned i=0;i<grid_.size();++i) grid_[i]=func(grid_[i]);
+  }
+}
+
 void Grid::writeHeader(OFile& ofile){
  for(unsigned i=0;i<dimension_;++i){
      ofile.addConstantField("min_" + argnames[i]);
@@ -622,4 +675,105 @@ void SparseGrid::writeToFile(OFile& ofile){
    ofile.printField();
  }
 }
+
+
+void Grid::projectOnLowDimension(double &val, std::vector<int> &vHigh, WeightBase * ptr2obj ){
+    unsigned i=0;
+    for(i=0;i<vHigh.size();i++){
+       if(vHigh[i]<0){
+    	  for(unsigned j=0;j<(getNbin())[i];j++){
+            vHigh[i]=int(j);  
+            projectOnLowDimension(val,vHigh,ptr2obj); // recursive function: this is the core of the mechanism
+            vHigh[i]=-1;
+          } 
+          return; // 
+       }
+    }
+    if(i==vHigh.size()){
+        //std::cerr<<"POINT: "; 
+        //for(unsigned j=0;j<vHigh.size();j++){
+        //   std::cerr<<vHigh[j]<<" ";
+        //} 
+        std::vector<unsigned> vv(vHigh.size()); 
+        for(unsigned j=0;j<vHigh.size();j++)vv[j]=unsigned(vHigh[j]);
+        //
+        // this is the real assignment !!!!! (hack this to have bias or other stuff)
+        //
+
+        // this case: produce fes
+        //val+=exp(beta*getValue(vv)) ;
+        double myv=getValue(vv);
+        val=ptr2obj->projectInnerLoop(val,myv) ;
+        // to be added: bias (same as before without negative sign) 
+        
+        //std::cerr<<" VAL: "<<val <<endl;
+    }
+};
+
+Grid Grid::project(const std::vector<std::string> proj , WeightBase *ptr2obj ){
+         // find extrema only for the projection
+         vector<string>   smallMin,smallMax;
+         vector<unsigned> smallBin;
+         vector<unsigned> dimMapping;
+         vector<bool> smallIsPeriodic;
+         vector<string> smallName;
+
+         // check if the two key methods are there
+         WeightBase* pp = dynamic_cast<WeightBase*>(ptr2obj);
+         if (!pp)plumed_merror("This WeightBase is not complete: you need a projectInnerLoop and projectOuterLoop ");
+ 
+         for(unsigned j=0;j<proj.size();j++){
+              for(unsigned i=0;i<getArgNames().size();i++){
+                    if(proj[j]==getArgNames()[i]){ 
+	    	         unsigned offset;		 
+ 	    	         // note that at sizetime the non periodic dimension get a bin more  	
+                         if(getIsPeriodic()[i]){offset=0;}else{offset=1;}
+                         smallMax.push_back(getMax()[i]);
+                         smallMin.push_back(getMin()[i]);
+                         smallBin.push_back(getNbin()[i]-offset);
+			 smallIsPeriodic.push_back(getIsPeriodic()[i]);
+                         dimMapping.push_back(i);
+			 smallName.push_back(getArgNames()[i]);
+                         break;
+                    }
+              }
+         }
+         Grid smallgrid("projection",smallName,smallMin,smallMax,smallBin,false,false,true,smallIsPeriodic,smallMin,smallMax);  
+         // check that the two grids are commensurate 
+         for(unsigned i=0;i<dimMapping.size();i++){
+              plumed_massert(  (smallgrid.getMax())[i] == (getMax())[dimMapping[i]],  "the two input grids are not compatible in max"   );  
+              plumed_massert(  (smallgrid.getMin())[i] == (getMin())[dimMapping[i]],  "the two input grids are not compatible in min"   );  
+              plumed_massert(  (smallgrid.getNbin())[i]== (getNbin())[dimMapping[i]], "the two input grids are not compatible in bin"   );  
+         }
+         vector<unsigned> toBeIntegrated;
+         for(unsigned i=0;i<getArgNames().size();i++){
+              bool doappend=true;
+         	for(unsigned j=0;j<dimMapping.size();j++){
+                 if(dimMapping[j]==i){doappend=false; break;}  
+              }
+              if(doappend)toBeIntegrated.push_back(i);
+         }
+         //for(unsigned i=0;i<dimMapping.size();i++ ){
+         //     cerr<<"Dimension to preserve "<<dimMapping[i]<<endl;
+         //}
+         //for(unsigned i=0;i<toBeIntegrated.size();i++ ){
+         //     cerr<<"Dimension to integrate "<<toBeIntegrated[i]<<endl;
+         //}
+
+         // loop over all the points in the Grid, find the corresponding fixed index, rotate over all the other ones  
+         for(unsigned i=0;i<smallgrid.getSize();i++){
+                 std::vector<unsigned> v;
+                 v=smallgrid.getIndices(i);
+                 std::vector<int> vHigh((getArgNames()).size(),-1);   
+                 for(unsigned j=0;j<dimMapping.size();j++)vHigh[dimMapping[j]]=int(v[j]);
+                 // the vector vhigh now contains at the beginning the index of the low dimension and -1 for the values that need to be calculated 
+                 double initval=0.;  
+                 projectOnLowDimension(initval,vHigh, ptr2obj); 
+                 // to integrate metadynamics
+                 smallgrid.setValue(i,ptr2obj->projectOuterLoop(initval));  
+         }
+
+     return smallgrid; 
+}
+
 }
diff --git a/src/tools/Grid.h b/src/tools/Grid.h
index 5e6391597..1be7cc0af 100644
--- a/src/tools/Grid.h
+++ b/src/tools/Grid.h
@@ -28,6 +28,36 @@
 
 namespace PLMD{ 
 
+
+// simple function to enable various weighting
+
+class WeightBase{
+    public:
+        virtual double projectInnerLoop(double &input, double &v)=0;
+        virtual double projectOuterLoop(double &v)=0;
+};
+
+class BiasWeight:public WeightBase{
+    public:
+      double beta,invbeta;
+      BiasWeight(double v){beta=v;invbeta=1./beta;};
+      double projectInnerLoop(double &input, double &v){return  input+exp(beta*v);};
+      double projectOuterLoop(double &v){return -invbeta*log(v);};
+};
+
+class ProbWeight:public WeightBase{
+    public:
+      double beta,invbeta;
+      ProbWeight(double v){beta=v;invbeta=1./beta;};
+      double projectInnerLoop(double &input, double &v){return  input+v;};
+      double projectOuterLoop(double &v){return -invbeta*log(v);};
+};
+
+
+
+
+
+
 class Value;
 class IFile;
 class OFile;
@@ -55,11 +85,19 @@ protected:
  virtual void clear();
  
 public:
+ /// this constructor here is Value-aware  
  Grid(const std::string& funcl, std::vector<Value*> args, const std::vector<std::string> & gmin, 
       const std::vector<std::string> & gmax, const std::vector<unsigned> & nbin, bool dospline, 
       bool usederiv, bool doclear=true);
-
-
+ /// this constructor here is not Value-aware  
+ Grid(const std::string& funcl, const std::vector<std::string> &names, const std::vector<std::string> & gmin, 
+      const std::vector<std::string> & gmax, const std::vector<unsigned> & nbin, bool dospline, 
+      bool usederiv, bool doclear, const std::vector<bool> &isperiodic, const std::vector<std::string> &pmin, 
+      const std::vector<std::string> &pmax );
+ /// this is the real initializator  
+ void Init(const std::string & funcl, const std::vector<std::string> &names, const std::vector<std::string> & gmin,
+      const std::vector<std::string> & gmax, const std::vector<unsigned> & nbin, bool dospline, bool usederiv,
+      bool doclear, const std::vector<bool> &isperiodic, const std::vector<std::string> &pmin, const std::vector<std::string> &pmax);
 /// get lower boundary
  std::vector<std::string> getMin() const;
 /// get upper boundary
@@ -74,6 +112,8 @@ public:
  std::vector<bool> getIsPeriodic() const;
 /// get grid dimension
  unsigned getDimension() const;
+/// get argument names  of this grid 
+ std::vector<std::string> getArgNames() const;
  
 /// methods to handle grid indices 
  std::vector<unsigned> getIndices(unsigned index) const;
@@ -127,7 +167,8 @@ public:
  virtual void addValueAndDerivatives(const std::vector<unsigned> & indices, double value, std::vector<double>& der); 
 /// Scale all grid values and derivatives by a constant factor
  virtual void scaleAllValuesAndDerivatives( const double& scalef );
-
+/// apply function: takes  pointer to  function that accepts a double and apply 
+ virtual void applyFunctionAllValuesAndDerivatives( double (*func)(double val), double (*funcder)(double valder) );
 /// add a kernel function to the grid
  void addKernel( const KernelFunctions& kernel );
 
@@ -135,6 +176,11 @@ public:
  virtual void writeToFile(OFile&);
 
  virtual ~Grid(){};
+
+/// project a high dimensional grid onto a low dimensional one: this should be changed at some time 
+/// to enable many types of weighting
+ Grid project( const std::vector<std::string> proj , WeightBase *ptr2obj  ); 
+ void projectOnLowDimension(double &val , std::vector<int> &varHigh, WeightBase* ptr2obj ); 
 };
 
   
@@ -185,7 +231,6 @@ class SparseGrid : public Grid
 
  virtual ~SparseGrid(){};
 };
-
 }
 
 #endif
-- 
GitLab