Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2012-2017 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/ActionRegister.h" 25 : #include "tools/Communicator.h" 26 : 27 : //+PLUMEDOC ANALYSIS GATHER_REPLICAS 28 : /* 29 : Create a vector that contains the copies of the input quantities from all replicas 30 : 31 : There are three ways that you can generate indistinguishable replicas of a system for calculating 32 : ensemble averages: 33 : 34 : - You can take a time average. 35 : - You can run a multiple replica simulation and average over the replicas. 36 : - You can do a spatial average and assume that there are multiple replicas of the system in the same simulation box and average over them. 37 : 38 : Many actions within PLUMED will perform average over more than one of these type of replica. However, since v2.10 39 : we have tried to separate out these three ways of generating multiple replicas for averaging. There are thus many actions 40 : where you take time averages by using [ACCUMULATE](ACCUMULATE.md) or [COLLECT](COLLECT.md). You take spatial averages by passing 41 : working the vectors of values. You then use this action to average over replicas. The way this works in practise is illustrated by the simple 42 : example shown below. 43 : 44 : ```plumed 45 : #SETTINGS NREPLICAS=2 46 : # Calculate distance between atoms 1 and 2 on for all 2 replicas 47 : d: DISTANCE ATOMS=1,2 48 : # Now gather the values of this distance on all the replicas: 49 : g: GATHER_REPLICAS ARG=d 50 : # Now average the two distances on the two replicas 51 : s: COMBINE ARG=g.rep-1,g.rep-2 COEFFICIENTS=0.5,0.5 PERIODIC=NO 52 : # And print the instantaneous average for the distance on the two replicas 53 : PRINT ARG=s FILE=colvar 54 : ``` 55 : 56 : Now suppose that we wanted to calculate a time average of the distance and an average over the replicas. We could use an input like this: 57 : 58 : ```plumed 59 : #SETTINGS NREPLICAS=2 60 : d: DISTANCE ATOMS=1,2 61 : g: GATHER_REPLICAS ARG=d 62 : s: COMBINE ARG=g.rep-1,g.rep-2 COEFFICIENTS=0.5,0.5 PERIODIC=NO 63 : # This does the time averaging 64 : a: AVERAGE ARG=s 65 : # And output the final average 66 : PRINT ARG=a FILE=colvar 67 : ``` 68 : 69 : */ 70 : //+ENDPLUMEDOC 71 : 72 : namespace PLMD { 73 : namespace generic { 74 : 75 : class GatherReplicas : 76 : public ActionWithValue, 77 : public ActionWithArguments { 78 : private: 79 : unsigned nreplicas; 80 : public: 81 : static void registerKeywords( Keywords& keys ); 82 : explicit GatherReplicas( const ActionOptions& ); 83 : unsigned getNumberOfDerivatives(); 84 : void calculate(); 85 : void apply(); 86 : }; 87 : 88 : PLUMED_REGISTER_ACTION(GatherReplicas,"GATHER_REPLICAS") 89 : 90 35 : void GatherReplicas::registerKeywords( Keywords& keys ) { 91 35 : Action::registerKeywords( keys ); 92 35 : ActionWithValue::registerKeywords( keys ); 93 35 : ActionWithArguments::registerKeywords( keys ); 94 70 : keys.addInputKeyword("compulsory","ARG","scalar/vector/matrix/grid","the argument from the various replicas that you would like to gather"); 95 70 : keys.addOutputComponent("rep","default","scalar/vector/matrix/grid","the input arguments for each of the replicas"); 96 35 : keys.remove("NUMERICAL_DERIVATIVES"); 97 35 : } 98 : 99 17 : GatherReplicas::GatherReplicas( const ActionOptions& ao ): 100 : Action(ao), 101 : ActionWithValue(ao), 102 17 : ActionWithArguments(ao) { 103 17 : if( getNumberOfArguments()!=1 ) { 104 0 : error("you can only gather one argument at a time with GatherReplicas"); 105 : } 106 : 107 17 : std::vector<std::size_t> shape( getPntrToArgument(0)->getShape() ); 108 : std::string min, max; 109 17 : nreplicas=getNumberOfReplicas(); 110 : bool periodic=false; 111 17 : if( getPntrToArgument(0)->isPeriodic() ) { 112 : periodic=true; 113 0 : getPntrToArgument(0)->getDomain( min, max ); 114 : } 115 : 116 98 : for(unsigned i=0; i<nreplicas; ++i) { 117 : std::string num; 118 81 : Tools::convert( i+1, num); 119 81 : if( getPntrToArgument(0)->hasDerivatives() ) { 120 162 : addComponentWithDerivatives( "rep-" + num, shape ); 121 : } else { 122 0 : addComponent( "rep-" + num, shape ); 123 : } 124 81 : if( periodic ) { 125 0 : componentIsPeriodic( "rep-" + num, min, max ); 126 : } else { 127 162 : componentIsNotPeriodic( "rep-" + num ); 128 : } 129 : } 130 17 : } 131 : 132 0 : unsigned GatherReplicas::getNumberOfDerivatives() { 133 0 : return getPntrToArgument(0)->getNumberOfDerivatives(); 134 : } 135 : 136 4236 : void GatherReplicas::calculate() { 137 : Value* myarg = getPntrToArgument(0); 138 4236 : std::size_t nvals = myarg->getNumberOfValues(), nder = myarg->getNumberOfDerivatives(); 139 4236 : std::vector<double> dval( nvals*(1+nder) ), datap(nreplicas*nvals*(1+nder) ); 140 8472 : for(unsigned i=0; i<nvals; ++i) { 141 4236 : dval[i*(1+nder)] = myarg->get(i); 142 4236 : if( myarg->getRank()==0 ) { 143 8640 : for(unsigned j=0; j<nder; ++j) { 144 4404 : dval[i*(1+nder)+1+j] = myarg->getDerivative(j); 145 : } 146 0 : } else if( myarg->hasDerivatives() ) { 147 0 : for(unsigned j=0; j<nder; ++j) { 148 0 : dval[i*(1+nder)+1+j] = myarg->getGridDerivative( i, j ); 149 : } 150 : } 151 : } 152 4236 : if(comm.Get_rank()==0) { 153 4230 : multi_sim_comm.Allgather(dval,datap); 154 : } 155 : // Only rank 0 of each replica takes part in the gather; share the result with 156 : // the other ranks of the replica, which would otherwise keep a zero-filled datap. 157 4236 : comm.Bcast(datap,0); 158 : 159 29604 : for(unsigned k=0; k<nreplicas; k++) { 160 25368 : Value* myout = getPntrToComponent(k); 161 25368 : if( myout->getNumberOfDerivatives()!=myarg->getNumberOfDerivatives() ) { 162 80 : myout->resizeDerivatives( myarg->getNumberOfDerivatives() ); 163 : } 164 25368 : unsigned sstart=k*nvals*(1+nder); 165 50736 : for(unsigned i=0; i<nvals; ++i) { 166 25368 : myout->set( i, datap[sstart+i*(1+nder)] ); 167 25368 : if( myarg->getRank()==0 ) { 168 51072 : for(unsigned j=0; j<nder; ++j) { 169 25704 : myout->setDerivative( j, dval[i*(1+nder)+1+j] ); 170 : } 171 0 : } else if( myarg->hasDerivatives() ) { 172 : // the derivatives of a grid are part of the data gathered from replica k 173 0 : for(unsigned j=0; j<nder; ++j) { 174 0 : myout->addGridDerivatives( i, j, datap[sstart+i*(1+nder)+1+j] ); 175 : } 176 : } 177 : } 178 : } 179 4236 : } 180 : 181 4236 : void GatherReplicas::apply() { 182 4236 : if( doNotCalculateDerivatives() ) { 183 4236 : return; 184 : } 185 0 : error("apply has not been implemented for GatherReplicas"); 186 : } 187 : 188 : } 189 : }