LCOV - code coverage report
Current view: top level - tools - HistogramBead.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 87 140 62.1 %
Date: 2025-11-25 13:55:50 Functions: 9 11 81.8 %

          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 "HistogramBead.h"
      23             : #include <vector>
      24             : #include <limits>
      25             : #include "Tools.h"
      26             : #include "Keywords.h"
      27             : 
      28             : namespace PLMD {
      29             : 
      30             : //+PLUMEDOC INTERNAL histogrambead
      31             : /*
      32             : A function that can be used to calculate whether quantities are between fixed upper and lower bounds.
      33             : 
      34             : If we have multiple instances of a variable we can estimate the probability density function
      35             : for that variable using a process called kernel density estimation:
      36             : 
      37             : \f[
      38             : P(s) = \sum_i K\left( \frac{s - s_i}{w} \right)
      39             : \f]
      40             : 
      41             : In this equation \f$K\f$ is a symmetric function that must integrate to one that is often
      42             : called a kernel function and \f$w\f$ is a smearing parameter.  From a probability density function calculated using
      43             : kernel density estimation we can calculate the number/fraction of values between an upper and lower
      44             : bound using:
      45             : 
      46             : \f[
      47             : w(s) = \int_a^b \sum_i K\left( \frac{s - s_i}{w} \right)
      48             : \f]
      49             : 
      50             : All the input to calculate a quantity like \f$w(s)\f$ is generally provided through a single
      51             : keyword that will have the following form:
      52             : 
      53             : KEYWORD={TYPE UPPER=\f$a\f$ LOWER=\f$b\f$ SMEAR=\f$\frac{w}{b-a}\f$}
      54             : 
      55             : This will calculate the number of values between \f$a\f$ and \f$b\f$.  To calculate
      56             : the fraction of values you add the word NORM to the input specification.  If the
      57             : function keyword SMEAR is not present \f$w\f$ is set equal to \f$0.5(b-a)\f$. Finally,
      58             : type should specify one of the kernel types that is present in plumed. These are listed
      59             : in the table below:
      60             : 
      61             : <table align=center frame=void width=95%% cellpadding=5%%>
      62             : <tr>
      63             : <td> TYPE </td> <td> FUNCTION </td>
      64             : </tr> <tr>
      65             : <td> GAUSSIAN </td> <td> \f$\frac{1}{\sqrt{2\pi}w} \exp\left( -\frac{(s-s_i)^2}{2w^2} \right)\f$ </td>
      66             : </tr> <tr>
      67             : <td> TRIANGULAR </td> <td> \f$ \frac{1}{2w} \left( 1. - \left| \frac{s-s_i}{w} \right| \right) \quad \frac{s-s_i}{w}<1 \f$ </td>
      68             : </tr>
      69             : </table>
      70             : 
      71             : Some keywords can also be used to calculate a discrete version of the histogram.  That
      72             : is to say the number of values between \f$a\f$ and \f$b\f$, the number of values between
      73             : \f$b\f$ and \f$c\f$ and so on.  A keyword that specifies this sort of calculation would look
      74             : something like
      75             : 
      76             : KEYWORD={TYPE UPPER=\f$a\f$ LOWER=\f$b\f$ NBINS=\f$n\f$ SMEAR=\f$\frac{w}{n(b-a)}\f$}
      77             : 
      78             : This specification would calculate the following vector of quantities:
      79             : 
      80             : \f[
      81             : w_j(s) = \int_{a + \frac{j-1}{n}(b-a)}^{a + \frac{j}{n}(b-a)} \sum_i K\left( \frac{s - s_i}{w} \right)
      82             : \f]
      83             : 
      84             : */
      85             : //+ENDPLUMEDOC
      86             : 
      87           0 : void HistogramBead::registerKeywords( Keywords& keys ) {
      88           0 :   keys.add("compulsory","LOWER","the lower boundary for this particular bin");
      89           0 :   keys.add("compulsory","UPPER","the upper boundary for this particular bin");
      90           0 :   keys.add("compulsory","SMEAR","0.5","the amount to smear the Gaussian for each value in the distribution");
      91           0 : }
      92             : 
      93      260467 : HistogramBead::HistogramBead():
      94      260467 :   init(false),
      95      260467 :   lowb(0.0),
      96      260467 :   highb(0.0),
      97      260467 :   width(0.0),
      98      260467 :   cutoff(std::numeric_limits<double>::max()),
      99      260467 :   type(gaussian),
     100      260467 :   periodicity(unset),
     101      260467 :   min(0.0),
     102      260467 :   max(0.0),
     103      260467 :   max_minus_min(0.0),
     104      260467 :   inv_max_minus_min(0.0) {
     105      260467 : }
     106             : 
     107          53 : std::string HistogramBead::description() const {
     108          53 :   std::ostringstream ostr;
     109          53 :   ostr<<"between "<<lowb<<" and "<<highb<<" width of gaussian window equals "<<width;
     110          53 :   return ostr.str();
     111          53 : }
     112             : 
     113           0 : void HistogramBead::generateBins( const std::string& params, std::vector<std::string>& bins ) {
     114           0 :   std::vector<std::string> data=Tools::getWords(params);
     115           0 :   plumed_massert(data.size()>=1,"There is no input for this keyword");
     116             : 
     117           0 :   std::string name=data[0];
     118             : 
     119             :   unsigned nbins;
     120           0 :   std::vector<double> range(2);
     121             :   std::string smear;
     122           0 :   bool found_nb=Tools::parse(data,"NBINS",nbins);
     123           0 :   plumed_massert(found_nb,"Number of bins in histogram not found");
     124           0 :   bool found_r=Tools::parse(data,"LOWER",range[0]);
     125           0 :   plumed_massert(found_r,"Lower bound for histogram not specified");
     126           0 :   found_r=Tools::parse(data,"UPPER",range[1]);
     127           0 :   plumed_massert(found_r,"Upper bound for histogram not specified");
     128           0 :   plumed_massert(range[0]<range[1],"Range specification is dubious");
     129           0 :   bool found_b=Tools::parse(data,"SMEAR",smear);
     130           0 :   if(!found_b) {
     131           0 :     Tools::convert(0.5,smear);
     132             :   }
     133             : 
     134             :   std::string lb,ub;
     135           0 :   double delr = ( range[1]-range[0] ) / static_cast<double>( nbins );
     136           0 :   for(unsigned i=0; i<nbins; ++i) {
     137           0 :     Tools::convert( range[0]+i*delr, lb );
     138           0 :     Tools::convert( range[0]+(i+1)*delr, ub );
     139           0 :     bins.push_back( name + " " +  "LOWER=" + lb + " " + "UPPER=" + ub + " " + "SMEAR=" + smear );
     140             :   }
     141           0 :   plumed_assert(bins.size()==nbins);
     142           0 : }
     143             : 
     144          53 : void HistogramBead::set( const std::string& params, std::string& errormsg ) {
     145          53 :   std::vector<std::string> data=Tools::getWords(params);
     146          53 :   if(data.size()<1) {
     147             :     errormsg="No input has been specified";
     148             :     return;
     149             :   }
     150             : 
     151          53 :   std::string name=data[0];
     152             :   const double DP2CUTOFF=6.25;
     153          53 :   if(name=="GAUSSIAN") {
     154          53 :     type=gaussian;
     155          53 :     cutoff=std::sqrt(2.0*DP2CUTOFF);
     156           0 :   } else if(name=="TRIANGULAR") {
     157           0 :     type=triangular;
     158           0 :     cutoff=1.;
     159             :   } else {
     160           0 :     plumed_merror("cannot understand kernel type " + name );
     161             :   }
     162             : 
     163             :   double smear;
     164          53 :   bool found_r=Tools::parse(data,"LOWER",lowb);
     165          53 :   if( !found_r ) {
     166             :     errormsg="Lower bound has not been specified use LOWER";
     167             :   }
     168          53 :   found_r=Tools::parse(data,"UPPER",highb);
     169          53 :   if( !found_r ) {
     170             :     errormsg="Upper bound has not been specified use UPPER";
     171             :   }
     172          53 :   if( lowb>=highb ) {
     173             :     errormsg="Lower bound is higher than upper bound";
     174             :   }
     175             : 
     176          53 :   smear=0.5;
     177          53 :   Tools::parse(data,"SMEAR",smear);
     178          53 :   width=smear*(highb-lowb);
     179          53 :   init=true;
     180          53 : }
     181             : 
     182     2668474 : void HistogramBead::set( double l, double h, double w) {
     183     2668474 :   init=true;
     184     2668474 :   lowb=l;
     185     2668474 :   highb=h;
     186     2668474 :   width=w;
     187             :   const double DP2CUTOFF=6.25;
     188     2668474 :   if( type==gaussian ) {
     189      733122 :     cutoff=std::sqrt(2.0*DP2CUTOFF);
     190     1935352 :   } else if( type==triangular ) {
     191     1935352 :     cutoff=1.;
     192             :   } else {
     193           0 :     plumed_error();
     194             :   }
     195     2668474 : }
     196             : 
     197      260148 : void HistogramBead::setKernelType( const std::string& ktype ) {
     198      260148 :   if(ktype=="gaussian") {
     199      233896 :     type=gaussian;
     200       26252 :   } else if(ktype=="triangular") {
     201       26252 :     type=triangular;
     202             :   } else {
     203           0 :     plumed_merror("cannot understand kernel type " + ktype );
     204             :   }
     205      260148 : }
     206             : 
     207      251184 : double HistogramBead::calculate( double x, double& df ) const {
     208             :   plumed_dbg_assert(init && periodicity!=unset );
     209             :   double lowB, upperB, f;
     210      251184 :   if( type==gaussian ) {
     211      251184 :     lowB = difference( x, lowb ) / ( std::sqrt(2.0) * width );
     212      251184 :     upperB = difference( x, highb ) / ( std::sqrt(2.0) * width );
     213      251184 :     df = ( exp( -lowB*lowB ) - exp( -upperB*upperB ) ) / ( std::sqrt(2*pi)*width );
     214      251184 :     f = 0.5*( erf( upperB ) - erf( lowB ) );
     215           0 :   } else if( type==triangular ) {
     216           0 :     lowB = ( difference( x, lowb ) / width );
     217           0 :     upperB = ( difference( x, highb ) / width );
     218           0 :     df=0;
     219           0 :     if( std::fabs(lowB)<1. ) {
     220           0 :       df = (1 - std::fabs(lowB)) / width;
     221             :     }
     222           0 :     if( std::fabs(upperB)<1. ) {
     223           0 :       df -= (1 - std::fabs(upperB)) / width;
     224             :     }
     225           0 :     if (upperB<=-1. || lowB >=1.) {
     226             :       f=0.;
     227             :     } else {
     228             :       double ia, ib;
     229           0 :       if( lowB>-1.0 ) {
     230             :         ia=lowB;
     231             :       } else {
     232             :         ia=-1.0;
     233             :       }
     234           0 :       if( upperB<1.0 ) {
     235             :         ib=upperB;
     236             :       } else {
     237             :         ib=1.0;
     238             :       }
     239           0 :       f = (ib*(2.-std::fabs(ib))-ia*(2.-std::fabs(ia)))*0.5;
     240             :     }
     241             :   } else {
     242           0 :     plumed_merror("function type does not exist");
     243             :   }
     244      251184 :   return f;
     245             : }
     246             : 
     247      920596 : double HistogramBead::calculateWithCutoff( double x, double& df ) const {
     248             :   plumed_dbg_assert(init && periodicity!=unset );
     249             : 
     250             :   double lowB, upperB, f;
     251      920596 :   lowB = difference( x, lowb ) / width ;
     252      920596 :   upperB = difference( x, highb ) / width;
     253      920596 :   if( upperB<=-cutoff || lowB>=cutoff ) {
     254       87679 :     df=0;
     255       87679 :     return 0;
     256             :   }
     257             : 
     258      832917 :   if( type==gaussian ) {
     259      446395 :     lowB /= std::sqrt(2.0);
     260      446395 :     upperB /= std::sqrt(2.0);
     261      446395 :     df = ( exp( -lowB*lowB ) - exp( -upperB*upperB ) ) / ( std::sqrt(2*pi)*width );
     262      446395 :     f = 0.5*( erf( upperB ) - erf( lowB ) );
     263      386522 :   } else if( type==triangular ) {
     264      386522 :     df=0;
     265      386522 :     if( std::fabs(lowB)<1. ) {
     266       98488 :       df = (1 - std::fabs(lowB)) / width;
     267             :     }
     268      386522 :     if( std::fabs(upperB)<1. ) {
     269       98488 :       df -= (1 - std::fabs(upperB)) / width;
     270             :     }
     271      386522 :     if (upperB<=-1. || lowB >=1.) {
     272             :       f=0.;
     273             :     } else {
     274             :       double ia, ib;
     275      386522 :       if( lowB>-1.0 ) {
     276             :         ia=lowB;
     277             :       } else {
     278             :         ia=-1.0;
     279             :       }
     280      386522 :       if( upperB<1.0 ) {
     281             :         ib=upperB;
     282             :       } else {
     283             :         ib=1.0;
     284             :       }
     285      386522 :       f = (ib*(2.-std::fabs(ib))-ia*(2.-std::fabs(ia)))*0.5;
     286             :     }
     287             :   } else {
     288           0 :     plumed_merror("function type does not exist");
     289             :   }
     290             :   return f;
     291             : }
     292             : 
     293        4680 : double HistogramBead::lboundDerivative( const double& x ) const {
     294        4680 :   if( type==gaussian ) {
     295        4680 :     double lowB = difference( x, lowb ) / ( std::sqrt(2.0) * width );
     296        4680 :     return exp( -lowB*lowB ) / ( std::sqrt(2*pi)*width );
     297           0 :   } else if ( type==triangular ) {
     298           0 :     plumed_error();
     299             : //      lowB = fabs( difference( x, lowb ) / width );
     300             : //      if( lowB<1 ) return ( 1 - (lowB) ) / 2*width;
     301             : //      else return 0;
     302             :   } else {
     303           0 :     plumed_merror("function type does not exist");
     304             :   }
     305             :   return 0;
     306             : }
     307             : 
     308        4680 : double HistogramBead::uboundDerivative( const double& x ) const {
     309             :   plumed_dbg_assert(init && periodicity!=unset );
     310        4680 :   if( type==gaussian ) {
     311        4680 :     double upperB = difference( x, highb ) / ( std::sqrt(2.0) * width );
     312        4680 :     return exp( -upperB*upperB ) / ( std::sqrt(2*pi)*width );
     313           0 :   } else if ( type==triangular ) {
     314           0 :     plumed_error();
     315             : //      upperB = fabs( difference( x, highb ) / width );
     316             : //      if( upperB<1 ) return ( 1 - (upperB) ) / 2*width;
     317             : //      else return 0;
     318             :   } else {
     319           0 :     plumed_merror("function type does not exist");
     320             :   }
     321             :   return 0;
     322             : }
     323             : 
     324             : }

Generated by: LCOV version 1.16