Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2013-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 "MultiColvarBase.h"
23 : #include "ActionVolume.h"
24 : #include "MultiColvarFilter.h"
25 : #include "vesselbase/Vessel.h"
26 : #include "vesselbase/BridgeVessel.h"
27 : #include "core/PlumedMain.h"
28 : #include "core/ActionSet.h"
29 : #include "tools/Pbc.h"
30 : #include "AtomValuePack.h"
31 : #include <vector>
32 : #include <string>
33 : #include <limits>
34 :
35 : namespace PLMD {
36 : namespace multicolvar {
37 :
38 425 : void MultiColvarBase::registerKeywords( Keywords& keys ) {
39 425 : Action::registerKeywords( keys );
40 425 : ActionWithValue::registerKeywords( keys );
41 425 : ActionAtomistic::registerKeywords( keys );
42 850 : keys.addFlag("NOPBC",false,"ignore the periodic boundary conditions when calculating distances");
43 425 : ActionWithVessel::registerKeywords( keys );
44 850 : keys.add("hidden","NL_STRIDE","the frequency with which the neighbor list should be updated. Between neighbour list update steps all quantities "
45 : "that contributed less than TOL at the previous neighbor list update step are ignored.");
46 425 : keys.setComponentsIntroduction("When the label of this action is used as the input for a second you are not referring to a scalar quantity as you are in "
47 : "regular collective variables. The label is used to reference the full set of quantities calculated by "
48 : "the action. This is usual when using \\ref multicolvarfunction. Generally when doing this the previously calculated "
49 : "multicolvar will be referenced using the DATA keyword rather than ARG.\n\n"
50 : "This Action can be used to calculate the following scalar quantities directly. These quantities are calculated by "
51 : "employing the keywords listed below. "
52 : "These quantities can then be referenced elsewhere in the input file by using this Action's label "
53 : "followed by a dot and the name of the quantity. Some of them can be calculated multiple times "
54 : "with different parameters. In this case the quantities calculated can be referenced elsewhere in the "
55 : "input by using the name of the quantity followed by a numerical identifier "
56 : "e.g. <em>label</em>.lessthan-1, <em>label</em>.lessthan-2 etc. When doing this and, for clarity we have "
57 : "made it so that the user can set a particular label for each of the components. As such by using the LABEL keyword in the description of the keyword "
58 : "input you can customize the component name");
59 850 : keys.reserve("atoms-3","SPECIES","this keyword is used for colvars such as coordination number. In that context it specifies that plumed should calculate "
60 : "one coordination number for each of the atoms specified. Each of these coordination numbers specifies how many of the "
61 : "other specified atoms are within a certain cutoff of the central atom. You can specify the atoms here as another multicolvar "
62 : "action or using a MultiColvarFilter or ActionVolume action. When you do so the quantity is calculated for those atoms specified "
63 : "in the previous multicolvar. This is useful if you would like to calculate the Steinhardt parameter for those atoms that have a "
64 : "coordination number more than four for example");
65 850 : keys.reserve("atoms-4","SPECIESA","this keyword is used for colvars such as the coordination number. In that context it species that plumed should calculate "
66 : "one coordination number for each of the atoms specified in SPECIESA. Each of these coordination numbers specifies how many "
67 : "of the atoms specifies using SPECIESB is within the specified cutoff. As with the species keyword the input can also be specified "
68 : "using the label of another multicolvar");
69 850 : keys.reserve("atoms-4","SPECIESB","this keyword is used for colvars such as the coordination number. It must appear with SPECIESA. For a full explanation see "
70 : "the documentation for that keyword");
71 1275 : keys.add("hidden","ALL_INPUT_SAME_TYPE","remove this keyword to remove certain checks in the input on the sanity of your input file. See code for details");
72 425 : }
73 :
74 350 : MultiColvarBase::MultiColvarBase(const ActionOptions&ao):
75 : Action(ao),
76 : ActionAtomistic(ao),
77 : ActionWithValue(ao),
78 : ActionWithVessel(ao),
79 350 : usepbc(false),
80 350 : allthirdblockintasks(false),
81 350 : uselinkforthree(false),
82 350 : linkcells(comm),
83 350 : threecells(comm),
84 350 : setup_completed(false),
85 350 : atomsWereRetrieved(false),
86 350 : matsums(false),
87 350 : usespecies(false),
88 700 : nblock(0)
89 : {
90 700 : if( keywords.exists("NOPBC") ) {
91 350 : bool nopbc=!usepbc; parseFlag("NOPBC",nopbc);
92 350 : usepbc=!nopbc;
93 : }
94 700 : if( keywords.exists("SPECIESA") ) { matsums=usespecies=true; }
95 350 : }
96 :
97 48 : void MultiColvarBase::readAtomsLikeKeyword( const std::string & key, const int& natoms, std::vector<AtomNumber>& all_atoms ) {
98 48 : plumed_assert( !usespecies );
99 48 : if( all_atoms.size()>0 ) return;
100 :
101 : std::vector<AtomNumber> t;
102 48 : for(int i=1;; ++i ) {
103 970 : parseAtomList(key, i, t );
104 970 : if( t.empty() ) break;
105 :
106 922 : log.printf(" Colvar %d is calculated from atoms : ", i);
107 3450 : for(unsigned j=0; j<t.size(); ++j) log.printf("%d ",t[j].serial() );
108 922 : log.printf("\n");
109 :
110 922 : if( i==1 && natoms<0 ) { ablocks.resize(t.size()); }
111 916 : else if( i==1 ) ablocks.resize(natoms);
112 922 : if( t.size()!=ablocks.size() ) {
113 0 : std::string ss; Tools::convert(i,ss);
114 0 : error(key + ss + " keyword has the wrong number of atoms");
115 : }
116 3450 : for(unsigned j=0; j<ablocks.size(); ++j) {
117 2528 : ablocks[j].push_back( ablocks.size()*(i-1)+j ); all_atoms.push_back( t[j] );
118 2528 : atom_lab.push_back( std::pair<unsigned,unsigned>( 0, ablocks.size()*(i-1)+j ) );
119 : }
120 922 : t.resize(0);
121 922 : }
122 48 : if( all_atoms.size()>0 ) {
123 48 : nblock=0;
124 970 : for(unsigned i=0; i<ablocks[0].size(); ++i) addTaskToList( i );
125 : }
126 : }
127 :
128 468 : bool MultiColvarBase::parseMultiColvarAtomList(const std::string& key, const int& num, std::vector<AtomNumber>& t) {
129 : std::vector<std::string> mlabs;
130 468 : if( num<0 ) parseVector(key,mlabs);
131 0 : else parseNumberedVector(key,num,mlabs);
132 :
133 468 : if( mlabs.size()==0 ) return false;
134 :
135 321 : std::string mname; unsigned found_mcolv=mlabs.size();
136 448 : for(unsigned i=0; i<mlabs.size(); ++i) {
137 326 : MultiColvarBase* mycolv = plumed.getActionSet().selectWithLabel<MultiColvarBase*>(mlabs[i]);
138 326 : if(!mycolv) { found_mcolv=i; break; }
139 : // Check all base multicolvars are of same type
140 127 : if( i==0 ) {
141 122 : mname = mycolv->getName();
142 244 : if( keywords.exists("ALL_INPUT_SAME_TYPE") && mycolv->isPeriodic() ) error("multicolvar functions don't work with this multicolvar");
143 : } else {
144 10 : if( keywords.exists("ALL_INPUT_SAME_TYPE") && mname!=mycolv->getName() ) error("All input multicolvars must be of same type");
145 : }
146 : // And track which variable stores each colvar
147 4458438 : for(unsigned j=0; j<mycolv->getFullNumberOfTasks(); ++j) atom_lab.push_back( std::pair<unsigned,unsigned>( mybasemulticolvars.size()+1, j ) );
148 : // And store the multicolvar base
149 127 : mybasemulticolvars.push_back( mycolv );
150 : // And create a basedata stash
151 127 : mybasedata.push_back( mybasemulticolvars[mybasemulticolvars.size()-1]->buildDataStashes( this ) );
152 : // Check if weight has derivatives
153 127 : if( mybasemulticolvars[ mybasemulticolvars.size()-1 ]->weightHasDerivatives ) weightHasDerivatives=true;
154 127 : plumed_assert( mybasemulticolvars.size()==mybasedata.size() );
155 : }
156 : // Have we conventional atoms to read in
157 321 : if( found_mcolv==0 ) {
158 199 : std::vector<AtomNumber> tt; ActionAtomistic::interpretAtomList( mlabs, tt );
159 89460 : for(unsigned i=0; i<tt.size(); ++i) { atom_lab.push_back( std::pair<unsigned,unsigned>( 0, t.size() + i ) ); }
160 199 : log.printf(" keyword %s takes atoms : ", key.c_str() );
161 89460 : for(unsigned i=0; i<tt.size(); ++i) { t.push_back( tt[i] ); log.printf("%d ",tt[i].serial() ); }
162 199 : log.printf("\n");
163 122 : } else if( found_mcolv==mlabs.size() ) {
164 122 : if( checkNumericalDerivatives() ) error("cannot use NUMERICAL_DERIVATIVES keyword with dynamic groups as input");
165 122 : log.printf(" keyword %s takes dynamic groups of atoms constructed from multicolvars labelled : ", key.c_str() );
166 249 : for(unsigned i=0; i<mlabs.size(); ++i) log.printf("%s ",mlabs[i].c_str() );
167 122 : log.printf("\n");
168 0 : } else if( found_mcolv<mlabs.size() ) {
169 0 : error("cannot mix multicolvar input and atom input in one line");
170 : }
171 : return true;
172 468 : }
173 :
174 76 : void MultiColvarBase::readTwoGroups( const std::string& key0, const std::string& key1, const std::string& key2, std::vector<AtomNumber>& all_atoms ) {
175 76 : plumed_dbg_assert( keywords.exists(key0) && keywords.exists(key1) && keywords.exists(key2) ); ablocks.resize( 2 );
176 :
177 76 : if( parseMultiColvarAtomList(key0,-1,all_atoms) ) {
178 78 : nblock=atom_lab.size(); for(unsigned i=0; i<2; ++i) ablocks[i].resize(nblock);
179 26 : resizeBookeepingArray( nblock, nblock );
180 5486 : for(unsigned i=0; i<nblock; ++i) ablocks[0][i]=ablocks[1][i]=i;
181 5460 : for(unsigned i=1; i<nblock; ++i) {
182 4216329 : for(unsigned j=0; j<i; ++j) {
183 4210895 : bookeeping(i,j).first=getFullNumberOfTasks();
184 4210895 : addTaskToList( i*nblock + j );
185 4210895 : bookeeping(i,j).second=getFullNumberOfTasks();
186 : }
187 : }
188 : } else {
189 50 : parseMultiColvarAtomList(key1,-1,all_atoms);
190 67 : ablocks[0].resize( atom_lab.size() ); for(unsigned i=0; i<ablocks[0].size(); ++i) ablocks[0][i]=i;
191 50 : parseMultiColvarAtomList(key2,-1,all_atoms);
192 1119 : ablocks[1].resize( atom_lab.size() - ablocks[0].size() ); for(unsigned i=0; i<ablocks[1].size(); ++i) ablocks[1][i]=ablocks[0].size() + i;
193 :
194 50 : if( ablocks[0].size()>ablocks[1].size() ) nblock = ablocks[0].size();
195 50 : else nblock=ablocks[1].size();
196 :
197 50 : resizeBookeepingArray( ablocks[0].size(), ablocks[1].size() );
198 67 : for(unsigned i=0; i<ablocks[0].size(); ++i) {
199 1126 : for(unsigned j=0; j<ablocks[1].size(); ++j) {
200 1109 : bookeeping(i,j).first=getFullNumberOfTasks();
201 1109 : if( atom_lab[ablocks[0][i]].first>0 && atom_lab[ablocks[1][j]].first>0 ) {
202 0 : if( mybasemulticolvars[atom_lab[ablocks[0][i]].first-1]->getLabel()!=mybasemulticolvars[atom_lab[ablocks[1][j]].first-1]->getLabel() &&
203 0 : atom_lab[ablocks[0][i]].second!=atom_lab[ablocks[1][j]].second ) addTaskToList( i*nblock + j );
204 1109 : } else if( all_atoms[atom_lab[ablocks[0][i]].second]!=all_atoms[atom_lab[ablocks[1][j]].second] ) addTaskToList( i*nblock + j );
205 1109 : bookeeping(i,j).second=getFullNumberOfTasks();
206 : }
207 : }
208 : }
209 76 : }
210 :
211 12 : void MultiColvarBase::readGroupKeywords( const std::string& key0, const std::string& key1, const std::string& key2, const std::string& key3,
212 : const bool& no_third_dim_accum, const bool& symmetric, std::vector<AtomNumber>& all_atoms ) {
213 12 : plumed_dbg_assert( keywords.exists(key0) && keywords.exists(key1) && keywords.exists(key2) && keywords.exists(key3) ); ablocks.resize( 3 );
214 :
215 12 : if( parseMultiColvarAtomList(key0,-1,all_atoms) ) {
216 10 : if( no_third_dim_accum ) {
217 10 : nblock=atom_lab.size(); ablocks[0].resize(nblock); ablocks[1].resize( nblock );
218 1481 : for(unsigned i=0; i<ablocks[0].size(); ++i) ablocks[0][i]=ablocks[1][i]=i;
219 10 : resizeBookeepingArray( nblock, nblock );
220 10 : if( symmetric ) {
221 : // This ensures that later parts of the code don't switch off allthirdblockintasks
222 1343 : for(unsigned i=0; i<nblock; ++i) { bookeeping(i,i).first=0; bookeeping(i,i).second=1; }
223 1337 : for(unsigned i=1; i<nblock; ++i) {
224 222447 : for(unsigned j=0; j<i; ++j) {
225 221116 : bookeeping(j,i).first=bookeeping(i,j).first=getFullNumberOfTasks();
226 221116 : addTaskToList( i*nblock + j );
227 221116 : bookeeping(j,i).second=bookeeping(i,j).second=getFullNumberOfTasks();
228 : }
229 : }
230 : } else {
231 138 : for(unsigned i=0; i<nblock; ++i) {
232 8344 : for(unsigned j=0; j<nblock; ++j) {
233 8210 : if( i==j ) continue ;
234 8076 : bookeeping(i,j).first=getFullNumberOfTasks();
235 8076 : addTaskToList( i*nblock + j );
236 8076 : bookeeping(i,j).second=getFullNumberOfTasks();
237 : }
238 : }
239 : }
240 10 : if( !parseMultiColvarAtomList(key3,-1,all_atoms) ) error("missing required keyword " + key3 + " in input");
241 10 : ablocks[2].resize( atom_lab.size() - ablocks[0].size() );
242 49845 : for(unsigned i=0; i<ablocks[2].size(); ++i) ablocks[2][i]=ablocks[0].size() + i;
243 : } else {
244 0 : nblock=atom_lab.size(); for(unsigned i=0; i<3; ++i) ablocks[i].resize(nblock);
245 0 : resizeBookeepingArray( nblock, nblock );
246 0 : for(unsigned i=0; i<nblock; ++i) { ablocks[0][i]=i; ablocks[1][i]=i; ablocks[2][i]=i; }
247 0 : if( symmetric ) {
248 0 : for(unsigned i=2; i<nblock; ++i) {
249 0 : for(unsigned j=1; j<i; ++j) {
250 0 : bookeeping(i,j).first=getFullNumberOfTasks();
251 0 : for(unsigned k=0; k<j; ++k) addTaskToList( i*nblock*nblock + j*nblock + k );
252 0 : bookeeping(i,j).second=getFullNumberOfTasks();
253 : }
254 : }
255 : } else {
256 0 : for(unsigned i=0; i<nblock; ++i) {
257 0 : for(unsigned j=0; j<nblock; ++j) {
258 0 : if( i==j ) continue;
259 0 : bookeeping(i,j).first=getFullNumberOfTasks();
260 0 : for(unsigned k=0; k<nblock; ++k) {
261 0 : if( i!=k && j!=k ) addTaskToList( i*nblock*nblock + j*nblock + k );
262 : }
263 0 : bookeeping(i,j).first=getFullNumberOfTasks();
264 : }
265 : }
266 : }
267 : }
268 : } else {
269 2 : readThreeGroups( key1, key2, key3, true, no_third_dim_accum, all_atoms );
270 : }
271 :
272 12 : }
273 :
274 10 : void MultiColvarBase::readThreeGroups( const std::string& key1, const std::string& key2, const std::string& key3,
275 : const bool& allow2, const bool& no_third_dim_accum, std::vector<AtomNumber>& all_atoms ) {
276 10 : plumed_dbg_assert( keywords.exists(key1) && keywords.exists(key2) && keywords.exists(key3) ); ablocks.resize( 3 );
277 :
278 10 : bool readkey1=parseMultiColvarAtomList(key1,-1,all_atoms);
279 31 : ablocks[0].resize( atom_lab.size() ); for(unsigned i=0; i<ablocks[0].size(); ++i) ablocks[0][i]=i;
280 10 : bool readkey2=parseMultiColvarAtomList(key2,-1,all_atoms);
281 10 : if( !readkey1 && !readkey2 ) return ;
282 225 : ablocks[1].resize( atom_lab.size() - ablocks[0].size() ); for(unsigned i=0; i<ablocks[1].size(); ++i) ablocks[1][i]=ablocks[0].size() + i;
283 :
284 10 : resizeBookeepingArray( ablocks[0].size(), ablocks[1].size() );
285 10 : bool readkey3=parseMultiColvarAtomList(key3,-1,all_atoms);
286 10 : if( !readkey3 && !allow2 ) {
287 0 : error("missing atom specification " + key3);
288 10 : } else if( !readkey3 ) {
289 2 : if( ablocks[1].size()>ablocks[0].size() ) nblock=ablocks[1].size();
290 0 : else nblock=ablocks[0].size();
291 :
292 2 : ablocks[2].resize( ablocks[1].size() );
293 200 : for(unsigned i=0; i<ablocks[1].size(); ++i) ablocks[2][i]=ablocks[0].size() + i;
294 4 : for(unsigned i=0; i<ablocks[0].size(); ++i) {
295 198 : for(unsigned j=1; j<ablocks[1].size(); ++j) {
296 196 : bookeeping(i,j).first=getFullNumberOfTasks();
297 9898 : for(unsigned k=0; k<j; ++k) {
298 9702 : if( atom_lab[ablocks[0][i]].first>0 && atom_lab[ablocks[1][j]].first>0 && atom_lab[ablocks[2][k]].first>0 ) {
299 0 : if( mybasemulticolvars[atom_lab[ablocks[0][i]].first-1]->getLabel()!=mybasemulticolvars[atom_lab[ablocks[1][j]].first-1]->getLabel() &&
300 0 : mybasemulticolvars[atom_lab[ablocks[0][i]].first-1]->getLabel()!=mybasemulticolvars[atom_lab[ablocks[2][k]].first-1]->getLabel() &&
301 0 : mybasemulticolvars[atom_lab[ablocks[1][j]].first-1]->getLabel()!=mybasemulticolvars[atom_lab[ablocks[2][k]].first-1]->getLabel() &&
302 0 : atom_lab[ablocks[0][i]].second!=atom_lab[ablocks[1][j]].second && atom_lab[ablocks[0][i]].second!=atom_lab[ablocks[2][k]].second &&
303 0 : atom_lab[ablocks[1][j]].second!=atom_lab[ablocks[2][k]].second ) addTaskToList( nblock*nblock*i + nblock*j + k );
304 9702 : } else if( all_atoms[atom_lab[ablocks[0][i]].second]!=all_atoms[atom_lab[ablocks[1][j]].second] &&
305 9702 : all_atoms[atom_lab[ablocks[0][i]].second]!=all_atoms[atom_lab[ablocks[2][k]].second] &&
306 9702 : all_atoms[atom_lab[ablocks[1][j]].second]!=all_atoms[atom_lab[ablocks[2][k]].second] ) addTaskToList( nblock*nblock*i + nblock*j + k );
307 : }
308 196 : bookeeping(i,j).second=getFullNumberOfTasks();
309 : }
310 : }
311 : } else {
312 8 : ablocks[2].resize( atom_lab.size() - ablocks[1].size() - ablocks[0].size() );
313 3182 : for(unsigned i=0; i<ablocks[2].size(); ++i) ablocks[2][i] = ablocks[0].size() + ablocks[1].size() + i;
314 :
315 8 : if( ablocks[1].size()>ablocks[0].size() ) nblock=ablocks[1].size();
316 8 : else nblock=ablocks[0].size();
317 8 : if( ablocks[2].size()>nblock ) nblock=ablocks[2].size();
318 :
319 8 : unsigned kcount; if( no_third_dim_accum ) kcount=1; else kcount=ablocks[2].size();
320 :
321 27 : for(unsigned i=0; i<ablocks[0].size(); ++i) {
322 128 : for(unsigned j=0; j<ablocks[1].size(); ++j) {
323 109 : bookeeping(i,j).first=getFullNumberOfTasks();
324 218 : for(unsigned k=0; k<kcount; ++k) {
325 109 : if( no_third_dim_accum ) addTaskToList( nblock*i + j );
326 0 : else if( atom_lab[ablocks[0][i]].first>0 && atom_lab[ablocks[1][j]].first>0 && atom_lab[ablocks[2][k]].first>0 ) {
327 0 : if( mybasemulticolvars[atom_lab[ablocks[0][i]].first-1]->getLabel()!=mybasemulticolvars[atom_lab[ablocks[1][j]].first-1]->getLabel() &&
328 0 : mybasemulticolvars[atom_lab[ablocks[0][i]].first-1]->getLabel()!=mybasemulticolvars[atom_lab[ablocks[2][k]].first-1]->getLabel() &&
329 0 : mybasemulticolvars[atom_lab[ablocks[1][j]].first-1]->getLabel()!=mybasemulticolvars[atom_lab[ablocks[2][k]].first-1]->getLabel() &&
330 0 : atom_lab[ablocks[0][i]].second!=atom_lab[ablocks[1][j]].second && atom_lab[ablocks[0][i]].second!=atom_lab[ablocks[2][k]].second &&
331 0 : atom_lab[ablocks[1][j]].second!=atom_lab[ablocks[2][k]].second ) addTaskToList( nblock*nblock*i + nblock*j + k );
332 0 : } else if( all_atoms[atom_lab[ablocks[0][i]].second]!=all_atoms[atom_lab[ablocks[1][j]].second] &&
333 0 : all_atoms[atom_lab[ablocks[0][i]].second]!=all_atoms[atom_lab[ablocks[2][k]].second] &&
334 0 : all_atoms[atom_lab[ablocks[1][j]].second]!=all_atoms[atom_lab[ablocks[2][k]].second] ) addTaskToList( nblock*nblock*i + nblock*j + k );
335 : }
336 109 : bookeeping(i,j).second=getFullNumberOfTasks();
337 : }
338 : }
339 : }
340 : }
341 :
342 4 : void MultiColvarBase::buildSets() {
343 : std::vector<AtomNumber> fake_atoms;
344 8 : if( !parseMultiColvarAtomList("DATA",-1,fake_atoms) ) error("missing DATA keyword");
345 4 : if( fake_atoms.size()>0 ) error("no atoms should appear in the specification for this object. Input should be other multicolvars");
346 :
347 4 : nblock = mybasemulticolvars[0]->getFullNumberOfTasks();
348 11 : for(unsigned i=0; i<mybasemulticolvars.size(); ++i) {
349 7 : if( mybasemulticolvars[i]->getFullNumberOfTasks()!=nblock ) {
350 0 : error("mismatch between numbers of tasks in various base multicolvars");
351 : }
352 : }
353 4 : ablocks.resize( mybasemulticolvars.size() ); usespecies=false;
354 11 : for(unsigned i=0; i<mybasemulticolvars.size(); ++i) {
355 7 : ablocks[i].resize( nblock );
356 27 : for(unsigned j=0; j<nblock; ++j) ablocks[i][j]=i*nblock+j;
357 : }
358 15 : for(unsigned i=0; i<nblock; ++i) {
359 11 : if( mybasemulticolvars.size()<4 ) {
360 11 : unsigned cvcode=0, tmpc=1;
361 31 : for(unsigned j=0; j<ablocks.size(); ++j) { cvcode += i*tmpc; tmpc *= nblock; }
362 11 : addTaskToList( cvcode );
363 : } else {
364 0 : addTaskToList( i );
365 : }
366 : }
367 4 : mybasedata[0]->resizeTemporyMultiValues( mybasemulticolvars.size() ); setupMultiColvarBase( fake_atoms );
368 4 : }
369 :
370 12512606 : void MultiColvarBase::addTaskToList( const unsigned& taskCode ) {
371 12512606 : plumed_assert( getNumberOfVessels()==0 );
372 12512606 : ActionWithVessel::addTaskToList( taskCode );
373 12512606 : }
374 :
375 96 : void MultiColvarBase::resizeBookeepingArray( const unsigned& num1, const unsigned& num2 ) {
376 96 : bookeeping.resize( num1, num2 );
377 7065 : for(unsigned i=0; i<num1; ++i) {
378 8887414 : for(unsigned j=0; j<num2; ++j) { bookeeping(i,j).first=0; bookeeping(i,j).second=0; }
379 : }
380 96 : }
381 :
382 308 : void MultiColvarBase::setupMultiColvarBase( const std::vector<AtomNumber>& atoms ) {
383 308 : if( !matsums && atom_lab.size()==0 ) error("No atoms have been read in");
384 : std::vector<AtomNumber> all_atoms;
385 : // Setup decoder array
386 308 : if( !usespecies && nblock>0 ) {
387 :
388 63 : ncentral=ablocks.size(); use_for_central_atom.resize( ablocks.size(), true );
389 63 : numberForCentralAtom = 1.0 / static_cast<double>( ablocks.size() );
390 63 : if( ablocks.size()==3 ) {
391 20 : allthirdblockintasks=uselinkforthree=true;
392 1512 : for(unsigned i=0; i<bookeeping.nrows(); ++i) {
393 453578 : for(unsigned j=0; j<bookeeping.ncols(); ++j) {
394 452086 : unsigned ntper = bookeeping(i,j).second - bookeeping(i,j).first;
395 452086 : if( i==j && ntper==0 ) {
396 136 : continue;
397 451950 : } else if( ntper == 1 && allthirdblockintasks ) {
398 451756 : allthirdblockintasks=true;
399 194 : } else if( ntper != ablocks[2].size() ) {
400 194 : allthirdblockintasks=uselinkforthree=false;
401 : } else {
402 0 : allthirdblockintasks=false;
403 : }
404 : }
405 : }
406 : }
407 :
408 63 : if( allthirdblockintasks ) {
409 18 : decoder.resize(2); plumed_assert( ablocks.size()==3 );
410 : // Check if number of atoms is too large
411 18 : if( std::pow( double(nblock), 2.0 )>std::numeric_limits<unsigned>::max() ) error("number of atoms in groups is too big for PLUMED to handle");
412 : } else {
413 45 : decoder.resize( ablocks.size() );
414 : // Check if number of atoms is too large
415 45 : if( std::pow( double(nblock), double(ablocks.size()) )>std::numeric_limits<unsigned>::max() ) error("number of atoms in groups is too big for PLUMED to handle");
416 : }
417 190 : unsigned code=1; for(unsigned i=0; i<decoder.size(); ++i) { decoder[decoder.size()-1-i]=code; code *= nblock; }
418 245 : } else if( !usespecies ) {
419 87 : ncentral=ablocks.size(); use_for_central_atom.resize( ablocks.size(), true );
420 87 : numberForCentralAtom = 1.0 / static_cast<double>( ablocks.size() );
421 316 : } else if( keywords.exists("SPECIESA") ) {
422 100 : plumed_assert( atom_lab.size()==0 && all_atoms.size()==0 );
423 100 : ablocks.resize( 1 ); bool readspecies=parseMultiColvarAtomList("SPECIES", -1, all_atoms);
424 100 : if( readspecies ) {
425 41493 : ablocks[0].resize( atom_lab.size() ); for(unsigned i=0; i<atom_lab.size(); ++i) { addTaskToList(i); ablocks[0][i]=i; }
426 : } else {
427 38 : if( !parseMultiColvarAtomList("SPECIESA", -1, all_atoms) ) error("missing SPECIES/SPECIESA keyword");
428 19 : unsigned nat1=atom_lab.size();
429 38 : if( !parseMultiColvarAtomList("SPECIESB", -1, all_atoms) ) error("missing SPECIESB keyword");
430 19 : unsigned nat2=atom_lab.size() - nat1;
431 :
432 662 : for(unsigned i=0; i<nat1; ++i) addTaskToList(i);
433 19 : ablocks[0].resize( nat2 );
434 3437 : for(unsigned i=0; i<nat2; ++i) {
435 : bool found=false; unsigned inum=0;
436 265449 : for(unsigned j=0; j<nat1; ++j) {
437 262113 : if( atom_lab[nat1+i].first>0 && atom_lab[j].first>0 ) {
438 252720 : if( atom_lab[nat1+i].first==atom_lab[j].first &&
439 0 : mybasemulticolvars[atom_lab[nat1+i].first-1]->getAbsoluteIndexOfCentralAtom(atom_lab[nat1+i].second)==
440 252720 : mybasemulticolvars[atom_lab[j].first-1]->getAbsoluteIndexOfCentralAtom(atom_lab[j].second) ) { found=true; inum=j; break; }
441 9393 : } else if( atom_lab[nat1+i].first>0 ) {
442 0 : if( mybasemulticolvars[atom_lab[nat1+i].first-1]->getAbsoluteIndexOfCentralAtom(atom_lab[nat1+i].second)==
443 0 : all_atoms[atom_lab[j].second] ) { found=true; inum=nat1 + i; break; }
444 9393 : } else if( atom_lab[j].first>0 ) {
445 0 : if( all_atoms[atom_lab[nat1+i].second]==
446 0 : mybasemulticolvars[atom_lab[j].first-1]->getAbsoluteIndexOfCentralAtom(atom_lab[j].second) ) { found=true; inum=nat1+i; break; }
447 9393 : } else if( all_atoms[atom_lab[nat1+i].second]==all_atoms[atom_lab[j].second] ) { found=true; inum=j; break; }
448 : }
449 : // This prevents mistakes being made in colvar setup
450 82 : if( found ) { ablocks[0][i]=inum; }
451 3336 : else { ablocks[0][i]=nat1 + i; }
452 : }
453 : }
454 : }
455 308 : if( mybasemulticolvars.size()>0 ) {
456 246 : for(unsigned i=0; i<mybasedata.size(); ++i) {
457 127 : mybasedata[i]->resizeTemporyMultiValues(2); mybasemulticolvars[i]->my_tmp_capacks.resize(2);
458 : }
459 : }
460 :
461 : // Copy lists of atoms involved from base multicolvars
462 : std::vector<AtomNumber> tmp_atoms;
463 435 : for(unsigned i=0; i<mybasemulticolvars.size(); ++i) {
464 127 : tmp_atoms=mybasemulticolvars[i]->getAbsoluteIndexes();
465 416316 : for(unsigned j=0; j<tmp_atoms.size(); ++j) all_atoms.push_back( tmp_atoms[j] );
466 : }
467 : // Copy atom lists from input
468 59575 : for(unsigned i=0; i<atoms.size(); ++i) all_atoms.push_back( atoms[i] );
469 :
470 : // Now make sure we get all the atom positions
471 308 : ActionAtomistic::requestAtoms( all_atoms );
472 : // And setup dependencies
473 435 : for(unsigned i=0; i<mybasemulticolvars.size(); ++i) addDependency( mybasemulticolvars[i] );
474 :
475 : // Setup underlying ActionWithVessel
476 308 : readVesselKeywords();
477 308 : }
478 :
479 19 : void MultiColvarBase::setAtomsForCentralAtom( const std::vector<bool>& catom_ind ) {
480 19 : unsigned nat=0; plumed_assert( catom_ind.size()==ablocks.size() );
481 89 : for(unsigned i=0; i<catom_ind.size(); ++i) {
482 70 : use_for_central_atom[i]=catom_ind[i];
483 70 : if( use_for_central_atom[i] ) nat++;
484 : }
485 19 : plumed_dbg_assert( nat>0 ); ncentral=nat;
486 19 : numberForCentralAtom = 1.0 / static_cast<double>( nat );
487 19 : }
488 :
489 429 : void MultiColvarBase::turnOnDerivatives() {
490 429 : ActionWithValue::turnOnDerivatives();
491 429 : needsDerivatives();
492 429 : forcesToApply.resize( getNumberOfDerivatives() );
493 429 : }
494 :
495 191 : void MultiColvarBase::setLinkCellCutoff( const double& lcut, double tcut ) {
496 191 : plumed_assert( usespecies || ablocks.size()<4 );
497 191 : if( tcut<0 ) tcut=lcut;
498 :
499 191 : if( !linkcells.enabled() ) {
500 191 : linkcells.setCutoff( lcut );
501 191 : threecells.setCutoff( tcut );
502 : } else {
503 0 : if( lcut>linkcells.getCutoff() ) linkcells.setCutoff( lcut );
504 0 : if( tcut>threecells.getCutoff() ) threecells.setCutoff( tcut );
505 : }
506 191 : }
507 :
508 8 : double MultiColvarBase::getLinkCellCutoff() const {
509 8 : return linkcells.getCutoff();
510 : }
511 :
512 1938 : void MultiColvarBase::setupLinkCells() {
513 1938 : if( (!usespecies && nblock==0) || !linkcells.enabled() ) return ;
514 : // Retrieve any atoms that haven't already been retrieved
515 1318 : for(std::vector<MultiColvarBase*>::iterator p=mybasemulticolvars.begin(); p!=mybasemulticolvars.end(); ++p) {
516 203 : (*p)->retrieveAtoms();
517 : }
518 1115 : retrieveAtoms();
519 :
520 : unsigned iblock;
521 1115 : if( usespecies ) {
522 : iblock=0;
523 217 : } else if( ablocks.size()<4 ) {
524 : iblock=1;
525 : } else {
526 0 : plumed_error();
527 : }
528 :
529 : // Count number of currently active atoms
530 1115 : nactive_atoms=0;
531 387665 : for(unsigned i=0; i<ablocks[iblock].size(); ++i) {
532 386550 : if( isCurrentlyActive( ablocks[iblock][i] ) ) nactive_atoms++;
533 : }
534 :
535 1115 : if( nactive_atoms>0 ) {
536 1115 : std::vector<Vector> ltmp_pos( nactive_atoms );
537 1115 : std::vector<unsigned> ltmp_ind( nactive_atoms );
538 :
539 1115 : nactive_atoms=0;
540 1115 : if( usespecies ) {
541 366350 : for(unsigned i=0; i<ablocks[0].size(); ++i) {
542 365452 : if( !isCurrentlyActive( ablocks[0][i] ) ) continue;
543 365356 : ltmp_ind[nactive_atoms]=ablocks[0][i];
544 365356 : ltmp_pos[nactive_atoms]=getPositionOfAtomForLinkCells( ltmp_ind[nactive_atoms] );
545 365356 : nactive_atoms++;
546 : }
547 : } else {
548 21315 : for(unsigned i=0; i<ablocks[1].size(); ++i) {
549 21098 : if( !isCurrentlyActive( ablocks[1][i] ) ) continue;
550 16178 : ltmp_ind[nactive_atoms]=i;
551 16178 : ltmp_pos[nactive_atoms]=getPositionOfAtomForLinkCells( ablocks[1][i] );
552 16178 : nactive_atoms++;
553 : }
554 : }
555 :
556 : // Build the lists for the link cells
557 1115 : linkcells.buildCellLists( ltmp_pos, ltmp_ind, getPbc() );
558 : }
559 : }
560 :
561 4628 : void MultiColvarBase::setupNonUseSpeciesLinkCells( const unsigned& my_always_active ) {
562 4628 : plumed_assert( !usespecies );
563 4628 : if( nblock==0 || !linkcells.enabled() ) return ;
564 210 : deactivateAllTasks();
565 : std::vector<unsigned> requiredlinkcells;
566 :
567 210 : if( !uselinkforthree && nactive_atoms>0 ) {
568 : // Get some parallel info
569 156 : unsigned stride=comm.Get_size();
570 156 : unsigned rank=comm.Get_rank();
571 156 : if( serialCalculation() ) { stride=1; rank=0; }
572 :
573 : // Ensure we only do tasks where atoms are in appropriate link cells
574 156 : std::vector<unsigned> linked_atoms( 1+ablocks[1].size() );
575 5757 : for(unsigned i=rank; i<ablocks[0].size(); i+=stride) {
576 5601 : if( !isCurrentlyActive( ablocks[0][i] ) ) continue;
577 2622 : unsigned natomsper=1; linked_atoms[0]=my_always_active; // Note we always check atom 0 because it is simpler than changing LinkCells.cpp
578 2622 : linkcells.retrieveNeighboringAtoms( getPositionOfAtomForLinkCells( ablocks[0][i] ), requiredlinkcells, natomsper, linked_atoms );
579 205705 : for(unsigned j=0; j<natomsper; ++j) {
580 353118 : for(unsigned k=bookeeping(i,linked_atoms[j]).first; k<bookeeping(i,linked_atoms[j]).second; ++k) taskFlags[k]=1;
581 : }
582 : }
583 210 : } else if( nactive_atoms>0 ) {
584 : // Get some parallel info
585 54 : unsigned stride=comm.Get_size();
586 54 : unsigned rank=comm.Get_rank();
587 54 : if( serialCalculation() ) { stride=1; rank=0; }
588 :
589 : unsigned nactive_three=0;
590 66599 : for(unsigned i=0; i<ablocks[2].size(); ++i) {
591 66545 : if( isCurrentlyActive( ablocks[2][i] ) ) nactive_three++;
592 : }
593 :
594 54 : std::vector<Vector> lttmp_pos( nactive_three );
595 54 : std::vector<unsigned> lttmp_ind( nactive_three );
596 :
597 : nactive_three=0;
598 54 : if( allthirdblockintasks ) {
599 66599 : for(unsigned i=0; i<ablocks[2].size(); ++i) {
600 66545 : if( !isCurrentlyActive( ablocks[2][i] ) ) continue;
601 66545 : lttmp_ind[nactive_three]=ablocks[2][i];
602 66545 : lttmp_pos[nactive_three]=getPositionOfAtomForLinkCells( ablocks[2][i] );
603 66545 : nactive_three++;
604 : }
605 : } else {
606 0 : for(unsigned i=0; i<ablocks[2].size(); ++i) {
607 0 : if( !isCurrentlyActive( ablocks[2][i] ) ) continue;
608 0 : lttmp_ind[nactive_three]=i;
609 0 : lttmp_pos[nactive_three]=getPositionOfAtomForLinkCells( ablocks[2][i] );
610 0 : nactive_three++;
611 : }
612 : }
613 : // Build the list of the link cells
614 54 : threecells.buildCellLists( lttmp_pos, lttmp_ind, getPbc() );
615 :
616 : // Ensure we only do tasks where atoms are in appropriate link cells
617 54 : std::vector<unsigned> linked_atoms( 1+ablocks[1].size() );
618 54 : std::vector<unsigned> tlinked_atoms( 1+ablocks[2].size() );
619 633 : for(unsigned i=rank; i<ablocks[0].size(); i+=stride) {
620 579 : if( !isCurrentlyActive( ablocks[0][i] ) ) continue;
621 579 : unsigned natomsper=1; linked_atoms[0]=my_always_active; // Note we always check atom 0 because it is simpler than changing LinkCells.cpp
622 579 : linkcells.retrieveNeighboringAtoms( getPositionOfAtomForLinkCells( ablocks[0][i] ), requiredlinkcells, natomsper, linked_atoms );
623 579 : if( allthirdblockintasks ) {
624 71832 : for(unsigned j=0; j<natomsper; ++j) {
625 142372 : for(unsigned k=bookeeping(i,linked_atoms[j]).first; k<bookeeping(i,linked_atoms[j]).second; ++k) taskFlags[k]=1;
626 : }
627 : } else {
628 0 : unsigned ntatomsper=1; tlinked_atoms[0]=lttmp_ind[0];
629 0 : threecells.retrieveNeighboringAtoms( getPositionOfAtomForLinkCells( ablocks[0][i] ), requiredlinkcells, ntatomsper, tlinked_atoms );
630 0 : for(unsigned j=0; j<natomsper; ++j) {
631 0 : for(unsigned k=0; k<ntatomsper; ++k) taskFlags[bookeeping(i,linked_atoms[j]).first+tlinked_atoms[k]]=1;
632 : }
633 : }
634 : }
635 : }
636 210 : if( !serialCalculation() ) comm.Sum( taskFlags );
637 210 : lockContributors();
638 : }
639 :
640 245988 : void MultiColvarBase::decodeIndexToAtoms( const unsigned& taskCode, std::vector<unsigned>& atoms ) const {
641 : plumed_dbg_assert( !usespecies && nblock>0 );
642 245988 : if( atoms.size()!=decoder.size() ) atoms.resize( decoder.size() );
643 :
644 245988 : unsigned scode = taskCode;
645 786464 : for(unsigned i=0; i<decoder.size(); ++i) {
646 540476 : unsigned ind=( scode / decoder[i] );
647 540476 : atoms[i] = ablocks[i][ind];
648 540476 : scode -= ind*decoder[i];
649 : }
650 245988 : }
651 :
652 451407 : bool MultiColvarBase::setupCurrentAtomList( const unsigned& taskCode, AtomValuePack& myatoms ) const {
653 451407 : if( isDensity() ) {
654 19070 : myatoms.setNumberOfAtoms( 1 ); myatoms.setAtom( 0, taskCode ); return true;
655 432337 : } else if( usespecies ) {
656 180751 : std::vector<unsigned> task_atoms(1); task_atoms[0]=taskCode;
657 180751 : unsigned natomsper=myatoms.setupAtomsFromLinkCells( task_atoms, getPositionOfAtomForLinkCells( taskCode ), linkcells );
658 180751 : return natomsper>1;
659 251586 : } else if( matsums ) {
660 : myatoms.setNumberOfAtoms( getNumberOfAtoms() );
661 52798 : for(unsigned i=0; i<getNumberOfAtoms(); ++i) myatoms.setAtom( i, i );
662 251145 : } else if( allthirdblockintasks ) {
663 39816 : plumed_dbg_assert( ablocks.size()==3 ); std::vector<unsigned> atoms(2); decodeIndexToAtoms( taskCode, atoms );
664 39816 : myatoms.setupAtomsFromLinkCells( atoms, getPositionOfAtomForLinkCells( atoms[0] ), threecells );
665 211329 : } else if( nblock>0 ) {
666 179551 : std::vector<unsigned> atoms( ablocks.size() );
667 179551 : decodeIndexToAtoms( taskCode, atoms ); myatoms.setNumberOfAtoms( ablocks.size() );
668 587153 : for(unsigned i=0; i<ablocks.size(); ++i) myatoms.setAtom( i, atoms[i] );
669 : } else {
670 31778 : myatoms.setNumberOfAtoms( ablocks.size() );
671 124498 : for(unsigned i=0; i<ablocks.size(); ++i) myatoms.setAtom( i, ablocks[i][taskCode] );
672 : }
673 : return true;
674 : }
675 :
676 9073 : void MultiColvarBase::setupActiveTaskSet( std::vector<unsigned>& active_tasks, const std::string& input_label ) {
677 9073 : if( !setup_completed ) {
678 : bool justVolumes=false;
679 1930 : if( usespecies ) {
680 : justVolumes=true;
681 1437 : for(unsigned i=0; i<getNumberOfVessels(); ++i) {
682 1317 : vesselbase::StoreDataVessel* mys=dynamic_cast<vesselbase::StoreDataVessel*>( getPntrToVessel(i) );
683 1317 : if( mys ) continue;
684 809 : vesselbase::BridgeVessel* myb=dynamic_cast<vesselbase::BridgeVessel*>( getPntrToVessel(i) );
685 809 : if( !myb ) { justVolumes=false; break; }
686 38 : ActionVolume* myv=dynamic_cast<ActionVolume*>( myb->getOutputAction() );
687 38 : if( !myv ) { justVolumes=false; break; }
688 : }
689 : }
690 1930 : deactivateAllTasks();
691 1930 : if( justVolumes && mydata ) {
692 88 : if( mydata->getNumberOfDataUsers()==0 ) justVolumes=false;
693 :
694 137 : for(unsigned i=0; i<mydata->getNumberOfDataUsers(); ++i) {
695 49 : MultiColvarBase* myu=dynamic_cast<MultiColvarBase*>( mydata->getDataUser(i) );
696 49 : if( myu ) {
697 49 : myu->setupActiveTaskSet( taskFlags, getLabel() );
698 : } else {
699 0 : for(unsigned i=0; i<getFullNumberOfTasks(); ++i) taskFlags[i]=1;
700 : }
701 : }
702 : }
703 1930 : if( justVolumes ) {
704 160 : for(unsigned j=0; j<getNumberOfVessels(); ++j) {
705 80 : vesselbase::BridgeVessel* myb=dynamic_cast<vesselbase::BridgeVessel*>( getPntrToVessel(j) );
706 80 : if( !myb ) continue ;
707 32 : ActionVolume* myv=dynamic_cast<ActionVolume*>( myb->getOutputAction() );
708 32 : if( !myv ) continue ;
709 32 : myv->retrieveAtoms(); myv->setupRegions();
710 :
711 148161 : for(unsigned i=0; i<getFullNumberOfTasks(); ++i) {
712 148129 : if( myv->inVolumeOfInterest(i) ) taskFlags[i]=1;
713 : }
714 : }
715 : } else {
716 4886605 : for(unsigned i=0; i<getFullNumberOfTasks(); ++i) taskFlags[i]=1;
717 : }
718 :
719 : // Now activate all this class
720 1930 : lockContributors();
721 : // Setup the link cells
722 1930 : setupLinkCells();
723 : // Ensures that setup is not performed multiple times during one cycle
724 1930 : setup_completed=true;
725 : }
726 :
727 : // And activate the tasks in input action
728 9073 : if( getLabel()!=input_label ) {
729 : int input_code=-1;
730 61 : for(unsigned i=0; i<mybasemulticolvars.size(); ++i) {
731 61 : if( mybasemulticolvars[i]->getLabel()==input_label ) { input_code=i+1; break; }
732 : }
733 :
734 49 : MultiValue my_tvals( getNumberOfQuantities(), getNumberOfDerivatives() );
735 49 : AtomValuePack mytmp_atoms( my_tvals, this );
736 148234 : for(unsigned i=0; i<getFullNumberOfTasks(); ++i) {
737 148185 : if( !taskIsCurrentlyActive(i) ) continue;
738 3529 : setupCurrentAtomList( getTaskCode(i), mytmp_atoms );
739 254796 : for(unsigned j=0; j<mytmp_atoms.getNumberOfAtoms(); ++j) {
740 : unsigned itask=mytmp_atoms.getIndex(j);
741 251267 : if( atom_lab[itask].first==input_code ) active_tasks[ atom_lab[itask].second ]=1;
742 : }
743 : }
744 49 : }
745 9073 : }
746 :
747 467 : bool MultiColvarBase::filtersUsedAsInput() {
748 : bool inputAreFilters=false;
749 728 : for(unsigned i=0; i<mybasemulticolvars.size(); ++i) {
750 261 : MultiColvarFilter* myfilt=dynamic_cast<MultiColvarFilter*>( mybasemulticolvars[i] );
751 261 : if( myfilt || mybasemulticolvars[i]->filtersUsedAsInput() ) inputAreFilters=true;
752 : }
753 467 : return inputAreFilters;
754 : }
755 :
756 9024 : void MultiColvarBase::calculate() {
757 : // Recursive function that sets up tasks
758 9024 : setupActiveTaskSet( taskFlags, getLabel() );
759 :
760 : // Check for filters and rerun setup of link cells if there are any
761 9024 : if( mybasemulticolvars.size()>0 && filtersUsedAsInput() ) setupLinkCells();
762 :
763 : // Setup the link cells if we are not using species
764 9024 : if( !usespecies && ablocks.size()>1 ) {
765 : // This loop finds the first active atom, which is always checked because
766 : // of a peculiarity in linkcells
767 4628 : unsigned first_active=std::numeric_limits<unsigned>::max();
768 4762 : for(unsigned i=0; i<ablocks[0].size(); ++i) {
769 4762 : if( !isCurrentlyActive( ablocks[1][i] ) ) continue;
770 : else {
771 4628 : first_active=i; break;
772 : }
773 : }
774 4628 : setupNonUseSpeciesLinkCells( first_active );
775 : }
776 : // And run all tasks
777 9024 : runAllTasks();
778 9024 : }
779 :
780 213 : void MultiColvarBase::calculateNumericalDerivatives( ActionWithValue* a ) {
781 213 : if( mybasemulticolvars.size()>0 ) plumed_merror("cannot calculate numerical derivatives for this quantity");
782 213 : calculateAtomicNumericalDerivatives( this, 0 );
783 213 : }
784 :
785 2959 : void MultiColvarBase::prepare() {
786 2959 : setup_completed=false; atomsWereRetrieved=false;
787 2959 : }
788 :
789 6548 : void MultiColvarBase::retrieveAtoms() {
790 6548 : if( !atomsWereRetrieved ) { ActionAtomistic::retrieveAtoms(); atomsWereRetrieved=true; }
791 6548 : }
792 :
793 38245 : void MultiColvarBase::mergeInputDerivatives( const unsigned& ival, const unsigned& start, const unsigned& end,
794 : const unsigned& jatom, const std::vector<double>& der,
795 : MultiValue& myder, AtomValuePack& myatoms ) const {
796 : MultiValue& myvals=myatoms.getUnderlyingMultiValue();
797 : plumed_dbg_assert( ival<myatoms.getUnderlyingMultiValue().getNumberOfValues() );
798 : plumed_dbg_assert( start<myder.getNumberOfValues() && end<=myder.getNumberOfValues() );
799 : plumed_dbg_assert( der.size()==myder.getNumberOfValues() && jatom<myatoms.getNumberOfAtoms() );
800 : // Convert input atom to local index
801 : unsigned katom = myatoms.getIndex( jatom ); plumed_dbg_assert( katom<atom_lab.size() ); plumed_dbg_assert( atom_lab[katom].first>0 );
802 : // Find base colvar
803 38245 : unsigned mmc=atom_lab[katom].first - 1; plumed_dbg_assert( mybasemulticolvars[mmc]->taskIsCurrentlyActive( atom_lab[katom].second ) );
804 : // Get start of indices for this atom
805 38855 : unsigned basen=0; for(unsigned i=0; i<mmc; ++i) basen+=mybasemulticolvars[i]->getNumberOfDerivatives() - 9;
806 : plumed_dbg_assert( basen%3==0 ); // Check the number of atoms is consistent with input derivatives
807 38245 : unsigned virbas = myvals.getNumberOfDerivatives()-9;
808 4805851 : for(unsigned j=0; j<myder.getNumberActive(); ++j) {
809 : unsigned jder=myder.getActiveIndex(j);
810 4767606 : if( jder<mybasemulticolvars[mmc]->getNumberOfDerivatives()-9 ) {
811 4434561 : unsigned kder=basen+jder;
812 110197260 : for(unsigned icomp=start; icomp<end; ++icomp) {
813 105762699 : myvals.addDerivative( ival, kder, der[icomp]*myder.getDerivative( icomp, jder ) );
814 : }
815 : } else {
816 333045 : unsigned kder=virbas + (jder - mybasemulticolvars[mmc]->getNumberOfDerivatives() + 9);
817 7551360 : for(unsigned icomp=start; icomp<end; ++icomp) {
818 7218315 : myvals.addDerivative( ival, kder, der[icomp]*myder.getDerivative( icomp, jder ) );
819 : }
820 : }
821 : }
822 38245 : }
823 :
824 106 : void MultiColvarBase::splitInputDerivatives( const unsigned& ival, const unsigned& start, const unsigned& end,
825 : const unsigned& jatom, const std::vector<double>& der,
826 : MultiValue& myder, AtomValuePack& myatoms ) const {
827 : MultiValue& myvals=myatoms.getUnderlyingMultiValue();
828 : plumed_dbg_assert( ival<myder.getNumberOfValues() );
829 : plumed_dbg_assert( start<myvals.getNumberOfValues() && end<=myvals.getNumberOfValues() );
830 : plumed_dbg_assert( der.size()==myatoms.getUnderlyingMultiValue().getNumberOfValues() && jatom<myatoms.getNumberOfAtoms() );
831 : // Convert input atom to local index
832 : unsigned katom = myatoms.getIndex( jatom ); plumed_dbg_assert( katom<atom_lab.size() ); plumed_dbg_assert( atom_lab[katom].first>0 );
833 : // Find base colvar
834 106 : unsigned mmc=atom_lab[katom].first - 1; plumed_dbg_assert( mybasemulticolvars[mmc]->taskIsCurrentlyActive( atom_lab[katom].second ) );
835 : // Get start of indices for this atom
836 154 : unsigned basen=0; for(unsigned i=0; i<mmc; ++i) basen+=mybasemulticolvars[i]->getNumberOfDerivatives() - 9;
837 : plumed_dbg_assert( basen%3==0 ); // Check the number of atoms is consistent with input derivatives
838 106 : unsigned virbas = myvals.getNumberOfDerivatives()-9;
839 19534 : for(unsigned j=0; j<myder.getNumberActive(); ++j) {
840 : unsigned jder=myder.getActiveIndex(j);
841 19428 : if( jder<mybasemulticolvars[mmc]->getNumberOfDerivatives()-9 ) {
842 18474 : unsigned kder=basen+jder; plumed_assert( kder<myvals.getNumberOfDerivatives() );
843 39942 : for(unsigned icomp=start; icomp<end; ++icomp) {
844 21468 : myvals.addDerivative( icomp, kder, der[icomp]*myder.getDerivative( ival, jder ) );
845 : }
846 : } else {
847 954 : unsigned kder=virbas + (jder - mybasemulticolvars[mmc]->getNumberOfDerivatives() + 9);
848 3942 : for(unsigned icomp=start; icomp<end; ++icomp) {
849 2988 : myvals.addDerivative( icomp, kder, der[icomp]*myder.getDerivative( ival, jder ) );
850 : }
851 : }
852 : }
853 106 : }
854 :
855 905762 : void MultiColvarBase::addComDerivatives( const int& ival, const unsigned& iatom, const Vector& der, multicolvar::AtomValuePack& myatoms ) const {
856 : plumed_dbg_assert( ival<static_cast<int>(myatoms.getUnderlyingMultiValue().getNumberOfValues()) && iatom<myatoms.getNumberOfAtoms() );
857 : // Convert input atom to local index
858 : unsigned katom = myatoms.getIndex( iatom ); plumed_dbg_assert( atom_lab[katom].first>0 );
859 : // Find base colvar
860 905762 : unsigned mmc = atom_lab[katom].first - 1; plumed_dbg_assert( mybasemulticolvars[mmc]->taskIsCurrentlyActive( atom_lab[katom].second ) );
861 905762 : if( usespecies && iatom==0 ) { myatoms.addComDerivatives( ival, der, mybasemulticolvars[mmc]->my_tmp_capacks[0] ); return; }
862 :
863 : // Get start of indices for this atom
864 646526 : unsigned basen=0; for(unsigned i=0; i<mmc; ++i) basen+=(mybasemulticolvars[i]->getNumberOfDerivatives() - 9) / 3;
865 489261 : mybasemulticolvars[mmc]->getCentralAtomPack( basen, atom_lab[katom].second, mybasemulticolvars[mmc]->my_tmp_capacks[1] );
866 489261 : myatoms.addComDerivatives( ival, der, mybasemulticolvars[mmc]->my_tmp_capacks[1] );
867 : }
868 :
869 1122658 : void MultiColvarBase::getInputData( const unsigned& ind, const bool& normed,
870 : const multicolvar::AtomValuePack& myatoms,
871 : std::vector<double>& orient ) const {
872 : // Converint input atom to local index
873 : unsigned katom = myatoms.getIndex(ind); plumed_dbg_assert( atom_lab[katom].first>0 );
874 : // Find base colvar
875 1122658 : unsigned mmc = atom_lab[katom].first - 1; plumed_dbg_assert( mybasemulticolvars[mmc]->taskIsCurrentlyActive( atom_lab[katom].second ) );
876 : // Check if orient is the correct size
877 1122658 : if( orient.size()!=mybasemulticolvars[mmc]->getNumberOfQuantities() ) orient.resize( mybasemulticolvars[mmc]->getNumberOfQuantities() );
878 : // Retrieve the value
879 1122658 : mybasedata[mmc]->retrieveValueWithIndex( atom_lab[katom].second, normed, orient );
880 1122658 : }
881 :
882 53063 : MultiValue& MultiColvarBase::getInputDerivatives( const unsigned& iatom, const bool& normed, const multicolvar::AtomValuePack& myatoms ) const {
883 : // Converint input atom to local index
884 : unsigned katom = myatoms.getIndex(iatom); plumed_dbg_assert( atom_lab[katom].first>0 );
885 : // Find base colvar
886 53063 : unsigned mmc = atom_lab[katom].first - 1; plumed_dbg_assert( mybasemulticolvars[mmc]->taskIsCurrentlyActive( atom_lab[katom].second ) );
887 53063 : if( usespecies && !normed && iatom==0 ) return mybasedata[mmc]->getTemporyMultiValue(0);
888 :
889 51265 : unsigned oval=0; if( iatom>0 ) oval=1;
890 51265 : MultiValue& myder=mybasedata[mmc]->getTemporyMultiValue(oval);
891 102491 : if( myder.getNumberOfValues()!=mybasemulticolvars[mmc]->getNumberOfQuantities() ||
892 51226 : myder.getNumberOfDerivatives()!=mybasemulticolvars[mmc]->getNumberOfDerivatives() ) {
893 39 : myder.resize( mybasemulticolvars[mmc]->getNumberOfQuantities(), mybasemulticolvars[mmc]->getNumberOfDerivatives() );
894 : }
895 51265 : mybasedata[mmc]->retrieveDerivatives( atom_lab[katom].second, normed, myder );
896 : return myder;
897 : }
898 :
899 64359697 : void MultiColvarBase::accumulateSymmetryFunction( const int& ival, const unsigned& iatom, const double& val, const Vector& der, const Tensor& vir, multicolvar::AtomValuePack& myatoms ) const {
900 : plumed_dbg_assert( usespecies ); unsigned katom=myatoms.getIndex(0), jatom=myatoms.getIndex(iatom);
901 64359697 : double weight0=1.0; if( atom_lab[katom].first>0 ) weight0=mybasedata[atom_lab[katom].first-1]->retrieveWeightWithIndex( atom_lab[katom].second );
902 64359697 : double weighti=1.0; if( atom_lab[jatom].first>0 ) weighti=mybasedata[atom_lab[jatom].first-1]->retrieveWeightWithIndex( atom_lab[jatom].second );
903 : // Accumulate the value
904 64359697 : if( ival<0 ) myatoms.getUnderlyingMultiValue().addTemporyValue( weight0*weighti*val );
905 61622797 : else myatoms.addValue( ival, weight0*weighti*val );
906 :
907 : // Return if we don't need derivatives
908 64359697 : if( doNotCalculateDerivatives() ) return ;
909 : // And virial
910 57992318 : if( ival<0 ) myatoms.addTemporyBoxDerivatives( weight0*weighti*vir );
911 55734171 : else myatoms.addBoxDerivatives( ival, weight0*weighti*vir );
912 :
913 : // Add derivatives of central atom
914 57992318 : if( atom_lab[katom].first>0 ) {
915 794 : addComDerivatives( ival, 0, -weight0*weighti*der, myatoms );
916 794 : std::vector<double> tmpder( mybasemulticolvars[atom_lab[katom].first - 1]->getNumberOfQuantities(), 0. );
917 794 : tmpder[0]=weighti*val; mergeInputDerivatives( ival, 0, 1, 0, tmpder, getInputDerivatives(0, false, myatoms), myatoms );
918 : } else {
919 57991524 : if( ival<0 ) myatoms.addTemporyAtomsDerivatives( 0, -der );
920 55733377 : else myatoms.addAtomsDerivatives( ival, 0, -der );
921 : }
922 : // Add derivatives of atom in coordination sphere
923 57992318 : if( atom_lab[jatom].first>0 ) {
924 794 : addComDerivatives( ival, iatom, weight0*weighti*der, myatoms );
925 794 : std::vector<double> tmpder( mybasemulticolvars[atom_lab[katom].first - 1]->getNumberOfQuantities(), 0. );
926 794 : tmpder[0]=weight0*val; mergeInputDerivatives( ival, 0, 1, iatom, tmpder, getInputDerivatives(iatom, false, myatoms), myatoms );
927 : } else {
928 57991524 : if( ival<0 ) myatoms.addTemporyAtomsDerivatives( iatom, der );
929 55733377 : else myatoms.addAtomsDerivatives( ival, iatom, der );
930 : }
931 : }
932 :
933 1428471 : void MultiColvarBase::addAtomDerivatives( const int& ival, const unsigned& iatom, const Vector& der, multicolvar::AtomValuePack& myatoms ) const {
934 1428471 : if( doNotCalculateDerivatives() ) return ;
935 : unsigned jatom=myatoms.getIndex(iatom);
936 :
937 1180895 : if( atom_lab[jatom].first>0 ) {
938 904174 : addComDerivatives( ival, iatom, der, myatoms );
939 : } else {
940 276721 : if( ival<0 ) myatoms.addTemporyAtomsDerivatives( iatom, der );
941 276721 : else myatoms.addAtomsDerivatives( ival, iatom, der );
942 : }
943 : }
944 :
945 242764 : double MultiColvarBase::calculateWeight( const unsigned& current, const double& weight, AtomValuePack& myvals ) const {
946 242764 : return 1.0;
947 : }
948 :
949 447878 : void MultiColvarBase::performTask( const unsigned& task_index, const unsigned& current, MultiValue& myvals ) const {
950 447878 : AtomValuePack myatoms( myvals, this );
951 : // Retrieve the atom list
952 447878 : if( !setupCurrentAtomList( current, myatoms ) ) return;
953 : // Get weight due to dynamic groups
954 447758 : double weight = 1.0;
955 447758 : if( !matsums ) {
956 386210280 : for(unsigned i=0; i<myatoms.getNumberOfAtoms(); ++i) {
957 385940036 : if( atom_lab[myatoms.getIndex(i)].first==0 ) continue;
958 : // Only need to do first two atoms for things like TopologyMatrix, HbondMatrix, Bridge and so on
959 244017 : if( allthirdblockintasks && i>1 ) break;
960 244017 : unsigned mmc = atom_lab[myatoms.getIndex(i)].first - 1;
961 244017 : weight *= mybasedata[mmc]->retrieveWeightWithIndex( atom_lab[myatoms.getIndex(i)].second );
962 : }
963 177514 : } else if( usespecies ) {
964 177073 : if( atom_lab[myatoms.getIndex(0)].first>0 ) {
965 9073 : if( mybasedata[atom_lab[myatoms.getIndex(0)].first-1]->retrieveWeightWithIndex( atom_lab[myatoms.getIndex(0)].second )<epsilon ) weight=0.;
966 : }
967 : }
968 : // Do a quick check on the size of this contribution
969 447758 : double multweight = calculateWeight( current, weight, myatoms );
970 447758 : if( weight*multweight<getTolerance() ) {
971 121536 : updateActiveAtoms( myatoms );
972 : return;
973 : }
974 : myatoms.setValue( 0, weight*multweight );
975 : // Deal with derivatives of weights due to dynamic groups
976 326222 : if( !matsums && !doNotCalculateDerivatives() && mybasemulticolvars.size()>0 ) {
977 39416 : MultiValue& outder=myatoms.getUnderlyingMultiValue(); MultiValue myder(0,0);
978 115123 : for(unsigned i=0; i<myatoms.getNumberOfAtoms(); ++i) {
979 : // Neglect any atoms without differentiable weights
980 75707 : if( atom_lab[myatoms.getIndex(i)].first==0 ) continue;
981 :
982 : // Retrieve derivatives
983 75707 : unsigned mmc = atom_lab[myatoms.getIndex(i)].first - 1;
984 75707 : if( myder.getNumberOfValues()!=mybasemulticolvars[mmc]->getNumberOfQuantities() || myder.getNumberOfDerivatives()!=mybasemulticolvars[mmc]->getNumberOfDerivatives() ) {
985 39416 : myder.resize( mybasemulticolvars[mmc]->getNumberOfQuantities(), mybasemulticolvars[mmc]->getNumberOfDerivatives() );
986 : }
987 75707 : mybasedata[mmc]->retrieveDerivatives( atom_lab[myatoms.getIndex(i)].second, false, myder );
988 :
989 : // Retrieve the prefactor (product of all other weights)
990 75707 : double prefactor = multweight*weight / mybasedata[mmc]->retrieveWeightWithIndex( atom_lab[myatoms.getIndex(i)].second );
991 : // And accumulate the derivatives
992 2927141 : for(unsigned j=0; j<myder.getNumberActive(); ++j) { unsigned jder=myder.getActiveIndex(j); outder.addDerivative( 0, jder, prefactor*myder.getDerivative(0,jder) ); }
993 75707 : myder.clearAll();
994 : }
995 39416 : }
996 : // Retrieve derivative stuff for central atom
997 326222 : if( !doNotCalculateDerivatives() ) {
998 206915 : if( usespecies && mybasemulticolvars.size()>0 && atom_lab[myatoms.getIndex(0)].first>0 ) {
999 1610 : unsigned mmc = atom_lab[0].first - 1;
1000 1610 : MultiValue& myder=mybasedata[mmc]->getTemporyMultiValue(0);
1001 3208 : if( myder.getNumberOfValues()!=mybasemulticolvars[mmc]->getNumberOfQuantities() ||
1002 1598 : myder.getNumberOfDerivatives()!=mybasemulticolvars[mmc]->getNumberOfDerivatives() ) {
1003 12 : myder.resize( mybasemulticolvars[mmc]->getNumberOfQuantities(), mybasemulticolvars[mmc]->getNumberOfDerivatives() );
1004 : }
1005 1610 : mybasedata[mmc]->retrieveDerivatives( atom_lab[myatoms.getIndex(0)].second, false, myder );
1006 1610 : unsigned basen=0; for(unsigned i=0; i<mmc; ++i) basen+=mybasemulticolvars[i]->getNumberOfDerivatives() - 9;
1007 1610 : mybasemulticolvars[mmc]->getCentralAtomPack( basen, atom_lab[myatoms.getIndex(0)].second, mybasemulticolvars[mmc]->my_tmp_capacks[0] );
1008 : }
1009 : }
1010 : // Compute everything
1011 326222 : double vv=compute( task_index, myatoms ); updateActiveAtoms( myatoms );
1012 : myatoms.setValue( 1, vv );
1013 326222 : return;
1014 : }
1015 :
1016 658140 : void MultiColvarBase::updateActiveAtoms( AtomValuePack& myatoms ) const {
1017 658140 : if( mybasemulticolvars.size()==0 ) myatoms.updateUsingIndices();
1018 141871 : else myatoms.updateDynamicList();
1019 658140 : }
1020 :
1021 2753935 : Vector MultiColvarBase::getCentralAtomPos( const unsigned& taskIndex ) {
1022 2753935 : unsigned curr=getTaskCode( taskIndex );
1023 :
1024 2753935 : if( usespecies || isDensity() ) {
1025 1459946 : return getPositionOfAtomForLinkCells(curr);
1026 1293989 : } else if( nblock>0 ) {
1027 : // double factor=1.0/static_cast<double>( ablocks.size() );
1028 2130 : Vector mypos; mypos.zero();
1029 2130 : std::vector<unsigned> atoms( ablocks.size() ); decodeIndexToAtoms( curr, atoms );
1030 6390 : for(unsigned i=0; i<ablocks.size(); ++i) {
1031 4260 : if( use_for_central_atom[i] ) mypos+=numberForCentralAtom*getPositionOfAtomForLinkCells(atoms[i]);
1032 : }
1033 2130 : return mypos;
1034 : } else {
1035 1291859 : Vector mypos; mypos.zero();
1036 5138646 : for(unsigned i=0; i<ablocks.size(); ++i) {
1037 3846787 : if( use_for_central_atom[i] ) mypos+=numberForCentralAtom*getPositionOfAtomForLinkCells(ablocks[i][curr]);
1038 : }
1039 1291859 : return mypos;
1040 : }
1041 : }
1042 :
1043 605410 : void MultiColvarBase::getCentralAtomPack( const unsigned& basn, const unsigned& taskIndex, CatomPack& mypack ) {
1044 605410 : unsigned curr=getTaskCode( taskIndex );
1045 :
1046 605410 : if(usespecies) {
1047 464996 : if( mypack.getNumberOfAtomsWithDerivatives()!=1 ) mypack.resize(1);
1048 464996 : mypack.setIndex( 0, basn + curr );
1049 464996 : mypack.setDerivative( 0, Tensor::identity() );
1050 140414 : } else if( nblock>0 ) {
1051 0 : if( mypack.getNumberOfAtomsWithDerivatives()!=ncentral ) mypack.resize(ncentral);
1052 0 : unsigned k=0;
1053 0 : std::vector<unsigned> atoms( ablocks.size() ); decodeIndexToAtoms( curr, atoms );
1054 0 : for(unsigned i=0; i<ablocks.size(); ++i) {
1055 0 : if( use_for_central_atom[i] ) {
1056 0 : mypack.setIndex( k, basn + atoms[i] );
1057 0 : mypack.setDerivative( k, numberForCentralAtom*Tensor::identity() );
1058 0 : k++;
1059 : }
1060 : }
1061 : } else {
1062 140414 : if( mypack.getNumberOfAtomsWithDerivatives()!=ncentral ) mypack.resize(ncentral);
1063 140414 : unsigned k=0;
1064 316748 : for(unsigned i=0; i<ablocks.size(); ++i) {
1065 176334 : if( use_for_central_atom[i] ) {
1066 173694 : mypack.setIndex( k, basn + ablocks[i][curr] );
1067 173694 : mypack.setDerivative( k, numberForCentralAtom*Tensor::identity() );
1068 173694 : k++;
1069 : }
1070 : }
1071 : }
1072 605410 : }
1073 :
1074 121150993 : Vector MultiColvarBase::getSeparation( const Vector& vec1, const Vector& vec2 ) const {
1075 121150993 : if(usepbc) { return pbcDistance( vec1, vec2 ); }
1076 0 : else { return delta( vec1, vec2 ); }
1077 : }
1078 :
1079 220567 : void MultiColvarBase::applyPbc(std::vector<Vector>& dlist, unsigned int max_index) const {
1080 220567 : if (usepbc) pbcApply(dlist, max_index);
1081 220567 : }
1082 :
1083 1994 : void MultiColvarBase::apply() {
1084 1994 : if( getForcesFromVessels( forcesToApply ) ) setForcesOnAtoms( forcesToApply );
1085 1994 : }
1086 :
1087 : }
1088 : }
|