LCOV - code coverage report
Current view: top level - ves - TD_Custom.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 68 79 86.1 %
Date: 2019-08-13 10:39:37 Functions: 11 12 91.7 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2016-2018 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       17677 : static std::map<std::string, double> leptonConstants= {
      36             :   {"e", std::exp(1.0)},
      37             :   {"log2e", 1.0/std::log(2.0)},
      38             :   {"log10e", 1.0/std::log(10.0)},
      39             :   {"ln2", std::log(2.0)},
      40             :   {"ln10", std::log(10.0)},
      41             :   {"pi", pi},
      42             :   {"pi_2", pi*0.5},
      43             :   {"pi_4", pi*0.25},
      44             : //  {"1_pi", 1.0/pi},
      45             : //  {"2_pi", 2.0/pi},
      46             : //  {"2_sqrtpi", 2.0/std::sqrt(pi)},
      47             :   {"sqrt2", std::sqrt(2.0)},
      48             :   {"sqrt1_2", std::sqrt(0.5)}
      49       16070 : };
      50             : 
      51             : 
      52             : //+PLUMEDOC VES_TARGETDIST TD_CUSTOM
      53             : /*
      54             : Target distribution given by an arbitrary mathematical expression (static or dynamic).
      55             : 
      56             : Use as a target distribution the distribution defined by
      57             : \f[
      58             : p(\mathbf{s}) =
      59             : \frac{f(\mathbf{s})}{\int d\mathbf{s} \, f(\mathbf{s})}
      60             : \f]
      61             : where \f$f(\mathbf{s})\f$ is some arbitrary mathematical function that
      62             : is parsed by the lepton library.
      63             : 
      64             : The function \f$f(\mathbf{s})\f$ is given by the FUNCTION keywords by
      65             : using _s1_,_s2_,..., as variables for the arguments
      66             : \f$\mathbf{s}=(s_1,s_2,\ldots,s_d)\f$.
      67             : If one variable is not given the target distribution will be
      68             : taken as uniform in that argument.
      69             : 
      70             : It is also possible to include the free energy surface \f$F(\mathbf{s})\f$
      71             : in the target distribution by using the _FE_ variable. In this case the
      72             : target distribution is dynamic and needs to be updated with current
      73             : best estimate of \f$F(\mathbf{s})\f$, similarly as for the
      74             : \ref TD_WELLTEMPERED "well-tempered target distribution".
      75             : Furthermore, the inverse temperature \f$\beta = (k_{\mathrm{B}}T)^{-1}\f$ and
      76             : the thermal energy \f$k_{\mathrm{B}}T\f$ can be included
      77             : by using the _beta_ and _kBT_ variables.
      78             : 
      79             : The target distribution will be automatically normalized over the region on
      80             : which it is defined on. Therefore, the function given in
      81             : FUNCTION needs to be non-negative and normalizable. The
      82             : code will perform checks to make sure that this is indeed the case.
      83             : 
      84             : 
      85             : \par Examples
      86             : 
      87             : Here we use as shifted [Maxwell-Boltzmann distribution](https://en.wikipedia.org/wiki/Maxwell%E2%80%93Boltzmann_distribution)
      88             : as a target distribution in one-dimension.
      89             : Note that it is not need to include the normalization factor as the distribution will be
      90             : automatically normalized.
      91             : \plumedfile
      92             : TD_CUSTOM ...
      93             :  FUNCTION=(s1+20)^2*exp(-(s1+20)^2/(2*10.0^2))
      94             :  LABEL=td
      95             : ... TD_CUSTOM
      96             : \endplumedfile
      97             : 
      98             : Here we have a two dimensional target distribution where we
      99             : use a [generalized normal distribution](https://en.wikipedia.org/wiki/Generalized_normal_distribution)
     100             : for argument \f$s_2\f$ while the distribution for \f$s_1\f$ is taken as
     101             : uniform as the variable _s1_ is not included in the function.
     102             : \plumedfile
     103             : TD_CUSTOM ...
     104             :  FUNCTION=exp(-(abs(s2-20.0)/5.0)^4.0)
     105             :  LABEL=td
     106             : ... TD_CUSTOM
     107             : \endplumedfile
     108             : 
     109             : By using the _FE_ variable the target distribution can depend on
     110             : the free energy surface \f$F(\mathbf{s})\f$. For example,
     111             : the following input is identical to using \ref TD_WELLTEMPERED with
     112             : BIASFACTOR=10.
     113             : \plumedfile
     114             : TD_CUSTOM ...
     115             :  FUNCTION=exp(-(beta/10.0)*FE)
     116             :  LABEL=td
     117             : ... TD_CUSTOM
     118             : \endplumedfile
     119             : Here the inverse temperature is automatically obtained by using the _beta_
     120             : variable. It is also possible to use the _kBT_ variable. The following
     121             : syntax will give the exact same results as the syntax above
     122             : \plumedfile
     123             : TD_CUSTOM ...
     124             :  FUNCTION=exp(-(1.0/(kBT*10.0))*FE)}
     125             :  LABEL=td
     126             : ... TD_CUSTOM
     127             : \endplumedfile
     128             : 
     129             : 
     130             : */
     131             : //+ENDPLUMEDOC
     132             : 
     133             : class TD_Custom : public TargetDistribution {
     134             : private:
     135             :   void setupAdditionalGrids(const std::vector<Value*>&, const std::vector<std::string>&, const std::vector<std::string>&, const std::vector<unsigned int>&);
     136             :   //
     137             :   lepton::CompiledExpression expression;
     138             :   //
     139             :   std::vector<unsigned int> cv_var_idx_;
     140             :   std::vector<std::string> cv_var_str_;
     141             :   //
     142             :   std::string cv_var_prefix_str_;
     143             :   std::string fes_var_str_;
     144             :   std::string kbt_var_str_;
     145             :   std::string beta_var_str_;
     146             :   //
     147             :   bool use_fes_;
     148             :   bool use_kbt_;
     149             :   bool use_beta_;
     150             : public:
     151             :   static void registerKeywords( Keywords&);
     152             :   explicit TD_Custom(const ActionOptions& ao);
     153             :   void updateGrid();
     154             :   double getValue(const std::vector<double>&) const;
     155          32 :   ~TD_Custom() {};
     156             : };
     157             : 
     158        4837 : PLUMED_REGISTER_ACTION(TD_Custom,"TD_CUSTOM")
     159             : 
     160             : 
     161          17 : void TD_Custom::registerKeywords(Keywords& keys) {
     162          17 :   TargetDistribution::registerKeywords(keys);
     163          17 :   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 _kBT_ and _beta_ variables.");
     164          17 :   keys.use("WELLTEMPERED_FACTOR");
     165          17 :   keys.use("SHIFT_TO_ZERO");
     166          17 : }
     167             : 
     168             : 
     169          16 : TD_Custom::TD_Custom(const ActionOptions& ao):
     170             :   PLUMED_VES_TARGETDISTRIBUTION_INIT(ao),
     171             : //
     172             :   cv_var_idx_(0),
     173             :   cv_var_str_(0),
     174             : //
     175             :   cv_var_prefix_str_("s"),
     176             :   fes_var_str_("FE"),
     177             :   kbt_var_str_("kBT"),
     178             :   beta_var_str_("beta"),
     179             : //
     180             :   use_fes_(false),
     181             :   use_kbt_(false),
     182          16 :   use_beta_(false)
     183             : {
     184          16 :   std::string func_str;
     185          16 :   parse("FUNCTION",func_str);
     186          16 :   checkRead();
     187             :   //
     188             :   try {
     189          16 :     lepton::ParsedExpression pe=lepton::Parser::parse(func_str).optimize(leptonConstants);
     190          16 :     log<<"  function as parsed by lepton: "<<pe<<"\n";
     191          16 :     expression=pe.createCompiledExpression();
     192             :   }
     193           0 :   catch(PLMD::lepton::Exception& exc) {
     194           0 :     plumed_merror("There was some problem in parsing the function "+func_str+" given in FUNCTION with lepton");
     195             :   }
     196             : 
     197          39 :   for(auto &p: expression.getVariables()) {
     198          23 :     std::string curr_var = p;
     199             :     unsigned int cv_idx;
     200          23 :     if(curr_var.substr(0,cv_var_prefix_str_.size())==cv_var_prefix_str_ && Tools::convert(curr_var.substr(cv_var_prefix_str_.size()),cv_idx) && cv_idx>0) {
     201          16 :       cv_var_idx_.push_back(cv_idx-1);
     202             :     }
     203           7 :     else if(curr_var==fes_var_str_) {
     204           3 :       use_fes_=true;
     205           3 :       setDynamic();
     206           3 :       setFesGridNeeded();
     207             :     }
     208           4 :     else if(curr_var==kbt_var_str_) {
     209           2 :       use_kbt_=true;
     210             :     }
     211           2 :     else if(curr_var==beta_var_str_) {
     212           2 :       use_beta_=true;
     213             :     }
     214             :     else {
     215           0 :       plumed_merror(getName()+": problem with parsing formula with lepton, cannot recognise the variable "+curr_var);
     216             :     }
     217          23 :   }
     218             :   //
     219          16 :   std::sort(cv_var_idx_.begin(),cv_var_idx_.end());
     220          16 :   cv_var_str_.resize(cv_var_idx_.size());
     221          32 :   for(unsigned int j=0; j<cv_var_idx_.size(); j++) {
     222          16 :     std::string str1; Tools::convert(cv_var_idx_[j]+1,str1);
     223          16 :     cv_var_str_[j] = cv_var_prefix_str_+str1;
     224          32 :   }
     225          16 : }
     226             : 
     227             : 
     228          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) {
     229          16 :   if(cv_var_idx_.size()>0 && cv_var_idx_[cv_var_idx_.size()-1]>getDimension()) {
     230           0 :     plumed_merror(getName()+": mismatch between CVs given in FUNC and the dimension of the target distribution");
     231             :   }
     232          16 : }
     233             : 
     234             : 
     235           0 : double TD_Custom::getValue(const std::vector<double>& argument) const {
     236           0 :   plumed_merror("getValue not implemented for TD_Custom");
     237             :   return 0.0;
     238             : }
     239             : 
     240             : 
     241          46 : void TD_Custom::updateGrid() {
     242          46 :   if(use_fes_) {
     243          33 :     plumed_massert(getFesGridPntr()!=NULL,"the FES grid has to be linked to the free energy in the target distribution");
     244             :   }
     245          46 :   if(use_kbt_) {
     246             :     try {
     247          22 :       expression.getVariableReference(kbt_var_str_) = 1.0/getBeta();
     248           0 :     } catch(PLMD::lepton::Exception& exc) {}
     249             :   }
     250          46 :   if(use_beta_) {
     251             :     try {
     252          22 :       expression.getVariableReference(beta_var_str_) = getBeta();
     253           0 :     } catch(PLMD::lepton::Exception& exc) {}
     254             :   }
     255             :   //
     256          46 :   std::vector<double> integration_weights = GridIntegrationWeights::getIntegrationWeights(getTargetDistGridPntr());
     257          46 :   double norm = 0.0;
     258             :   //
     259       40909 :   for(Grid::index_t l=0; l<targetDistGrid().getSize(); l++) {
     260       40863 :     std::vector<double> point = targetDistGrid().getPoint(l);
     261       99928 :     for(unsigned int k=0; k<cv_var_str_.size() ; k++) {
     262             :       try {
     263       59065 :         expression.getVariableReference(cv_var_str_[k]) = point[cv_var_idx_[k]];
     264           0 :       } catch(PLMD::lepton::Exception& exc) {}
     265             :     }
     266       40863 :     if(use_fes_) {
     267             :       try {
     268        3300 :         expression.getVariableReference(fes_var_str_) = getFesGridPntr()->getValue(l);
     269           0 :       } catch(PLMD::lepton::Exception& exc) {}
     270             :     }
     271       40863 :     double value = expression.evaluate();
     272             : 
     273       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.");}
     274       40863 :     targetDistGrid().setValue(l,value);
     275       40863 :     norm += integration_weights[l]*value;
     276       40863 :     logTargetDistGrid().setValue(l,-std::log(value));
     277       40863 :   }
     278          46 :   if(norm>0.0) {
     279          45 :     targetDistGrid().scaleAllValuesAndDerivatives(1.0/norm);
     280             :   }
     281           1 :   else if(!isTargetDistGridShiftedToZero()) {
     282           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.");
     283             :   }
     284          46 :   logTargetDistGrid().setMinToZero();
     285          46 : }
     286             : 
     287             : 
     288             : }
     289        4821 : }

Generated by: LCOV version 1.13