LCOV - code coverage report
Current view: top level - generic - Average.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 42 48 87.5 %
Date: 2025-04-08 18:07:56 Functions: 2 3 66.7 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2016-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 "core/ActionShortcut.h"
      23             : #include "core/ActionWithArguments.h"
      24             : #include "core/PlumedMain.h"
      25             : #include "core/ActionRegister.h"
      26             : 
      27             : //+PLUMEDOC GRIDCALC AVERAGE
      28             : /*
      29             : Calculate the ensemble average of a collective variable
      30             : 
      31             : The ensemble average for a non-periodic, collective variable, $s$ is given by the following expression:
      32             : 
      33             : $$
      34             : \langle s \rangle = \frac{ \sum_{t'=0}^t w(t') s(t') }{ \sum_{t'=0}^t w(t') }
      35             : $$
      36             : 
      37             : Here the sum runs over a the trajectory and $s(t')$ is used to denote the value of the collective variable
      38             : at time $t'$.  The final quantity evaluated is a weighted
      39             : average as the weights, If the simulation is unbiassed then all the $w(t')$ values in teh expression above are
      40             : zero.  If the simulation is biased then the $w(t')$ weights are set in a way that ensures the effect any bias
      41             : has on the region of phase space sampled by the system is negated.
      42             : 
      43             : As the following example input shows you can use the AVERAGE shortcut to calculate the ensemble average of a CV using this formula:
      44             : 
      45             : ```plumed
      46             : d1: DISTANCE ATOMS=1,2
      47             : d1a: AVERAGE ARG=d1
      48             : PRINT ARG=d1a FILE=colvar STRIDE=100
      49             : ```
      50             : 
      51             : In this example no bias is acting on the system so the weights, $w(t')$ in the formulas above can thus all be set equal
      52             : to one. The shortcut illustrates how the averaging is achieved by using the [ACCUMULATE](ACCUMULATE.md) action.
      53             : 
      54             : When the variable is periodic (e.g. [TORSION](TORSION.md)) and has a value, $s$, in $a \le s \le b$ the ensemble average is evaluated using:
      55             : 
      56             : $$
      57             : \langle s \rangle = a + \frac{b - a}{2\pi} \arctan \left[ \frac{ \sum_{t'=0}^t w(t') \sin\left( \frac{2\pi [s(t')-a]}{b - a} \right) }{ \sum_{t'=0}^t w(t') \cos\left( \frac{2\pi [s(t')-a]}{b - a} \right) } \right]
      58             : $$
      59             : 
      60             : You can see how [ACCUMULATE](ACCUMULATE.md) and [CUSTOM](CUSTOM.md) can be used to implement this formula by expanding the shortcuts in the following example input:
      61             : 
      62             : ```plumed
      63             : t1: TORSION ATOMS=1,2,3,4
      64             : t1a: AVERAGE ARG=t1 CLEAR=100
      65             : PRINT ARG=t1a FILE=colvar STRIDE=100
      66             : ```
      67             : 
      68             : Notice that by using the `CLEAR` keyword we have specified that block averages
      69             : are to be calculated.  Consequently, after 100 steps all the information acquired thus far in the simulation is
      70             : forgotten and the process of averaging is begun again.  The quantities output in the colvar file are thus the
      71             : block averages taken over the first 100 frames of the trajectory, the block average over the second 100 frames
      72             : of trajectory and so on.
      73             : 
      74             : 
      75             : If a bias is acting upon the system then the $w(t')$ values in the expression above are non-zero.  You can calculate the $w(t') values
      76             : by using [REWEIGHT_BIAS](REWEIGHT_BIAS.md) or similar.  To pass these weights to the average action you would then use an input something
      77             : like the following:
      78             : 
      79             : ```plumed
      80             : t1: TORSION ATOMS=1,2,3,4
      81             : RESTRAINT ARG=t1 AT=pi KAPPA=100.
      82             : ww: REWEIGHT_BIAS TEMP=300
      83             : t1a: AVERAGE ARG=t1 LOGWEIGHTS=ww CLEAR=100
      84             : PRINT ARG=t1a FILE=colvar STRIDE=100
      85             : ```
      86             : 
      87             : This AVERAGE action in this input is a shortcut once again so by expanding it you can obtain a better understanding of how the
      88             : formulas above are applied in this case.
      89             : 
      90             : */
      91             : //+ENDPLUMEDOC
      92             : 
      93             : namespace PLMD {
      94             : namespace generic {
      95             : 
      96             : class Average : public ActionShortcut {
      97             : public:
      98             :   static void registerKeywords( Keywords& keys );
      99             :   explicit Average( const ActionOptions& );
     100             : };
     101             : 
     102             : PLUMED_REGISTER_ACTION(Average,"AVERAGE")
     103             : 
     104           6 : void Average::registerKeywords( Keywords& keys ) {
     105           6 :   ActionShortcut::registerKeywords( keys );
     106          12 :   keys.addInputKeyword("compulsory","ARG","scalar/grid","the quantity that is being averaged");
     107           6 :   keys.add("optional","LOGWEIGHTS","the logarithm of the quantity to use as the weights when calculating averages");
     108           6 :   keys.add("compulsory","STRIDE","1","the frequency with which to store data for averaging");
     109           6 :   keys.add("compulsory","CLEAR","0","the frequency with whihc to clear the data that is being averaged");
     110           6 :   keys.add("optional","NORMALIZATION","keyword for old version of the code that is there to maintain back compatibility only. Adding this keyword does nothing");
     111          12 :   keys.setValueDescription("scalar/grid","the value of the average");
     112           6 :   keys.needsAction("COMBINE");
     113           6 :   keys.needsAction("CUSTOM");
     114           6 :   keys.needsAction("ONES");
     115           6 :   keys.needsAction("ACCUMULATE");
     116           6 : }
     117             : 
     118           4 : Average::Average( const ActionOptions& ao ):
     119             :   Action(ao),
     120           4 :   ActionShortcut(ao) {
     121             : 
     122             :   std::string lw;
     123           8 :   parse("LOGWEIGHTS",lw);
     124             :   std::string stride, clearstride;
     125           4 :   parse("STRIDE",stride);
     126           8 :   parse("CLEAR",clearstride);
     127           4 :   if( lw.length()>0 ) {
     128           2 :     readInputLine( getShortcutLabel() + "_wsum: COMBINE ARG=" + lw + " PERIODIC=NO");
     129           2 :     readInputLine( getShortcutLabel() + "_weight: CUSTOM ARG=" + getShortcutLabel() + "_wsum FUNC=exp(x) PERIODIC=NO");
     130             :   } else {
     131           6 :     readInputLine( getShortcutLabel() + "_weight: ONES SIZE=1" );
     132             :   }
     133             : 
     134             :   std::vector<std::string> arg;
     135           8 :   parseVector("ARG",arg);
     136           4 :   if( arg.size()!=1 ) {
     137           0 :     error("should only be one argument to this action");
     138             :   }
     139             :   std::vector<Value*> vals;
     140           4 :   ActionWithArguments::interpretArgumentList( arg, plumed.getActionSet(), this, vals );
     141             : 
     142           8 :   readInputLine( getShortcutLabel() + "_denom: ACCUMULATE ARG=" + getShortcutLabel() + "_weight STRIDE=" + stride + " CLEAR=" + clearstride );
     143           4 :   if( vals[0]->isPeriodic() ) {
     144             :     std::string lbound, ubound, pfactor;
     145           1 :     vals[0]->getDomain( lbound, ubound );
     146           2 :     pfactor = "((" + ubound + "-" + lbound + ")/(pi+pi))";
     147           2 :     readInputLine( getShortcutLabel() + "_sin: CUSTOM ARG=" + arg[0] + "," + getShortcutLabel() + "_weight FUNC=y*sin((x-" + lbound + ")/" + pfactor + ") PERIODIC=NO");
     148           2 :     readInputLine( getShortcutLabel() + "_cos: CUSTOM ARG=" + arg[0] + "," + getShortcutLabel() + "_weight FUNC=y*cos((x-" + lbound + ")/" + pfactor + ") PERIODIC=NO");
     149           2 :     readInputLine( getShortcutLabel() + "_sinsum: ACCUMULATE ARG=" + getShortcutLabel() + "_sin STRIDE=" + stride + " CLEAR=" + clearstride );
     150           2 :     readInputLine( getShortcutLabel() + "_cossum: ACCUMULATE ARG=" + getShortcutLabel() + "_cos STRIDE=" + stride + " CLEAR=" + clearstride );
     151           2 :     readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_sinsum," + getShortcutLabel() + "_cossum," + getShortcutLabel() + "_denom FUNC=" + lbound + "+" + pfactor + "*atan2(x/z,y/z) PERIODIC=" + lbound +"," + ubound);
     152             :   } else {
     153             :     std::string normstr;
     154           6 :     parse("NORMALIZATION",normstr);
     155           6 :     if( normstr=="true" || normstr=="false" ) {
     156           0 :       warning("NORMALIZATION is deprecated. You are advised to take this out of input files in future and use the new syntax with ACCUMULATE for unormalized data rather than the shortcut AVERAGE");
     157           3 :     } else if( normstr.length()>0 ) {
     158           0 :       error("NORMALIZATION=" + normstr + " is not valid PLUMED input.  If you want an unormalised 'average' use ACCUMULATE");
     159             :     }
     160           6 :     readInputLine( getShortcutLabel() + "_prod: CUSTOM ARG=" + arg[0] + "," + getShortcutLabel() + "_weight FUNC=x*y PERIODIC=NO");
     161           3 :     if( normstr.length()==0 || normstr=="true" ) {
     162           6 :       readInputLine( getShortcutLabel() + "_numer: ACCUMULATE ARG=" + getShortcutLabel() + "_prod STRIDE=" + stride + " CLEAR=" + clearstride  );
     163           6 :       readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_numer," + getShortcutLabel() + "_denom FUNC=x/y PERIODIC=NO");
     164           0 :     } else if( normstr=="false" ) {
     165           0 :       readInputLine( getShortcutLabel() + ": ACCUMULATE ARG=" + getShortcutLabel() + "_prod STRIDE=" + stride + " CLEAR=" + clearstride  );
     166             :     } else {
     167           0 :       plumed_error();
     168             :     }
     169             :   }
     170           8 : }
     171             : 
     172             : }
     173             : }

Generated by: LCOV version 1.16