LCOV - code coverage report
Current view: top level - opes - ECVcustom.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 91 99 91.9 %
Date: 2024-10-18 13:59:31 Functions: 10 11 90.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             : PLUMED_REGISTER_ACTION(ECVcustom,"ECV_CUSTOM")
      86             : 
      87           4 : void ECVcustom::registerKeywords(Keywords& keys)
      88             : {
      89           4 :   ExpansionCVs::registerKeywords(keys);
      90           8 :   keys.addInputKeyword("compulsory","ARG","scalar","the labels of the single ECVs. Delta U_i, in energy units");
      91           8 :   keys.addFlag("ADD_P0",false,"add the unbiased Boltzmann distribution to the target distribution, to make sure to sample it");
      92           8 :   keys.addFlag("DIMENSIONLESS",false,"consider ARG as dimensionless rather than an energy, thus do not multiply it by beta");
      93           8 :   keys.add("optional","BARRIER","a guess of the free energy barrier to be overcome (better to stay higher than lower)");
      94           4 : }
      95             : 
      96           2 : ECVcustom::ECVcustom(const ActionOptions&ao)
      97             :   : Action(ao)
      98             :   , ExpansionCVs(ao)
      99           2 :   , beta0_(1./kbt_)
     100             : {
     101             : //set beta0_
     102             :   bool dimensionless;
     103           2 :   parseFlag("DIMENSIONLESS",dimensionless);
     104           2 :   if(dimensionless)
     105           0 :     beta0_=1;
     106             : 
     107             : //set P0_contribution_
     108           2 :   bool add_P0=false;
     109           2 :   parseFlag("ADD_P0",add_P0);
     110           2 :   if(add_P0)
     111           2 :     P0_contribution_=1;
     112             :   else
     113           0 :     P0_contribution_=0;
     114             : 
     115             : //set barrier_
     116           2 :   barrier_=std::numeric_limits<double>::infinity();
     117           2 :   parse("BARRIER",barrier_);
     118             : 
     119           2 :   checkRead();
     120             : 
     121             : //set ECVs stuff
     122           2 :   totNumECVs_=getNumberOfArguments()+P0_contribution_;
     123           2 :   ECVs_.resize(getNumberOfArguments(),std::vector<double>(totNumECVs_));
     124           2 :   derECVs_.resize(getNumberOfArguments(),std::vector<double>(totNumECVs_));
     125           6 :   for(unsigned j=0; j<getNumberOfArguments(); j++)
     126           4 :     derECVs_[j][j+P0_contribution_]=beta0_; //always constant
     127             : 
     128             : //print some info
     129           2 :   if(dimensionless)
     130           0 :     log.printf(" -- DIMENSIONLESS: the ARG is not multiplied by beta\n");
     131           2 :   if(barrier_!=std::numeric_limits<double>::infinity())
     132             :   {
     133           0 :     log.printf("  guess for free energy BARRIER = %g\n",barrier_);
     134           0 :     if(dimensionless)
     135           0 :       log.printf("    also the BARRIER is considered to be DIMENSIONLESS\n");
     136             :   }
     137           2 :   if(P0_contribution_==1)
     138           2 :     log.printf(" -- ADD_P0: the target includes also the unbiased probability itself\n");
     139           2 : }
     140             : 
     141          91 : void ECVcustom::calculateECVs(const double * cv)
     142             : {
     143         273 :   for(unsigned j=0; j<getNumberOfArguments(); j++)
     144         182 :     ECVs_[j][j+P0_contribution_]=beta0_*cv[j];
     145             :   //derivative is constant
     146          91 : }
     147             : 
     148           4 : const double * ECVcustom::getPntrToECVs(unsigned j)
     149             : {
     150           4 :   plumed_massert(isReady_,"cannot access ECVs before initialization");
     151           4 :   plumed_massert(j<getNumberOfArguments(),getName()+" has fewer CVs");
     152           4 :   return &ECVs_[j][0];
     153             : }
     154             : 
     155           4 : const double * ECVcustom::getPntrToDerECVs(unsigned j)
     156             : {
     157           4 :   plumed_massert(isReady_,"cannot access ECVs before initialization");
     158           4 :   plumed_massert(j<getNumberOfArguments(),getName()+" has fewer CVs");
     159           4 :   return &derECVs_[j][0];
     160             : }
     161             : 
     162           2 : std::vector< std::vector<unsigned> > ECVcustom::getIndex_k() const
     163             : {
     164           2 :   plumed_massert(isReady_ && totNumECVs_>0,"cannot access getIndex_k() of ECV before initialization");
     165           2 :   std::vector< std::vector<unsigned> > index_k(totNumECVs_,std::vector<unsigned>(getNumberOfArguments()));
     166           8 :   for(unsigned k=0; k<totNumECVs_; k++)
     167          18 :     for(unsigned j=0; j<getNumberOfArguments(); j++)
     168          12 :       if(k==j+P0_contribution_)
     169           4 :         index_k[k][j]=k;
     170           2 :   return index_k;
     171           0 : }
     172             : 
     173           2 : std::vector<std::string> ECVcustom::getLambdas() const
     174             : {
     175           2 :   std::vector<std::string> lambdas(totNumECVs_);
     176           2 :   if(P0_contribution_==1)
     177             :   {
     178           2 :     std::ostringstream subs;
     179           2 :     subs<<"P0";
     180           4 :     for(unsigned j=1; j<getNumberOfArguments(); j++)
     181           2 :       subs<<"_P0";
     182           2 :     lambdas[0]=subs.str();
     183           2 :   }
     184           6 :   for(unsigned k=P0_contribution_; k<totNumECVs_; k++)
     185             :   {
     186           4 :     const unsigned kk=k-P0_contribution_;
     187           4 :     std::ostringstream subs;
     188             : //the getLambdas method is const, so it complains if one tries to access a non-const pointer, hence the const_cast
     189           4 :     if(kk==0)
     190             :       subs<<const_cast<ECVcustom *>(this)->getPntrToArgument(kk)->getName();
     191             :     else
     192           2 :       subs<<"NaN";
     193           8 :     for(unsigned j=1; j<getNumberOfArguments(); j++)
     194             :     {
     195           4 :       if(kk==j)
     196           2 :         subs<<"_"<<const_cast<ECVcustom *>(this)->getPntrToArgument(kk)->getName();
     197             :       else
     198           2 :         subs<<"_NaN";
     199             :     }
     200           4 :     lambdas[k]=subs.str();
     201           4 :   }
     202           2 :   return lambdas;
     203           0 : }
     204             : 
     205           2 : void ECVcustom::initECVs()
     206             : {
     207           2 :   plumed_massert(!isReady_,"initialization should not be called twice");
     208           2 :   isReady_=true;
     209           2 :   log.printf("  *%4u ECVs for %s\n",totNumECVs_,getName().c_str());
     210           2 : }
     211             : 
     212           1 : void ECVcustom::initECVs_observ(const std::vector<double>& all_obs_cvs,const unsigned ncv,const unsigned index_j)
     213             : {
     214           1 :   initECVs();
     215           1 :   calculateECVs(&all_obs_cvs[index_j]);
     216           3 :   for(unsigned j=0; j<getNumberOfArguments(); j++)
     217           4 :     ECVs_[j][j+P0_contribution_]=std::min(barrier_*beta0_,ECVs_[j][j+P0_contribution_]);
     218           1 : }
     219             : 
     220           1 : void ECVcustom::initECVs_restart(const std::vector<std::string>& lambdas)
     221             : {
     222             :   std::size_t pos=0;
     223           2 :   for(unsigned j=0; j<getNumberOfArguments()-1; j++)
     224           1 :     pos=lambdas[0].find("_",pos+1); //checking only lambdas[0] is hopefully enough
     225           1 :   plumed_massert(pos<lambdas[0].length(),"this should not happen, fewer '_' than expected in "+getName());
     226           1 :   pos=lambdas[0].find("_",pos+1);
     227           1 :   plumed_massert(pos>lambdas[0].length(),"this should not happen, more '_' than expected in "+getName());
     228             : 
     229           1 :   std::vector<std::string> myLambdas=getLambdas();
     230           1 :   plumed_massert(myLambdas.size()==lambdas.size(),"RESTART - mismatch in number of "+getName());
     231           1 :   plumed_massert(std::equal(myLambdas.begin(),myLambdas.end(),lambdas.begin()),"RESTART - mismatch in lambda values of "+getName());
     232             : 
     233           1 :   initECVs();
     234           1 : }
     235             : 
     236             : }
     237             : }

Generated by: LCOV version 1.16