LCOV - code coverage report
Current view: top level - opes - ECVmultiThermal.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 106 111 95.5 %
Date: 2024-10-18 13:59:31 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2020-2021 of Michele Invernizzi.
       3             : 
       4             :    This file is part of the OPES plumed module.
       5             : 
       6             :    The OPES plumed module is free software: you can redistribute it and/or modify
       7             :    it under the terms of the GNU Lesser General Public License as published by
       8             :    the Free Software Foundation, either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    The OPES plumed module is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU Lesser General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU Lesser General Public License
      17             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      18             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      19             : #include "ExpansionCVs.h"
      20             : #include "core/ActionRegister.h"
      21             : 
      22             : namespace PLMD {
      23             : namespace opes {
      24             : 
      25             : //+PLUMEDOC OPES_EXPANSION_CV ECV_MULTITHERMAL
      26             : /*
      27             : Expand a simulation to sample multiple temperatures simultaneously.
      28             : 
      29             : The internal energy \f$U\f$ of of the system should be used as ARG.
      30             : \f[
      31             :   \Delta u_{\beta'}=(\beta'-\beta) U\, ,
      32             : \f]
      33             : where \f$\beta'\f$ are the temperatures to be sampled and \f$\beta\f$ is the temperature at which the simulation is conducted.
      34             : In case of fixed volume, the internal energy is simply the potential energy given by the \ref ENERGY colvar\f$U=E\f$, and you will run a multicanonical simulation.
      35             : If instead the simulation is at fixed pressure \f$p\f$, the contribution of the volume must be added \f$U=E+pV\f$ (see example below).
      36             : 
      37             : By defauly the needed steps in temperatures are automatically guessed from few initial unbiased MD steps, as descibed in \cite Invernizzi2020unified.
      38             : Otherwise you can manually set this number with TEMP_STEPS.
      39             : In both cases the steps will be geometrically spaced in temperature.
      40             : Use instead the keyword NO_GEOM_SPACING for a linear spacing in the inverse temperature (beta), that typically increases the focus on lower temperatures.
      41             : Finally, you can use instead the keyword TEMP_SET_ALL and explicitly provide each temperature.
      42             : 
      43             : You can reweight the resulting simulation at any temperature in the chosen range, using e.g. \ref REWEIGHT_TEMP_PRESS.
      44             : A similar target distribution can be sampled using \ref TD_MULTICANONICAL.
      45             : 
      46             : 
      47             : \par Examples
      48             : 
      49             : Fixed volume, multicanonical simulation:
      50             : 
      51             : \plumedfile
      52             : ene: ENERGY
      53             : ecv: ECV_MULTITHERMAL ARG=ene TEMP=300 TEMP_MIN=300 TEMP_MAX=800
      54             : opes: OPES_EXPANDED ARG=ecv.ene PACE=500
      55             : \endplumedfile
      56             : 
      57             : which, if your MD code passes the temperature to PLUMED, is equivalent to:
      58             : 
      59             : \plumedfile
      60             : ene: ENERGY
      61             : ecv: ECV_MULTITHERMAL ARG=ene TEMP_MAX=800
      62             : opes: OPES_EXPANDED ARG=ecv.ene PACE=500
      63             : \endplumedfile
      64             : 
      65             : If instead the pressure is fixed and the volume changes, you shuld calculate the internal energy first, \f$U=E+pV\f$
      66             : 
      67             : \plumedfile
      68             : ene: ENERGY
      69             : vol: VOLUME
      70             : intEne: CUSTOM PERIODIC=NO ARG=ene,vol FUNC=x+0.06022140857*y
      71             : ecv: ECV_MULTITHERMAL ARG=intEne TEMP_MAX=800
      72             : opes: OPES_EXPANDED ARG=ecv.intEne PACE=500
      73             : \endplumedfile
      74             : 
      75             : Notice that \f$p=0.06022140857\f$ corresponds to 1 bar when using the default PLUMED units.
      76             : 
      77             : */
      78             : //+ENDPLUMEDOC
      79             : 
      80             : class ECVmultiThermal :
      81             :   public ExpansionCVs
      82             : {
      83             : private:
      84             :   bool todoAutomatic_;
      85             :   bool geom_spacing_;
      86             :   std::vector<double> ECVs_;
      87             :   std::vector<double> derECVs_; //(beta_k-beta0) or (temp0/temp_k-1)/kbt
      88             :   void initECVs();
      89             : 
      90             : public:
      91             :   explicit ECVmultiThermal(const ActionOptions&);
      92             :   static void registerKeywords(Keywords& keys);
      93             :   void calculateECVs(const double *) override;
      94             :   const double * getPntrToECVs(unsigned) override;
      95             :   const double * getPntrToDerECVs(unsigned) override;
      96             :   std::vector<std::string> getLambdas() const override;
      97             :   void initECVs_observ(const std::vector<double>&,const unsigned,const unsigned) override;
      98             :   void initECVs_restart(const std::vector<std::string>&) override;
      99             : };
     100             : 
     101             : PLUMED_REGISTER_ACTION(ECVmultiThermal,"ECV_MULTITHERMAL")
     102             : 
     103          13 : void ECVmultiThermal::registerKeywords(Keywords& keys)
     104             : {
     105          13 :   ExpansionCVs::registerKeywords(keys);
     106          26 :   keys.addInputKeyword("compulsory","ARG","scalar","the label of the internal energy of the system. If volume is fixed it is calculated by the ENERGY colvar");
     107          26 :   keys.add("optional","TEMP_MIN","the minimum of the temperature range");
     108          26 :   keys.add("optional","TEMP_MAX","the maximum of the temperature range");
     109          26 :   keys.add("optional","TEMP_STEPS","the number of steps in temperature");
     110          26 :   keys.add("optional","TEMP_SET_ALL","manually set all the temperatures");
     111          26 :   keys.addFlag("NO_GEOM_SPACING",false,"do not use geometrical spacing in temperature, but instead linear spacing in inverse temperature");
     112          13 : }
     113             : 
     114          11 : ECVmultiThermal::ECVmultiThermal(const ActionOptions&ao)
     115             :   : Action(ao)
     116             :   , ExpansionCVs(ao)
     117          11 :   , todoAutomatic_(false)
     118             : {
     119          11 :   plumed_massert(getNumberOfArguments()==1,"only the internal energy should be given as ARG");
     120             : 
     121             : //set temp0
     122          11 :   const double temp0=kbt_/getKBoltzmann();
     123             : 
     124             : //parse temp range
     125          11 :   double temp_min=-1;
     126          11 :   double temp_max=-1;
     127          11 :   parse("TEMP_MIN",temp_min);
     128          11 :   parse("TEMP_MAX",temp_max);
     129          11 :   unsigned temp_steps=0;
     130          22 :   parse("TEMP_STEPS",temp_steps);
     131             :   std::vector<double> temps;
     132          11 :   parseVector("TEMP_SET_ALL",temps);
     133          11 :   parseFlag("NO_GEOM_SPACING",geom_spacing_);
     134          11 :   geom_spacing_=!geom_spacing_;
     135             : 
     136          11 :   checkRead();
     137             : 
     138             : //set the intermediate temperatures
     139          11 :   if(temps.size()>0)
     140             :   {
     141           2 :     plumed_massert(temp_steps==0,"cannot set both TEMP_STEPS and TEMP_SET_ALL");
     142           2 :     plumed_massert(temp_min==-1 && temp_max==-1,"cannot set both TEMP_SET_ALL and TEMP_MIN/MAX");
     143           2 :     plumed_massert(temps.size()>=2,"set at least 2 temperatures");
     144           2 :     temp_min=temps[0];
     145           2 :     temp_max=temps[temps.size()-1];
     146           2 :     derECVs_.resize(temps.size());
     147          10 :     for(unsigned k=0; k<derECVs_.size(); k++)
     148             :     {
     149           8 :       derECVs_[k]=(temp0/temps[k]-1.)/kbt_;
     150           8 :       if(k<derECVs_.size()-1)
     151           6 :         plumed_massert(temps[k]<=temps[k+1],"TEMP_SET_ALL must be properly ordered");
     152             :     }
     153             :   }
     154             :   else
     155             :   { //get TEMP_MIN and TEMP_MAX
     156           9 :     plumed_massert(temp_min!=-1 || temp_max!=-1,"TEMP_MIN, TEMP_MAX or both, should be set");
     157           9 :     if(temp_min==-1)
     158             :     {
     159           2 :       temp_min=temp0;
     160           2 :       log.printf("  no TEMP_MIN provided, using TEMP_MIN=TEMP\n");
     161             :     }
     162           9 :     if(temp_max==-1)
     163             :     {
     164           0 :       temp_max=temp0;
     165           0 :       log.printf("  no TEMP_MAX provided, using TEMP_MAX=TEMP\n");
     166             :     }
     167           9 :     plumed_massert(temp_max>=temp_min,"TEMP_MAX should be bigger than TEMP_MIN");
     168           9 :     derECVs_.resize(2);
     169           9 :     derECVs_[0]=(temp0/temp_min-1.)/kbt_;
     170           9 :     derECVs_[1]=(temp0/temp_max-1.)/kbt_;
     171           9 :     if(temp_min==temp_max && temp_steps==0)
     172           0 :       temp_steps=1;
     173           9 :     if(temp_steps>0)
     174          14 :       derECVs_=getSteps(derECVs_[0],derECVs_[1],temp_steps,"TEMP",geom_spacing_,1./kbt_);
     175             :     else
     176           2 :       todoAutomatic_=true;
     177             :   }
     178             :   const double tol=1e-3; //if temp is taken from MD engine it might be numerically slightly different
     179          11 :   if(temp0<(1-tol)*temp_min || temp0>(1+tol)*temp_max)
     180           0 :     log.printf(" +++ WARNING +++ running at TEMP=%g which is outside the chosen temperature range\n",temp0);
     181             : 
     182             : //print some info
     183          11 :   log.printf("  targeting a temperature range from TEMP_MIN=%g to TEMP_MAX=%g\n",temp_min,temp_max);
     184          11 :   if(!geom_spacing_)
     185           1 :     log.printf(" -- NO_GEOM_SPACING: inverse temperatures will be linearly spaced\n");
     186          11 : }
     187             : 
     188         586 : void ECVmultiThermal::calculateECVs(const double * ene)
     189             : {
     190        3618 :   for(unsigned k=0; k<derECVs_.size(); k++)
     191        3032 :     ECVs_[k]=derECVs_[k]*ene[0];
     192             : // derivatives never change: derECVs_k=(beta_k-beta0)
     193         586 : }
     194             : 
     195          11 : const double * ECVmultiThermal::getPntrToECVs(unsigned j)
     196             : {
     197          11 :   plumed_massert(isReady_,"cannot access ECVs before initialization");
     198          11 :   plumed_massert(j==0,getName()+" has only one CV, the ENERGY");
     199          11 :   return &ECVs_[0];
     200             : }
     201             : 
     202          11 : const double * ECVmultiThermal::getPntrToDerECVs(unsigned j)
     203             : {
     204          11 :   plumed_massert(isReady_,"cannot access ECVs before initialization");
     205          11 :   plumed_massert(j==0,getName()+" has only one CV, the ENERGY");
     206          11 :   return &derECVs_[0];
     207             : }
     208             : 
     209          11 : std::vector<std::string> ECVmultiThermal::getLambdas() const
     210             : {
     211          11 :   plumed_massert(!todoAutomatic_,"cannot access lambdas before initializing them");
     212          11 :   const double temp0=kbt_/getKBoltzmann();
     213          11 :   std::vector<std::string> lambdas(derECVs_.size());
     214          70 :   for(unsigned k=0; k<derECVs_.size(); k++)
     215             :   {
     216          59 :     std::ostringstream subs;
     217          59 :     subs<<temp0/(derECVs_[k]*kbt_+1); //temp_k
     218          59 :     lambdas[k]=subs.str();
     219          59 :   }
     220          11 :   return lambdas;
     221           0 : }
     222             : 
     223          11 : void ECVmultiThermal::initECVs()
     224             : {
     225          11 :   plumed_massert(!isReady_,"initialization should not be called twice");
     226          11 :   plumed_massert(!todoAutomatic_,"this should not happen");
     227          11 :   totNumECVs_=derECVs_.size();
     228          11 :   ECVs_.resize(derECVs_.size());
     229          11 :   isReady_=true;
     230          11 :   log.printf("  *%4lu temperatures for %s\n",derECVs_.size(),getName().c_str());
     231          11 : }
     232             : 
     233           8 : void ECVmultiThermal::initECVs_observ(const std::vector<double>& all_obs_cvs,const unsigned ncv,const unsigned index_j)
     234             : {
     235           8 :   if(todoAutomatic_) //estimate the steps in beta from observations
     236             :   {
     237           1 :     plumed_massert(all_obs_cvs.size()%ncv==0 && index_j<ncv,"initECVs_observ parameters are inconsistent");
     238           1 :     std::vector<double> obs_ene(all_obs_cvs.size()/ncv); //copy only useful observation (would be better not to copy...)
     239          11 :     for(unsigned t=0; t<obs_ene.size(); t++)
     240          10 :       obs_ene[t]=all_obs_cvs[t*ncv+index_j];
     241           1 :     const unsigned temp_steps=estimateNumSteps(derECVs_[0],derECVs_[1],obs_ene,"TEMP");
     242           1 :     log.printf("    (spacing is in beta, not in temperature)\n");
     243           2 :     derECVs_=getSteps(derECVs_[0],derECVs_[1],temp_steps,"TEMP",geom_spacing_,1./kbt_);
     244           1 :     todoAutomatic_=false;
     245             :   }
     246           8 :   initECVs();
     247           8 :   calculateECVs(&all_obs_cvs[index_j]);
     248           8 : }
     249             : 
     250           3 : void ECVmultiThermal::initECVs_restart(const std::vector<std::string>& lambdas)
     251             : {
     252           3 :   std::size_t pos=lambdas[0].find("_");
     253           3 :   plumed_massert(pos==std::string::npos,"this should not happen, only one CV is used in "+getName());
     254           3 :   if(todoAutomatic_)
     255             :   {
     256           2 :     derECVs_=getSteps(derECVs_[0],derECVs_[1],lambdas.size(),"TEMP",geom_spacing_,1./kbt_);
     257           1 :     todoAutomatic_=false;
     258             :   }
     259           3 :   std::vector<std::string> myLambdas=getLambdas();
     260           3 :   plumed_massert(myLambdas.size()==lambdas.size(),"RESTART - mismatch in number of "+getName());
     261           3 :   plumed_massert(std::equal(myLambdas.begin(),myLambdas.end(),lambdas.begin()),"RESTART - mismatch in lambda values of "+getName());
     262             : 
     263           3 :   initECVs();
     264           3 : }
     265             : 
     266             : }
     267             : }

Generated by: LCOV version 1.16