LCOV - code coverage report
Current view: top level - analysis - Histogram.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 102 104 98.1 %
Date: 2018-12-19 07:49:13 Functions: 15 17 88.2 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-2018 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 "core/ActionRegister.h"
      23             : #include "tools/KernelFunctions.h"
      24             : #include "gridtools/ActionWithGrid.h"
      25             : #include "vesselbase/ActionWithVessel.h"
      26             : #include "vesselbase/StoreDataVessel.h"
      27             : #include "core/PlumedMain.h"
      28             : #include "core/ActionSet.h"
      29             : 
      30             : namespace PLMD {
      31             : namespace analysis {
      32             : 
      33             : //+PLUMEDOC GRIDCALC HISTOGRAM
      34             : /*
      35             : Accumulate the average probability density along a few CVs from a trajectory.
      36             : 
      37             : When using this method it is supposed that you have some collective variable \f$\zeta\f$ that
      38             : gives a reasonable description of some physical or chemical phenomenon.  As an example of what we
      39             : mean by this suppose you wish to examine the following SN2 reaction:
      40             : 
      41             : \f[
      42             :  \textrm{OH}^- + \textrm{CH}_3Cl  \rightarrow \textrm{CH}_3OH + \textrm{Cl}^-
      43             : \f]
      44             : 
      45             : The distance between the chlorine atom and the carbon is an excellent collective variable, \f$\zeta\f$,
      46             : in this case because this distance is short for the reactant, \f$\textrm{CH}_3Cl\f$, because the carbon
      47             : and chlorine are chemically bonded, and because it is long for the product state when these two atoms are
      48             : not chemically bonded.  We thus might want to accumulate the probability density, \f$P(\zeta)\f$, as a function of this distance
      49             : as this will provide us with information about the overall likelihood of the reaction.   Furthermore, the
      50             : free energy, \f$F(\zeta)\f$, is related to this probability density via:
      51             : 
      52             : \f[
      53             : F(\zeta) = - k_B T \ln P(\zeta)
      54             : \f]
      55             : 
      56             : Accumulating these probability densities is precisely what this Action can be used to do.  Furthermore, the conversion
      57             : of the histogram to the free energy can be achieved by using the method \ref CONVERT_TO_FES.
      58             : 
      59             : We calculate histograms within PLUMED using a method known as kernel density estimation, which you can read more about here:
      60             : 
      61             : https://en.wikipedia.org/wiki/Kernel_density_estimation
      62             : 
      63             : In PLUMED the value of \f$\zeta\f$ at each discrete instant in time in the trajectory is accumulated.  A kernel, \f$K(\zeta-\zeta(t'),\sigma)\f$,
      64             : centered at the current value, \f$\zeta(t)\f$, of this quantity is generated with a bandwith \f$\sigma\f$, which
      65             : is set by the user.  These kernels are then used to accumulate the ensemble average for the probability density:
      66             : 
      67             : \f[
      68             : \langle P(\zeta) \rangle = \frac{ \sum_{t'=0}^t w(t') K(\zeta-\zeta(t'),\sigma) }{ \sum_{t'=0}^t w(t') }
      69             : \f]
      70             : 
      71             : Here the sums run over a portion of the trajectory specified by the user.  The final quantity evalulated is a weighted
      72             : average as the weights, \f$w(t')\f$, allow us to negate the effect any bias might have on the region of phase space
      73             : sampled by the system.  This is discussed in the section of the manual on \ref Analysis.
      74             : 
      75             : A discrete analogue of kernel density estimation can also be used.  In this analogue the kernels in the above formula
      76             : are replaced by dirac delta functions.   When this method is used the final function calculated is no longer a probability
      77             : density - it is instead a probability mass function as each element of the function tells you the value of an integral
      78             : between two points on your grid rather than the value of a (continuous) function on a grid.
      79             : 
      80             : Additional material and examples can be also found in the tutorial \ref belfast-1.
      81             : 
      82             : \par Examples
      83             : 
      84             : The following input monitors two torsional angles during a simulation
      85             : and outputs a continuos histogram as a function of them at the end of the simulation.
      86             : \verbatim
      87             : TORSION ATOMS=1,2,3,4 LABEL=r1
      88             : TORSION ATOMS=2,3,4,5 LABEL=r2
      89             : HISTOGRAM ...
      90             :   ARG=r1,r2
      91             :   GRID_MIN=-3.14,-3.14
      92             :   GRID_MAX=3.14,3.14
      93             :   GRID_BIN=200,200
      94             :   BANDWIDTH=0.05,0.05
      95             :   LABEL=hh
      96             : ... HISTOGRAM
      97             : 
      98             : DUMPGRID GRID=hh FILE=histo
      99             : \endverbatim
     100             : 
     101             : The following input monitors two torsional angles during a simulation
     102             : and outputs a discrete histogram as a function of them at the end of the simulation.
     103             : \verbatim
     104             : TORSION ATOMS=1,2,3,4 LABEL=r1
     105             : TORSION ATOMS=2,3,4,5 LABEL=r2
     106             : HISTOGRAM ...
     107             :   ARG=r1,r2
     108             :   KERNEL=DISCRETE
     109             :   GRID_MIN=-3.14,-3.14
     110             :   GRID_MAX=3.14,3.14
     111             :   GRID_BIN=200,200
     112             :   LABEL=hh
     113             : ... HISTOGRAM
     114             : 
     115             : DUMPGRID GRID=hh FILE=histo
     116             : \endverbatim
     117             : 
     118             : The following input monitors two torsional angles during a simulation
     119             : and outputs the histogram accumulated thus far every 100000 steps.
     120             : \verbatim
     121             : TORSION ATOMS=1,2,3,4 LABEL=r1
     122             : TORSION ATOMS=2,3,4,5 LABEL=r2
     123             : HISTOGRAM ...
     124             :   ARG=r1,r2
     125             :   GRID_MIN=-3.14,-3.14
     126             :   GRID_MAX=3.14,3.14
     127             :   GRID_BIN=200,200
     128             :   BANDWIDTH=0.05,0.05
     129             :   LABEL=hh
     130             : ... HISTOGRAM
     131             : 
     132             : DUMPGRID GRID=hh FILE=histo STRIDE=100000
     133             : \endverbatim
     134             : 
     135             : The following input monitors two torsional angles during a simulation
     136             : and outputs a separate histogram for each 100000 steps worth of trajectory.
     137             : Notice how the CLEAR keyword is used here and how it is not used in the
     138             : previous example.
     139             : 
     140             : \verbatim
     141             : TORSION ATOMS=1,2,3,4 LABEL=r1
     142             : TORSION ATOMS=2,3,4,5 LABEL=r2
     143             : HISTOGRAM ...
     144             :   ARG=r1,r2 CLEAR=100000
     145             :   GRID_MIN=-3.14,-3.14
     146             :   GRID_MAX=3.14,3.14
     147             :   GRID_BIN=200,200
     148             :   BANDWIDTH=0.05,0.05
     149             :   GRID_WFILE=histo
     150             :   LABEL=hh
     151             : ... HISTOGRAM
     152             : 
     153             : DUMPGRID GRID=hh FILE=histo STRIDE=100000
     154             : \endverbatim
     155             : 
     156             : */
     157             : //+ENDPLUMEDOC
     158             : 
     159          34 : class Histogram : public gridtools::ActionWithGrid {
     160             : private:
     161             :   double ww;
     162             :   KernelFunctions* kernel;
     163             :   vesselbase::ActionWithVessel* myvessel;
     164             :   vesselbase::StoreDataVessel* stash;
     165             :   gridtools::HistogramOnGrid* myhist;
     166             : public:
     167             :   static void registerKeywords( Keywords& keys );
     168             :   explicit Histogram(const ActionOptions&ao);
     169             :   unsigned getNumberOfQuantities() const ;
     170             :   void prepareForAveraging();
     171             :   void performOperations( const bool& from_update );
     172             :   void finishAveraging();
     173           0 :   bool isPeriodic() { return false; }
     174             :   unsigned getNumberOfDerivatives();
     175             :   void compute( const unsigned&, MultiValue& ) const ;
     176             : };
     177             : 
     178        2540 : PLUMED_REGISTER_ACTION(Histogram,"HISTOGRAM")
     179             : 
     180          18 : void Histogram::registerKeywords( Keywords& keys ) {
     181          18 :   gridtools::ActionWithGrid::registerKeywords( keys ); keys.use("ARG");
     182          18 :   keys.add("optional","DATA","input data from action with vessel and compute histogram");
     183          18 :   keys.add("compulsory","GRID_MIN","the lower bounds for the grid");
     184          18 :   keys.add("compulsory","GRID_MAX","the upper bounds for the grid");
     185          18 :   keys.add("optional","GRID_BIN","the number of bins for the grid");
     186          18 :   keys.add("optional","GRID_SPACING","the approximate grid spacing (to be used as an alternative or together with GRID_BIN)");
     187          18 :   keys.use("UPDATE_FROM"); keys.use("UPDATE_UNTIL");
     188          18 : }
     189             : 
     190          17 : Histogram::Histogram(const ActionOptions&ao):
     191             :   Action(ao),
     192             :   ActionWithGrid(ao),
     193             :   ww(0.0),
     194             :   kernel(NULL),
     195             :   myvessel(NULL),
     196          17 :   stash(NULL)
     197             : {
     198             :   // Read in arguments
     199          17 :   std::string mlab; parse("DATA",mlab);
     200          17 :   if( mlab.length()>0 ) {
     201           1 :     myvessel = plumed.getActionSet().selectWithLabel<ActionWithVessel*>(mlab);
     202           1 :     if(!myvessel) error("action labelled " + mlab + " does not exist or is not an ActionWithVessel");
     203           1 :     stash = myvessel->buildDataStashes( NULL );
     204           1 :     log.printf("  for all base quantities calculated by %s \n",myvessel->getLabel().c_str() );
     205             :     // Add the dependency
     206           1 :     addDependency( myvessel );
     207             :   } else {
     208          16 :     std::vector<Value*> arg; parseArgumentList("ARG",arg);
     209          16 :     if(!arg.empty()) {
     210          16 :       log.printf("  with arguments");
     211          16 :       for(unsigned i=0; i<arg.size(); i++) log.printf(" %s",arg[i]->getName().c_str());
     212          16 :       log.printf("\n");
     213             :       // Retrieve the bias acting and make sure we request this also
     214          16 :       std::vector<Value*> bias( ActionWithArguments::getArguments() );
     215          16 :       for(unsigned i=0; i<bias.size(); ++i) arg.push_back( bias[i] );
     216          16 :       requestArguments(arg);
     217          16 :     }
     218             :   }
     219             : 
     220             :   // Read stuff for grid
     221          17 :   unsigned narg = getNumberOfArguments();
     222          17 :   if( myvessel ) narg=1;
     223          34 :   std::vector<std::string> gmin( narg ), gmax( narg );
     224          17 :   parseVector("GRID_MIN",gmin); parseVector("GRID_MAX",gmax);
     225          34 :   std::vector<unsigned> nbin; parseVector("GRID_BIN",nbin);
     226          34 :   std::vector<double> gspacing; parseVector("GRID_SPACING",gspacing);
     227          17 :   if( nbin.size()!=narg && gspacing.size()!=narg ) {
     228           0 :     error("GRID_BIN or GRID_SPACING must be set");
     229             :   }
     230             : 
     231             :   // Input of name and labels
     232          34 :   std::string vstring="COMPONENTS=" + getLabel();
     233          17 :   if( myvessel ) {
     234           1 :     vstring += " COORDINATES=" + myvessel->getLabel();
     235             :     // Input for PBC
     236           1 :     if( myvessel->isPeriodic() ) vstring+=" PBC=T";
     237           1 :     else vstring+=" PBC=F";
     238             :   } else {
     239          16 :     vstring += " COORDINATES=" + getPntrToArgument(0)->getName();
     240          16 :     for(unsigned i=1; i<getNumberOfArguments(); ++i) vstring += "," + getPntrToArgument(i)->getName();
     241             :     // Input for PBC
     242          16 :     if( getPntrToArgument(0)->isPeriodic() ) vstring+=" PBC=T";
     243          16 :     else vstring+=" PBC=F";
     244          24 :     for(unsigned i=1; i<getNumberOfArguments(); ++i) {
     245           8 :       if( getPntrToArgument(i)->isPeriodic() ) vstring+=",T";
     246           8 :       else vstring+=",F";
     247             :     }
     248             :   }
     249             :   // And create the grid
     250          17 :   createGrid( "histogram", vstring );
     251          17 :   mygrid->setBounds( gmin, gmax, nbin, gspacing );
     252          17 :   myhist = dynamic_cast<gridtools::HistogramOnGrid*>( mygrid );
     253          17 :   plumed_assert( myhist );
     254          17 :   if( myvessel ) {
     255             :     // Create a task list
     256           1 :     for(unsigned i=0; i<myvessel->getFullNumberOfTasks(); ++i) addTaskToList(i);
     257           1 :     setAveragingAction( mygrid, true );
     258             :   } else {
     259             :     // Create a task list
     260          16 :     for(unsigned i=0; i<mygrid->getNumberOfPoints(); ++i) addTaskToList(i);
     261          16 :     myhist->addOneKernelEachTimeOnly();
     262          16 :     setAveragingAction( mygrid, myhist->noDiscreteKernels() );
     263             :   }
     264          34 :   checkRead();
     265          17 : }
     266             : 
     267         104 : unsigned Histogram::getNumberOfDerivatives() {
     268         104 :   if( myvessel) return 1;
     269          96 :   return getNumberOfArguments();
     270             : }
     271             : 
     272         104 : unsigned Histogram::getNumberOfQuantities() const {
     273         104 :   if( myvessel ) return 3;
     274          96 :   return 2;
     275             : }
     276             : 
     277          33 : void Histogram::prepareForAveraging() {
     278          33 :   if( myvessel ) {
     279           4 :     deactivateAllTasks(); double norm=0;
     280           4 :     std::vector<double> cvals( myvessel->getNumberOfQuantities() );
     281          12 :     for(unsigned i=0; i<stash->getNumberOfStoredValues(); ++i) {
     282           8 :       taskFlags[i]=1; stash->retrieveSequentialValue(i, false, cvals );
     283           8 :       norm += cvals[0];
     284             :     }
     285           4 :     lockContributors(); ww = cweight / norm;
     286             :   } else {
     287             :     // Now fetch the kernel and the active points
     288          29 :     std::vector<double> point( getNumberOfArguments() );
     289          29 :     for(unsigned i=0; i<point.size(); ++i) point[i]=getArgument(i);
     290          58 :     unsigned num_neigh; std::vector<unsigned> neighbors(1);
     291          29 :     kernel = myhist->getKernelAndNeighbors( point, num_neigh, neighbors );
     292             : 
     293          29 :     if( num_neigh>1 ) {
     294             :       // Activate relevant tasks
     295          24 :       deactivateAllTasks();
     296          24 :       for(unsigned i=0; i<num_neigh; ++i) taskFlags[neighbors[i]]=1;
     297          24 :       lockContributors();
     298             :     } else {
     299             :       // This is used when we are not doing kernel density evaluation
     300           5 :       mygrid->addToGridElement( neighbors[0], 0, cweight );
     301          29 :     }
     302             :   }
     303          33 : }
     304             : 
     305           5 : void Histogram::performOperations( const bool& from_update ) { if( !myvessel ) plumed_dbg_assert( !myhist->noDiscreteKernels() ); }
     306             : 
     307          33 : void Histogram::finishAveraging() {
     308          33 :   if( !myvessel ) delete kernel;
     309          33 : }
     310             : 
     311       11052 : void Histogram::compute( const unsigned& current, MultiValue& myvals ) const {
     312       11052 :   if( myvessel ) {
     313           8 :     std::vector<double> cvals( myvessel->getNumberOfQuantities() );
     314           8 :     stash->retrieveSequentialValue( current, false, cvals );
     315           8 :     myvals.setValue( 0, cvals[0] ); myvals.setValue( 1, cvals[1] ); myvals.setValue( 2, ww );
     316             :   } else {
     317       11044 :     std::vector<Value*> vv( myhist->getVectorOfValues() );
     318       22083 :     std::vector<double> val( getNumberOfArguments() ), der( getNumberOfArguments() );
     319             :     // Retrieve the location of the grid point at which we are evaluating the kernel
     320       11047 :     mygrid->getGridPointCoordinates( current, val );
     321       11048 :     for(unsigned i=0; i<getNumberOfArguments(); ++i) vv[i]->set( val[i] );
     322             :     // Evaluate the histogram at the relevant grid point and set the values
     323       11048 :     double vvh = kernel->evaluate( vv, der,true); myvals.setValue( 1, vvh );
     324             :     // Set the derivatives and delete the vector of values
     325       22091 :     for(unsigned i=0; i<getNumberOfArguments(); ++i) { myvals.setDerivative( 1, i, der[i] ); delete vv[i]; }
     326             :   }
     327       11055 : }
     328             : 
     329             : }
     330        2523 : }

Generated by: LCOV version 1.13