LCOV - code coverage report
Current view: top level - tools - KernelFunctions.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 121 298 40.6 %
Date: 2026-11-05 10:19:04 Functions: 8 11 72.7 %

          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 "KernelFunctions.h"
      23             : #include <limits>
      24             : #include "IFile.h"
      25             : #include <cmath>
      26             : 
      27             : namespace PLMD {
      28             : 
      29             : //+PLUMEDOC INTERNAL kernelfunctions
      30             : /*
      31             : Functions that are used to construct histograms
      32             : 
      33             : Constructing histograms is something you learned to do relatively early in life. You perform an experiment a number of times,
      34             : count the number of times each result comes up and then draw a bar graph that describes how often each of the results came up.
      35             : This only works when there are a finite number of possible results.  If the result a number between 0 and 1 the bar chart is
      36             : less easy to draw as there are as many possible results as there are numbers between zero and one - an infinite number.
      37             : To resolve this problem we replace probability, \f$P\f$ with probability density, \f$\pi\f$, and write the probability of getting
      38             : a number between \f$a\f$ and \f$b\f$ as:
      39             : 
      40             : \f[
      41             : P = \int_{a}^b \textrm{d}x \pi(x)
      42             : \f]
      43             : 
      44             : To calculate probability densities from a set of results we use a process called kernel density estimation.
      45             : Histograms are accumulated by adding up kernel functions, \f$K\f$, with finite spatial extent, that integrate to one.
      46             : These functions are centered on each of the \f$n\f$-dimensional data points, \f$\mathbf{x}_i\f$. The overall effect of this
      47             : is that each result we obtain in our experiments contributes to the probability density in a finite sized region of the space.
      48             : 
      49             : Expressing all this mathematically in kernel density estimation we write the probability density as:
      50             : 
      51             : \f[
      52             : \pi(\mathbf{x}) =  \sum_i K\left[ (\mathbf{x} - \mathbf{x}_i)^T \Sigma (\mathbf{x} - \mathbf{x}_i) \right]
      53             : \f]
      54             : 
      55             : where \f$\Sigma\f$ is an \f$n \times n\f$ matrix called the bandwidth that controls the spatial extent of
      56             : the kernel. Whenever we accumulate a histogram (e.g. in \ref HISTOGRAM or in \ref METAD) we use this
      57             : technique.
      58             : 
      59             : There is thus some flexibility in the particular function we use for \f$K[\mathbf{r}]\f$ in the above.
      60             : The following variants are available.
      61             : 
      62             : <table align=center frame=void width=95%% cellpadding=5%%>
      63             : <tr>
      64             : <td> TYPE </td> <td> FUNCTION </td>
      65             : </tr> <tr>
      66             : <td> gaussian </td> <td> \f$f(r) = \frac{1}{(2 \pi)^{n} \sqrt{|\Sigma^{-1}|}} \exp\left(-0.5 r^2 \right)\f$ </td>
      67             : </tr> <tr>
      68             : <td> truncated-gaussian </td> <td> \f$f(r) = \frac{1}{(2 \pi)^{n} \sqrt{|\Sigma^{-1}|} \left(\frac{\mathrm{erf}(-6.25/sqrt{2}) - \mathrm{erf}(-6.25/sqrt{2})}{2}\right)^n} \exp\left(-0.5 r^2 \right)\f$ </td>
      69             : </tr> <tr>
      70             : <td> triangular </td> <td> \f$f(r) = \frac{3}{V} ( 1 - | r | )H(1-|r|) \f$ </td>
      71             : </tr> <tr>
      72             : <td> uniform </td> <td> \f$f(r) = \frac{1}{V}H(1-|r|)\f$ </td>
      73             : </tr>
      74             : </table>
      75             : 
      76             : In the above \f$H(y)\f$ is a function that is equal to one when \f$y>0\f$ and zero when \f$y \le 0\f$. \f$n\f$ is
      77             : the dimensionality of the vector \f$\mathbf{x}\f$ and \f$V\f$ is the volume of an ellipse in an \f$n\f$ dimensional
      78             : space which is given by:
      79             : 
      80             : \f{eqnarray*}{
      81             : V &=& | \Sigma^{-1} | \frac{ \pi^{\frac{n}{2}} }{\left( \frac{n}{2} \right)! } \qquad \textrm{for even} \quad n \\
      82             : V &=& | \Sigma^{-1} | \frac{ 2^{\frac{n+1}{2}} \pi^{\frac{n-1}{2}} }{ n!! }
      83             : \f}
      84             : 
      85             : In \ref METAD the normalization constants are ignored so that the value of the function at \f$r=0\f$ is equal
      86             : to one.  In addition in \ref METAD we must be able to differentiate the bias in order to get forces.  This limits
      87             : the kernels we can use in this method.  Notice also that Gaussian kernels should have infinite support.  When used
      88             : with grids, however, they are assumed to only be non-zero over a finite range.  The difference between the
      89             : truncated-gaussian and regular gaussian is that the truncated gaussian is scaled so that its integral over the grid
      90             : is equal to one when it is normalized.  The integral of a regular gaussian when it is evaluated on a grid will be
      91             : slightly less that one because of the truncation of a function that should have infinite support.
      92             : */
      93             : //+ENDPLUMEDOC
      94             : 
      95           0 : KernelFunctions::KernelFunctions( const std::string& input ) {
      96             : 
      97           0 :   if(!dp2cutoffNoStretch()) {
      98           0 :     stretchA=dp2cutoffA;
      99           0 :     stretchB=dp2cutoffB;
     100             :   }
     101             : 
     102           0 :   std::vector<std::string> data=Tools::getWords(input);
     103           0 :   std::string name=data[0];
     104             :   data.erase(data.begin());
     105             : 
     106           0 :   std::vector<double> at;
     107           0 :   bool foundc = Tools::parseVector(data,"CENTER",at);
     108           0 :   if(!foundc) {
     109           0 :     plumed_merror("failed to find center keyword in definition of kernel");
     110             :   }
     111           0 :   std::vector<double> sig;
     112           0 :   bool founds = Tools::parseVector(data,"SIGMA",sig);
     113           0 :   if(!founds) {
     114           0 :     plumed_merror("failed to find sigma keyword in definition of kernel");
     115             :   }
     116             : 
     117           0 :   bool multi=false;
     118           0 :   Tools::parseFlag(data,"MULTIVARIATE",multi);
     119           0 :   bool vonmises=false;
     120           0 :   Tools::parseFlag(data,"VON-MISSES",vonmises);
     121           0 :   if( center.size()==1 && multi ) {
     122           0 :     plumed_merror("one dimensional kernel cannot be multivariate");
     123             :   }
     124           0 :   if( center.size()==1 && vonmises ) {
     125           0 :     plumed_merror("one dimensional kernel cannot be von-misses");
     126             :   }
     127           0 :   if( center.size()==1 && sig.size()!=1 ) {
     128           0 :     plumed_merror("size mismatch between center size and sigma size");
     129             :   }
     130           0 :   if( multi && center.size()>1 && sig.size()!=0.5*center.size()*(center.size()-1) ) {
     131           0 :     plumed_merror("size mismatch between center size and sigma size");
     132             :   }
     133           0 :   if( !multi && center.size()>1 && sig.size()!=center.size() ) {
     134           0 :     plumed_merror("size mismatch between center size and sigma size");
     135             :   }
     136             : 
     137             :   double h;
     138           0 :   bool foundh = Tools::parse(data,"HEIGHT",h);
     139           0 :   if( !foundh) {
     140           0 :     h=1.0;
     141             :   }
     142             : 
     143           0 :   if( multi ) {
     144           0 :     setData( at, sig, name, "MULTIVARIATE", h );
     145           0 :   } else if( vonmises ) {
     146           0 :     setData( at, sig, name, "VON-MISSES", h );
     147             :   } else {
     148           0 :     setData( at, sig, name, "DIAGONAL", h );
     149             :   }
     150           0 : }
     151             : 
     152        6143 : KernelFunctions::KernelFunctions( const std::vector<double>& at,
     153             :                                   const std::vector<double>& sig,
     154             :                                   const std::string& type,
     155             :                                   const std::string& mtype,
     156        6143 :                                   const double& w ) {
     157             : 
     158        6143 :   if(!dp2cutoffNoStretch()) {
     159        6143 :     stretchA=dp2cutoffA;
     160        6143 :     stretchB=dp2cutoffB;
     161             :   }
     162             : 
     163        6143 :   setData( at, sig, type, mtype, w );
     164        6143 : }
     165             : 
     166           0 : KernelFunctions::KernelFunctions( const KernelFunctions* in ):
     167           0 :   dtype(in->dtype),
     168           0 :   ktype(in->ktype),
     169           0 :   center(in->center),
     170           0 :   width(in->width),
     171           0 :   height(in->height) {
     172           0 :   if(!dp2cutoffNoStretch()) {
     173           0 :     stretchA=dp2cutoffA;
     174           0 :     stretchB=dp2cutoffB;
     175             :   }
     176           0 : }
     177             : 
     178        6143 : void KernelFunctions::setData( const std::vector<double>& at,
     179             :                                const std::vector<double>& sig,
     180             :                                const std::string& type,
     181             :                                const std::string& mtype,
     182             :                                const double& w ) {
     183             : 
     184        6143 :   height=w;
     185        6143 :   center.resize( at.size() );
     186       18208 :   for(unsigned i=0; i<at.size(); ++i) {
     187       12065 :     center[i]=at[i];
     188             :   }
     189        6143 :   width.resize( sig.size() );
     190       22576 :   for(unsigned i=0; i<sig.size(); ++i) {
     191       16433 :     width[i]=sig[i];
     192             :   }
     193        6143 :   if( mtype=="MULTIVARIATE" ) {
     194        4368 :     dtype=matrixType::multi;
     195        1775 :   } else if( mtype=="VON-MISSES" ) {
     196           0 :     dtype=matrixType::vonmises;
     197        1775 :   } else if( mtype=="DIAGONAL" ) {
     198        1775 :     dtype=matrixType::diagonal;
     199             :   } else {
     200           0 :     plumed_merror(mtype + " is not a valid metric type");
     201             :   }
     202             : 
     203             :   // Setup the kernel type
     204        6143 :   if(type=="GAUSSIAN" || type=="gaussian" ) {
     205           0 :     ktype=kernelType::gaussian;
     206        6143 :   } else if(type=="STRETCHED-GAUSSIAN" || type=="stretched-gaussian" ) {
     207        6143 :     ktype=kernelType::stretchedgaussian;
     208           0 :   } else if(type=="TRUNCATED-GAUSSIAN" || type=="truncated-gaussian" ) {
     209           0 :     ktype=kernelType::truncatedgaussian;
     210           0 :   } else if(type=="UNIFORM" || type=="uniform") {
     211           0 :     ktype=kernelType::uniform;
     212           0 :   } else if(type=="TRIANGULAR" || type=="triangular") {
     213           0 :     ktype=kernelType::triangular;
     214             :   } else {
     215           0 :     plumed_merror(type+" is an invalid kernel type\n");
     216             :   }
     217        6143 : }
     218             : 
     219           0 : void KernelFunctions::normalize( const std::vector<Value*>& myvals ) {
     220             : 
     221             :   double det=1.;
     222             :   unsigned ncv=ndim();
     223           0 :   if(dtype==matrixType::diagonal) {
     224           0 :     for(unsigned i=0; i<width.size(); ++i) {
     225           0 :       det*=width[i]*width[i];
     226             :     }
     227           0 :   } else if(dtype==matrixType::multi) {
     228             :     // det is the determinant of the inverse of mymatrix
     229           0 :     Matrix<double> mymatrix( getMatrix() );
     230             :     double logd;
     231           0 :     logdet( mymatrix, logd );
     232           0 :     det=std::exp(-logd);
     233             :   }
     234           0 :   if( dtype==matrixType::diagonal || dtype==matrixType::multi ) {
     235             :     double volume;
     236           0 :     if( ktype==kernelType::gaussian || ktype==kernelType::stretchedgaussian ) {
     237           0 :       volume=pow( 2*pi, 0.5*ncv ) * pow( det, 0.5 );
     238           0 :     } else if( ktype==kernelType::truncatedgaussian ) {
     239             :       // This makes it so the gaussian integrates to one over the range over which it has support
     240             :       const double DP2CUTOFF=std::sqrt(6.25);
     241           0 :       volume=pow( 2*pi, 0.5*ncv ) * pow( det, 0.5 ) * pow( 0.5 * ( erf(DP2CUTOFF) - erf(-DP2CUTOFF) ), ncv);
     242           0 :     } else if( ktype==kernelType::uniform || ktype==kernelType::triangular ) {
     243           0 :       if( ncv%2==1 ) {
     244             :         double dfact=1;
     245           0 :         for(unsigned i=1; i<ncv; i+=2) {
     246           0 :           dfact*=static_cast<double>(i);
     247             :         }
     248           0 :         volume=( pow( pi, (ncv-1)/2 ) ) * ( pow( 2., (ncv+1)/2 ) ) / dfact;
     249             :       } else {
     250             :         double fact=1.;
     251           0 :         for(unsigned i=1; i<ncv/2; ++i) {
     252           0 :           fact*=static_cast<double>(i);
     253             :         }
     254           0 :         volume=pow( pi,ncv/2 ) / fact;
     255             :       }
     256           0 :       if(ktype==kernelType::uniform) {
     257           0 :         volume*=det;
     258           0 :       } else if(ktype==kernelType::triangular) {
     259           0 :         volume*=det / 3.;
     260             :       }
     261             :     } else {
     262           0 :       plumed_merror("not a valid kernel type");
     263             :     }
     264           0 :     height /= volume;
     265           0 :     return;
     266             :   }
     267           0 :   plumed_assert( dtype==matrixType::vonmises && ktype==kernelType::gaussian );
     268             :   // Now calculate determinant for aperiodic variables
     269             :   unsigned naper=0;
     270           0 :   for(unsigned i=0; i<ncv; ++i) {
     271           0 :     if( !myvals[i]->isPeriodic() ) {
     272           0 :       naper++;
     273             :     }
     274             :   }
     275             :   // Now construct sub matrix
     276             :   double volume=1;
     277           0 :   if( naper>0 ) {
     278             :     unsigned isub=0;
     279           0 :     Matrix<double> mymatrix( getMatrix() ), mysub( naper, naper );
     280           0 :     for(unsigned i=0; i<ncv; ++i) {
     281           0 :       if( myvals[i]->isPeriodic() ) {
     282             :         continue;
     283             :       }
     284             :       unsigned jsub=0;
     285           0 :       for(unsigned j=0; j<ncv; ++j) {
     286           0 :         if( myvals[j]->isPeriodic() ) {
     287           0 :           continue;
     288             :         }
     289           0 :         mysub( isub, jsub ) = mymatrix( i, j );
     290           0 :         jsub++;
     291             :       }
     292           0 :       isub++;
     293             :     }
     294           0 :     Matrix<double> myisub( naper, naper );
     295             :     double logd;
     296           0 :     Invert( mysub, myisub );
     297           0 :     logdet( myisub, logd );
     298           0 :     det=std::exp(logd);
     299           0 :     volume=pow( 2*pi, 0.5*ncv ) * pow( det, 0.5 );
     300             :   }
     301             : 
     302             :   // Calculate volume of periodic variables
     303             :   unsigned nper=0;
     304           0 :   for(unsigned i=0; i<ncv; ++i) {
     305           0 :     if( myvals[i]->isPeriodic() ) {
     306           0 :       nper++;
     307             :     }
     308             :   }
     309             : 
     310             :   // Now construct sub matrix
     311           0 :   if( nper>0 ) {
     312             :     unsigned isub=0;
     313           0 :     Matrix<double> mymatrix( getMatrix() ),  mysub( nper, nper );
     314           0 :     for(unsigned i=0; i<ncv; ++i) {
     315           0 :       if( !myvals[i]->isPeriodic() ) {
     316             :         continue;
     317             :       }
     318             :       unsigned jsub=0;
     319           0 :       for(unsigned j=0; j<ncv; ++j) {
     320           0 :         if( !myvals[j]->isPeriodic() ) {
     321           0 :           continue;
     322             :         }
     323           0 :         mysub( isub, jsub ) = mymatrix( i, j );
     324           0 :         jsub++;
     325             :       }
     326           0 :       isub++;
     327             :     }
     328           0 :     Matrix<double>  eigvec( nper, nper );
     329           0 :     std::vector<double> eigval( nper );
     330           0 :     diagMat( mysub, eigval, eigvec );
     331             :     unsigned iper=0;
     332             :     volume=1;
     333           0 :     for(unsigned i=0; i<ncv; ++i) {
     334           0 :       if( myvals[i]->isPeriodic() ) {
     335           0 :         volume *= myvals[i]->getMaxMinusMin()*Tools::bessel0(eigval[iper])*std::exp(-eigval[iper]);
     336           0 :         iper++;
     337             :       }
     338             :     }
     339             :   }
     340           0 :   height /= volume;
     341             : }
     342             : 
     343       10985 : double KernelFunctions::getCutoff(const double baseWidth) const {
     344             :   const double DP2CUTOFF=6.25;
     345       10985 :   if( ktype==kernelType::gaussian
     346             :       || ktype==kernelType::truncatedgaussian
     347       10985 :       || ktype==kernelType::stretchedgaussian ) {
     348       10985 :     return std::sqrt(2.0*DP2CUTOFF)*baseWidth;
     349           0 :   } else if(ktype==kernelType::triangular ) {
     350             :     return baseWidth;
     351           0 :   } else if(ktype==kernelType::uniform) {
     352             :     return baseWidth;
     353             :   } else {
     354           0 :     plumed_merror("No valid kernel type");
     355             :   }
     356             :   return 0.0;
     357             : }
     358             : 
     359        5609 : std::vector<double> KernelFunctions::getContinuousSupport( ) const {
     360             :   unsigned ncv=ndim();
     361        5609 :   std::vector<double> support( ncv );
     362        5609 :   if(dtype==matrixType::diagonal) {
     363        3490 :     for(unsigned i=0; i<ncv; ++i) {
     364        2249 :       support[i]=getCutoff(width[i]);
     365             :     }
     366        4368 :   } else if(dtype==matrixType::multi) {
     367        4368 :     Matrix<double> mymatrix( getMatrix() ), myinv( ncv,ncv );
     368        4368 :     Invert(mymatrix,myinv);
     369        4368 :     Matrix<double> myautovec(ncv,ncv);
     370        4368 :     std::vector<double> myautoval(ncv);
     371        4368 :     diagMat(myinv,myautoval,myautovec);
     372             :     double maxautoval=0.;
     373             :     unsigned ind_maxautoval=0;
     374       13104 :     for (unsigned i=0; i<ncv; i++) {
     375        8736 :       if(myautoval[i]>maxautoval) {
     376             :         maxautoval=myautoval[i];
     377             :         ind_maxautoval=i;
     378             :       }
     379             :     }
     380       13104 :     for(unsigned i=0; i<ncv; ++i) {
     381        8736 :       double extent=std::fabs(std::sqrt(maxautoval)*myautovec(i,ind_maxautoval));
     382        8736 :       support[i]=getCutoff( extent );
     383             :     }
     384             :   } else {
     385           0 :     plumed_merror("cannot find support if metric is not multi or diagonal type");
     386             :   }
     387        5609 :   return support;
     388             : }
     389             : 
     390        3787 : std::vector<unsigned> KernelFunctions::getSupport( const std::vector<double>& dx ) const {
     391        3787 :   plumed_assert( ndim()==dx.size() );
     392        3787 :   std::vector<unsigned> support( dx.size() );
     393        3787 :   std::vector<double> vv=getContinuousSupport( );
     394       11128 :   for(unsigned i=0; i<dx.size(); ++i) {
     395        7341 :     support[i]=static_cast<unsigned>(ceil( vv[i]/dx[i] ));
     396             :   }
     397        3787 :   return support;
     398             : }
     399             : 
     400      970504 : double KernelFunctions::evaluate( const std::vector<Value*>& pos,
     401             :                                   std::vector<double>& derivatives,
     402             :                                   const bool usederiv,
     403             :                                   const bool doInt,
     404             :                                   const double lowI_,
     405             :                                   const double uppI_) const {
     406             :   plumed_dbg_assert( pos.size()==ndim() && derivatives.size()==ndim() );
     407             : #ifndef NDEBUG
     408             :   if( usederiv ) {
     409             :     plumed_massert( ktype!=kernelType::uniform, "step function can not be differentiated" );
     410             :   }
     411             : #endif
     412      970504 :   if(doInt) {
     413             :     plumed_dbg_assert(center.size()==1);
     414           0 :     if(pos[0]->get()<lowI_) {
     415           0 :       pos[0]->set(lowI_);
     416             :     }
     417           0 :     if(pos[0]->get()>uppI_) {
     418           0 :       pos[0]->set(uppI_);
     419             :     }
     420             :   }
     421             :   double r2=0;
     422      970504 :   if(dtype==matrixType::diagonal) {
     423           0 :     for(unsigned i=0; i<ndim(); ++i) {
     424           0 :       derivatives[i]=-pos[i]->difference( center[i] ) / width[i];
     425           0 :       r2+=derivatives[i]*derivatives[i];
     426           0 :       derivatives[i] /= width[i];
     427             :     }
     428      970504 :   } else if(dtype==matrixType::multi) {
     429      970504 :     Matrix<double> mymatrix( getMatrix() );
     430     2911512 :     for(unsigned i=0; i<mymatrix.nrows(); ++i) {
     431             :       double dp_i, dp_j;
     432     1941008 :       derivatives[i]=0;
     433     1941008 :       dp_i=-pos[i]->difference( center[i] );
     434     5823024 :       for(unsigned j=0; j<mymatrix.ncols(); ++j) {
     435     3882016 :         if(i==j) {
     436             :           dp_j=dp_i;
     437             :         } else {
     438     1941008 :           dp_j=-pos[j]->difference( center[j] );
     439             :         }
     440             : 
     441     3882016 :         derivatives[i]+=mymatrix(i,j)*dp_j;
     442     3882016 :         r2+=dp_i*dp_j*mymatrix(i,j);
     443             :       }
     444             :     }
     445           0 :   } else if(dtype==matrixType::vonmises) {
     446           0 :     std::vector<double> costmp( ndim() ), sintmp( ndim() ), sinout( ndim(), 0.0 );
     447           0 :     for(unsigned i=0; i<ndim(); ++i) {
     448           0 :       if( pos[i]->isPeriodic() ) {
     449           0 :         sintmp[i]=std::sin( 2.*pi*(pos[i]->get() - center[i])/pos[i]->getMaxMinusMin() );
     450           0 :         costmp[i]=std::cos( 2.*pi*(pos[i]->get() - center[i])/pos[i]->getMaxMinusMin() );
     451             :       } else {
     452           0 :         sintmp[i]=pos[i]->get() - center[i];
     453           0 :         costmp[i]=1.0;
     454             :       }
     455             :     }
     456             : 
     457           0 :     Matrix<double> mymatrix( getMatrix() );
     458           0 :     for(unsigned i=0; i<mymatrix.nrows(); ++i) {
     459           0 :       derivatives[i]=0;
     460           0 :       if( pos[i]->isPeriodic() ) {
     461           0 :         r2+=2*( 1 - costmp[i] )*mymatrix(i,i);
     462             :       } else {
     463           0 :         r2+=sintmp[i]*sintmp[i]*mymatrix(i,i);
     464             :       }
     465           0 :       for(unsigned j=0; j<mymatrix.ncols(); ++j) {
     466           0 :         if( i!=j ) {
     467           0 :           sinout[i]+=mymatrix(i,j)*sintmp[j];
     468             :         }
     469             :       }
     470           0 :       derivatives[i] = mymatrix(i,i)*sintmp[i] + sinout[i]*costmp[i];
     471           0 :       if( pos[i]->isPeriodic() ) {
     472           0 :         derivatives[i] *= (2*pi/pos[i]->getMaxMinusMin());
     473             :       }
     474             :     }
     475           0 :     for(unsigned i=0; i<sinout.size(); ++i) {
     476           0 :       r2+=sintmp[i]*sinout[i];
     477             :     }
     478             :   }
     479             :   double kderiv, kval;
     480      970504 :   if(ktype==kernelType::gaussian || ktype==kernelType::truncatedgaussian) {
     481           0 :     kval=height*std::exp(-0.5*r2);
     482           0 :     kderiv=-kval;
     483      970504 :   } else if(ktype==kernelType::stretchedgaussian) {
     484      970504 :     auto dp=0.5*r2;
     485      970504 :     if(dp<dp2cutoff) {
     486      604935 :       auto ee=std::exp(-0.5*r2);
     487      604935 :       kval=height*(stretchA*ee+stretchB);
     488      604935 :       kderiv=-height*stretchA*ee;
     489             :     } else {
     490             :       kval=0.0;
     491             :       kderiv=0.0;
     492             :     }
     493             :   } else {
     494           0 :     double r=std::sqrt(r2);
     495           0 :     if(ktype==kernelType::triangular) {
     496           0 :       if( r<1.0 ) {
     497             :         if(r==0) {
     498             :           kderiv=0;
     499             :         }
     500             :         kderiv=-1;
     501           0 :         kval=height*( 1. - std::fabs(r) );
     502             :       } else {
     503             :         kval=0.;
     504             :         kderiv=0.;
     505             :       }
     506           0 :     } else if(ktype==kernelType::uniform) {
     507             :       kderiv=0.;
     508           0 :       if(r<1.0) {
     509           0 :         kval=height;
     510             :       } else {
     511             :         kval=0;
     512             :       }
     513             :     } else {
     514           0 :       plumed_merror("Not a valid kernel type");
     515             :     }
     516           0 :     kderiv*=height / r ;
     517             :   }
     518     2911512 :   for(unsigned i=0; i<ndim(); ++i) {
     519     1941008 :     derivatives[i]*=kderiv;
     520             :   }
     521      970504 :   if(doInt) {
     522           0 :     if((pos[0]->get() <= lowI_ || pos[0]->get() >= uppI_) && usederiv )
     523           0 :       for(unsigned i=0; i<ndim(); ++i) {
     524           0 :         derivatives[i]=0;
     525             :       }
     526             :   }
     527      970504 :   return kval;
     528             : }
     529             : 
     530        3787 : bool KernelFunctions::getSeparableGaussian(double & h, double & a, double & b, double & r2cut, std::vector<double> & w) const {
     531        3787 :   if(dtype!=matrixType::diagonal) {
     532             :     return false;
     533             :   }
     534        1057 :   if(ktype==kernelType::gaussian || ktype==kernelType::truncatedgaussian) {
     535           0 :     a=1.0;
     536           0 :     b=0.0;
     537           0 :     r2cut=std::numeric_limits<double>::max();
     538        1057 :   } else if(ktype==kernelType::stretchedgaussian) {
     539        1057 :     a=stretchA;
     540        1057 :     b=stretchB;
     541        1057 :     r2cut=2.0*dp2cutoff;
     542             :   } else {
     543             :     return false;
     544             :   }
     545        1057 :   h=height;
     546        1057 :   w=width;
     547        1057 :   return true;
     548             : }
     549             : 
     550        5051 : std::unique_ptr<KernelFunctions> KernelFunctions::read( IFile* ifile,
     551             :     const bool& cholesky,
     552             :     const std::vector<std::string>& valnames ) {
     553             :   double h;
     554       10102 :   if( !ifile->scanField("height",h) ) {
     555             :     return NULL;
     556             :   };
     557             : 
     558             :   std::string sss;
     559        5051 :   ifile->scanField("multivariate",sss);
     560        5051 :   std::string ktype="stretched-gaussian";
     561       10102 :   if( ifile->FieldExist("kerneltype") ) {
     562        7918 :     ifile->scanField("kerneltype",ktype);
     563             :   }
     564        5051 :   plumed_massert( sss=="false"
     565             :                   || sss=="true"
     566             :                   || sss=="von-misses",
     567             :                   "multivariate flag must be either false, true or von-misses");
     568             : 
     569             :   // Read the position of the center
     570        5051 :   std::vector<double> cc( valnames.size() );
     571       14932 :   for(unsigned i=0; i<valnames.size(); ++i) {
     572        9881 :     ifile->scanField(valnames[i],cc[i]);
     573             :   }
     574             : 
     575        5051 :   std::vector<double> sig;
     576        5051 :   if( sss=="false" ) {
     577         683 :     sig.resize( valnames.size() );
     578        1828 :     for(unsigned i=0; i<valnames.size(); ++i) {
     579        1145 :       ifile->scanField("sigma_"+valnames[i],sig[i]);
     580        1145 :       if( !cholesky ) {
     581           0 :         sig[i]=std::sqrt(sig[i]);
     582             :       }
     583             :     }
     584             :     return Tools::make_unique<KernelFunctions>(cc, sig, ktype, "DIAGONAL", h);
     585             :   }
     586             : 
     587        4368 :   unsigned ncv=valnames.size();
     588        4368 :   sig.resize( (ncv*(ncv+1))/2 );
     589        4368 :   Matrix<double> upper(ncv,ncv);
     590        4368 :   Matrix<double> lower(ncv,ncv);
     591        4368 :   Matrix<double> mymult(ncv,ncv);
     592        4368 :   Matrix<double> invmatrix(ncv,ncv);
     593       13104 :   for(unsigned i=0; i<ncv; ++i) {
     594       21840 :     for(unsigned j=0; j<ncv-i; j++) {
     595       39312 :       ifile->scanField("sigma_" +valnames[j+i] + "_" + valnames[j], lower(j+i,j) );
     596       13104 :       upper(j,j+i)=lower(j+i,j);
     597       13104 :       mymult(j+i,j)=mymult(j,j+i)=lower(j+i,j);
     598             :     }
     599             :   }
     600        4368 :   if( cholesky ) {
     601        4368 :     mult(lower,upper,mymult);
     602             :   }
     603        4368 :   Invert( mymult, invmatrix );
     604             :   unsigned k=0;
     605       13104 :   for(unsigned i=0; i<ncv; i++) {
     606       21840 :     for(unsigned j=i; j<ncv; j++) {
     607       13104 :       sig[k]=invmatrix(i,j);
     608       13104 :       k++;
     609             :     }
     610             :   }
     611        4368 :   if( sss=="true" ) {
     612             :     return Tools::make_unique<KernelFunctions>(cc, sig, ktype, "MULTIVARIATE", h);
     613             :   }
     614             :   return Tools::make_unique<KernelFunctions>( cc, sig, ktype, "VON-MISSES", h );
     615             : }
     616             : 
     617             : }

Generated by: LCOV version 1.16