LCOV - code coverage report
Current view: top level - ves - TD_Custom.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 78 90 86.7 %
Date: 2024-10-11 08:09:47 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2016-2021 The VES code team
       3             :    (see the PEOPLE-VES file at the root of this folder for a list of names)
       4             : 
       5             :    See http://www.ves-code.org for more information.
       6             : 
       7             :    This file is part of VES code module.
       8             : 
       9             :    The VES code module is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    The VES code module is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with the VES code module.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : 
      23             : #include "TargetDistribution.h"
      24             : #include "GridIntegrationWeights.h"
      25             : 
      26             : #include "core/ActionRegister.h"
      27             : #include "tools/Grid.h"
      28             : 
      29             : #include "lepton/Lepton.h"
      30             : 
      31             : 
      32             : namespace PLMD {
      33             : namespace ves {
      34             : 
      35             : //+PLUMEDOC VES_TARGETDIST TD_CUSTOM
      36             : /*
      37             : Target distribution given by an arbitrary mathematical expression (static or dynamic).
      38             : 
      39             : Use as a target distribution the distribution defined by
      40             : \f[
      41             : p(\mathbf{s}) =
      42             : \frac{f(\mathbf{s})}{\int d\mathbf{s} \, f(\mathbf{s})}
      43             : \f]
      44             : where \f$f(\mathbf{s})\f$ is some arbitrary mathematical function that
      45             : is parsed by the lepton library.
      46             : 
      47             : The function \f$f(\mathbf{s})\f$ is given by the FUNCTION keywords by
      48             : using _s1_,_s2_,..., as variables for the arguments
      49             : \f$\mathbf{s}=(s_1,s_2,\ldots,s_d)\f$.
      50             : If one variable is not given the target distribution will be
      51             : taken as uniform in that argument.
      52             : 
      53             : It is also possible to include the free energy surface \f$F(\mathbf{s})\f$
      54             : in the target distribution by using the _FE_ variable. In this case the
      55             : target distribution is dynamic and needs to be updated with current
      56             : best estimate of \f$F(\mathbf{s})\f$, similarly as for the
      57             : \ref TD_WELLTEMPERED "well-tempered target distribution".
      58             : Furthermore, the inverse temperature \f$\beta = (k_{\mathrm{B}}T)^{-1}\f$ and
      59             : the thermal energy \f$k_{\mathrm{B}}T\f$ can be included
      60             : by using the _beta_ and \f$k_B T\f$ variables.
      61             : 
      62             : The target distribution will be automatically normalized over the region on
      63             : which it is defined on. Therefore, the function given in
      64             : FUNCTION needs to be non-negative and it must be possible to normalize the function. The
      65             : code will perform checks to make sure that this is indeed the case.
      66             : 
      67             : 
      68             : \par Examples
      69             : 
      70             : Here we use as shifted [Maxwell-Boltzmann distribution](https://en.wikipedia.org/wiki/Maxwell%E2%80%93Boltzmann_distribution)
      71             : as a target distribution in one-dimension.
      72             : Note that it is not need to include the normalization factor as the distribution will be
      73             : automatically normalized.
      74             : \plumedfile
      75             : TD_CUSTOM ...
      76             :  FUNCTION=(s1+20)^2*exp(-(s1+20)^2/(2*10.0^2))
      77             :  LABEL=td
      78             : ... TD_CUSTOM
      79             : \endplumedfile
      80             : 
      81             : Here we have a two dimensional target distribution where we
      82             : use a [generalized normal distribution](https://en.wikipedia.org/wiki/Generalized_normal_distribution)
      83             : for argument \f$s_2\f$ while the distribution for \f$s_1\f$ is taken as
      84             : uniform as the variable _s1_ is not included in the function.
      85             : \plumedfile
      86             : TD_CUSTOM ...
      87             :  FUNCTION=exp(-(abs(s2-20.0)/5.0)^4.0)
      88             :  LABEL=td
      89             : ... TD_CUSTOM
      90             : \endplumedfile
      91             : 
      92             : By using the _FE_ variable the target distribution can depend on
      93             : the free energy surface \f$F(\mathbf{s})\f$. For example,
      94             : the following input is identical to using \ref TD_WELLTEMPERED with
      95             : a bias factor of 10.
      96             : \plumedfile
      97             : TD_CUSTOM ...
      98             :  FUNCTION=exp(-(beta/10.0)*FE)
      99             :  LABEL=td
     100             : ... TD_CUSTOM
     101             : \endplumedfile
     102             : Here the inverse temperature is automatically obtained by using the _beta_
     103             : variable. It is also possible to use the \f$k_B T\f$ variable. The following
     104             : syntax will give the exact same results as the syntax above
     105             : \plumedfile
     106             : TD_CUSTOM ...
     107             :  FUNCTION=exp(-(1.0/(kBT*10.0))*FE)
     108             :  LABEL=td
     109             : ... TD_CUSTOM
     110             : \endplumedfile
     111             : 
     112             : 
     113             : */
     114             : //+ENDPLUMEDOC
     115             : 
     116             : class TD_Custom : public TargetDistribution {
     117             : private:
     118             :   void setupAdditionalGrids(const std::vector<Value*>&, const std::vector<std::string>&, const std::vector<std::string>&, const std::vector<unsigned int>&) override;
     119             :   //
     120             :   lepton::CompiledExpression expression;
     121             :   //
     122             :   std::vector<double*> cv_var_lepton_refs_;
     123             :   double* kbt_var_lepton_ref_;
     124             :   double* beta_var_lepton_ref_;
     125             :   double* fes_var_lepton_ref_;
     126             :   //
     127             :   std::vector<unsigned int> cv_var_idx_;
     128             :   std::vector<std::string> cv_var_str_;
     129             :   //
     130             :   std::string cv_var_prefix_str_;
     131             :   std::string fes_var_str_;
     132             :   std::string kbt_var_str_;
     133             :   std::string beta_var_str_;
     134             :   //
     135             :   bool use_fes_;
     136             :   bool use_kbt_;
     137             :   bool use_beta_;
     138             : public:
     139             :   static void registerKeywords( Keywords&);
     140             :   explicit TD_Custom(const ActionOptions& ao);
     141             :   void updateGrid() override;
     142             :   double getValue(const std::vector<double>&) const override;
     143          64 :   ~TD_Custom() {};
     144             : };
     145             : 
     146       10435 : PLUMED_REGISTER_ACTION(TD_Custom,"TD_CUSTOM")
     147             : 
     148             : 
     149          17 : void TD_Custom::registerKeywords(Keywords& keys) {
     150          17 :   TargetDistribution::registerKeywords(keys);
     151          34 :   keys.add("compulsory","FUNCTION","The function you wish to use for the target distribution where you should use the variables _s1_,_s2_,... for the arguments. You can also use the current estimate of the FES by using the variable _FE_ and the temperature by using the \\f$k_B T\\f$ and _beta_ variables.");
     152          17 :   keys.use("WELLTEMPERED_FACTOR");
     153          17 :   keys.use("SHIFT_TO_ZERO");
     154          17 : }
     155             : 
     156             : 
     157          16 : TD_Custom::TD_Custom(const ActionOptions& ao):
     158             :   PLUMED_VES_TARGETDISTRIBUTION_INIT(ao),
     159             : //
     160          16 :   cv_var_lepton_refs_(0,nullptr),
     161          16 :   kbt_var_lepton_ref_(nullptr),
     162          16 :   beta_var_lepton_ref_(nullptr),
     163          16 :   fes_var_lepton_ref_(nullptr),
     164             : //
     165          16 :   cv_var_idx_(0),
     166          16 :   cv_var_str_(0),
     167             : //
     168          16 :   cv_var_prefix_str_("s"),
     169          16 :   fes_var_str_("FE"),
     170          16 :   kbt_var_str_("kBT"),
     171          16 :   beta_var_str_("beta"),
     172             : //
     173          16 :   use_fes_(false),
     174          16 :   use_kbt_(false),
     175          32 :   use_beta_(false)
     176             : {
     177             :   std::string func_str;
     178          16 :   parse("FUNCTION",func_str);
     179          16 :   checkRead();
     180             :   //
     181             :   try {
     182          16 :     lepton::ParsedExpression pe=lepton::Parser::parse(func_str).optimize(lepton::Constants());
     183          16 :     log<<"  function as parsed by lepton: "<<pe<<"\n";
     184          16 :     expression=pe.createCompiledExpression();
     185             :   }
     186           0 :   catch(PLMD::lepton::Exception& exc) {
     187           0 :     plumed_merror("There was some problem in parsing the function "+func_str+" given in FUNCTION with lepton");
     188           0 :   }
     189             : 
     190          39 :   for(auto &p: expression.getVariables()) {
     191          23 :     std::string curr_var = p;
     192             :     unsigned int cv_idx;
     193          39 :     if(curr_var.substr(0,cv_var_prefix_str_.size())==cv_var_prefix_str_ && Tools::convertNoexcept(curr_var.substr(cv_var_prefix_str_.size()),cv_idx) && cv_idx>0) {
     194          16 :       cv_var_idx_.push_back(cv_idx-1);
     195             :     }
     196           7 :     else if(curr_var==fes_var_str_) {
     197           3 :       use_fes_=true;
     198             :       setDynamic();
     199             :       setFesGridNeeded();
     200             :     }
     201           4 :     else if(curr_var==kbt_var_str_) {
     202           2 :       use_kbt_=true;
     203             :     }
     204           2 :     else if(curr_var==beta_var_str_) {
     205           2 :       use_beta_=true;
     206             :     }
     207             :     else {
     208           0 :       plumed_merror(getName()+": problem with parsing formula with lepton, cannot recognise the variable "+curr_var);
     209             :     }
     210             :   }
     211             :   //
     212          16 :   std::sort(cv_var_idx_.begin(),cv_var_idx_.end());
     213          16 :   cv_var_str_.resize(cv_var_idx_.size());
     214          16 :   cv_var_lepton_refs_.resize(cv_var_str_.size());
     215          32 :   for(unsigned int j=0; j<cv_var_idx_.size(); j++) {
     216          16 :     std::string str1; Tools::convert(cv_var_idx_[j]+1,str1);
     217          32 :     cv_var_str_[j] = cv_var_prefix_str_+str1;
     218             :     try {
     219          16 :       cv_var_lepton_refs_[j] = &expression.getVariableReference(cv_var_str_[j]);
     220           0 :     } catch(PLMD::lepton::Exception& exc) {}
     221             :   }
     222             : 
     223          16 :   if(use_kbt_) {
     224             :     try {
     225           2 :       kbt_var_lepton_ref_ = &expression.getVariableReference(kbt_var_str_);
     226           0 :     } catch(PLMD::lepton::Exception& exc) {}
     227             :   }
     228          16 :   if(use_beta_) {
     229             :     try {
     230           2 :       beta_var_lepton_ref_ = &expression.getVariableReference(beta_var_str_);
     231           0 :     } catch(PLMD::lepton::Exception& exc) {}
     232             :   }
     233          16 :   if(use_fes_) {
     234             :     try {
     235           3 :       fes_var_lepton_ref_ = &expression.getVariableReference(fes_var_str_);
     236           0 :     } catch(PLMD::lepton::Exception& exc) {}
     237             :   }
     238             : 
     239          16 : }
     240             : 
     241             : 
     242          16 : void TD_Custom::setupAdditionalGrids(const std::vector<Value*>& arguments, const std::vector<std::string>& min, const std::vector<std::string>& max, const std::vector<unsigned int>& nbins) {
     243          16 :   if(cv_var_idx_.size()>0 && cv_var_idx_[cv_var_idx_.size()-1]>getDimension()) {
     244           0 :     plumed_merror(getName()+": mismatch between CVs given in FUNC and the dimension of the target distribution");
     245             :   }
     246          16 : }
     247             : 
     248             : 
     249           0 : double TD_Custom::getValue(const std::vector<double>& argument) const {
     250           0 :   plumed_merror("getValue not implemented for TD_Custom");
     251             :   return 0.0;
     252             : }
     253             : 
     254             : 
     255          46 : void TD_Custom::updateGrid() {
     256          46 :   if(use_fes_) {
     257          33 :     plumed_massert(getFesGridPntr()!=NULL,"the FES grid has to be linked to the free energy in the target distribution");
     258             :   }
     259          46 :   if(use_kbt_) {
     260          22 :     if(kbt_var_lepton_ref_) {*kbt_var_lepton_ref_= 1.0/getBeta();}
     261             :   }
     262          46 :   if(use_beta_) {
     263          22 :     if(beta_var_lepton_ref_) {*beta_var_lepton_ref_= getBeta();}
     264             :   }
     265             :   //
     266          92 :   std::vector<double> integration_weights = GridIntegrationWeights::getIntegrationWeights(getTargetDistGridPntr());
     267             :   double norm = 0.0;
     268             :   //
     269       40909 :   for(Grid::index_t l=0; l<targetDistGrid().getSize(); l++) {
     270       40863 :     std::vector<double> point = targetDistGrid().getPoint(l);
     271       99928 :     for(unsigned int k=0; k<cv_var_str_.size() ; k++) {
     272       59065 :       if(cv_var_lepton_refs_[k]) {*cv_var_lepton_refs_[k] = point[cv_var_idx_[k]];}
     273             :     }
     274       40863 :     if(use_fes_) {
     275        3300 :       if(fes_var_lepton_ref_) {*fes_var_lepton_ref_ = getFesGridPntr()->getValue(l);}
     276             :     }
     277       40863 :     double value = expression.evaluate();
     278             : 
     279       40863 :     if(value<0.0 && !isTargetDistGridShiftedToZero()) {plumed_merror(getName()+": The target distribution function gives negative values. You should change the definition of the function used for the target distribution to avoid this. You can also use the SHIFT_TO_ZERO keyword to avoid this problem.");}
     280       40863 :     targetDistGrid().setValue(l,value);
     281       40863 :     norm += integration_weights[l]*value;
     282       40863 :     logTargetDistGrid().setValue(l,-std::log(value));
     283             :   }
     284          46 :   if(norm>0.0) {
     285          45 :     targetDistGrid().scaleAllValuesAndDerivatives(1.0/norm);
     286             :   }
     287           1 :   else if(!isTargetDistGridShiftedToZero()) {
     288           0 :     plumed_merror(getName()+": The target distribution function cannot be normalized proberly. You should change the definition of the function used for the target distribution to avoid this. You can also use the SHIFT_TO_ZERO keyword to avoid this problem.");
     289             :   }
     290          46 :   logTargetDistGrid().setMinToZero();
     291          46 : }
     292             : 
     293             : 
     294             : }
     295             : }

Generated by: LCOV version 1.15