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. Notice that you can achieve a similar effect using UPDATE_FROM and UPDATE_UNTIL as 69 : shown below: 70 : 71 : ```plumed 72 : c: CONSTANT VALUE=1 73 : d: DISTANCE ATOMS=1,2 74 : s1: ACCUMULATE ARG=d STRIDE=1 UPDATE_UNTIL=1000 75 : n1: ACCUMULATE ARG=c STRIDE=1 UPDATE_UNTIL=1000 76 : a1: CUSTOM ARG=s1,n1 FUNC=x/y PERIODIC=NO 77 : s2: ACCUMULATE ARG=d STRIDE=1 UPDATE_FROM=1000 78 : n2: ACCUMULATE ARG=c STRIDE=1 UPDATE_FROM=1000 79 : a2: CUSTOM ARG=s2,n2 FUNC=x/y PERIODIC=NO 80 : diff: CUSTOM ARG=a1,a2 FUNC=x-y PERIODIC=NO 81 : PRINT ARG=a1,a2,diff FILE=colver STRIDE=2000 82 : ``` 83 : 84 : This output calculates the average distance for the first 1000 frames of the trajectory and for the second 1000 frames of the trajectory. 85 : These two averages are then output to a file called colvar as well as the difference between them. 86 : 87 : ## Estimating histograms 88 : 89 : We can also use this action to construct histograms. The example below shows how you can estimate the 90 : distribution of distances between atoms 1 and 2 that are sampled over the course of the trajectory. 91 : 92 : ```plumed 93 : c: CONSTANT VALUE=1 94 : d: DISTANCE ATOMS=1,2 95 : # Construct the instantaneous histogram from the instantaneous value of the distance 96 : kde: KDE ARG=d BANDWIDTH=0.05 GRID_MIN=0 GRID_MAX=5 GRID_BIN=250 97 : # Now add together all the instantaneous histograms 98 : hist: ACCUMULATE ARG=kde STRIDE=1 99 : # And normalise the histogram 100 : n: ACCUMULATE ARG=c STRIDE=1 101 : a: CUSTOM ARG=hist,n FUNC=x/y PERIODIC=NO 102 : # And print out the final histogram 103 : DUMPGRID ARG=a FILE=histo.grid 104 : ``` 105 : 106 : At first glance the fact that we use a [KDE](KDE.md) action to construct an instaneous histogram from a single 107 : distance may appear odd. The reason for doing this, however, is to introduce a clear distinction between 108 : the syntaxes that are used for spatial and temporal averaging. To see what I mean consider the following input: 109 : 110 : 111 : ```plumed 112 : c: CONSTANT VALUE=5 113 : d: DISTANCE ATOMS1=1,2 ATOMS2=3,4 ATOMS3=5,6 ATOMS4=7,8 ATOMS5=9,10 114 : # Construct the instantaneous histogram from the instantaneous value of the distance 115 : kde: KDE ARG=d BANDWIDTH=0.05 GRID_MIN=0 GRID_MAX=5 GRID_BIN=250 116 : # Now add together all the instantaneous histograms 117 : hist: ACCUMULATE ARG=kde STRIDE=1 118 : # And normalise the histogram 119 : n: ACCUMULATE ARG=c STRIDE=1 120 : a: CUSTOM ARG=hist,n FUNC=x/y PERIODIC=NO 121 : # And print out the final histogram 122 : DUMPGRID ARG=a FILE=histo.grid 123 : ``` 124 : 125 : This input computes 5 distances. Kernels correpsonding to all five of these distances are added to the instaneous 126 : histogram that is constructed using the [KDE](KDE.md) action. When we call the accumulate action here we are thus 127 : not simply adding a single kernel to the accumulated grid when we add the the elements from `kde`. 128 : 129 : */ 130 : //+ENDPLUMEDOC 131 : 132 : namespace PLMD { 133 : namespace generic { 134 : 135 : class Accumulate : 136 : public ActionWithValue, 137 : public ActionWithArguments, 138 : public ActionPilot { 139 : private: 140 : bool clearnextstep; 141 : unsigned clearstride; 142 : public: 143 : static void registerKeywords( Keywords& keys ); 144 : Accumulate( const ActionOptions& ); 145 : unsigned getNumberOfDerivatives() override; 146 4802 : bool calculateOnUpdate() override { 147 4802 : return false; 148 : } 149 130 : bool calculateConstantValues( const bool& have_atoms ) override { 150 130 : return false; 151 : } 152 2338 : void calculate() override {} 153 2338 : void apply() override {} 154 : void update() override ; 155 : }; 156 : 157 : PLUMED_REGISTER_ACTION(Accumulate,"ACCUMULATE") 158 : 159 131 : void Accumulate::registerKeywords( Keywords& keys ) { 160 131 : Action::registerKeywords( keys ); 161 131 : ActionWithValue::registerKeywords( keys ); 162 131 : ActionWithArguments::registerKeywords( keys ); 163 131 : ActionPilot::registerKeywords( keys ); 164 131 : keys.use("UPDATE_FROM"); 165 131 : keys.use("UPDATE_UNTIL"); 166 131 : keys.remove("NUMERICAL_DERIVATIVES"); 167 262 : keys.addInputKeyword("compulsory","ARG","scalar/grid","the label of the argument that is being added to on each timestep"); 168 131 : keys.add("compulsory","STRIDE","1","the frequency with which the data should be collected and added to the quantity being averaged"); 169 131 : keys.add("compulsory","CLEAR","0","the frequency with which to clear all the accumulated data. The default value " 170 : "of 0 implies that all the data will be used and that the grid will never be cleared"); 171 262 : keys.setValueDescription("scalar/grid","a sum calculated from the time series of the input quantity"); 172 131 : } 173 : 174 65 : Accumulate::Accumulate( const ActionOptions& ao ): 175 : Action(ao), 176 : ActionWithValue(ao), 177 : ActionWithArguments(ao), 178 : ActionPilot(ao), 179 65 : clearnextstep(true) { 180 65 : if( getNumberOfArguments()!=1 ) { 181 0 : error("there should only be one argument to this action"); 182 : } 183 65 : if( !getPntrToArgument(0)->hasDerivatives() && getPntrToArgument(0)->getRank()!=0 ) { 184 0 : error("input to the accumulate action should be a scalar or a grid"); 185 : } 186 : 187 65 : parse("CLEAR",clearstride); 188 65 : if( clearstride>0 ) { 189 11 : if( clearstride%getStride()!=0 ) { 190 0 : error("CLEAR parameter must be a multiple of STRIDE"); 191 : } 192 11 : log.printf(" clearing average every %u steps \n",clearstride); 193 : } 194 65 : std::vector<std::size_t> shape( getPntrToArgument(0)->getShape() ); 195 65 : addValueWithDerivatives( shape ); 196 65 : setNotPeriodic(); 197 65 : if( getPntrToArgument(0)->isPeriodic() ) { 198 0 : error("you cannot accumulate a periodic quantity"); 199 : } 200 65 : } 201 : 202 37 : unsigned Accumulate::getNumberOfDerivatives() { 203 37 : if( getPntrToArgument(0)->getRank()>0 ) { 204 37 : return getPntrToArgument(0)->getNumberOfGridDerivatives(); 205 : } 206 0 : return getPntrToArgument(0)->getNumberOfDerivatives(); 207 : } 208 : 209 2334 : void Accumulate::update() { 210 2334 : if( clearnextstep ) { 211 72 : if( getPntrToComponent(0)->getNumberOfValues()!=getPntrToArgument(0)->getNumberOfValues() ) { 212 0 : getPntrToComponent(0)->setShape( getPntrToArgument(0)->getShape() ); 213 : } 214 72 : clearnextstep=false; 215 72 : getPntrToComponent(0)->set(0,0.0); 216 72 : getPntrToComponent(0)->clearDerivatives(true); 217 : } 218 2334 : if( getStep()==0 ) { 219 : return; 220 : } 221 : 222 : Value* myarg=getPntrToArgument(0); 223 2270 : Value* myout = getPntrToComponent(0); 224 2270 : if( getPntrToArgument(0)->getRank()>0 ) { 225 122 : unsigned nvals = myarg->getNumberOfValues(), nder = myarg->getNumberOfGridDerivatives(); 226 270717 : for(unsigned i=0; i<nvals; ++i) { 227 270595 : myout->set( i, myout->get(i) + myarg->get(i) ); 228 816178 : for(unsigned j=0; j<nder; ++j) { 229 545583 : myout->addGridDerivatives( i, j, myarg->getGridDerivative( i, j ) ); 230 : } 231 : } 232 : } else { 233 2148 : getPntrToComponent(0)->add( getPntrToArgument(0)->get() ); 234 : } 235 : 236 : // Clear if required 237 2270 : if( clearstride>0 && getStep()%clearstride==0 ) { 238 18 : clearnextstep=true; 239 : } 240 : } 241 : 242 : } 243 : }