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 instaneous 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 29 : void GatherReplicas::registerKeywords( Keywords& keys ) { 91 29 : Action::registerKeywords( keys ); 92 29 : ActionWithValue::registerKeywords( keys ); 93 29 : ActionWithArguments::registerKeywords( keys ); 94 58 : keys.addInputKeyword("compulsory","ARG","scalar/vector/matrix/grid","the argument from the various replicas that you would like to gather"); 95 58 : keys.addOutputComponent("rep","default","scalar/vector/matrix/grid","the input arguments for each of the replicas"); 96 29 : } 97 : 98 13 : GatherReplicas::GatherReplicas( const ActionOptions& ao ): 99 : Action(ao), 100 : ActionWithValue(ao), 101 13 : ActionWithArguments(ao) { 102 13 : if( getNumberOfArguments()!=1 ) { 103 0 : error("you can only gather one argument at a time with GatherReplicas"); 104 : } 105 : 106 13 : std::vector<unsigned> shape( getPntrToArgument(0)->getShape() ); 107 : std::string min, max; 108 13 : nreplicas=multi_sim_comm.Get_size(); 109 : bool periodic=false; 110 13 : if( getPntrToArgument(0)->isPeriodic() ) { 111 : periodic=true; 112 0 : getPntrToArgument(0)->getDomain( min, max ); 113 : } 114 : 115 86 : for(unsigned i=0; i<nreplicas; ++i) { 116 : std::string num; 117 73 : Tools::convert( i+1, num); 118 73 : if( getPntrToArgument(0)->hasDerivatives() ) { 119 146 : addComponentWithDerivatives( "rep-" + num, shape ); 120 : } else { 121 0 : addComponent( "rep-" + num, shape ); 122 : } 123 73 : if( periodic ) { 124 0 : componentIsPeriodic( "rep-" + num, min, max ); 125 : } else { 126 146 : componentIsNotPeriodic( "rep-" + num ); 127 : } 128 : } 129 13 : } 130 : 131 0 : unsigned GatherReplicas::getNumberOfDerivatives() { 132 0 : return getPntrToArgument(0)->getNumberOfDerivatives(); 133 : } 134 : 135 4224 : void GatherReplicas::calculate() { 136 : Value* myarg = getPntrToArgument(0); 137 4224 : unsigned nvals = myarg->getNumberOfValues(), nder = myarg->getNumberOfDerivatives(); 138 4224 : std::vector<double> dval( nvals*(1+nder) ), datap(nreplicas*nvals*(1+nder) ); 139 8448 : for(unsigned i=0; i<nvals; ++i) { 140 4224 : dval[i*(1+nder)] = myarg->get(i); 141 4224 : if( myarg->getRank()==0 ) { 142 8448 : for(unsigned j=0; j<nder; ++j) { 143 4224 : dval[i*(1+nder)+1+j] = myarg->getDerivative(j); 144 : } 145 0 : } else if( myarg->hasDerivatives() ) { 146 0 : for(unsigned j=0; j<nder; ++j) { 147 0 : dval[i*(1+nder)+1+j] = myarg->getGridDerivative( i, j ); 148 : } 149 : } 150 : } 151 4224 : if(comm.Get_rank()==0) { 152 4224 : multi_sim_comm.Allgather(dval,datap); 153 : } 154 : 155 29568 : for(unsigned k=0; k<nreplicas; k++) { 156 25344 : Value* myout = getPntrToComponent(k); 157 25344 : if( myout->getNumberOfDerivatives()!=myarg->getNumberOfDerivatives() ) { 158 72 : myout->resizeDerivatives( myarg->getNumberOfDerivatives() ); 159 : } 160 25344 : unsigned sstart=k*nvals*(1+nder); 161 50688 : for(unsigned i=0; i<nvals; ++i) { 162 25344 : myout->set( i, datap[sstart+i*(1+nder)] ); 163 25344 : if( myarg->getRank()==0 ) { 164 50688 : for(unsigned j=0; j<nder; ++j) { 165 25344 : myout->setDerivative( j, dval[i*(1+nder)+1+j] ); 166 : } 167 0 : } else if( myarg->hasDerivatives() ) { 168 0 : for(unsigned j=0; j<nder; ++j) { 169 0 : myout->addGridDerivatives( i, j, dval[i*(1+nder)+1+j] ); 170 : } 171 : } 172 : } 173 : } 174 4224 : } 175 : 176 4224 : void GatherReplicas::apply() { 177 4224 : if( doNotCalculateDerivatives() ) { 178 4224 : return; 179 : } 180 0 : error("apply has not been implemented for GatherReplicas"); 181 : } 182 : 183 : } 184 : }