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 instantaneous 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 corresponding to all five of these distances are added to the instantaneous 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 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 5806 : bool calculateOnUpdate() override { 147 5806 : return false; 148 : } 149 174 : bool calculateConstantValues( const bool& have_atoms ) override { 150 174 : return false; 151 : } 152 2818 : void calculate() override {} 153 2818 : void apply() override {} 154 : void update() override ; 155 : }; 156 : 157 : PLUMED_REGISTER_ACTION(Accumulate,"ACCUMULATE") 158 : 159 176 : void Accumulate::registerKeywords( Keywords& keys ) { 160 176 : Action::registerKeywords( keys ); 161 176 : ActionWithValue::registerKeywords( keys ); 162 176 : ActionWithArguments::registerKeywords( keys ); 163 176 : ActionPilot::registerKeywords( keys ); 164 176 : keys.use("UPDATE_FROM"); 165 176 : keys.use("UPDATE_UNTIL"); 166 176 : keys.remove("NUMERICAL_DERIVATIVES"); 167 352 : keys.addInputKeyword("compulsory","ARG","scalar/grid","the label of the argument that is being added to on each timestep"); 168 176 : keys.add("compulsory","STRIDE","1","the frequency with which the data should be collected and added to the quantity being averaged"); 169 176 : 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 352 : keys.setValueDescription("scalar/grid","a sum calculated from the time series of the input quantity"); 172 176 : } 173 : 174 88 : Accumulate::Accumulate( const ActionOptions& ao ): 175 : Action(ao), 176 : ActionWithValue(ao), 177 : ActionWithArguments(ao), 178 : ActionPilot(ao), 179 88 : clearnextstep(true) { 180 88 : if( getNumberOfArguments()!=1 ) { 181 0 : error("there should only be one argument to this action"); 182 : } 183 88 : 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 88 : if( getStride()==0 ) { 188 1 : error("STRIDE should be a positive number of steps"); 189 : } 190 87 : parse("CLEAR",clearstride); 191 87 : if( clearstride>0 ) { 192 11 : if( clearstride%getStride()!=0 ) { 193 1 : error("CLEAR parameter must be a multiple of STRIDE"); 194 : } 195 11 : log.printf(" clearing average every %u steps \n",clearstride); 196 : } 197 87 : std::vector<std::size_t> shape( getPntrToArgument(0)->getShape() ); 198 87 : addValueWithDerivatives( shape ); 199 87 : setNotPeriodic(); 200 87 : if( getPntrToArgument(0)->isPeriodic() ) { 201 0 : error("you cannot accumulate a periodic quantity"); 202 : } 203 90 : } 204 : 205 48 : unsigned Accumulate::getNumberOfDerivatives() { 206 48 : if( getPntrToArgument(0)->getRank()>0 ) { 207 48 : return getPntrToArgument(0)->getNumberOfGridDerivatives(); 208 : } 209 0 : return getPntrToArgument(0)->getNumberOfDerivatives(); 210 : } 211 : 212 2814 : void Accumulate::update() { 213 2814 : if( clearnextstep ) { 214 94 : if( getPntrToComponent(0)->getNumberOfValues()!=getPntrToArgument(0)->getNumberOfValues() ) { 215 0 : getPntrToComponent(0)->setShape( getPntrToArgument(0)->getShape() ); 216 : } 217 94 : clearnextstep=false; 218 94 : getPntrToComponent(0)->set(0,0.0); 219 94 : getPntrToComponent(0)->clearDerivatives(true); 220 : } 221 2814 : if( getStep()==0 ) { 222 : return; 223 : } 224 : 225 : Value* myarg=getPntrToArgument(0); 226 2728 : Value* myout = getPntrToComponent(0); 227 2728 : if( getPntrToArgument(0)->getRank()>0 ) { 228 351 : unsigned nvals = myarg->getNumberOfValues(), nder = myarg->getNumberOfGridDerivatives(); 229 277070 : for(unsigned i=0; i<nvals; ++i) { 230 276719 : myout->set( i, myout->get(i) + myarg->get(i) ); 231 829866 : for(unsigned j=0; j<nder; ++j) { 232 553147 : myout->addGridDerivatives( i, j, myarg->getGridDerivative( i, j ) ); 233 : } 234 : } 235 : } else { 236 2377 : getPntrToComponent(0)->add( getPntrToArgument(0)->get() ); 237 : } 238 : 239 : // Clear if required 240 2728 : if( clearstride>0 && getStep()%clearstride==0 ) { 241 18 : clearnextstep=true; 242 : } 243 : } 244 : 245 : } 246 : }