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/ActionWithValue.h" 23 : #include "core/ActionWithArguments.h" 24 : #include "core/ActionPilot.h" 25 : #include "core/ActionRegister.h" 26 : #include "core/PlumedMain.h" 27 : #include "core/ActionSet.h" 28 : 29 : //+PLUMEDOC GRIDCALC ACCUMULATE 30 : /* 31 : Sum the elements of this value over the course of the trajectory 32 : 33 : This action is used to sum the outputs from another action over the course of the trajectory. This is useful 34 : if you want to calculate the average value that a CV took over the course of a simulation. As an example, the following 35 : input can be used to calculate the average distance between atom 1 and atom 2. 36 : 37 : ```plumed 38 : c: CONSTANT VALUE=1 39 : d: DISTANCE ATOMS=1,2 40 : # This adds together the value of the distance on every step 41 : s: ACCUMULATE ARG=d STRIDE=1 42 : # This adds one every time we add a new distance to the value s 43 : n: ACCUMULATE ARG=c STRIDE=1 44 : # This is thus the average distance 45 : a: CUSTOM ARG=s,n FUNC=x/y PERIODIC=NO 46 : # This prints out the average over the whole trajectory (STRIDE=0 means print at end only) 47 : PRINT ARG=a FILE=average.dat STRIDE=0 48 : ``` 49 : 50 : You can use this action for block averaging by using the `CLEAR` keyword as shown below: 51 : 52 : ```plumed 53 : c: CONSTANT VALUE=1 54 : d: DISTANCE ATOMS=1,2 55 : # This adds together the value of the distance on every step 56 : s: ACCUMULATE ARG=d STRIDE=1 CLEAR=1000 57 : # This adds one every time we add a new distance to the value s 58 : n: ACCUMULATE ARG=c STRIDE=1 CLEAR=1000 59 : # This is thus the average distance 60 : a: CUSTOM ARG=s,n FUNC=x/y PERIODIC=NO 61 : # This prints out the average over the whole trajectory (STRIDE=0 means print at end only) 62 : PRINT ARG=a FILE=average.dat STRIDE=1000 63 : ``` 64 : 65 : The instructions `CLEAR=1000` in the above input tells PLUMED to set the values `s` and `n` back to 66 : zero after 1000 new steps have been performed. The PRINT action will thus print a block average that 67 : is taken from the first 1000 steps of the trajectory, a second block average from the second 1000 steps 68 : of the trajectory and so on. 69 : 70 : ## Estimating histograms 71 : 72 : We can also use this action to construct histograms. The example below shows how you can estimate the 73 : distribution of distances between atoms 1 and 2 that are sampled over the course of the trajectory. 74 : 75 : ```plumed 76 : c: CONSTANT VALUE=1 77 : d: DISTANCE ATOMS=1,2 78 : # Construct the instantaneous histogram from the instantaneous value of the distance 79 : kde: KDE ARG=d BANDWIDTH=0.05 GRID_MIN=0 GRID_MAX=5 GRID_BIN=250 80 : # Now add together all the instantaneous histograms 81 : hist: ACCUMULATE ARG=kde STRIDE=1 82 : # And normalise the histogram 83 : n: ACCUMULATE ARG=c STRIDE=1 84 : a: CUSTOM ARG=hist,n FUNC=x/y PERIODIC=NO 85 : # And print out the final histogram 86 : DUMPGRID ARG=a FILE=histo.grid 87 : ``` 88 : 89 : At first glance the fact that we use a [KDE](KDE.md) action to construct an instaneous histogram from a single 90 : distance may appear odd. The reason for doing this, however, is to introduce a clear distinction between 91 : the syntaxes that are used for spatial and temporal averaging. To see what I mean consider the following input: 92 : 93 : 94 : ```plumed 95 : c: CONSTANT VALUE=5 96 : d: DISTANCE ATOMS1=1,2 ATOMS2=3,4 ATOMS3=5,6 ATOMS4=7,8 ATOMS5=9,10 97 : # Construct the instantaneous histogram from the instantaneous value of the distance 98 : kde: KDE ARG=d BANDWIDTH=0.05 GRID_MIN=0 GRID_MAX=5 GRID_BIN=250 99 : # Now add together all the instantaneous histograms 100 : hist: ACCUMULATE ARG=kde STRIDE=1 101 : # And normalise the histogram 102 : n: ACCUMULATE ARG=c STRIDE=1 103 : a: CUSTOM ARG=hist,n FUNC=x/y PERIODIC=NO 104 : # And print out the final histogram 105 : DUMPGRID ARG=a FILE=histo.grid 106 : ``` 107 : 108 : This input computes 5 distances. Kernels correpsonding to all five of these distances are added to the instaneous 109 : histogram that is constructed using the [KDE](KDE.md) action. When we call the accumulate action here we are thus 110 : not simply adding a single kernel to the accumulated grid when we add the the elements from `kde`. 111 : 112 : */ 113 : //+ENDPLUMEDOC 114 : 115 : namespace PLMD { 116 : namespace generic { 117 : 118 : class Accumulate : 119 : public ActionWithValue, 120 : public ActionWithArguments, 121 : public ActionPilot { 122 : private: 123 : bool clearnextstep; 124 : unsigned clearstride; 125 : public: 126 : static void registerKeywords( Keywords& keys ); 127 : Accumulate( const ActionOptions& ); 128 : unsigned getNumberOfDerivatives(); 129 4778 : bool calculateOnUpdate() override { 130 4778 : return false; 131 : } 132 126 : bool calculateConstantValues( const bool& have_atoms ) override { 133 126 : return false; 134 : } 135 2328 : void calculate() override {} 136 2328 : void apply() override {} 137 : void update() override ; 138 : }; 139 : 140 : PLUMED_REGISTER_ACTION(Accumulate,"ACCUMULATE") 141 : 142 127 : void Accumulate::registerKeywords( Keywords& keys ) { 143 127 : Action::registerKeywords( keys ); 144 127 : ActionWithValue::registerKeywords( keys ); 145 127 : ActionWithArguments::registerKeywords( keys ); 146 127 : ActionPilot::registerKeywords( keys ); 147 127 : keys.use("UPDATE_FROM"); 148 127 : keys.use("UPDATE_UNTIL"); 149 254 : keys.addInputKeyword("compulsory","ARG","scalar/grid","the label of the argument that is being added to on each timestep"); 150 127 : keys.add("compulsory","STRIDE","1","the frequency with which the data should be collected and added to the quantity being averaged"); 151 127 : keys.add("compulsory","CLEAR","0","the frequency with which to clear all the accumulated data. The default value " 152 : "of 0 implies that all the data will be used and that the grid will never be cleared"); 153 254 : keys.setValueDescription("scalar/grid","a sum calculated from the time series of the input quantity"); 154 127 : } 155 : 156 63 : Accumulate::Accumulate( const ActionOptions& ao ): 157 : Action(ao), 158 : ActionWithValue(ao), 159 : ActionWithArguments(ao), 160 : ActionPilot(ao), 161 63 : clearnextstep(true) { 162 63 : if( getNumberOfArguments()!=1 ) { 163 0 : error("there should only be one argument to this action"); 164 : } 165 63 : if( !getPntrToArgument(0)->hasDerivatives() && getPntrToArgument(0)->getRank()!=0 ) { 166 0 : error("input to the accumulate action should be a scalar or a grid"); 167 : } 168 : 169 63 : parse("CLEAR",clearstride); 170 63 : if( clearstride>0 ) { 171 11 : if( clearstride%getStride()!=0 ) { 172 0 : error("CLEAR parameter must be a multiple of STRIDE"); 173 : } 174 11 : log.printf(" clearing average every %u steps \n",clearstride); 175 : } 176 63 : std::vector<unsigned> shape( getPntrToArgument(0)->getShape() ); 177 63 : addValueWithDerivatives( shape ); 178 63 : setNotPeriodic(); 179 63 : if( getPntrToArgument(0)->isPeriodic() ) { 180 0 : error("you cannot accumulate a periodic quantity"); 181 : } 182 63 : } 183 : 184 36 : unsigned Accumulate::getNumberOfDerivatives() { 185 36 : if( getPntrToArgument(0)->getRank()>0 ) { 186 36 : return getPntrToArgument(0)->getNumberOfGridDerivatives(); 187 : } 188 0 : return getPntrToArgument(0)->getNumberOfDerivatives(); 189 : } 190 : 191 2324 : void Accumulate::update() { 192 2324 : if( clearnextstep ) { 193 70 : if( getPntrToComponent(0)->getNumberOfValues()!=getPntrToArgument(0)->getNumberOfValues() ) { 194 0 : getPntrToComponent(0)->setShape( getPntrToArgument(0)->getShape() ); 195 : } 196 70 : clearnextstep=false; 197 70 : getPntrToComponent(0)->set(0,0.0); 198 70 : getPntrToComponent(0)->clearDerivatives(true); 199 : } 200 2324 : if( getStep()==0 ) { 201 : return; 202 : } 203 : 204 : Value* myarg=getPntrToArgument(0); 205 2262 : Value* myout = getPntrToComponent(0); 206 2262 : if( getPntrToArgument(0)->getRank()>0 ) { 207 118 : unsigned nvals = myarg->getNumberOfValues(), nder = myarg->getNumberOfGridDerivatives(); 208 270581 : for(unsigned i=0; i<nvals; ++i) { 209 270463 : myout->set( i, myout->get(i) + myarg->get(i) ); 210 1353300 : for(unsigned j=0; j<nder; ++j) { 211 1082837 : myout->addGridDerivatives( i, j, myarg->getGridDerivative( i, j ) ); 212 : } 213 : } 214 : } else { 215 2144 : getPntrToComponent(0)->add( getPntrToArgument(0)->get() ); 216 : } 217 : 218 : // Clear if required 219 2262 : if( clearstride>0 && getStep()%clearstride==0 ) { 220 18 : clearnextstep=true; 221 : } 222 : } 223 : 224 : } 225 : }