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

Generated by: LCOV version 1.16