From ed8d4ad1293d7febd597dc46f59ba11e67d964fe Mon Sep 17 00:00:00 2001
From: Michele Invernizzi <inve.michele@gmail.com>
Date: Tue, 18 Dec 2018 08:34:12 +0100
Subject: [PATCH] restyling for the reweighting c(t) calculation (#401)

* restyling for the reweighting c(t) calculation

* fixed bug in multidimensional grid integration

* fixed regtest for 2D reweighting

* changed reweighting keywords and some doc
---
 regtest/basic/rt67-mpi/plumed.dat |   3 +-
 regtest/basic/rt67/plumed.dat     |   3 +-
 src/bias/MetaD.cpp                | 168 +++++++++++++-----------------
 src/tools/Grid.cpp                |   4 +-
 4 files changed, 77 insertions(+), 101 deletions(-)

diff --git a/regtest/basic/rt67-mpi/plumed.dat b/regtest/basic/rt67-mpi/plumed.dat
index 6eed71d03..f7bdb27d5 100644
--- a/regtest/basic/rt67-mpi/plumed.dat
+++ b/regtest/basic/rt67-mpi/plumed.dat
@@ -13,8 +13,7 @@ METAD ...
  GRID_MIN=-pi,-pi
  GRID_MAX=pi,pi
  GRID_BIN=150,150
- REWEIGHTING_NGRID=150,150
- REWEIGHTING_NHILLS=1
+ CALC_RCT
 ... METAD
 
 PRINT ...
diff --git a/regtest/basic/rt67/plumed.dat b/regtest/basic/rt67/plumed.dat
index 95c5cd685..0a3c0f830 100644
--- a/regtest/basic/rt67/plumed.dat
+++ b/regtest/basic/rt67/plumed.dat
@@ -12,8 +12,7 @@ METAD ...
  GRID_MIN=-pi,-pi
  GRID_MAX=pi,pi
  GRID_BIN=150,150
- REWEIGHTING_NGRID=150,150
- REWEIGHTING_NHILLS=1
+ CALC_RCT
 ... METAD
 
 PRINT ...
diff --git a/src/bias/MetaD.cpp b/src/bias/MetaD.cpp
index 87ed4ed35..fc019fc3b 100644
--- a/src/bias/MetaD.cpp
+++ b/src/bias/MetaD.cpp
@@ -67,7 +67,7 @@ The free energy can be reconstructed from a metadynamics calculation because the
 by:
 
 \f[
-V(\vec{s}) = -F(\vec(s))
+V(\vec{s}) = -F(\vec{s})
 \f]
 
 During post processing the free energy can be calculated in this way using the \ref sum_hills
@@ -135,19 +135,18 @@ for replica exchange methods to be computed correctly.
 Multiple walkers  \cite multiplewalkers can also be used. See below the examples.
 
 
-The c(t) reweighting factor can also be calculated on the fly using the equations
+The \\f$c(t)\\f$ reweighting factor can also be calculated on the fly using the equations
 presented in \cite Tiwary_jp504920s.
-The expression used to calculate c(t) follows directly from using Eq. 12 in
-Eq. 3 in \cite Tiwary_jp504920s and gives smoother results than equivalent Eqs. 13
-and Eqs. 14 in that paper. The c(t) is given by the rct component while the bias
-normalized by c(t) is given by the rbias component (rbias=bias-ct) which can be used
+The expression used to calculate \\f$c(t)\\f$ follows directly from Eq. 3 in \cite Tiwary_jp504920s,
+where \\f$F(\vec{s})=-\gamma/(\gamma-1) V(\vec{s})\\f$.
+This gives smoother results than equivalent Eqs. 13 and Eqs. 14 in that paper.
+The \\f$c(t)\\f$ is given by the rct component while the bias
+normalized by \\f$c(t)\\f$ is given by the rbias component (rbias=bias-rct) which can be used
 to obtain a reweighted histogram.
-The calculation of c(t) is enabled by using the keyword REWEIGHTING_NGRID where the grid used for the
-calculation is specified.   This grid should have a size that is equal or larger than the grid given in GRID_BIN./
-By default c(t) is updated every 50 Gaussian hills but this
-can be changed by using the REWEIGHTING_NHILLS keyword.
-This option can only be employed together with Well-Tempered Metadynamics and requires that
-a grid is used.
+The calculation of \\f$c(t)\\f$ is enabled by using the keyword CALC_RCT.
+By default \\f$c(t)\\f$ is updated every time the bias changes, but if this slows down the simulation
+the keyword RCT_USTRIDE can be set to a value higher than 1.
+This option requires that a grid is used.
 
 Additional material and examples can be also found in the tutorials:
 
@@ -234,26 +233,25 @@ one update and the other. Since version 2.2.5, hills files are automatically
 flushed every WALKERS_RSTRIDE steps.
 
 \par
-The c(t) reweighting factor can be calculated on the fly using the equations
+The \\f$c(t)\\f$ reweighting factor can be calculated on the fly using the equations
 presented in \cite Tiwary_jp504920s as described above.
-This is enabled by using the keyword REWEIGHTING_NGRID where the grid used for
-the calculation is set. The number of grid points given in REWEIGHTING_NGRID
-should be equal or larger than the number of grid points given in GRID_BIN.
+This is enabled by using the keyword CALC_RCT,
+and can be done only if the bias is defined on a grid.
 \plumedfile
 METAD ...
  LABEL=metad
  ARG=phi,psi SIGMA=0.20,0.20 HEIGHT=1.20 BIASFACTOR=5 TEMP=300.0 PACE=500
  GRID_MIN=-pi,-pi GRID_MAX=pi,pi GRID_BIN=150,150
- REWEIGHTING_NGRID=150,150
- REWEIGHTING_NHILLS=20
+ CALC_RCT
+ RCT_USTRIDE=10
 ... METAD
 \endplumedfile
-Here we have asked that the calculation is performed every 20 hills by using
-REWEIGHTING_NHILLS keyword. If this keyword is not given the calculation will
-by default be performed every 50 hills. The c(t) reweighting factor will be given
+Here we have asked that the calculation is performed every 10 hills deposition by using
+RCT_USTRIDE keyword. If this keyword is not given, the calculation will
+by default be performed every time the bias changes. The \\f$c(t)\\f$ reweighting factor will be given
 in the rct component while the instantaneous value of the bias potential
-normalized using the c(t) reweighting factor is given in the rbias component
-[rbias=bias-c(t)] which can be used to obtain a reweighted histogram or
+normalized using the \\f$c(t)\\f$ reweighting factor is given in the rbias component
+[rbias=bias-rct] which can be used to obtain a reweighted histogram or
 free energy surface using the \ref HISTOGRAM analysis.
 
 \par
@@ -444,9 +442,9 @@ private:
   double lowI_;
   bool doInt_;
   bool isFirstStep;
-  double reweight_factor;
-  vector<unsigned> rewf_grid_;
-  unsigned rewf_ustride_;
+  bool calc_rct_;
+  double reweight_factor_;
+  unsigned rct_ustride_;
   double work_;
   long int last_step_warn_grid;
 
@@ -480,9 +478,9 @@ PLUMED_REGISTER_ACTION(MetaD,"METAD")
 
 void MetaD::registerKeywords(Keywords& keys) {
   Bias::registerKeywords(keys);
-  keys.addOutputComponent("rbias","REWEIGHTING_NGRID","the instantaneous value of the bias normalized using the \\f$c(t)\\f$ reweighting factor [rbias=bias-c(t)]."
+  keys.addOutputComponent("rbias","CALC_RCT","the instantaneous value of the bias normalized using the \\f$c(t)\\f$ reweighting factor [rbias=bias-rct]."
                           "This component can be used to obtain a reweighted histogram.");
-  keys.addOutputComponent("rct","REWEIGHTING_NGRID","the reweighting factor \\f$c(t)\\f$.");
+  keys.addOutputComponent("rct","CALC_RCT","the reweighting factor \\f$c(t)\\f$.");
   keys.addOutputComponent("work","default","accumulator for work");
   keys.addOutputComponent("acc","ACCELERATION","the metadynamics acceleration factor");
   keys.addOutputComponent("maxbias", "CALC_MAX_BIAS", "the maximum of the metadynamics V(s, t)");
@@ -507,12 +505,10 @@ void MetaD::registerKeywords(Keywords& keys) {
   keys.add("optional","GRID_MAX","the upper bounds for the grid");
   keys.add("optional","GRID_BIN","the number of bins for the grid");
   keys.add("optional","GRID_SPACING","the approximate grid spacing (to be used as an alternative or together with GRID_BIN)");
-  keys.add("optional","REWEIGHTING_NGRID","calculate the c(t) reweighting factor and use that to obtain the normalized bias [rbias=bias-c(t)]."
-           "Here you should specify the number of grid points required in each dimension."
-           "The number of grid points should be equal or larger to the number of grid points given in GRID_BIN."
-           "This method is not compatible with metadynamics not on a grid.");
-  keys.add("optional","REWEIGHTING_NHILLS","how many Gaussian hills should be deposited between calculating the c(t) reweighting factor."
-           "The default is to do this every 50 hills.");
+  keys.addFlag("CALC_RCT",false,"calculate the \\f$c(t)\\f$ reweighting factor and use that to obtain the normalized bias [rbias=bias-rct]."
+               "This method is not compatible with metadynamics not on a grid.");
+  keys.add("optional","RCT_USTRIDE","the update stride for calculating the \\f$c(t)\\f$ reweighting factor."
+           "The default 1, so \\f$c(t)\\f$ is updated every time the bias is updated.");
   keys.addFlag("GRID_SPARSE",false,"use a sparse grid to store hills");
   keys.addFlag("GRID_NOSPLINE",false,"don't use spline interpolation with grids");
   keys.add("optional","GRID_WSTRIDE","write the grid to a file every N steps");
@@ -579,8 +575,9 @@ MetaD::MetaD(const ActionOptions& ao):
 // Interval initialization
   uppI_(-1), lowI_(-1), doInt_(false),
   isFirstStep(true),
-  reweight_factor(0.0),
-  rewf_ustride_(1),
+  calc_rct_(false),
+  reweight_factor_(0.0),
+  rct_ustride_(1),
   work_(0),
   last_step_warn_grid(0)
 {
@@ -781,18 +778,12 @@ MetaD::MetaD(const ActionOptions& ao):
   if(!grid_&&gridfilename_.length()> 0) error("To write a grid you need first to define it!");
   if(!grid_&&gridreadfilename_.length()>0) error("To read a grid you need first to define it!");
 
-  if(grid_) {
-    parseVector("REWEIGHTING_NGRID",rewf_grid_);
-    if(rewf_grid_.size()>0 && rewf_grid_.size()!=getNumberOfArguments()) {
-      error("size mismatch for REWEIGHTING_NGRID keyword");
-    } else if(rewf_grid_.size()==getNumberOfArguments()) {
-      for(unsigned j=0; j<getNumberOfArguments(); ++j) {
-        if( !getPntrToArgument(j)->isPeriodic() ) rewf_grid_[j] += 1;
-      }
-    }
-    if(adaptive_==FlexibleBin::diffusion || adaptive_==FlexibleBin::geometry) warning("reweighting has not been proven to work with adaptive Gaussians");
-    rewf_ustride_=50; parse("REWEIGHTING_NHILLS",rewf_ustride_);
-  }
+  // Reweighting factor rct
+  parseFlag("CALC_RCT",calc_rct_);
+  if (calc_rct_)
+    plumed_massert(grid_,"CALC_RCT is supported only if bias is on a grid");
+  parse("RCT_USTRIDE",rct_ustride_);
+
   if(dampfactor_>0.0) {
     if(!grid_) error("With DAMPFACTOR you should use grids");
   }
@@ -968,11 +959,11 @@ MetaD::MetaD(const ActionOptions& ao):
     log.printf("  Flying Gaussian method with %d walkers active\n",mpi_nw_);
   }
 
-  if( rewf_grid_.size()>0 ) {
+  if(calc_rct_) {
     addComponent("rbias"); componentIsNotPeriodic("rbias");
     addComponent("rct"); componentIsNotPeriodic("rct");
-    log.printf("  the c(t) reweighting factor will be calculated every %u hills\n",rewf_ustride_);
-    getPntrToComponent("rct")->set(reweight_factor);
+    log.printf("  The c(t) reweighting factor will be calculated every %u hills\n",rct_ustride_);
+    getPntrToComponent("rct")->set(reweight_factor_);
   }
   addComponent("work"); componentIsNotPeriodic("work");
 
@@ -1213,7 +1204,7 @@ MetaD::MetaD(const ActionOptions& ao):
   }
 
   // Calculate the Tiwary-Parrinello reweighting factor if we are restarting from previous hills
-  if(getRestart() && rewf_grid_.size()>0 ) computeReweightingFactor();
+  if(getRestart() && calc_rct_) computeReweightingFactor();
   // Calculate all special bias quantities desired if restarting with nonzero bias.
   if(getRestart() && calc_max_bias_) {
     max_bias_ = BiasGrid_->getMaxValue();
@@ -1287,8 +1278,8 @@ MetaD::MetaD(const ActionOptions& ao):
                     "Baftizadeh, Cossio, Pietrucci, and Laio, Curr. Phys. Chem. 2, 79 (2012)");
   if(acceleration) log<<plumed.cite(
                           "Pratyush and Parrinello, Phys. Rev. Lett. 111, 230602 (2013)");
-  if(rewf_grid_.size()>0) log<<plumed.cite(
-                                 "Pratyush and Parrinello, J. Phys. Chem. B, 119, 736 (2015)");
+  if(calc_rct_) log<<plumed.cite(
+                       "Pratyush and Parrinello, J. Phys. Chem. B, 119, 736 (2015)");
   if(concurrent || rect_biasf_.size()>0) log<<plumed.cite(
           "Gil-Ley and Bussi, J. Chem. Theory Comput. 11, 1077 (2015)");
   if(rect_biasf_.size()>0 && walkers_mpi) log<<plumed.cite(
@@ -1684,7 +1675,7 @@ void MetaD::calculate()
   }
 
   setBias(ene);
-  if( rewf_grid_.size()>0 ) getPntrToComponent("rbias")->set(ene - reweight_factor);
+  if(calc_rct_) getPntrToComponent("rbias")->set(ene - reweight_factor_);
   // calculate the acceleration factor
   if(acceleration&&!isFirstStep) {
     acc += static_cast<double>(getStride()) * exp(ene/(kbt_));
@@ -1845,12 +1836,12 @@ void MetaD::update() {
   }
   // Recalculate special bias quantities whenever the bias has been changed by the update.
   bool bias_has_changed = (nowAddAHill || (mw_n_ > 1 && getStep() % mw_rstride_ == 0));
-  if(getStep()%(stride_*rewf_ustride_)==0 && nowAddAHill && rewf_grid_.size()>0 ) computeReweightingFactor();
+  if (calc_rct_ && bias_has_changed && getStep()%(stride_*rct_ustride_)==0) computeReweightingFactor();
   if (calc_max_bias_ && bias_has_changed) {
     max_bias_ = BiasGrid_->getMaxValue();
     getPntrToComponent("maxbias")->set(max_bias_);
   }
-  if (calc_transition_bias_ && (nowAddAHill || (mw_n_ > 1 && getStep() % mw_rstride_ == 0))) {
+  if (calc_transition_bias_ && bias_has_changed) {
     transition_bias_ = getTransitionBarrierBias();
     getPntrToComponent("transbias")->set(transition_bias_);
   }
@@ -1934,48 +1925,35 @@ bool MetaD::scanOneHill(IFile *ifile,  vector<Value> &tmpvalues, vector<double>
 
 void MetaD::computeReweightingFactor()
 {
-  if( !welltemp_ ) error("cannot compute the c(t) reweighting factors for non well-tempered metadynamics");
-
-  if(biasf_==1.0) {
-// in this case we have no bias, so reweight factor is 0.0
+  if(biasf_==1.0) { // in this case we have no bias, so reweight factor is 0.0
     getPntrToComponent("rct")->set(0.0);
     return;
   }
 
-  // Recover the minimum values for the grid
-  unsigned ncv=getNumberOfArguments();
-  unsigned ntotgrid=1;
-  std::vector<double> dmin( ncv ),dmax( ncv ), grid_spacing( ncv ), vals( ncv );
-  for(unsigned j=0; j<ncv; ++j) {
-    Tools::convert( BiasGrid_->getMin()[j], dmin[j] );
-    Tools::convert( BiasGrid_->getMax()[j], dmax[j] );
-    grid_spacing[j] = ( dmax[j] - dmin[j] ) / static_cast<double>( rewf_grid_[j] );
-    if( !getPntrToArgument(j)->isPeriodic() ) dmax[j] += grid_spacing[j];
-    ntotgrid *= rewf_grid_[j];
-  }
-
-  // Now sum over whole grid
-  reweight_factor=0.0;
-  std::unique_ptr<double[]> der(new double[ncv]);
-  std::vector<unsigned> t_index( ncv );
-  double sum1=0.0; double sum2=0.0;
-  double afactor = biasf_ / (kbt_*(biasf_-1.0)); double afactor2 = 1.0 / (kbt_*(biasf_-1.0));
-  unsigned rank=comm.Get_rank(), stride=comm.Get_size();
-  for(unsigned i=rank; i<ntotgrid; i+=stride) {
-    t_index[0]=(i%rewf_grid_[0]);
-    unsigned kk=i;
-    for(unsigned j=1; j<ncv-1; ++j) { kk=(kk-t_index[j-1])/rewf_grid_[j-1]; t_index[j]=(kk%rewf_grid_[j]); }
-    if( ncv>=2 ) t_index[ncv-1]=((kk-t_index[ncv-2])/rewf_grid_[ncv-2]);
-
-    for(unsigned j=0; j<ncv; ++j) vals[j]=dmin[j] + t_index[j]*grid_spacing[j];
-
-    double currentb=getBiasAndDerivatives(vals,der.get());
-    sum1 += exp( afactor*currentb );
-    sum2 += exp( afactor2*currentb );
-  }
-  comm.Sum( sum1 ); comm.Sum( sum2 );
-  reweight_factor = kbt_ * std::log( sum1/sum2 );
-  getPntrToComponent("rct")->set(reweight_factor);
+  double Z_0=0; //proportional to the integral of exp(-beta*F)
+  double Z_V=0; //proportional to the integral of exp(-beta*(F+V))
+  double minusBetaF=biasf_/(biasf_-1.)/kbt_;
+  double minusBetaFplusV=1./(biasf_-1.)/kbt_;
+  if (biasf_==-1.0) { //non well-tempered case
+    minusBetaF=1;
+    minusBetaFplusV=0;
+  }
+  const double big_number=minusBetaF*BiasGrid_->getMaxValue(); //to avoid exp overflow
+
+  const unsigned rank=comm.Get_rank();
+  const unsigned stride=comm.Get_size();
+  for (Grid::index_t t=rank; t<BiasGrid_->getSize(); t+=stride) {
+    const double val=BiasGrid_->getValue(t);
+    Z_0+=std::exp(minusBetaF*val-big_number);
+    Z_V+=std::exp(minusBetaFplusV*val-big_number);
+  }
+  if (stride>1) {
+    comm.Sum(Z_0);
+    comm.Sum(Z_V);
+  }
+
+  reweight_factor_=kbt_*std::log(Z_0/Z_V);
+  getPntrToComponent("rct")->set(reweight_factor_);
 }
 
 double MetaD::getTransitionBarrierBias() {
diff --git a/src/tools/Grid.cpp b/src/tools/Grid.cpp
index 3548707e4..0f9bc89d5 100644
--- a/src/tools/Grid.cpp
+++ b/src/tools/Grid.cpp
@@ -989,8 +989,8 @@ double Grid::integrate( std::vector<unsigned>& npoints ) {
   for(unsigned i=0; i<ntotgrid; ++i) {
     t_index[0]=(i%npoints[0]);
     unsigned kk=i;
-    for(unsigned j=1; j<dimension_-1; ++j) { kk=(kk-t_index[j-1])/npoints[i-1]; t_index[j]=(kk%npoints[i]); }
-    if( dimension_>=2 ) t_index[dimension_-1]=((kk-t_index[dimension_-1])/npoints[dimension_-2]);
+    for(unsigned j=1; j<dimension_-1; ++j) { kk=(kk-t_index[j-1])/npoints[j-1]; t_index[j]=(kk%npoints[j]); }
+    if( dimension_>=2 ) t_index[dimension_-1]=((kk-t_index[dimension_-2])/npoints[dimension_-2]);
 
     for(unsigned j=0; j<dimension_; ++j) vals[j]=min_[j] + t_index[j]*ispacing[j];
 
-- 
GitLab