LCOV - code coverage report
Current view: top level - opes - ECVcustom.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 93 101 92.1 %
Date: 2024-10-11 08:09:47 Functions: 13 14 92.9 %

          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_CUSTOM
      26             : /*
      27             : Use some given CVs as a set of expansion collective variables (ECVs).
      28             : 
      29             : This can be useful e.g. for quickly testing new ECVs, but from a performance point of view it is probably better to implement a new ECV class.
      30             : 
      31             : By default the ARGs are expeted to be energies, \f$\Delta U_i\f$, and are then multiplied by the inverse temperature \f$\beta\f$
      32             : \f[
      33             :   \Delta u_i=\beta \Delta U_i\, .
      34             : \f]
      35             : Use the DIMENSIONLESS flag to avoid this multiplication.
      36             : 
      37             : The flag ADD_P0 adds also the unbiased distribution to the target.
      38             : It is possible to specify a BARRIER as in \ref ECV_UMBRELLAS_LINE, to avoid a too high initial bias.
      39             : 
      40             : \par Examples
      41             : 
      42             : \plumedfile
      43             : ene: ENERGY
      44             : t1: CUSTOM PERIODIC=NO ARG=ene FUNC=(300/500-1)*x
      45             : t2: CUSTOM PERIODIC=NO ARG=ene FUNC=(300/1000-1)*x
      46             : ecv: ECV_CUSTOM ARG=t1,t2 TEMP=300 ADD_P0
      47             : opes: OPES_EXPANDED ARG=ecv.* PACE=500
      48             : \endplumedfile
      49             : 
      50             : It is equivalent to the following:
      51             : 
      52             : \plumedfile
      53             : ene: ENERGY
      54             : ecv: ECV_MULTITHERMAL ARG=ene TEMP=300 TEMP_SET_ALL=300,500,1000
      55             : opes: OPES_EXPANDED ARG=ecv.* PACE=500
      56             : \endplumedfile
      57             : 
      58             : */
      59             : //+ENDPLUMEDOC
      60             : 
      61             : class ECVcustom :
      62             :   public ExpansionCVs
      63             : {
      64             : private:
      65             :   unsigned P0_contribution_;
      66             :   double barrier_;
      67             :   double beta0_;
      68             : 
      69             :   std::vector< std::vector<double> > ECVs_;
      70             :   std::vector< std::vector<double> > derECVs_;
      71             :   void initECVs();
      72             : 
      73             : public:
      74             :   explicit ECVcustom(const ActionOptions&);
      75             :   static void registerKeywords(Keywords& keys);
      76             :   void calculateECVs(const double *) override;
      77             :   const double * getPntrToECVs(unsigned) override;
      78             :   const double * getPntrToDerECVs(unsigned) override;
      79             :   std::vector< std::vector<unsigned> > getIndex_k() const override;
      80             :   std::vector<std::string> getLambdas() const override;
      81             :   void initECVs_observ(const std::vector<double>&,const unsigned,const unsigned) override;
      82             :   void initECVs_restart(const std::vector<std::string>&) override;
      83             : };
      84             : 
      85       10423 : PLUMED_REGISTER_ACTION(ECVcustom,"ECV_CUSTOM")
      86             : 
      87           3 : void ECVcustom::registerKeywords(Keywords& keys)
      88             : {
      89           3 :   ExpansionCVs::registerKeywords(keys);
      90           3 :   keys.remove("ARG");
      91           6 :   keys.add("compulsory","ARG","the labels of the single ECVs, \\f$\\Delta U_i\\f$, in energy units");
      92           6 :   keys.addFlag("ADD_P0",false,"add the unbiased Boltzmann distribution to the target distribution, to make sure to sample it");
      93           6 :   keys.addFlag("DIMENSIONLESS",false,"consider ARG as dimensionless rather than an energy, thus do not multiply it by \\f$\\beta\\f$");
      94           6 :   keys.add("optional","BARRIER","a guess of the free energy barrier to be overcome (better to stay higher than lower)");
      95           3 : }
      96             : 
      97           2 : ECVcustom::ECVcustom(const ActionOptions&ao)
      98             :   : Action(ao)
      99             :   , ExpansionCVs(ao)
     100           2 :   , beta0_(1./kbt_)
     101             : {
     102             : //set beta0_
     103             :   bool dimensionless;
     104           2 :   parseFlag("DIMENSIONLESS",dimensionless);
     105           2 :   if(dimensionless)
     106           0 :     beta0_=1;
     107             : 
     108             : //set P0_contribution_
     109           2 :   bool add_P0=false;
     110           2 :   parseFlag("ADD_P0",add_P0);
     111           2 :   if(add_P0)
     112           2 :     P0_contribution_=1;
     113             :   else
     114           0 :     P0_contribution_=0;
     115             : 
     116             : //set barrier_
     117           2 :   barrier_=std::numeric_limits<double>::infinity();
     118           2 :   parse("BARRIER",barrier_);
     119             : 
     120           2 :   checkRead();
     121             : 
     122             : //set ECVs stuff
     123           2 :   totNumECVs_=getNumberOfArguments()+P0_contribution_;
     124           2 :   ECVs_.resize(getNumberOfArguments(),std::vector<double>(totNumECVs_));
     125           2 :   derECVs_.resize(getNumberOfArguments(),std::vector<double>(totNumECVs_));
     126           6 :   for(unsigned j=0; j<getNumberOfArguments(); j++)
     127           4 :     derECVs_[j][j+P0_contribution_]=beta0_; //always constant
     128             : 
     129             : //print some info
     130           2 :   if(dimensionless)
     131           0 :     log.printf(" -- DIMENSIONLESS: the ARG is not multiplied by beta\n");
     132           2 :   if(barrier_!=std::numeric_limits<double>::infinity())
     133             :   {
     134           0 :     log.printf("  guess for free energy BARRIER = %g\n",barrier_);
     135           0 :     if(dimensionless)
     136           0 :       log.printf("    also the BARRIER is considered to be DIMENSIONLESS\n");
     137             :   }
     138           2 :   if(P0_contribution_==1)
     139           2 :     log.printf(" -- ADD_P0: the target includes also the unbiased probability itself\n");
     140           2 : }
     141             : 
     142          91 : void ECVcustom::calculateECVs(const double * cv)
     143             : {
     144         273 :   for(unsigned j=0; j<getNumberOfArguments(); j++)
     145         182 :     ECVs_[j][j+P0_contribution_]=beta0_*cv[j];
     146             :   //derivative is constant
     147          91 : }
     148             : 
     149           4 : const double * ECVcustom::getPntrToECVs(unsigned j)
     150             : {
     151           4 :   plumed_massert(isReady_,"cannot access ECVs before initialization");
     152           4 :   plumed_massert(j<getNumberOfArguments(),getName()+" has fewer CVs");
     153           4 :   return &ECVs_[j][0];
     154             : }
     155             : 
     156           4 : const double * ECVcustom::getPntrToDerECVs(unsigned j)
     157             : {
     158           4 :   plumed_massert(isReady_,"cannot access ECVs before initialization");
     159           4 :   plumed_massert(j<getNumberOfArguments(),getName()+" has fewer CVs");
     160           4 :   return &derECVs_[j][0];
     161             : }
     162             : 
     163           2 : std::vector< std::vector<unsigned> > ECVcustom::getIndex_k() const
     164             : {
     165           2 :   plumed_massert(isReady_ && totNumECVs_>0,"cannot access getIndex_k() of ECV before initialization");
     166           2 :   std::vector< std::vector<unsigned> > index_k(totNumECVs_,std::vector<unsigned>(getNumberOfArguments()));
     167           8 :   for(unsigned k=0; k<totNumECVs_; k++)
     168          18 :     for(unsigned j=0; j<getNumberOfArguments(); j++)
     169          12 :       if(k==j+P0_contribution_)
     170           4 :         index_k[k][j]=k;
     171           2 :   return index_k;
     172           0 : }
     173             : 
     174           2 : std::vector<std::string> ECVcustom::getLambdas() const
     175             : {
     176           2 :   std::vector<std::string> lambdas(totNumECVs_);
     177           2 :   if(P0_contribution_==1)
     178             :   {
     179           2 :     std::ostringstream subs;
     180           2 :     subs<<"P0";
     181           4 :     for(unsigned j=1; j<getNumberOfArguments(); j++)
     182           2 :       subs<<"_P0";
     183           2 :     lambdas[0]=subs.str();
     184           2 :   }
     185           6 :   for(unsigned k=P0_contribution_; k<totNumECVs_; k++)
     186             :   {
     187           4 :     const unsigned kk=k-P0_contribution_;
     188           4 :     std::ostringstream subs;
     189             : //the getLambdas method is const, so it complains if one tries to access a non-const pointer, hence the const_cast
     190           4 :     if(kk==0)
     191             :       subs<<const_cast<ECVcustom *>(this)->getPntrToArgument(kk)->getName();
     192             :     else
     193           2 :       subs<<"NaN";
     194           8 :     for(unsigned j=1; j<getNumberOfArguments(); j++)
     195             :     {
     196           4 :       if(kk==j)
     197           2 :         subs<<"_"<<const_cast<ECVcustom *>(this)->getPntrToArgument(kk)->getName();
     198             :       else
     199           2 :         subs<<"_NaN";
     200             :     }
     201           4 :     lambdas[k]=subs.str();
     202           4 :   }
     203           2 :   return lambdas;
     204           0 : }
     205             : 
     206           2 : void ECVcustom::initECVs()
     207             : {
     208           2 :   plumed_massert(!isReady_,"initialization should not be called twice");
     209           2 :   isReady_=true;
     210           2 :   log.printf("  *%4u ECVs for %s\n",totNumECVs_,getName().c_str());
     211           2 : }
     212             : 
     213           1 : void ECVcustom::initECVs_observ(const std::vector<double>& all_obs_cvs,const unsigned ncv,const unsigned index_j)
     214             : {
     215           1 :   initECVs();
     216           1 :   calculateECVs(&all_obs_cvs[index_j]);
     217           3 :   for(unsigned j=0; j<getNumberOfArguments(); j++)
     218           4 :     ECVs_[j][j+P0_contribution_]=std::min(barrier_*beta0_,ECVs_[j][j+P0_contribution_]);
     219           1 : }
     220             : 
     221           1 : void ECVcustom::initECVs_restart(const std::vector<std::string>& lambdas)
     222             : {
     223             :   std::size_t pos=0;
     224           2 :   for(unsigned j=0; j<getNumberOfArguments()-1; j++)
     225           1 :     pos=lambdas[0].find("_",pos+1); //checking only lambdas[0] is hopefully enough
     226           1 :   plumed_massert(pos<lambdas[0].length(),"this should not happen, fewer '_' than expected in "+getName());
     227           1 :   pos=lambdas[0].find("_",pos+1);
     228           1 :   plumed_massert(pos>lambdas[0].length(),"this should not happen, more '_' than expected in "+getName());
     229             : 
     230           1 :   std::vector<std::string> myLambdas=getLambdas();
     231           1 :   plumed_massert(myLambdas.size()==lambdas.size(),"RESTART - mismatch in number of "+getName());
     232           1 :   plumed_massert(std::equal(myLambdas.begin(),myLambdas.end(),lambdas.begin()),"RESTART - mismatch in lambda values of "+getName());
     233             : 
     234           1 :   initECVs();
     235           1 : }
     236             : 
     237             : }
     238             : }

Generated by: LCOV version 1.15