LCOV - code coverage report
Current view: top level - tools - SwitchingFunction.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 393 417 94.2 %
Date: 2025-11-25 13:55:50 Functions: 82 106 77.4 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-2023 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             : 
       5             :    See http://www.plumed.org for more information.
       6             : 
       7             :    This file is part of plumed, version 2.
       8             : 
       9             :    plumed 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             :    plumed 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 plumed.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : #include "SwitchingFunction.h"
      23             : #include "Tools.h"
      24             : #include "Keywords.h"
      25             : #include "OpenMP.h"
      26             : #include <cmath>
      27             : #include <vector>
      28             : #include <limits>
      29             : #include <algorithm>
      30             : #include <optional>
      31             : 
      32             : namespace PLMD {
      33             : 
      34             : //+PLUMEDOC INTERNAL switchingfunction
      35             : /*
      36             : Functions that measure whether values are less than a certain quantity.
      37             : 
      38             : Switching functions \f$s(r)\f$ take a minimum of one input parameter \f$r_0\f$.
      39             : For \f$r \le d_0 \quad s(r)=1.0\f$ while for \f$r > d_0\f$ the function decays smoothly to 0.
      40             : The various switching functions available in PLUMED differ in terms of how this decay is performed.
      41             : 
      42             : Where there is an accepted convention in the literature (e.g. \ref COORDINATION) on the form of the
      43             : switching function we use the convention as the default.  However, the flexibility to use different
      44             : switching functions is always present generally through a single keyword. This keyword generally
      45             : takes an input with the following form:
      46             : 
      47             : \verbatim
      48             : KEYWORD={TYPE <list of parameters>}
      49             : \endverbatim
      50             : 
      51             : The following table contains a list of the various switching functions that are available in PLUMED 2
      52             : together with an example input.
      53             : 
      54             : <table align=center frame=void width=95%% cellpadding=5%%>
      55             : <tr>
      56             : <td> TYPE </td> <td> FUNCTION </td> <td> EXAMPLE INPUT </td> <td> DEFAULT PARAMETERS </td>
      57             : </tr> <tr> <td>RATIONAL </td> <td>
      58             : \f$
      59             : s(r)=\frac{ 1 - \left(\frac{ r - d_0 }{ r_0 }\right)^{n} }{ 1 - \left(\frac{ r - d_0 }{ r_0 }\right)^{m} }
      60             : \f$
      61             : </td> <td>
      62             : {RATIONAL R_0=\f$r_0\f$ D_0=\f$d_0\f$ NN=\f$n\f$ MM=\f$m\f$}
      63             : </td> <td> \f$d_0=0.0\f$, \f$n=6\f$, \f$m=2n\f$ </td>
      64             : </tr> <tr>
      65             : <td> EXP </td> <td>
      66             : \f$
      67             : s(r)=\exp\left(-\frac{ r - d_0 }{ r_0 }\right)
      68             : \f$
      69             : </td> <td>
      70             : {EXP  R_0=\f$r_0\f$ D_0=\f$d_0\f$}
      71             : </td> <td> \f$d_0=0.0\f$ </td>
      72             : </tr> <tr>
      73             : <td> GAUSSIAN </td> <td>
      74             : \f$
      75             : s(r)=\exp\left(-\frac{ (r - d_0)^2 }{ 2r_0^2 }\right)
      76             : \f$
      77             : </td> <td>
      78             : {GAUSSIAN R_0=\f$r_0\f$ D_0=\f$d_0\f$}
      79             : </td> <td> \f$d_0=0.0\f$ </td>
      80             : </tr> <tr>
      81             : <td> SMAP </td> <td>
      82             : \f$
      83             : s(r) = \left[ 1 + ( 2^{a/b} -1 )\left( \frac{r-d_0}{r_0} \right)^a \right]^{-b/a}
      84             : \f$
      85             : </td> <td>
      86             : {SMAP R_0=\f$r_0\f$ D_0=\f$d_0\f$ A=\f$a\f$ B=\f$b\f$}
      87             : </td> <td> \f$d_0=0.0\f$ </td>
      88             : </tr> <tr>
      89             : <td> Q </td> <td>
      90             : \f$
      91             : s(r) = \frac{1}{1 + \exp(\beta(r_{ij} - \lambda r_{ij}^0))}
      92             : \f$
      93             : </td> <td>
      94             : {Q REF=\f$r_{ij}^0\f$ BETA=\f$\beta\f$ LAMBDA=\f$\lambda\f$ }
      95             : </td> <td> \f$\lambda=1.8\f$,  \f$\beta=50 nm^-1\f$ (all-atom)<br/>\f$\lambda=1.5\f$,  \f$\beta=50 nm^-1\f$ (coarse-grained)  </td>
      96             : </tr> <tr>
      97             : <td> CUBIC </td> <td>
      98             : \f$
      99             : s(r) = (y-1)^2(1+2y) \qquad \textrm{where} \quad y = \frac{r - r_1}{r_0-r_1}
     100             : \f$
     101             : </td> <td>
     102             : {CUBIC D_0=\f$r_1\f$ D_MAX=\f$r_0\f$}
     103             : </td> <td> </td>
     104             : </tr> <tr>
     105             : <td> TANH </td> <td>
     106             : \f$
     107             : s(r) = 1 - \tanh\left( \frac{ r - d_0 }{ r_0 } \right)
     108             : \f$
     109             : </td> <td>
     110             : {TANH R_0=\f$r_0\f$ D_0=\f$d_0\f$}
     111             : </td> <td> </td>
     112             : </tr> <tr>
     113             : <td> COSINUS </td> <td>
     114             : \f$s(r) =\left\{\begin{array}{ll}
     115             :    1                                                           & \mathrm{if } r \leq d_0 \\
     116             :    0.5 \left( \cos ( \frac{ r - d_0 }{ r_0 } \pi ) + 1 \right) & \mathrm{if } d_0 < r\leq d_0 + r_0 \\
     117             :    0                                                           & \mathrm{if } r < d_0 + r_0
     118             :   \end{array}\right.
     119             : \f$
     120             : </td> <td>
     121             : {COSINUS R_0=\f$r_0\f$ D_0=\f$d_0\f$}
     122             : </td> <td> </td>
     123             : </tr> <tr>
     124             : <td> CUSTOM </td> <td>
     125             : \f$
     126             : s(r) = FUNC
     127             : \f$
     128             : </td> <td>
     129             : {CUSTOM FUNC=1/(1+x^6) R_0=\f$r_0\f$ D_0=\f$d_0\f$}
     130             : </td> <td> </td>
     131             : </tr>
     132             : </table>
     133             : 
     134             : Notice that most commonly used rational functions are better optimized and might run faster.
     135             : 
     136             : Notice that for backward compatibility we allow using `MATHEVAL` instead of `CUSTOM`.
     137             : Also notice that if the a `CUSTOM` switching function only depends on even powers of `x` it can be
     138             : made faster by using `x2` as a variable. For instance
     139             : \verbatim
     140             : {CUSTOM FUNC=1/(1+x2^3) R_0=0.3}
     141             : \endverbatim
     142             : is equivalent to
     143             : \verbatim
     144             : {CUSTOM FUNC=1/(1+x^6) R_0=0.3}
     145             : \endverbatim
     146             : but runs faster. The reason is that there is an expensive square root calculation that can be optimized out.
     147             : 
     148             : 
     149             : \attention
     150             : With the default implementation CUSTOM is slower than other functions
     151             : (e.g., it is slower than an equivalent RATIONAL function by approximately a factor 2).
     152             : Checkout page \ref Lepton to see how to improve its performance.
     153             : 
     154             : For all the switching functions in the above table one can also specify a further (optional) parameter using the parameter
     155             : keyword D_MAX to assert that for \f$r>d_{\textrm{max}}\f$ the switching function can be assumed equal to zero.
     156             : In this case the function is brought smoothly to zero by stretching and shifting it.
     157             : \verbatim
     158             : KEYWORD={RATIONAL R_0=1 D_MAX=3}
     159             : \endverbatim
     160             : the resulting switching function will be
     161             : \f$
     162             : s(r) = \frac{s'(r)-s'(d_{max})}{s'(0)-s'(d_{max})}
     163             : \f$
     164             : where
     165             : \f$
     166             : s'(r)=\frac{1-r^6}{1-r^{12}}
     167             : \f$
     168             : Since PLUMED 2.2 this is the default. The old behavior (no stretching) can be obtained with the
     169             : NOSTRETCH flag. The NOSTRETCH keyword is only provided for backward compatibility and might be
     170             : removed in the future. Similarly, the STRETCH keyword is still allowed but has no effect.
     171             : 
     172             : Notice that switching functions defined with the simplified syntax are never stretched
     173             : for backward compatibility. This might change in the future.
     174             : 
     175             : */
     176             : //+ENDPLUMEDOC
     177             : 
     178             : namespace switchContainers {
     179             : 
     180        1702 : baseSwitch::baseSwitch(double D0,double DMAX, double R0, std::string_view name)
     181        1702 :   : d0(D0),
     182        1702 :     dmax(DMAX),
     183        1702 :     dmax_2([](const double d) {
     184        1702 :   if(d<std::sqrt(std::numeric_limits<double>::max())) {
     185         290 :     return  d*d;
     186             :   } else {
     187             :     return std::numeric_limits<double>::max();
     188             :   }
     189             : }(dmax)),
     190        1702 : invr0(1.0/R0),
     191        1702 : invr0_2(invr0*invr0),
     192        1992 : mytype(name) {}
     193             : 
     194        1702 : baseSwitch::~baseSwitch()=default;
     195             : 
     196   162833480 : double baseSwitch::calculate(const double distance, double& dfunc) const {
     197             :   double res = 0.0;//RVO!
     198   162833480 :   dfunc = 0.0;
     199   162833480 :   if(distance <= dmax) {
     200             :     res = 1.0;
     201   156015847 :     const double rdist = (distance-d0)*invr0;
     202   156015847 :     if(rdist > 0.0) {
     203    59653094 :       res = function(rdist,dfunc);
     204             :       //the following comments came from the original
     205             :       // this is for the chain rule (derivative of rdist):
     206    59653094 :       dfunc *= invr0;
     207             :       // for any future switching functions, be aware that multiplying invr0 is only
     208             :       // correct for functions of rdist = (r-d0)/r0.
     209             : 
     210             :       // this is because calculate() sets dfunc to the derivative divided times the
     211             :       // distance.
     212             :       // (I think this is misleading and I would like to modify it - GB)
     213    59653094 :       dfunc /= distance;
     214             :     }
     215   156015847 :     res=res*stretch+shift;
     216   156015847 :     dfunc*=stretch;
     217             :   }
     218   162833480 :   return res;
     219             : }
     220             : 
     221    31818704 : double baseSwitch::calculateSqr(double distance2,double&dfunc) const {
     222    31818704 :   double res= calculate(std::sqrt(distance2),dfunc);//RVO!
     223    31818704 :   return res;
     224             : }
     225           8 : double baseSwitch::get_d0() const {
     226           8 :   return d0;
     227             : }
     228        1534 : double baseSwitch::get_r0() const {
     229        1534 :   return 1.0/invr0;
     230             : }
     231   536580542 : double baseSwitch::get_dmax() const {
     232   536580542 :   return dmax;
     233             : }
     234    49030642 : double baseSwitch::get_dmax2() const {
     235    49030642 :   return dmax_2;
     236             : }
     237        1502 : std::string baseSwitch::description() const {
     238        1502 :   std::ostringstream ostr;
     239        1502 :   ostr<<get_r0()
     240             :       <<".  Using "
     241             :       << mytype
     242        3004 :       <<" switching function with parameters d0="<< d0
     243        3004 :       << specificDescription();
     244        1502 :   return ostr.str();
     245        1502 : }
     246         150 : std::string baseSwitch::specificDescription() const {
     247         150 :   return "";
     248             : }
     249         239 : void baseSwitch::setupStretch() {
     250         239 :   if(dmax!=std::numeric_limits<double>::max()) {
     251         239 :     stretch=1.0;
     252         239 :     shift=0.0;
     253             :     double dummy;
     254         239 :     double s0=calculate(0.0,dummy);
     255         239 :     double sd=calculate(dmax,dummy);
     256         239 :     stretch=1.0/(s0-sd);
     257         239 :     shift=-sd*stretch;
     258             :   }
     259         239 : }
     260           0 : void baseSwitch::removeStretch() {
     261           0 :   stretch=1.0;
     262           0 :   shift=0.0;
     263           0 : }
     264             : template<int N, std::enable_if_t< (N >0), bool> = true, std::enable_if_t< (N %2 == 0), bool> = true>
     265             :     class fixedRational :public baseSwitch {
     266         263 :   std::string specificDescription() const override {
     267         263 :     std::ostringstream ostr;
     268         263 :     ostr << " nn=" << N << " mm=" <<N*2;
     269         263 :     return ostr.str();
     270         263 :   }
     271             : public:
     272         284 :   fixedRational(double D0,double DMAX, double R0)
     273         284 :     :baseSwitch(D0,DMAX,R0,"rational") {}
     274             : 
     275             :   template <int POW>
     276        1382 :   static inline double doRational(const double rdist, double&dfunc, double result=0.0) {
     277             :     const double rNdist=Tools::fastpow<POW-1>(rdist);
     278    27485057 :     result=1.0/(1.0+rNdist*rdist);
     279    27485057 :     dfunc = -POW*rNdist*result*result;
     280        1382 :     return result;
     281             :   }
     282             : 
     283    16154945 :   inline double function(double rdist,double&dfunc) const override {
     284             :     //preRes and preDfunc are passed already set
     285        1382 :     dfunc=0.0;
     286        1382 :     double result = doRational<N>(rdist,dfunc);
     287    16154945 :     return result;
     288             :   }
     289             : 
     290    11475870 :   double calculateSqr(double distance2,double&dfunc) const override {
     291             :     double result=0.0;
     292    11475870 :     dfunc=0.0;
     293    11475870 :     if(distance2 <= dmax_2) {
     294    11330112 :       const double rdist = distance2*invr0_2;
     295             :       result = doRational<N/2>(rdist,dfunc);
     296    11330112 :       dfunc*=2*invr0_2;
     297             :       // stretch:
     298    11330112 :       result=result*stretch+shift;
     299    11330112 :       dfunc*=stretch;
     300             :     }
     301    11475870 :     return result;
     302             : 
     303             :   }
     304             : };
     305             : 
     306             : //these enums are useful for clarifying the settings in the factory
     307             : //and the code is autodocumented ;)
     308             : enum class rationalPow:bool {standard, fast};
     309             : enum class rationalForm:bool {standard, simplified};
     310             : 
     311             : template<rationalPow isFast, rationalForm nis2m>
     312             : class rational : public baseSwitch {
     313             : protected:
     314             :   const int nn=6;
     315             :   const int mm=12;
     316             :   const double preRes;
     317             :   const double preDfunc;
     318             :   const double preSecDev;
     319             :   const int nnf;
     320             :   const int mmf;
     321             :   const double preDfuncF;
     322             :   const double preSecDevF;
     323             :   //I am using PLMD::epsilon to be certain to call the one defined in Tools.h
     324             :   static constexpr double moreThanOne=1.0+5.0e10*PLMD::epsilon;
     325             :   static constexpr double lessThanOne=1.0-5.0e10*PLMD::epsilon;
     326             : 
     327         177 :   std::string specificDescription() const override {
     328         177 :     std::ostringstream ostr;
     329         177 :     ostr << " nn=" << nn << " mm=" <<mm;
     330         177 :     return ostr.str();
     331         177 :   }
     332             : public:
     333         202 :   rational(double D0,double DMAX, double R0, int N, int M)
     334             :     :baseSwitch(D0,DMAX,R0,"rational"),
     335         202 :      nn(N),
     336         202 :      mm([](int m,int n) {
     337         202 :     if (m==0) {
     338          91 :       return n*2;
     339             :     } else {
     340             :       return m;
     341             :     }
     342             :   }(M,N)),
     343         202 :   preRes(static_cast<double>(nn)/mm),
     344         202 :   preDfunc(0.5*nn*(nn-mm)/static_cast<double>(mm)),
     345             :   //wolfram <3:lim_(x->1) d^2/(dx^2) (1 - x^N)/(1 - x^M) = (N (M^2 - 3 M (-1 + N) + N (-3 + 2 N)))/(6 M)
     346         202 :   preSecDev ((nn * (mm * mm - 3.0* mm * (-1 + nn ) + nn *(-3 + 2* nn )))/(6.0* mm )),
     347         202 :   nnf(nn/2),
     348         202 :   mmf(mm/2),
     349         202 :   preDfuncF(0.5*nnf*(nnf-mmf)/static_cast<double>(mmf)),
     350         202 :   preSecDevF((nnf* (mmf*mmf - 3.0* mmf* (-1 + nnf) + nnf*(-3 + 2* nnf)))/(6.0* mmf)) {}
     351             : 
     352    18240750 :   static inline double doRational(const double rdist, double&dfunc,double secDev, const int N,
     353             :                                   const int M,double result=0.0) {
     354             :     //the result and dfunc are assigned in the drivers for doRational
     355             :     //if(rdist>(1.0-100.0*epsilon) && rdist<(1.0+100.0*epsilon)) {
     356             :     //result=preRes;
     357             :     //dfunc=preDfunc;
     358             :     //} else {
     359             :     if constexpr (nis2m==rationalForm::simplified) {
     360     2114004 :       const double rNdist=Tools::fastpow(rdist,N-1);
     361     2114004 :       result=1.0/(1.0+rNdist*rdist);
     362     2114004 :       dfunc = -N*rNdist*result*result;
     363             :     } else {
     364    16126746 :       if(!((rdist > lessThanOne) && (rdist < moreThanOne))) {
     365    16126734 :         const double rNdist=Tools::fastpow(rdist,N-1);
     366    16126734 :         const double rMdist=Tools::fastpow(rdist,M-1);
     367    16126734 :         const double num = 1.0-rNdist*rdist;
     368    16126734 :         const double iden = 1.0/(1.0-rMdist*rdist);
     369    16126734 :         result = num*iden;
     370    16126734 :         dfunc = ((M*result*rMdist)-(N*rNdist))*iden;
     371    16126734 :       } else {
     372             :         //here I imply that the correct initialized are being passed to doRational
     373          12 :         const double x =(rdist-1.0);
     374          12 :         result = result+ x * ( dfunc + 0.5 * x * secDev);
     375          12 :         dfunc  = dfunc + x * secDev;
     376             :       }
     377             :     }
     378    18240750 :     return result;
     379             :   }
     380    18240684 :   inline double function(double rdist,double&dfunc) const override {
     381             :     //preRes and preDfunc are passed already set
     382    18240684 :     dfunc=preDfunc;
     383    18240684 :     double result = doRational(rdist,dfunc,preSecDev,nn,mm,preRes);
     384    18240684 :     return result;
     385             :   }
     386             : 
     387     3408419 :   double calculateSqr(double distance2,double&dfunc) const override {
     388             :     if constexpr (isFast==rationalPow::fast) {
     389             :       double result=0.0;
     390          80 :       dfunc=0.0;
     391          80 :       if(distance2 <= dmax_2) {
     392          66 :         const double rdist = distance2*invr0_2;
     393          66 :         dfunc=preDfuncF;
     394          66 :         result = doRational(rdist,dfunc,preSecDevF,nnf,mmf,preRes);
     395          66 :         dfunc*=2*invr0_2;
     396             : // stretch:
     397          66 :         result=result*stretch+shift;
     398          66 :         dfunc*=stretch;
     399             :       }
     400          80 :       return result;
     401             :     } else {
     402     3408339 :       double res= calculate(std::sqrt(distance2),dfunc);//RVO!
     403     3408339 :       return res;
     404             :     }
     405             :   }
     406             : };
     407             : 
     408             : 
     409             : template<int EXP,std::enable_if_t< (EXP %2 == 0), bool> = true>
     410        1087 : std::optional<std::unique_ptr<baseSwitch>> fixedRationalFactory(double D0,double DMAX, double R0, int N) {
     411             :   if constexpr (EXP == 0) {
     412           0 :     return  std::nullopt;
     413             :   } else {
     414        1087 :     if (N==EXP) {
     415         284 :       return PLMD::Tools::make_unique<switchContainers::fixedRational<EXP>>(D0,DMAX,R0);
     416             :     } else {
     417         803 :       return fixedRationalFactory<EXP-2>(D0,DMAX,R0,N);
     418             :     }
     419             :   }
     420             : }
     421             : 
     422             : std::unique_ptr<baseSwitch>
     423         486 : rationalFactory(double D0,double DMAX, double R0, int N, int M) {
     424         486 :   bool fast = N%2==0 && M%2==0 && D0==0.0;
     425             :   //if (M==0) M will automatically became 2*NN
     426             :   constexpr int highestPrecompiledPower=12;
     427             :   //precompiled rational
     428         486 :   if(((2*N)==M || M == 0) && fast && N<=highestPrecompiledPower) {
     429         284 :     auto tmp = fixedRationalFactory<highestPrecompiledPower>(D0,DMAX,R0,N);
     430         284 :     if(tmp) {
     431             :       return std::move(*tmp);
     432             :     }
     433             :     //else continue with the at runtime implementation
     434             :   }
     435             :   //template<bool isFast, bool n2m>
     436             :   //class rational : public baseSwitch
     437         202 :   if(2*N==M || M == 0) {
     438         134 :     if(fast) {
     439             :       //fast rational
     440             :       return PLMD::Tools::make_unique<switchContainers::rational<
     441           0 :              rationalPow::fast,rationalForm::simplified>>(D0,DMAX,R0,N,M);
     442             :     }
     443             :     return PLMD::Tools::make_unique<switchContainers::rational<
     444         134 :            rationalPow::standard,rationalForm::simplified>>(D0,DMAX,R0,N,M);
     445             :   }
     446          68 :   if(fast) {
     447             :     //fast rational
     448             :     return PLMD::Tools::make_unique<switchContainers::rational<
     449          63 :            rationalPow::fast,rationalForm::standard>>(D0,DMAX,R0,N,M);
     450             :   }
     451             :   return PLMD::Tools::make_unique<switchContainers::rational<
     452           5 :          rationalPow::standard,rationalForm::standard>>(D0,DMAX,R0,N,M);
     453             : }
     454             : //function =
     455             : 
     456             : class exponentialSwitch: public baseSwitch {
     457             : public:
     458          77 :   exponentialSwitch(double D0, double DMAX, double R0)
     459          77 :     :baseSwitch(D0,DMAX,R0,"exponential") {}
     460             : protected:
     461     2404268 :   inline double function(const double rdist,double&dfunc) const override {
     462     2404268 :     double result = std::exp(-rdist);
     463     2404268 :     dfunc=-result;
     464     2404268 :     return result;
     465             :   }
     466             : };
     467             : 
     468             : class gaussianSwitch: public baseSwitch {
     469             : public:
     470          68 :   gaussianSwitch(double D0, double DMAX, double R0)
     471          68 :     :baseSwitch(D0,DMAX,R0,"gaussian") {}
     472             : protected:
     473      279665 :   inline double function(const double rdist,double&dfunc) const override {
     474      279665 :     double result = std::exp(-0.5*rdist*rdist);
     475      279665 :     dfunc=-rdist*result;
     476      279665 :     return result;
     477             :   }
     478             : };
     479             : 
     480             : class fastGaussianSwitch: public baseSwitch {
     481             : public:
     482         116 :   fastGaussianSwitch(double /*D0*/, double DMAX, double /*R0*/)
     483         116 :     :baseSwitch(0.0,DMAX,1.0,"fastgaussian") {}
     484             : protected:
     485          14 :   inline double function(const double rdist,double&dfunc) const override {
     486          14 :     double result = std::exp(-0.5*rdist*rdist);
     487          14 :     dfunc=-rdist*result;
     488          14 :     return result;
     489             :   }
     490    38317832 :   inline double calculateSqr(double distance2,double&dfunc) const override {
     491             :     double result = 0.0;
     492    38317832 :     dfunc = 0.0;
     493    38317832 :     if (distance2 <dmax_2) {
     494             :       result=1.0;
     495             : 
     496    38317818 :       if(distance2>0.0) {
     497    38317754 :         result = exp(-0.5*distance2);
     498    38317754 :         dfunc = -result;
     499             :         // stretch:
     500    38317754 :         result=result*stretch+shift;
     501    38317754 :         dfunc*=stretch;
     502             :       }
     503             :     }
     504    38317832 :     return result;
     505             :   }
     506             : };
     507             : 
     508             : class smapSwitch: public baseSwitch {
     509             :   const int a=0;
     510             :   const int b=0;
     511             :   const double c=0.0;
     512             :   const double d=0.0;
     513             : protected:
     514          15 :   std::string specificDescription() const override {
     515          15 :     std::ostringstream ostr;
     516          15 :     ostr<<" a="<<a<<" b="<<b;
     517          15 :     return ostr.str();
     518          15 :   }
     519             : public:
     520          17 :   smapSwitch(double D0, double DMAX, double R0, int A, int B)
     521          17 :     :baseSwitch(D0,DMAX,R0,"smap"),
     522          17 :      a(A),
     523          17 :      b(B),
     524          17 :      c(std::pow(2., static_cast<double>(a)/static_cast<double>(b) ) - 1.0),
     525          17 :      d(-static_cast<double>(b) / static_cast<double>(a)) {}
     526             : protected:
     527    21911351 :   inline double function(const double rdist,double&dfunc) const override {
     528             : 
     529    21911351 :     const double sx=c*Tools::fastpow( rdist, a );
     530    21911351 :     double result=std::pow( 1.0 + sx, d );
     531    21911351 :     dfunc=-b*sx/rdist*result/(1.0+sx);
     532    21911351 :     return result;
     533             :   }
     534             : };
     535             : 
     536             : class cubicSwitch: public baseSwitch {
     537             : protected:
     538          15 :   std::string specificDescription() const override {
     539          15 :     std::ostringstream ostr;
     540          15 :     ostr<<" dmax="<<dmax;
     541          15 :     return ostr.str();
     542          15 :   }
     543             : public:
     544          17 :   cubicSwitch(double D0, double DMAX)
     545          17 :     :baseSwitch(D0,DMAX,DMAX-D0,"cubic") {
     546             :     //this operation should be already done!!
     547             :     // R0 = dmax - d0;
     548             :     // invr0 = 1/R0;
     549             :     // invr0_2 = invr0*invr0;
     550          17 :   }
     551          17 :   ~cubicSwitch()=default;
     552             : protected:
     553      127277 :   inline double function(const double rdist,double&dfunc) const override {
     554      127277 :     const double tmp1 = rdist - 1.0;
     555      127277 :     const double tmp2 = 1.0+2.0*rdist;
     556             :     //double result = tmp1*tmp1*tmp2;
     557      127277 :     dfunc = 2*tmp1*tmp2 + 2*tmp1*tmp1;
     558      127277 :     return tmp1*tmp1*tmp2;
     559             :   }
     560             : };
     561             : 
     562             : class tanhSwitch: public baseSwitch {
     563             : public:
     564           6 :   tanhSwitch(double D0, double DMAX, double R0)
     565           6 :     :baseSwitch(D0,DMAX,R0,"tanh") {}
     566             : protected:
     567       12743 :   inline double function(const double rdist,double&dfunc) const override {
     568       12743 :     const double tmp1 = std::tanh(rdist);
     569             :     //was dfunc=-(1-tmp1*tmp1);
     570       12743 :     dfunc = tmp1 * tmp1 - 1.0;
     571             :     //return result;
     572       12743 :     return 1.0 - tmp1;
     573             :   }
     574             : };
     575             : 
     576             : class cosinusSwitch: public baseSwitch {
     577             : public:
     578           5 :   cosinusSwitch(double D0, double DMAX, double R0)
     579           5 :     :baseSwitch(D0,DMAX,R0,"cosinus") {}
     580             : protected:
     581      522147 :   inline double function(const double rdist,double&dfunc) const override {
     582             :     double result = 0.0;
     583      522147 :     dfunc=0.0;
     584      522147 :     if(rdist<=1.0) {
     585             : // rdist = (r-r1)/(r2-r1) ; 0.0<=rdist<=1.0 if r1 <= r <=r2; (r2-r1)/(r2-r1)=1
     586      227036 :       double rdistPI = rdist * PLMD::pi;
     587      227036 :       result = 0.5 * (std::cos ( rdistPI ) + 1.0);
     588      227036 :       dfunc = -0.5 * PLMD::pi * std::sin ( rdistPI );
     589             :     }
     590      522147 :     return result;
     591             :   }
     592             : };
     593             : 
     594             : class nativeqSwitch: public baseSwitch {
     595             :   double beta = 50.0;  // nm-1
     596             :   double lambda = 1.8; // unitless
     597             :   double ref=0.0;
     598             : protected:
     599         864 :   std::string specificDescription() const override {
     600         864 :     std::ostringstream ostr;
     601         864 :     ostr<<" beta="<<beta<<" lambda="<<lambda<<" ref="<<ref;
     602         864 :     return ostr.str();
     603         864 :   }
     604           0 :   inline double function(const double rdist,double&dfunc) const override {
     605           0 :     return 0.0;
     606             :   }
     607             : public:
     608             :   nativeqSwitch(double D0, double DMAX, double R0, double BETA, double LAMBDA,double REF)
     609         866 :     :  baseSwitch(D0,DMAX,R0,"nativeq"),beta(BETA),lambda(LAMBDA),ref(REF) {}
     610      292966 :   double calculate(const double distance, double& dfunc) const override {
     611             :     double res = 0.0;//RVO!
     612      292966 :     dfunc = 0.0;
     613      292966 :     if(distance<=dmax) {
     614             :       res = 1.0;
     615      292946 :       if(distance > d0) {
     616      292934 :         const double rdist = beta*(distance - lambda * ref);
     617      292934 :         double exprdist=std::exp(rdist);
     618      292934 :         res=1.0/(1.0+exprdist);
     619             :         /*2.9
     620             :         //need to see if this (5op+assign)
     621             :         //double exprmdist=1.0 + exprdist;
     622             :         //dfunc = - (beta *exprdist)/(exprmdist*exprmdist);
     623             :         //or this (5op but 2 divisions) is faster
     624             :         dfunc = - beta /(exprdist+ 2.0 +1.0/exprdist);
     625             :         //this cames from - beta * exprdist/(exprdist*exprdist+ 2.0 *exprdist +1.0)
     626             :         //dfunc *= invr0;
     627             :         dfunc /= distance;
     628             :         */
     629             :         //2.10
     630      292934 :         dfunc = - beta /(exprdist+ 2.0 +1.0/exprdist) /distance;
     631             : 
     632      292934 :         dfunc*=stretch;
     633             :       }
     634      292946 :       res=res*stretch+shift;
     635             :     }
     636      292966 :     return res;
     637             :   }
     638             : };
     639             : 
     640             : class leptonSwitch: public baseSwitch {
     641             : /// Lepton expression.
     642         134 :   class funcAndDeriv {
     643             :     lepton::CompiledExpression expression;
     644             :     lepton::CompiledExpression deriv;
     645             :     double* varRef=nullptr;
     646             :     double* varDevRef=nullptr;
     647             :   public:
     648          44 :     funcAndDeriv(const std::string &func) {
     649          44 :       lepton::ParsedExpression pe=lepton::Parser::parse(func).optimize(lepton::Constants());
     650          44 :       expression=pe.createCompiledExpression();
     651          46 :       std::string arg="x";
     652             : 
     653             :       {
     654          44 :         auto vars=expression.getVariables();
     655          44 :         bool found_x=std::find(vars.begin(),vars.end(),"x")!=vars.end();
     656          44 :         bool found_x2=std::find(vars.begin(),vars.end(),"x2")!=vars.end();
     657             : 
     658          44 :         if(found_x2) {
     659             :           arg="x2";
     660             :         }
     661          44 :         if (vars.size()==0) {
     662             : // this is necessary since in some cases lepton thinks a variable is not present even though it is present
     663             : // e.g. func=0*x
     664           0 :           varRef=nullptr;
     665          44 :         } else if(vars.size()==1 && (found_x || found_x2)) {
     666          42 :           varRef=&expression.getVariableReference(arg);
     667             :         } else {
     668           4 :           plumed_error()
     669             :               <<"Please declare a function with only ONE argument that can only be x or x2. Your function is: "
     670           4 :               << func;
     671             :         }
     672             :       }
     673             : 
     674          86 :       lepton::ParsedExpression ped=lepton::Parser::parse(func).differentiate(arg).optimize(lepton::Constants());
     675          42 :       deriv=ped.createCompiledExpression();
     676             :       {
     677          42 :         auto vars=expression.getVariables();
     678          42 :         if (vars.size()==0) {
     679           0 :           varDevRef=nullptr;
     680             :         } else {
     681          42 :           varDevRef=&deriv.getVariableReference(arg);
     682             :         }
     683             :       }
     684             : 
     685          46 :     }
     686          92 :     funcAndDeriv (const funcAndDeriv& other):
     687          92 :       expression(other.expression),
     688          92 :       deriv(other.deriv) {
     689          92 :       std::string arg="x";
     690             : 
     691             :       {
     692          92 :         auto vars=expression.getVariables();
     693          92 :         bool found_x=std::find(vars.begin(),vars.end(),"x")!=vars.end();
     694          92 :         bool found_x2=std::find(vars.begin(),vars.end(),"x2")!=vars.end();
     695             : 
     696          92 :         if(found_x2) {
     697             :           arg="x2";
     698             :         }
     699          92 :         if (vars.size()==0) {
     700           0 :           varRef=nullptr;
     701          92 :         } else if(vars.size()==1 && (found_x || found_x2)) {
     702          92 :           varRef=&expression.getVariableReference(arg);
     703             :         }// UB: I assume that the function is already correct
     704             :       }
     705             : 
     706             :       {
     707          92 :         auto vars=expression.getVariables();
     708          92 :         if (vars.size()==0) {
     709           0 :           varDevRef=nullptr;
     710             :         } else {
     711          92 :           varDevRef=&deriv.getVariableReference(arg);
     712             :         }
     713             :       }
     714          92 :     }
     715             : 
     716             :     funcAndDeriv& operator= (const funcAndDeriv& other) {
     717             :       if(this != &other) {
     718             :         expression = other.expression;
     719             :         deriv = other.deriv;
     720             :         std::string arg="x";
     721             : 
     722             :         {
     723             :           auto vars=expression.getVariables();
     724             :           bool found_x=std::find(vars.begin(),vars.end(),"x")!=vars.end();
     725             :           bool found_x2=std::find(vars.begin(),vars.end(),"x2")!=vars.end();
     726             : 
     727             :           if(found_x2) {
     728             :             arg="x2";
     729             :           }
     730             :           if (vars.size()==0) {
     731             :             varRef=nullptr;
     732             :           } else if(vars.size()==1 && (found_x || found_x2)) {
     733             :             varRef=&expression.getVariableReference(arg);
     734             :           }// UB: I assume that the function is already correct
     735             :         }
     736             : 
     737             :         {
     738             :           auto vars=expression.getVariables();
     739             :           if (vars.size()==0) {
     740             :             varDevRef=nullptr;
     741             :           } else {
     742             :             varDevRef=&deriv.getVariableReference(arg);
     743             :           }
     744             :         }
     745             :       }
     746             :       return *this;
     747             :     }
     748             : 
     749     6515577 :     std::pair<double,double> operator()(double const x) const {
     750             :       //FAQ: why this works? this thing is const and you are modifying things!
     751             :       //Actually I am modifying something that is pointed at, not my pointers,
     752             :       //so I am not mutating the state of this!
     753     6515577 :       if(varRef) {
     754     6515577 :         *varRef=x;
     755             :       }
     756     6515577 :       if(varDevRef) {
     757     6515577 :         *varDevRef=x;
     758             :       }
     759             :       return std::make_pair(
     760     6515577 :                expression.evaluate(),
     761     6515577 :                deriv.evaluate());
     762             :     }
     763             : 
     764             :     auto& getVariables() const {
     765          42 :       return expression.getVariables();
     766             :     }
     767             :     auto& getVariables_derivative() const {
     768             :       return deriv.getVariables();
     769             :     }
     770             :   };
     771             :   /// Function for lepton
     772             :   std::string lepton_func;
     773             :   /// \warning Since lepton::CompiledExpression is mutable, a vector is necessary for multithreading!
     774             :   std::vector <funcAndDeriv> expressions{};
     775             :   /// Set to true if lepton only uses x2
     776             :   bool leptonx2=false;
     777             : protected:
     778          18 :   std::string specificDescription() const override {
     779          18 :     std::ostringstream ostr;
     780          18 :     ostr<<" func=" << lepton_func;
     781          18 :     return ostr.str();
     782          18 :   }
     783           0 :   inline double function(const double,double&) const override {
     784           0 :     return 0.0;
     785             :   }
     786             : public:
     787          44 :   leptonSwitch(double D0, double DMAX, double R0, const std::string & func)
     788          44 :     :baseSwitch(D0,DMAX,R0,"lepton"),
     789          44 :      lepton_func(func),
     790          86 :      expressions  (OpenMP::getNumThreads(), lepton_func) {
     791             :     //this is a bit odd, but it works
     792             :     auto vars=expressions[0].getVariables();
     793          42 :     leptonx2=std::find(vars.begin(),vars.end(),"x2")!=vars.end();
     794          44 :   }
     795             : 
     796     5878300 :   double calculate(const double distance,double&dfunc) const override {
     797     5878300 :     double res = 0.0;//RVO!
     798     5878300 :     dfunc = 0.0;
     799     5878300 :     if(leptonx2) {
     800           2 :       res= calculateSqr(distance*distance,dfunc);
     801             :     } else {
     802     5878298 :       if(distance<=dmax) {
     803     5573465 :         res = 1.0;
     804     5573465 :         const double rdist = (distance-d0)*invr0;
     805     5573465 :         if(rdist > 0.0) {
     806     5267475 :           const unsigned t=OpenMP::getThreadNum();
     807     5267475 :           plumed_assert(t<expressions.size());
     808     5267475 :           std::tie(res,dfunc) = expressions[t](rdist);
     809     5267475 :           dfunc *= invr0;
     810     5267475 :           dfunc /= distance;
     811             :         }
     812     5573465 :         res=res*stretch+shift;
     813     5573465 :         dfunc*=stretch;
     814             :       }
     815             :     }
     816     5878300 :     return res;
     817             :   }
     818             : 
     819     7126130 :   double calculateSqr(const double distance2,double&dfunc) const override {
     820     7126130 :     double result =0.0;
     821     7126130 :     dfunc=0.0;
     822     7126130 :     if(leptonx2) {
     823     1248110 :       if(distance2<=dmax_2) {
     824     1248102 :         const unsigned t=OpenMP::getThreadNum();
     825     1248102 :         const double rdist_2 = distance2*invr0_2;
     826     1248102 :         plumed_assert(t<expressions.size());
     827     1248102 :         std::tie(result,dfunc) = expressions[t](rdist_2);
     828             :         // chain rule:
     829     1248102 :         dfunc*=2*invr0_2;
     830             :         // stretch:
     831     1248102 :         result=result*stretch+shift;
     832     1248102 :         dfunc*=stretch;
     833             :       }
     834             :     } else {
     835     5878020 :       result = calculate(std::sqrt(distance2),dfunc);
     836             :     }
     837     7126130 :     return result;
     838             :   }
     839             : };
     840             : } // namespace switchContainers
     841             : 
     842           0 : void SwitchingFunction::registerKeywords( Keywords& keys ) {
     843           0 :   keys.add("compulsory","R_0","the value of R_0 in the switching function");
     844           0 :   keys.add("compulsory","D_0","0.0","the value of D_0 in the switching function");
     845           0 :   keys.add("optional","D_MAX","the value at which the switching function can be assumed equal to zero");
     846           0 :   keys.add("compulsory","NN","6","the value of n in the switching function (only needed for TYPE=RATIONAL)");
     847           0 :   keys.add("compulsory","MM","0","the value of m in the switching function (only needed for TYPE=RATIONAL); 0 implies 2*NN");
     848           0 :   keys.add("compulsory","A","the value of a in the switching function (only needed for TYPE=SMAP)");
     849           0 :   keys.add("compulsory","B","the value of b in the switching function (only needed for TYPE=SMAP)");
     850           0 : }
     851             : 
     852        1629 : void SwitchingFunction::set(const std::string & definition,std::string& errormsg) {
     853        1629 :   std::vector<std::string> data=Tools::getWords(definition);
     854             : #define CHECKandPARSE(datastring,keyword,variable,errormsg) \
     855             :   if(Tools::findKeyword(datastring,keyword) && !Tools::parse(datastring,keyword,variable))\
     856             :     errormsg="could not parse " keyword; //adiacent strings are automagically concatenated
     857             : #define REQUIREDPARSE(datastring,keyword,variable,errormsg) \
     858             :   if(!Tools::parse(datastring,keyword,variable))\
     859             :     errormsg=keyword " is required for " + name ; //adiacent strings are automagically concatenated
     860             : 
     861        1629 :   if( data.size()<1 ) {
     862             :     errormsg="missing all input for switching function";
     863             :     return;
     864             :   }
     865        1629 :   std::string name=data[0];
     866             :   data.erase(data.begin());
     867        1629 :   double r0=0.0;
     868        1629 :   double d0=0.0;
     869        1629 :   double dmax=std::numeric_limits<double>::max();
     870        1629 :   init=true;
     871        2097 :   CHECKandPARSE(data,"D_0",d0,errormsg);
     872        2061 :   CHECKandPARSE(data,"D_MAX",dmax,errormsg);
     873             : 
     874        1629 :   bool dostretch=false;
     875        1629 :   Tools::parseFlag(data,"STRETCH",dostretch); // this is ignored now
     876        1629 :   dostretch=true;
     877        1629 :   bool dontstretch=false;
     878        1629 :   Tools::parseFlag(data,"NOSTRETCH",dontstretch); // this is ignored now
     879        1629 :   if(dontstretch) {
     880         199 :     dostretch=false;
     881             :   }
     882        1629 :   if(name=="CUBIC") {
     883             :     //cubic is the only switch type that only uses d0 and dmax
     884          17 :     function = PLMD::Tools::make_unique<switchContainers::cubicSwitch>(d0,dmax);
     885             :   } else {
     886        3224 :     REQUIREDPARSE(data,"R_0",r0,errormsg);
     887        1612 :     if(name=="RATIONAL") {
     888         412 :       int nn=6;
     889         412 :       int mm=0;
     890         680 :       CHECKandPARSE(data,"NN",nn,errormsg);
     891         670 :       CHECKandPARSE(data,"MM",mm,errormsg);
     892         824 :       function = switchContainers::rationalFactory(d0,dmax,r0,nn,mm);
     893        1200 :     } else if(name=="SMAP") {
     894          17 :       int a=0;
     895          17 :       int b=0;
     896             :       //in the original a and b are "default=0",
     897             :       //but you divide by a and b during the initialization!
     898             :       //better an error message than an UB, so no default
     899          34 :       REQUIREDPARSE(data,"A",a,errormsg);
     900          34 :       REQUIREDPARSE(data,"B",b,errormsg);
     901          17 :       function = PLMD::Tools::make_unique<switchContainers::smapSwitch>(d0,dmax,r0,a,b);
     902        1183 :     } else if(name=="Q") {
     903         866 :       double beta = 50.0;  // nm-1
     904         866 :       double lambda = 1.8; // unitless
     905             :       double ref;
     906        2598 :       CHECKandPARSE(data,"BETA",beta,errormsg);
     907        2598 :       CHECKandPARSE(data,"LAMBDA",lambda,errormsg);
     908        1732 :       REQUIREDPARSE(data,"REF",ref,errormsg);
     909             :       //the original error message was not standard
     910             :       // if(!Tools::parse(data,"REF",ref))
     911             :       //   errormsg="REF (reference distaance) is required for native Q";
     912         866 :       function = PLMD::Tools::make_unique<switchContainers::nativeqSwitch>(d0,dmax,r0,beta,lambda,ref);
     913         317 :     } else if(name=="EXP") {
     914          77 :       function = PLMD::Tools::make_unique<switchContainers::exponentialSwitch>(d0,dmax,r0);
     915         240 :     } else if(name=="GAUSSIAN") {
     916         184 :       if ( r0==1.0 && d0==0.0 ) {
     917         116 :         function = PLMD::Tools::make_unique<switchContainers::fastGaussianSwitch>(d0,dmax,r0);
     918             :       } else {
     919          68 :         function = PLMD::Tools::make_unique<switchContainers::gaussianSwitch>(d0,dmax,r0);
     920             :       }
     921          56 :     } else if(name=="TANH") {
     922           6 :       function = PLMD::Tools::make_unique<switchContainers::tanhSwitch>(d0,dmax,r0);
     923          50 :     } else if(name=="COSINUS") {
     924           5 :       function = PLMD::Tools::make_unique<switchContainers::cosinusSwitch>(d0,dmax,r0);
     925          87 :     } else if((name=="MATHEVAL" || name=="CUSTOM")) {
     926             :       std::string func;
     927          88 :       Tools::parse(data,"FUNC",func);
     928          42 :       function = PLMD::Tools::make_unique<switchContainers::leptonSwitch>(d0,dmax,r0,func);
     929             :     } else {
     930           2 :       errormsg="cannot understand switching function type '"+name+"'";
     931             :     }
     932             :   }
     933             : #undef CHECKandPARSE
     934             : #undef REQUIREDPARSE
     935             : 
     936        1627 :   if( !data.empty() ) {
     937             :     errormsg="found the following rogue keywords in switching function input : ";
     938           0 :     for(unsigned i=0; i<data.size(); ++i) {
     939           2 :       errormsg = errormsg + data[i] + " ";
     940             :     }
     941             :   }
     942             : 
     943        1627 :   if(dostretch && dmax!=std::numeric_limits<double>::max()) {
     944         165 :     function->setupStretch();
     945             :   }
     946        1629 : }
     947             : 
     948        1502 : std::string SwitchingFunction::description() const {
     949             :   // if this error is necessary, something went wrong in the constructor
     950             :   //  plumed_merror("Unknown switching function type");
     951        1502 :   return function->description();
     952             : }
     953             : 
     954    92146953 : double SwitchingFunction::calculateSqr(double distance2,double&dfunc)const {
     955    92146953 :   return function -> calculateSqr(distance2, dfunc);
     956             : }
     957             : 
     958   127899205 : double SwitchingFunction::calculate(double distance,double&dfunc)const {
     959   127899205 :   plumed_massert(init,"you are trying to use an unset SwitchingFunction");
     960   127899205 :   double result=function->calculate(distance,dfunc);
     961   127899205 :   return result;
     962             : }
     963             : 
     964          74 : void SwitchingFunction::set(const int nn,int mm, const double r0, const double d0) {
     965          74 :   init=true;
     966          74 :   if(mm == 0) {
     967          70 :     mm = 2*nn;
     968             :   }
     969          74 :   double dmax=d0+r0*std::pow(0.00001,1./(nn-mm));
     970         148 :   function = switchContainers::rationalFactory(d0,dmax,r0,nn,mm);
     971          74 :   function->setupStretch();
     972          74 : }
     973             : 
     974          32 : double SwitchingFunction::get_r0() const {
     975          32 :   return function->get_r0();
     976             : }
     977             : 
     978           8 : double SwitchingFunction::get_d0() const {
     979           8 :   return function->get_d0();
     980             : }
     981             : 
     982   536580542 : double SwitchingFunction::get_dmax() const {
     983   536580542 :   return function->get_dmax();
     984             : }
     985             : 
     986    49030642 : double SwitchingFunction::get_dmax2() const {
     987    49030642 :   return function->get_dmax2();
     988             : }
     989             : 
     990             : }// Namespace PLMD

Generated by: LCOV version 1.16