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