Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2013-2018 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 "CatomPack.h"
32 : #include "CatomPack.h"
33 : #include <vector>
34 : #include <string>
35 : #include <limits>
36 :
37 : using namespace std;
38 :
39 : namespace PLMD {
40 : namespace multicolvar {
41 :
42 320 : void MultiColvarBase::registerKeywords( Keywords& keys ) {
43 320 : Action::registerKeywords( keys );
44 320 : ActionWithValue::registerKeywords( keys );
45 320 : ActionAtomistic::registerKeywords( keys );
46 320 : keys.addFlag("NOPBC",false,"ignore the periodic boundary conditions when calculating distances");
47 320 : ActionWithVessel::registerKeywords( keys );
48 : keys.add("hidden","NL_STRIDE","the frequency with which the neighbor list should be updated. Between neighbour list update steps all quantities "
49 320 : "that contributed less than TOL at the previous neighbor list update step are ignored.");
50 : 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 "
51 : "regular collective variables. The label is used to reference the full set of quantities calculated by "
52 : "the action. This is usual when using \\ref multicolvarfunction. Generally when doing this the previously calculated "
53 : "multicolvar will be referenced using the DATA keyword rather than ARG.\n\n"
54 : "This Action can be used to calculate the following scalar quantities directly. These quantities are calculated by "
55 : "employing the keywords listed below. "
56 : "These quantities can then be referenced elsewhere in the input file by using this Action's label "
57 : "followed by a dot and the name of the quantity. Some amongst them can be calculated multiple times "
58 : "with different parameters. In this case the quantities calculated can be referenced elsewhere in the "
59 : "input by using the name of the quantity followed by a numerical identifier "
60 : "e.g. <em>label</em>.lessthan-1, <em>label</em>.lessthan-2 etc. When doing this and, for clarity we have "
61 : "made the label of the components customizable. As such by using the LABEL keyword in the description of the keyword "
62 320 : "input you can customize the component name");
63 : keys.reserve("atoms-3","SPECIES","this keyword is used for colvars such as coordination number. In that context it specifies that plumed should calculate "
64 : "one coordination number for each of the atoms specified. Each of these coordination numbers specifies how many of the "
65 : "other specified atoms are within a certain cutoff of the central atom. You can specify the atoms here as another multicolvar "
66 : "action or using a MultiColvarFilter or ActionVolume action. When you do so the quantity is calculated for those atoms specified "
67 : "in the previous multicolvar. This is useful if you would like to calculate the Steinhardt parameter for those atoms that have a "
68 320 : "coordination number more than four for example");
69 : 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 "
70 : "one coordination number for each of the atoms specified in SPECIESA. Each of these cooordination numbers specifies how many "
71 : "of the atoms specifies using SPECIESB is within the specified cutoff. As with the species keyword the input can also be specified "
72 320 : "using the label of another multicolvar");
73 : 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 "
74 320 : "the documentation for that keyword");
75 320 : }
76 :
77 264 : MultiColvarBase::MultiColvarBase(const ActionOptions&ao):
78 : Action(ao),
79 : ActionAtomistic(ao),
80 : ActionWithValue(ao),
81 : ActionWithVessel(ao),
82 : usepbc(false),
83 : allthirdblockintasks(false),
84 : uselinkforthree(false),
85 : linkcells(comm),
86 : threecells(comm),
87 : setup_completed(false),
88 : atomsWereRetrieved(false),
89 : matsums(false),
90 : usespecies(false),
91 264 : nblock(0)
92 : {
93 264 : if( keywords.exists("NOPBC") ) {
94 264 : bool nopbc=!usepbc; parseFlag("NOPBC",nopbc);
95 264 : usepbc=!nopbc;
96 : }
97 264 : if( keywords.exists("SPECIESA") ) { matsums=usespecies=true; }
98 264 : }
99 :
100 350 : bool MultiColvarBase::parseMultiColvarAtomList(const std::string& key, const int& num, std::vector<AtomNumber>& t) {
101 350 : std::vector<std::string> mlabs;
102 350 : if( num<0 ) parseVector(key,mlabs);
103 0 : else parseNumberedVector(key,num,mlabs);
104 :
105 350 : if( mlabs.size()==0 ) return false;
106 :
107 440 : std::string mname; unsigned found_mcolv=mlabs.size();
108 303 : for(unsigned i=0; i<mlabs.size(); ++i) {
109 222 : MultiColvarBase* mycolv = plumed.getActionSet().selectWithLabel<MultiColvarBase*>(mlabs[i]);
110 222 : if(!mycolv) { found_mcolv=i; break; }
111 : // Check all base multicolvars are of same type
112 83 : if( i==0 ) {
113 81 : mname = mycolv->getName();
114 81 : if( mycolv->isPeriodic() ) error("multicolvar functions don't work with this multicolvar");
115 : } else {
116 2 : if( mname!=mycolv->getName() ) error("All input multicolvars must be of same type");
117 : }
118 : // And track which variable stores each colvar
119 83 : for(unsigned j=0; j<mycolv->getFullNumberOfTasks(); ++j) atom_lab.push_back( std::pair<unsigned,unsigned>( mybasemulticolvars.size()+1, j ) );
120 : // And store the multicolvar base
121 83 : mybasemulticolvars.push_back( mycolv );
122 : // And create a basedata stash
123 83 : mybasedata.push_back( mybasemulticolvars[mybasemulticolvars.size()-1]->buildDataStashes( this ) );
124 : // Check if weight has derivatives
125 83 : if( mybasemulticolvars[ mybasemulticolvars.size()-1 ]->weightHasDerivatives ) weightHasDerivatives=true;
126 83 : plumed_assert( mybasemulticolvars.size()==mybasedata.size() );
127 : }
128 : // Have we conventional atoms to read in
129 220 : if( found_mcolv==0 ) {
130 139 : std::vector<AtomNumber> tt; ActionAtomistic::interpretAtomList( mlabs, tt );
131 139 : for(unsigned i=0; i<tt.size(); ++i) { atom_lab.push_back( std::pair<unsigned,unsigned>( 0, t.size() + i ) ); }
132 139 : log.printf(" keyword %s takes atoms : ", key.c_str() );
133 139 : for(unsigned i=0; i<tt.size(); ++i) { t.push_back( tt[i] ); log.printf("%d ",tt[i].serial() ); }
134 139 : log.printf("\n");
135 81 : } else if( found_mcolv==mlabs.size() ) {
136 81 : if( checkNumericalDerivatives() ) error("cannot use NUMERICAL_DERIVATIVES keyword with dynamic groups as input");
137 81 : log.printf(" keyword %s takes dynamic groups of atoms constructed from multicolvars labelled : ", key.c_str() );
138 81 : for(unsigned i=0; i<mlabs.size(); ++i) log.printf("%s ",mlabs[i].c_str() );
139 81 : log.printf("\n");
140 0 : } else if( found_mcolv<mlabs.size() ) {
141 0 : error("cannot mix multicolvar input and atom input in one line");
142 : }
143 570 : return true;
144 : }
145 :
146 68 : void MultiColvarBase::readTwoGroups( const std::string& key0, const std::string& key1, const std::string& key2, std::vector<AtomNumber>& all_atoms ) {
147 68 : plumed_dbg_assert( keywords.exists(key0) && keywords.exists(key1) && keywords.exists(key2) ); ablocks.resize( 2 );
148 :
149 68 : if( parseMultiColvarAtomList(key0,-1,all_atoms) ) {
150 20 : nblock=atom_lab.size(); for(unsigned i=0; i<2; ++i) ablocks[i].resize(nblock);
151 20 : resizeBookeepingArray( nblock, nblock );
152 20 : for(unsigned i=0; i<nblock; ++i) ablocks[0][i]=ablocks[1][i]=i;
153 5430 : for(unsigned i=1; i<nblock; ++i) {
154 4216245 : for(unsigned j=0; j<i; ++j) {
155 4210835 : bookeeping(i,j).first=getFullNumberOfTasks();
156 4210835 : addTaskToList( i*nblock + j );
157 4210835 : bookeeping(i,j).second=getFullNumberOfTasks();
158 : }
159 : }
160 : } else {
161 48 : parseMultiColvarAtomList(key1,-1,all_atoms);
162 48 : ablocks[0].resize( atom_lab.size() ); for(unsigned i=0; i<ablocks[0].size(); ++i) ablocks[0][i]=i;
163 48 : parseMultiColvarAtomList(key2,-1,all_atoms);
164 48 : 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;
165 :
166 48 : if( ablocks[0].size()>ablocks[1].size() ) nblock = ablocks[0].size();
167 48 : else nblock=ablocks[1].size();
168 :
169 48 : resizeBookeepingArray( ablocks[0].size(), ablocks[1].size() );
170 65 : for(unsigned i=0; i<ablocks[0].size(); ++i) {
171 1126 : for(unsigned j=0; j<ablocks[1].size(); ++j) {
172 1109 : bookeeping(i,j).first=getFullNumberOfTasks();
173 1109 : if( atom_lab[ablocks[0][i]].first>0 && atom_lab[ablocks[1][j]].first>0 ) {
174 0 : if( mybasemulticolvars[atom_lab[ablocks[0][i]].first-1]->getLabel()!=mybasemulticolvars[atom_lab[ablocks[1][j]].first-1]->getLabel() &&
175 0 : atom_lab[ablocks[0][i]].second!=atom_lab[ablocks[1][j]].second ) addTaskToList( i*nblock + j );
176 1109 : } else if( all_atoms[atom_lab[ablocks[0][i]].second]!=all_atoms[atom_lab[ablocks[1][j]].second] ) addTaskToList( i*nblock + j );
177 1109 : bookeeping(i,j).second=getFullNumberOfTasks();
178 : }
179 : }
180 : }
181 68 : }
182 :
183 4 : void MultiColvarBase::readGroupKeywords( const std::string& key0, const std::string& key1, const std::string& key2, const std::string& key3,
184 : const bool& no_third_dim_accum, const bool& symmetric, std::vector<AtomNumber>& all_atoms ) {
185 4 : plumed_dbg_assert( keywords.exists(key0) && keywords.exists(key1) && keywords.exists(key2) && keywords.exists(key3) ); ablocks.resize( 3 );
186 :
187 4 : if( parseMultiColvarAtomList(key0,-1,all_atoms) ) {
188 2 : if( no_third_dim_accum ) {
189 2 : nblock=atom_lab.size(); ablocks[0].resize(nblock); ablocks[1].resize( nblock );
190 2 : for(unsigned i=0; i<ablocks[0].size(); ++i) ablocks[0][i]=ablocks[1][i]=i;
191 2 : resizeBookeepingArray( nblock, nblock );
192 2 : if( symmetric ) {
193 0 : for(unsigned i=1; i<nblock; ++i) {
194 0 : for(unsigned j=0; j<i; ++j) {
195 0 : bookeeping(i,j).first=getFullNumberOfTasks();
196 0 : addTaskToList( i*nblock + j );
197 0 : bookeeping(i,j).second=getFullNumberOfTasks();
198 : }
199 : }
200 : } else {
201 69 : for(unsigned i=0; i<nblock; ++i) {
202 4172 : for(unsigned j=0; j<nblock; ++j) {
203 4105 : if( i==j ) continue ;
204 4038 : bookeeping(i,j).first=getFullNumberOfTasks();
205 4038 : addTaskToList( i*nblock + j );
206 4038 : bookeeping(i,j).second=getFullNumberOfTasks();
207 : }
208 : }
209 : }
210 2 : if( !parseMultiColvarAtomList(key3,-1,all_atoms) ) error("missing required keyword " + key3 + " in input");
211 2 : ablocks[2].resize( atom_lab.size() - ablocks[0].size() );
212 2 : for(unsigned i=0; i<ablocks[2].size(); ++i) ablocks[2][i]=ablocks[0].size() + i;
213 : } else {
214 0 : nblock=atom_lab.size(); for(unsigned i=0; i<3; ++i) ablocks[i].resize(nblock);
215 0 : resizeBookeepingArray( nblock, nblock );
216 0 : for(unsigned i=0; i<nblock; ++i) { ablocks[0][i]=i; ablocks[1][i]=i; ablocks[2][i]=i; }
217 0 : if( symmetric ) {
218 0 : for(unsigned i=2; i<nblock; ++i) {
219 0 : for(unsigned j=1; j<i; ++j) {
220 0 : bookeeping(i,j).first=getFullNumberOfTasks();
221 0 : for(unsigned k=0; k<j; ++k) addTaskToList( i*nblock*nblock + j*nblock + k );
222 0 : bookeeping(i,j).second=getFullNumberOfTasks();
223 : }
224 : }
225 : } else {
226 0 : for(unsigned i=0; i<nblock; ++i) {
227 0 : for(unsigned j=0; j<nblock; ++j) {
228 0 : if( i==j ) continue;
229 0 : bookeeping(i,j).first=getFullNumberOfTasks();
230 0 : for(unsigned k=0; k<nblock; ++k) {
231 0 : if( i!=k && j!=k ) addTaskToList( i*nblock*nblock + j*nblock + k );
232 : }
233 0 : bookeeping(i,j).first=getFullNumberOfTasks();
234 : }
235 : }
236 : }
237 : }
238 : } else {
239 2 : readThreeGroups( key1, key2, key3, true, no_third_dim_accum, all_atoms );
240 : }
241 :
242 4 : }
243 :
244 11 : void MultiColvarBase::readThreeGroups( const std::string& key1, const std::string& key2, const std::string& key3,
245 : const bool& allow2, const bool& no_third_dim_accum, std::vector<AtomNumber>& all_atoms ) {
246 11 : plumed_dbg_assert( keywords.exists(key1) && keywords.exists(key2) && keywords.exists(key3) ); ablocks.resize( 3 );
247 :
248 11 : bool readkey1=parseMultiColvarAtomList(key1,-1,all_atoms);
249 11 : ablocks[0].resize( atom_lab.size() ); for(unsigned i=0; i<ablocks[0].size(); ++i) ablocks[0][i]=i;
250 11 : bool readkey2=parseMultiColvarAtomList(key2,-1,all_atoms);
251 22 : if( !readkey1 && !readkey2 ) return ;
252 11 : 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;
253 :
254 11 : resizeBookeepingArray( ablocks[0].size(), ablocks[1].size() );
255 11 : bool readkey3=parseMultiColvarAtomList(key3,-1,all_atoms);
256 11 : if( !readkey3 && !allow2 ) {
257 0 : error("missing atom specification " + key3);
258 11 : } else if( !readkey3 ) {
259 2 : if( ablocks[1].size()>ablocks[0].size() ) nblock=ablocks[1].size();
260 0 : else nblock=ablocks[0].size();
261 :
262 2 : ablocks[2].resize( ablocks[1].size() );
263 2 : for(unsigned i=0; i<ablocks[1].size(); ++i) ablocks[2][i]=ablocks[0].size() + i;
264 4 : for(unsigned i=0; i<ablocks[0].size(); ++i) {
265 198 : for(unsigned j=1; j<ablocks[1].size(); ++j) {
266 196 : bookeeping(i,j).first=getFullNumberOfTasks();
267 9898 : for(unsigned k=0; k<j; ++k) {
268 9702 : if( atom_lab[ablocks[0][i]].first>0 && atom_lab[ablocks[1][j]].first>0 && atom_lab[ablocks[2][k]].first>0 ) {
269 0 : if( mybasemulticolvars[atom_lab[ablocks[0][i]].first-1]->getLabel()!=mybasemulticolvars[atom_lab[ablocks[1][j]].first-1]->getLabel() &&
270 0 : mybasemulticolvars[atom_lab[ablocks[0][i]].first-1]->getLabel()!=mybasemulticolvars[atom_lab[ablocks[2][k]].first-1]->getLabel() &&
271 0 : mybasemulticolvars[atom_lab[ablocks[1][j]].first-1]->getLabel()!=mybasemulticolvars[atom_lab[ablocks[2][k]].first-1]->getLabel() &&
272 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 &&
273 0 : atom_lab[ablocks[1][j]].second!=atom_lab[ablocks[2][k]].second ) addTaskToList( nblock*nblock*i + nblock*j + k );
274 29106 : } else if( all_atoms[atom_lab[ablocks[0][i]].second]!=all_atoms[atom_lab[ablocks[1][j]].second] &&
275 19404 : all_atoms[atom_lab[ablocks[0][i]].second]!=all_atoms[atom_lab[ablocks[2][k]].second] &&
276 19404 : all_atoms[atom_lab[ablocks[1][j]].second]!=all_atoms[atom_lab[ablocks[2][k]].second] ) addTaskToList( nblock*nblock*i + nblock*j + k );
277 : }
278 196 : bookeeping(i,j).second=getFullNumberOfTasks();
279 : }
280 : }
281 : } else {
282 9 : ablocks[2].resize( atom_lab.size() - ablocks[1].size() - ablocks[0].size() );
283 9 : for(unsigned i=0; i<ablocks[2].size(); ++i) ablocks[2][i] = ablocks[0].size() + ablocks[1].size() + i;
284 :
285 9 : if( ablocks[1].size()>ablocks[0].size() ) nblock=ablocks[1].size();
286 9 : else nblock=ablocks[0].size();
287 9 : if( ablocks[2].size()>nblock ) nblock=ablocks[2].size();
288 :
289 9 : unsigned kcount; if( no_third_dim_accum ) kcount=1; else kcount=ablocks[2].size();
290 :
291 38 : for(unsigned i=0; i<ablocks[0].size(); ++i) {
292 238 : for(unsigned j=0; j<ablocks[1].size(); ++j) {
293 209 : bookeeping(i,j).first=getFullNumberOfTasks();
294 418 : for(unsigned k=0; k<kcount; ++k) {
295 209 : if( no_third_dim_accum ) addTaskToList( nblock*i + j );
296 0 : else if( atom_lab[ablocks[0][i]].first>0 && atom_lab[ablocks[1][j]].first>0 && atom_lab[ablocks[2][k]].first>0 ) {
297 0 : if( mybasemulticolvars[atom_lab[ablocks[0][i]].first-1]->getLabel()!=mybasemulticolvars[atom_lab[ablocks[1][j]].first-1]->getLabel() &&
298 0 : mybasemulticolvars[atom_lab[ablocks[0][i]].first-1]->getLabel()!=mybasemulticolvars[atom_lab[ablocks[2][k]].first-1]->getLabel() &&
299 0 : mybasemulticolvars[atom_lab[ablocks[1][j]].first-1]->getLabel()!=mybasemulticolvars[atom_lab[ablocks[2][k]].first-1]->getLabel() &&
300 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 &&
301 0 : atom_lab[ablocks[1][j]].second!=atom_lab[ablocks[2][k]].second ) addTaskToList( nblock*nblock*i + nblock*j + k );
302 0 : } else if( all_atoms[atom_lab[ablocks[0][i]].second]!=all_atoms[atom_lab[ablocks[1][j]].second] &&
303 0 : all_atoms[atom_lab[ablocks[0][i]].second]!=all_atoms[atom_lab[ablocks[2][k]].second] &&
304 0 : all_atoms[atom_lab[ablocks[1][j]].second]!=all_atoms[atom_lab[ablocks[2][k]].second] ) addTaskToList( nblock*nblock*i + nblock*j + k );
305 : }
306 209 : bookeeping(i,j).second=getFullNumberOfTasks();
307 : }
308 : }
309 : }
310 : }
311 :
312 12264184 : void MultiColvarBase::addTaskToList( const unsigned& taskCode ) {
313 12264184 : plumed_assert( getNumberOfVessels()==0 );
314 12264184 : ActionWithVessel::addTaskToList( taskCode );
315 12264184 : }
316 :
317 81 : void MultiColvarBase::resizeBookeepingArray( const unsigned& num1, const unsigned& num2 ) {
318 81 : bookeeping.resize( num1, num2 );
319 5626 : for(unsigned i=0; i<num1; ++i) {
320 5545 : for(unsigned j=0; j<num2; ++j) { bookeeping(i,j).first=0; bookeeping(i,j).second=0; }
321 : }
322 81 : }
323 :
324 221 : void MultiColvarBase::setupMultiColvarBase( const std::vector<AtomNumber>& atoms ) {
325 221 : if( !matsums && atom_lab.size()==0 ) error("No atoms have been read in");
326 221 : std::vector<AtomNumber> all_atoms;
327 : // Setup decoder array
328 221 : if( !usespecies && nblock>0 ) {
329 :
330 46 : ncentral=ablocks.size(); use_for_central_atom.resize( ablocks.size(), true );
331 46 : numberForCentralAtom = 1.0 / static_cast<double>( ablocks.size() );
332 46 : if( ablocks.size()==3 ) {
333 13 : allthirdblockintasks=uselinkforthree=true;
334 111 : for(unsigned i=0; i<bookeeping.nrows(); ++i) {
335 4610 : for(unsigned j=0; j<bookeeping.ncols(); ++j) {
336 4512 : unsigned ntper = bookeeping(i,j).second - bookeeping(i,j).first;
337 4512 : if( i==j && ntper==0 ) {
338 69 : continue;
339 4443 : } else if( ntper == 1 && allthirdblockintasks ) {
340 4249 : allthirdblockintasks=true;
341 194 : } else if( ntper != ablocks[2].size() ) {
342 194 : allthirdblockintasks=uselinkforthree=false;
343 : } else {
344 0 : allthirdblockintasks=false;
345 : }
346 : }
347 : }
348 : }
349 :
350 46 : if( allthirdblockintasks ) {
351 11 : decoder.resize(2); plumed_assert( ablocks.size()==3 );
352 : // Check if number of atoms is too large
353 11 : if( pow( double(nblock), 2.0 )>std::numeric_limits<unsigned>::max() ) error("number of atoms in groups is too big for PLUMED to handle");
354 : } else {
355 35 : decoder.resize( ablocks.size() );
356 : // Check if number of atoms is too large
357 35 : if( pow( double(nblock), double(ablocks.size()) )>std::numeric_limits<unsigned>::max() ) error("number of atoms in groups is too big for PLUMED to handle");
358 : }
359 46 : unsigned code=1; for(unsigned i=0; i<decoder.size(); ++i) { decoder[decoder.size()-1-i]=code; code *= nblock; }
360 175 : } else if( !usespecies ) {
361 76 : ncentral=ablocks.size(); use_for_central_atom.resize( ablocks.size(), true );
362 76 : numberForCentralAtom = 1.0 / static_cast<double>( ablocks.size() );
363 99 : } else if( keywords.exists("SPECIESA") ) {
364 70 : plumed_assert( atom_lab.size()==0 && all_atoms.size()==0 );
365 70 : ablocks.resize( 1 ); bool readspecies=parseMultiColvarAtomList("SPECIES", -1, all_atoms);
366 70 : if( readspecies ) {
367 62 : ablocks[0].resize( atom_lab.size() ); for(unsigned i=0; i<atom_lab.size(); ++i) { addTaskToList(i); ablocks[0][i]=i; }
368 : } else {
369 8 : if( !parseMultiColvarAtomList("SPECIESA", -1, all_atoms) ) error("missing SPECIES/SPECIESA keyword");
370 8 : unsigned nat1=atom_lab.size();
371 8 : if( !parseMultiColvarAtomList("SPECIESB", -1, all_atoms) ) error("missing SPECIESB keyword");
372 8 : unsigned nat2=atom_lab.size() - nat1;
373 :
374 8 : for(unsigned i=0; i<nat1; ++i) addTaskToList(i);
375 8 : ablocks[0].resize( nat2 );
376 718 : for(unsigned i=0; i<nat2; ++i) {
377 710 : bool found=false; unsigned inum;
378 6241 : for(unsigned j=0; j<nat1; ++j) {
379 5597 : if( atom_lab[nat1+i].first>0 && atom_lab[j].first>0 ) {
380 8160 : if( atom_lab[nat1+i].first==atom_lab[j].first &&
381 2720 : mybasemulticolvars[atom_lab[nat1+i].first-1]->getAbsoluteIndexOfCentralAtom(atom_lab[nat1+i].second)==
382 0 : mybasemulticolvars[atom_lab[j].first-1]->getAbsoluteIndexOfCentralAtom(atom_lab[j].second) ) { found=true; inum=j; break; }
383 2877 : } else if( atom_lab[nat1+i].first>0 ) {
384 0 : if( mybasemulticolvars[atom_lab[nat1+i].first-1]->getAbsoluteIndexOfCentralAtom(atom_lab[nat1+i].second)==
385 0 : all_atoms[atom_lab[j].second] ) { found=true; inum=nat1 + i; break; }
386 2877 : } else if( atom_lab[j].first>0 ) {
387 0 : if( all_atoms[atom_lab[nat1+i].second]==
388 0 : mybasemulticolvars[atom_lab[j].first-1]->getAbsoluteIndexOfCentralAtom(atom_lab[j].second) ) { found=true; inum=nat1+i; break; }
389 2877 : } else if( all_atoms[atom_lab[nat1+i].second]==all_atoms[atom_lab[j].second] ) { found=true; inum=j; break; }
390 : }
391 : // This prevents mistakes being made in colvar setup
392 710 : if( found ) { ablocks[0][i]=inum; }
393 644 : else { ablocks[0][i]=nat1 + i; }
394 : }
395 : }
396 : }
397 221 : if( mybasemulticolvars.size()>0 ) { for(unsigned i=0; i<mybasedata.size(); ++i) mybasedata[i]->resizeTemporyMultiValues(2); }
398 :
399 : // Copy lists of atoms involved from base multicolvars
400 442 : std::vector<AtomNumber> tmp_atoms;
401 304 : for(unsigned i=0; i<mybasemulticolvars.size(); ++i) {
402 83 : tmp_atoms=mybasemulticolvars[i]->getAbsoluteIndexes();
403 83 : for(unsigned j=0; j<tmp_atoms.size(); ++j) all_atoms.push_back( tmp_atoms[j] );
404 : }
405 : // Copy atom lists from input
406 221 : if( !usespecies ) {
407 122 : for(unsigned i=0; i<atoms.size(); ++i) all_atoms.push_back( atoms[i] );
408 : }
409 :
410 : // Now make sure we get all the atom positions
411 221 : ActionAtomistic::requestAtoms( all_atoms );
412 : // And setup dependencies
413 221 : for(unsigned i=0; i<mybasemulticolvars.size(); ++i) addDependency( mybasemulticolvars[i] );
414 :
415 : // Setup underlying ActionWithVessel
416 442 : readVesselKeywords();
417 221 : }
418 :
419 19 : void MultiColvarBase::setAtomsForCentralAtom( const std::vector<bool>& catom_ind ) {
420 19 : unsigned nat=0; plumed_assert( catom_ind.size()==ablocks.size() );
421 88 : for(unsigned i=0; i<catom_ind.size(); ++i) {
422 69 : use_for_central_atom[i]=catom_ind[i];
423 69 : if( use_for_central_atom[i] ) nat++;
424 : }
425 19 : plumed_dbg_assert( nat>0 ); ncentral=nat;
426 19 : numberForCentralAtom = 1.0 / static_cast<double>( nat );
427 19 : }
428 :
429 339 : void MultiColvarBase::turnOnDerivatives() {
430 339 : ActionWithValue::turnOnDerivatives();
431 339 : needsDerivatives();
432 339 : forcesToApply.resize( getNumberOfDerivatives() );
433 339 : }
434 :
435 146 : void MultiColvarBase::setLinkCellCutoff( const double& lcut, double tcut ) {
436 146 : plumed_assert( usespecies || ablocks.size()<4 );
437 146 : if( tcut<0 ) tcut=lcut;
438 146 : linkcells.setCutoff( lcut );
439 146 : threecells.setCutoff( tcut );
440 146 : }
441 :
442 4 : double MultiColvarBase::getLinkCellCutoff() const {
443 4 : return linkcells.getCutoff();
444 : }
445 :
446 1509 : void MultiColvarBase::setupLinkCells() {
447 3018 : if( (!usespecies && nblock==0) || !linkcells.enabled() ) return ;
448 : // Retrieve any atoms that haven't already been retrieved
449 900 : for(std::vector<MultiColvarBase*>::iterator p=mybasemulticolvars.begin(); p!=mybasemulticolvars.end(); ++p) {
450 168 : (*p)->retrieveAtoms();
451 : }
452 732 : retrieveAtoms();
453 :
454 : unsigned iblock;
455 732 : if( usespecies ) {
456 556 : iblock=0;
457 176 : } else if( ablocks.size()<4 ) {
458 176 : iblock=1;
459 : } else {
460 0 : plumed_error();
461 : }
462 :
463 : // Count number of currently active atoms
464 732 : nactive_atoms=0;
465 197122 : for(unsigned i=0; i<ablocks[iblock].size(); ++i) {
466 196390 : if( isCurrentlyActive( ablocks[iblock][i] ) ) nactive_atoms++;
467 : }
468 :
469 732 : if( nactive_atoms>0 ) {
470 732 : std::vector<Vector> ltmp_pos( nactive_atoms );
471 1464 : std::vector<unsigned> ltmp_ind( nactive_atoms );
472 :
473 732 : nactive_atoms=0;
474 732 : if( usespecies ) {
475 177372 : for(unsigned i=0; i<ablocks[0].size(); ++i) {
476 176816 : if( !isCurrentlyActive( ablocks[0][i] ) ) continue;
477 176720 : ltmp_ind[nactive_atoms]=ablocks[0][i];
478 176720 : ltmp_pos[nactive_atoms]=getPositionOfAtomForLinkCells( ltmp_ind[nactive_atoms] );
479 176720 : nactive_atoms++;
480 : }
481 : } else {
482 19750 : for(unsigned i=0; i<ablocks[1].size(); ++i) {
483 19574 : if( !isCurrentlyActive( ablocks[1][i] ) ) continue;
484 14654 : ltmp_ind[nactive_atoms]=i;
485 14654 : ltmp_pos[nactive_atoms]=getPositionOfAtomForLinkCells( ablocks[1][i] );
486 14654 : nactive_atoms++;
487 : }
488 : }
489 :
490 : // Build the lists for the link cells
491 1464 : linkcells.buildCellLists( ltmp_pos, ltmp_ind, getPbc() );
492 : }
493 : }
494 :
495 5214 : void MultiColvarBase::setupNonUseSpeciesLinkCells( const unsigned& my_always_active ) {
496 5214 : plumed_assert( !usespecies );
497 10428 : if( nblock==0 || !linkcells.enabled() ) return ;
498 664 : deactivateAllTasks();
499 :
500 664 : if( !uselinkforthree && nactive_atoms>0 ) {
501 : // Get some parallel info
502 126 : unsigned stride=comm.Get_size();
503 126 : unsigned rank=comm.Get_rank();
504 126 : if( serialCalculation() ) { stride=1; rank=0; }
505 :
506 : // Ensure we only do tasks where atoms are in appropriate link cells
507 126 : std::vector<unsigned> linked_atoms( 1+ablocks[1].size() );
508 5577 : for(unsigned i=rank; i<ablocks[0].size(); i+=stride) {
509 5451 : if( !isCurrentlyActive( ablocks[0][i] ) ) continue;
510 2472 : unsigned natomsper=1; linked_atoms[0]=my_always_active; // Note we always check atom 0 because it is simpler than changing LinkCells.cpp
511 2472 : linkcells.retrieveNeighboringAtoms( getPositionOfAtomForLinkCells( ablocks[0][i] ), natomsper, linked_atoms );
512 204805 : for(unsigned j=0; j<natomsper; ++j) {
513 202333 : for(unsigned k=bookeeping(i,linked_atoms[j]).first; k<bookeeping(i,linked_atoms[j]).second; ++k) taskFlags[k]=1;
514 : }
515 126 : }
516 538 : } else if( nactive_atoms>0 ) {
517 : // Get some parallel info
518 538 : unsigned stride=comm.Get_size();
519 538 : unsigned rank=comm.Get_rank();
520 538 : if( serialCalculation() ) { stride=1; rank=0; }
521 :
522 538 : unsigned nactive_three=0;
523 21538 : for(unsigned i=0; i<ablocks[2].size(); ++i) {
524 21000 : if( isCurrentlyActive( ablocks[2][i] ) ) nactive_three++;
525 : }
526 :
527 538 : std::vector<Vector> lttmp_pos( nactive_three );
528 1076 : std::vector<unsigned> lttmp_ind( nactive_three );
529 :
530 538 : nactive_three=0;
531 538 : if( allthirdblockintasks ) {
532 21538 : for(unsigned i=0; i<ablocks[2].size(); ++i) {
533 21000 : if( !isCurrentlyActive( ablocks[2][i] ) ) continue;
534 21000 : lttmp_ind[nactive_three]=ablocks[2][i];
535 21000 : lttmp_pos[nactive_three]=getPositionOfAtomForLinkCells( ablocks[2][i] );
536 21000 : nactive_three++;
537 : }
538 : } else {
539 0 : for(unsigned i=0; i<ablocks[2].size(); ++i) {
540 0 : if( !isCurrentlyActive( ablocks[2][i] ) ) continue;
541 0 : lttmp_ind[nactive_three]=i;
542 0 : lttmp_pos[nactive_three]=getPositionOfAtomForLinkCells( ablocks[2][i] );
543 0 : nactive_three++;
544 : }
545 : }
546 : // Build the list of the link cells
547 538 : threecells.buildCellLists( lttmp_pos, lttmp_ind, getPbc() );
548 :
549 : // Ensure we only do tasks where atoms are in appropriate link cells
550 1076 : std::vector<unsigned> linked_atoms( 1+ablocks[1].size() );
551 1076 : std::vector<unsigned> tlinked_atoms( 1+ablocks[2].size() );
552 5692 : for(unsigned i=rank; i<ablocks[0].size(); i+=stride) {
553 5154 : if( !isCurrentlyActive( ablocks[0][i] ) ) continue;
554 5154 : unsigned natomsper=1; linked_atoms[0]=my_always_active; // Note we always check atom 0 because it is simpler than changing LinkCells.cpp
555 5154 : linkcells.retrieveNeighboringAtoms( getPositionOfAtomForLinkCells( ablocks[0][i] ), natomsper, linked_atoms );
556 5154 : if( allthirdblockintasks ) {
557 59796 : for(unsigned j=0; j<natomsper; ++j) {
558 54642 : for(unsigned k=bookeeping(i,linked_atoms[j]).first; k<bookeeping(i,linked_atoms[j]).second; ++k) taskFlags[k]=1;
559 : }
560 : } else {
561 0 : unsigned ntatomsper=1; tlinked_atoms[0]=lttmp_ind[0];
562 0 : threecells.retrieveNeighboringAtoms( getPositionOfAtomForLinkCells( ablocks[0][i] ), ntatomsper, tlinked_atoms );
563 0 : for(unsigned j=0; j<natomsper; ++j) {
564 0 : for(unsigned k=0; k<ntatomsper; ++k) taskFlags[bookeeping(i,linked_atoms[j]).first+tlinked_atoms[k]]=1;
565 : }
566 : }
567 538 : }
568 : }
569 664 : if( !serialCalculation() ) comm.Sum( taskFlags );
570 664 : lockContributors();
571 : }
572 :
573 269144 : void MultiColvarBase::decodeIndexToAtoms( const unsigned& taskCode, std::vector<unsigned>& atoms ) const {
574 : plumed_dbg_assert( !usespecies && nblock>0 );
575 269144 : if( atoms.size()!=decoder.size() ) atoms.resize( decoder.size() );
576 :
577 269132 : unsigned scode = taskCode;
578 855761 : for(unsigned i=0; i<decoder.size(); ++i) {
579 586673 : unsigned ind=( scode / decoder[i] );
580 586684 : atoms[i] = ablocks[i][ind];
581 586589 : scode -= ind*decoder[i];
582 : }
583 269123 : }
584 :
585 423461 : bool MultiColvarBase::setupCurrentAtomList( const unsigned& taskCode, AtomValuePack& myatoms ) const {
586 423461 : if( isDensity() ) {
587 17146 : myatoms.setNumberOfAtoms( 1 ); myatoms.setAtom( 0, taskCode ); return true;
588 406258 : } else if( usespecies ) {
589 136815 : std::vector<unsigned> task_atoms(1); task_atoms[0]=taskCode;
590 136891 : unsigned natomsper=myatoms.setupAtomsFromLinkCells( task_atoms, getLinkCellPosition(task_atoms), linkcells );
591 136884 : return natomsper>1;
592 269443 : } else if( matsums ) {
593 303 : myatoms.setNumberOfAtoms( getNumberOfAtoms() );
594 303 : for(unsigned i=0; i<getNumberOfAtoms(); ++i) myatoms.setAtom( i, i );
595 269140 : } else if( allthirdblockintasks ) {
596 54567 : plumed_dbg_assert( ablocks.size()==3 ); std::vector<unsigned> atoms(2); decodeIndexToAtoms( taskCode, atoms );
597 54563 : myatoms.setupAtomsFromLinkCells( atoms, getLinkCellPosition(atoms), threecells );
598 214573 : } else if( nblock>0 ) {
599 179087 : std::vector<unsigned> atoms( ablocks.size() );
600 179111 : decodeIndexToAtoms( taskCode, atoms ); myatoms.setNumberOfAtoms( ablocks.size() );
601 179101 : for(unsigned i=0; i<ablocks.size(); ++i) myatoms.setAtom( i, atoms[i] );
602 : } else {
603 35486 : myatoms.setNumberOfAtoms( ablocks.size() );
604 35516 : for(unsigned i=0; i<ablocks.size(); ++i) myatoms.setAtom( i, ablocks[i][taskCode] );
605 : }
606 269509 : return true;
607 : }
608 :
609 8781 : void MultiColvarBase::setupActiveTaskSet( std::vector<unsigned>& active_tasks, const std::string& input_label ) {
610 8781 : if( !setup_completed ) {
611 1501 : bool justVolumes=false;
612 1501 : if( usespecies ) {
613 555 : justVolumes=true;
614 817 : for(unsigned i=0; i<getNumberOfVessels(); ++i) {
615 555 : vesselbase::StoreDataVessel* mys=dynamic_cast<vesselbase::StoreDataVessel*>( getPntrToVessel(i) );
616 555 : if( mys ) continue;
617 304 : vesselbase::BridgeVessel* myb=dynamic_cast<vesselbase::BridgeVessel*>( getPntrToVessel(i) );
618 304 : if( !myb ) { justVolumes=false; break; }
619 17 : ActionVolume* myv=dynamic_cast<ActionVolume*>( myb->getOutputAction() );
620 17 : if( !myv ) { justVolumes=false; break; }
621 : }
622 : }
623 1501 : deactivateAllTasks();
624 1501 : if( justVolumes && mydata ) {
625 251 : if( mydata->getNumberOfDataUsers()==0 ) justVolumes=false;
626 :
627 269 : for(unsigned i=0; i<mydata->getNumberOfDataUsers(); ++i) {
628 18 : MultiColvarBase* myu=dynamic_cast<MultiColvarBase*>( mydata->getDataUser(i) );
629 18 : if( myu ) {
630 18 : myu->setupActiveTaskSet( taskFlags, getLabel() );
631 : } else {
632 0 : for(unsigned i=0; i<getFullNumberOfTasks(); ++i) taskFlags[i]=1;
633 : }
634 : }
635 : }
636 1501 : if( justVolumes ) {
637 56 : for(unsigned j=0; j<getNumberOfVessels(); ++j) {
638 28 : vesselbase::BridgeVessel* myb=dynamic_cast<vesselbase::BridgeVessel*>( getPntrToVessel(j) );
639 28 : if( !myb ) continue ;
640 11 : ActionVolume* myv=dynamic_cast<ActionVolume*>( myb->getOutputAction() );
641 11 : if( !myv ) continue ;
642 11 : myv->retrieveAtoms(); myv->setupRegions();
643 :
644 61515 : for(unsigned i=0; i<getFullNumberOfTasks(); ++i) {
645 61504 : if( myv->inVolumeOfInterest(i) ) taskFlags[i]=1;
646 : }
647 : }
648 : } else {
649 1473 : for(unsigned i=0; i<getFullNumberOfTasks(); ++i) taskFlags[i]=1;
650 : }
651 :
652 : // Now activate all this class
653 1501 : lockContributors();
654 : // Setup the link cells
655 1501 : setupLinkCells();
656 : // Ensures that setup is not performed multiple times during one cycle
657 1501 : setup_completed=true;
658 : }
659 :
660 : // And activate the tasks in input action
661 8781 : if( getLabel()!=input_label ) {
662 18 : int input_code=-1;
663 20 : for(unsigned i=0; i<mybasemulticolvars.size(); ++i) {
664 20 : if( mybasemulticolvars[i]->getLabel()==input_label ) { input_code=i+1; break; }
665 : }
666 :
667 18 : MultiValue my_tvals( getNumberOfQuantities(), getNumberOfDerivatives() );
668 18 : AtomValuePack mytmp_atoms( my_tvals, this );
669 61548 : for(unsigned i=0; i<getFullNumberOfTasks(); ++i) {
670 61530 : if( !taskIsCurrentlyActive(i) ) continue;
671 3123 : setupCurrentAtomList( getTaskCode(i), mytmp_atoms );
672 238948 : for(unsigned j=0; j<mytmp_atoms.getNumberOfAtoms(); ++j) {
673 235825 : unsigned itask=mytmp_atoms.getIndex(j);
674 235825 : if( atom_lab[itask].first==input_code ) active_tasks[ atom_lab[itask].second ]=1;
675 : }
676 18 : }
677 : }
678 8781 : }
679 :
680 326 : bool MultiColvarBase::filtersUsedAsInput() {
681 326 : bool inputAreFilters=false;
682 508 : for(unsigned i=0; i<mybasemulticolvars.size(); ++i) {
683 182 : MultiColvarFilter* myfilt=dynamic_cast<MultiColvarFilter*>( mybasemulticolvars[i] );
684 182 : if( myfilt || mybasemulticolvars[i]->filtersUsedAsInput() ) inputAreFilters=true;
685 : }
686 326 : return inputAreFilters;
687 : }
688 :
689 8763 : void MultiColvarBase::calculate() {
690 : // Recursive function that sets up tasks
691 8763 : setupActiveTaskSet( taskFlags, getLabel() );
692 :
693 : // Check for filters and rerun setup of link cells if there are any
694 8763 : if( mybasemulticolvars.size()>0 && filtersUsedAsInput() ) setupLinkCells();
695 :
696 : // Setup the link cells if we are not using species
697 8763 : if( !usespecies && ablocks.size()>1 ) {
698 : // This loop finds the first active atom, which is always checked because
699 : // of a peculiarity in linkcells
700 5214 : unsigned first_active=std::numeric_limits<unsigned>::max();
701 5348 : for(unsigned i=0; i<ablocks[0].size(); ++i) {
702 5348 : if( !isCurrentlyActive( ablocks[1][i] ) ) continue;
703 : else {
704 5214 : first_active=i; break;
705 : }
706 : }
707 5214 : setupNonUseSpeciesLinkCells( first_active );
708 : }
709 : // And run all tasks
710 8763 : runAllTasks();
711 8763 : }
712 :
713 217 : void MultiColvarBase::calculateNumericalDerivatives( ActionWithValue* a ) {
714 217 : if( mybasemulticolvars.size()>0 ) plumed_merror("cannot calculate numerical derivatives for this quantity");
715 217 : calculateAtomicNumericalDerivatives( this, 0 );
716 217 : }
717 :
718 2471 : void MultiColvarBase::prepare() {
719 2471 : setup_completed=false; atomsWereRetrieved=false;
720 2471 : }
721 :
722 5600 : void MultiColvarBase::retrieveAtoms() {
723 5600 : if( !atomsWereRetrieved ) { ActionAtomistic::retrieveAtoms(); atomsWereRetrieved=true; }
724 5600 : }
725 :
726 36973 : void MultiColvarBase::mergeInputDerivatives( const unsigned& ival, const unsigned& start, const unsigned& end,
727 : const unsigned& jatom, const std::vector<double>& der,
728 : MultiValue& myder, AtomValuePack& myatoms ) const {
729 36973 : MultiValue& myvals=myatoms.getUnderlyingMultiValue();
730 : plumed_dbg_assert( ival<myatoms.getUnderlyingMultiValue().getNumberOfValues() );
731 : plumed_dbg_assert( start<myder.getNumberOfValues() && end<=myder.getNumberOfValues() );
732 : plumed_dbg_assert( der.size()==myder.getNumberOfValues() && jatom<myatoms.getNumberOfAtoms() );
733 : // Convert input atom to local index
734 36973 : unsigned katom = myatoms.getIndex( jatom ); plumed_dbg_assert( katom<atom_lab.size() ); plumed_dbg_assert( atom_lab[katom].first>0 );
735 : // Find base colvar
736 36973 : unsigned mmc=atom_lab[katom].first - 1; plumed_dbg_assert( mybasemulticolvars[mmc]->taskIsCurrentlyActive( atom_lab[katom].second ) );
737 : // Get start of indices for this atom
738 36973 : unsigned basen=0; for(unsigned i=0; i<mmc; ++i) basen+=mybasemulticolvars[i]->getNumberOfDerivatives() - 9;
739 : plumed_dbg_assert( basen%3==0 ); // Check the number of atoms is consistent with input derivatives
740 :
741 36973 : unsigned virbas = myvals.getNumberOfDerivatives()-9;
742 4782403 : for(unsigned j=0; j<myder.getNumberActive(); ++j) {
743 4745430 : unsigned jder=myder.getActiveIndex(j);
744 4745430 : if( jder<mybasemulticolvars[mmc]->getNumberOfDerivatives()-9 ) {
745 4423473 : unsigned kder=basen+jder;
746 110152908 : for(unsigned icomp=start; icomp<end; ++icomp) {
747 105729435 : myvals.addDerivative( ival, kder, der[icomp]*myder.getDerivative( icomp, jder ) );
748 : }
749 : } else {
750 321957 : unsigned kder=virbas + (jder - mybasemulticolvars[mmc]->getNumberOfDerivatives() + 9);
751 7507008 : for(unsigned icomp=start; icomp<end; ++icomp) {
752 7185051 : myvals.addDerivative( ival, kder, der[icomp]*myder.getDerivative( icomp, jder ) );
753 : }
754 : }
755 : }
756 36973 : }
757 :
758 93393 : void MultiColvarBase::addComDerivatives( const int& ival, const unsigned& iatom, const Vector& der, multicolvar::AtomValuePack& myatoms ) const {
759 : plumed_dbg_assert( ival<static_cast<int>(myatoms.getUnderlyingMultiValue().getNumberOfValues()) && iatom<myatoms.getNumberOfAtoms() );
760 : // Convert input atom to local index
761 93393 : unsigned katom = myatoms.getIndex( iatom ); plumed_dbg_assert( atom_lab[katom].first>0 );
762 : // Find base colvar
763 93393 : unsigned mmc = atom_lab[katom].first - 1; plumed_dbg_assert( mybasemulticolvars[mmc]->taskIsCurrentlyActive( atom_lab[katom].second ) );
764 : // Get start of indices for this atom
765 93393 : unsigned basen=0; for(unsigned i=0; i<mmc; ++i) basen+=(mybasemulticolvars[i]->getNumberOfDerivatives() - 9) / 3;
766 93393 : multicolvar::CatomPack atom0=mybasemulticolvars[mmc]->getCentralAtomPack( basen, atom_lab[katom].second );
767 93393 : myatoms.addComDerivatives( ival, der, atom0 );
768 93393 : }
769 :
770 1114367 : void MultiColvarBase::getInputData( const unsigned& ind, const bool& normed,
771 : const multicolvar::AtomValuePack& myatoms,
772 : std::vector<double>& orient ) const {
773 : // Converint input atom to local index
774 1114367 : unsigned katom = myatoms.getIndex(ind); plumed_dbg_assert( atom_lab[katom].first>0 );
775 : // Find base colvar
776 1114367 : unsigned mmc = atom_lab[katom].first - 1; plumed_dbg_assert( mybasemulticolvars[mmc]->taskIsCurrentlyActive( atom_lab[katom].second ) );
777 : // Check if orient is the correct size
778 1114367 : if( orient.size()!=mybasemulticolvars[mmc]->getNumberOfQuantities() ) orient.resize( mybasemulticolvars[mmc]->getNumberOfQuantities() );
779 : // Retrieve the value
780 1114367 : mybasedata[mmc]->retrieveValueWithIndex( atom_lab[katom].second, normed, orient );
781 1114367 : }
782 :
783 44762 : MultiValue& MultiColvarBase::getInputDerivatives( const unsigned& iatom, const bool& normed, const multicolvar::AtomValuePack& myatoms ) const {
784 : // Converint input atom to local index
785 44762 : unsigned katom = myatoms.getIndex(iatom); plumed_dbg_assert( atom_lab[katom].first>0 );
786 : // Find base colvar
787 44762 : unsigned mmc = atom_lab[katom].first - 1; plumed_dbg_assert( mybasemulticolvars[mmc]->taskIsCurrentlyActive( atom_lab[katom].second ) );
788 44762 : if( usespecies && !normed && iatom==0 ) return mybasedata[mmc]->getTemporyMultiValue(0);
789 :
790 43860 : unsigned oval=0; if( iatom>0 ) oval=1;
791 43860 : MultiValue& myder=mybasedata[mmc]->getTemporyMultiValue(oval);
792 87694 : if( myder.getNumberOfValues()!=mybasemulticolvars[mmc]->getNumberOfQuantities() ||
793 43834 : myder.getNumberOfDerivatives()!=mybasemulticolvars[mmc]->getNumberOfDerivatives() ) {
794 26 : myder.resize( mybasemulticolvars[mmc]->getNumberOfQuantities(), mybasemulticolvars[mmc]->getNumberOfDerivatives() );
795 : }
796 43860 : mybasedata[mmc]->retrieveDerivatives( atom_lab[katom].second, normed, myder );
797 43860 : return myder;
798 : }
799 :
800 58692014 : void MultiColvarBase::accumulateSymmetryFunction( const int& ival, const unsigned& iatom, const double& val, const Vector& der, const Tensor& vir, multicolvar::AtomValuePack& myatoms ) const {
801 58692014 : plumed_dbg_assert( usespecies ); unsigned katom=myatoms.getIndex(0), jatom=myatoms.getIndex(iatom);
802 58702075 : double weight0=1.0; if( atom_lab[katom].first>0 ) weight0=mybasedata[atom_lab[katom].first-1]->retrieveWeightWithIndex( atom_lab[katom].second );
803 58703001 : double weighti=1.0; if( atom_lab[jatom].first>0 ) weighti=mybasedata[atom_lab[jatom].first-1]->retrieveWeightWithIndex( atom_lab[jatom].second );
804 : // Accumulate the value
805 58704478 : if( ival<0 ) myatoms.getUnderlyingMultiValue().addTemporyValue( weight0*weighti*val );
806 56023828 : else myatoms.addValue( ival, weight0*weighti*val );
807 :
808 : // Return if we don't need derivatives
809 117407317 : if( doNotCalculateDerivatives() ) return ;
810 : // And virial
811 52142426 : if( ival<0 ) myatoms.addTemporyBoxDerivatives( weight0*weighti*vir );
812 49956430 : else myatoms.addBoxDerivatives( ival, weight0*weighti*vir );
813 :
814 : // Add derivatives of central atom
815 52141304 : if( atom_lab[katom].first>0 ) {
816 774 : addComDerivatives( ival, 0, -weight0*weighti*der, myatoms );
817 774 : std::vector<double> tmpder( mybasemulticolvars[atom_lab[katom].first - 1]->getNumberOfQuantities(), 0. );
818 774 : tmpder[0]=weighti*val; mergeInputDerivatives( ival, 0, 1, 0, tmpder, getInputDerivatives(0, false, myatoms), myatoms );
819 : } else {
820 52140648 : if( ival<0 ) myatoms.addTemporyAtomsDerivatives( 0, -der );
821 49954657 : else myatoms.addAtomsDerivatives( ival, 0, -der );
822 : }
823 : // Add derivatives of atom in coordination sphere
824 52140708 : if( atom_lab[jatom].first>0 ) {
825 774 : addComDerivatives( ival, iatom, weight0*weighti*der, myatoms );
826 774 : std::vector<double> tmpder( mybasemulticolvars[atom_lab[katom].first - 1]->getNumberOfQuantities(), 0. );
827 774 : tmpder[0]=weight0*val; mergeInputDerivatives( ival, 0, 1, iatom, tmpder, getInputDerivatives(iatom, false, myatoms), myatoms );
828 : } else {
829 52139904 : if( ival<0 ) myatoms.addTemporyAtomsDerivatives( iatom, der );
830 49953914 : else myatoms.addAtomsDerivatives( ival, iatom, der );
831 : }
832 : }
833 :
834 2113722 : void MultiColvarBase::addAtomDerivatives( const int& ival, const unsigned& iatom, const Vector& der, multicolvar::AtomValuePack& myatoms ) const {
835 4229133 : if( doNotCalculateDerivatives() ) return ;
836 1843473 : unsigned jatom=myatoms.getIndex(iatom);
837 :
838 1842111 : if( atom_lab[jatom].first>0 ) {
839 91845 : addComDerivatives( ival, iatom, der, myatoms );
840 : } else {
841 1750591 : if( ival<0 ) myatoms.addTemporyAtomsDerivatives( iatom, der );
842 1750591 : else myatoms.addAtomsDerivatives( ival, iatom, der );
843 : }
844 : }
845 :
846 246733 : double MultiColvarBase::calculateWeight( const unsigned& current, const double& weight, AtomValuePack& myvals ) const {
847 246733 : return 1.0;
848 : }
849 :
850 420442 : void MultiColvarBase::performTask( const unsigned& task_index, const unsigned& current, MultiValue& myvals ) const {
851 420442 : AtomValuePack myatoms( myvals, this );
852 : // Retrieve the atom list
853 420464 : if( !setupCurrentAtomList( current, myatoms ) ) return;
854 : // Get weight due to dynamic groups
855 420274 : double weight = 1.0;
856 420274 : if( !matsums ) {
857 1958267 : for(unsigned i=0; i<myatoms.getNumberOfAtoms(); ++i) {
858 1672232 : if( atom_lab[myatoms.getIndex(i)].first==0 ) continue;
859 : // Only need to do first two atoms for thigns like TopologyMatrix, HbondMatrix, Bridge and so on
860 242089 : if( allthirdblockintasks && i>1 ) break;
861 241089 : unsigned mmc = atom_lab[myatoms.getIndex(i)].first - 1;
862 242089 : weight *= mybasedata[mmc]->retrieveWeightWithIndex( atom_lab[myatoms.getIndex(i)].second );
863 : }
864 133991 : } else if( usespecies ) {
865 133674 : if( atom_lab[myatoms.getIndex(0)].first>0 ) {
866 8104 : if( mybasedata[atom_lab[myatoms.getIndex(0)].first-1]->retrieveWeightWithIndex( atom_lab[myatoms.getIndex(0)].second )<epsilon ) weight=0.;
867 : }
868 : }
869 : // Do a quick check on the size of this contribution
870 420334 : double multweight = calculateWeight( current, weight, myatoms );
871 420154 : if( weight*multweight<getTolerance() ) {
872 99739 : updateActiveAtoms( myatoms );
873 99739 : return;
874 : }
875 320456 : myatoms.setValue( 0, weight*multweight );
876 : // Deal with derivatives of weights due to dynamic groups
877 320587 : if( !matsums && !doNotCalculateDerivatives() && mybasemulticolvars.size()>0 ) {
878 37536 : MultiValue& outder=myatoms.getUnderlyingMultiValue(); MultiValue myder(0,0);
879 111315 : for(unsigned i=0; i<myatoms.getNumberOfAtoms(); ++i) {
880 : // Neglect any atoms without differentiable weights
881 73779 : if( atom_lab[myatoms.getIndex(i)].first==0 ) continue;
882 :
883 : // Retrieve derivatives
884 73779 : unsigned mmc = atom_lab[myatoms.getIndex(i)].first - 1;
885 73779 : if( myder.getNumberOfValues()!=mybasemulticolvars[mmc]->getNumberOfQuantities() || myder.getNumberOfDerivatives()!=mybasemulticolvars[mmc]->getNumberOfDerivatives() ) {
886 37536 : myder.resize( mybasemulticolvars[mmc]->getNumberOfQuantities(), mybasemulticolvars[mmc]->getNumberOfDerivatives() );
887 : }
888 73779 : mybasedata[mmc]->retrieveDerivatives( atom_lab[myatoms.getIndex(i)].second, false, myder );
889 :
890 : // Retrieve the prefactor (product of all other weights)
891 73779 : double prefactor = multweight*weight / mybasedata[mmc]->retrieveWeightWithIndex( atom_lab[myatoms.getIndex(i)].second );
892 : // And accumulate the derivatives
893 73779 : for(unsigned j=0; j<myder.getNumberActive(); ++j) { unsigned jder=myder.getActiveIndex(j); outder.addDerivative( 0, jder, prefactor*myder.getDerivative(0,jder) ); }
894 73779 : myder.clearAll();
895 37536 : }
896 : }
897 : // Compute everything
898 320572 : double vv=doCalculation( task_index, myatoms );
899 320536 : myatoms.setValue( 1, vv );
900 320549 : return;
901 : }
902 :
903 224917 : double MultiColvarBase::doCalculation( const unsigned& taskIndex, AtomValuePack& myatoms ) const {
904 224917 : if( usespecies && mybasemulticolvars.size()>0 && atom_lab[myatoms.getIndex(0)].first>0 ) {
905 8026 : unsigned mmc = atom_lab[0].first - 1;
906 8026 : MultiValue& myder=mybasedata[mmc]->getTemporyMultiValue(0);
907 16038 : if( myder.getNumberOfValues()!=mybasemulticolvars[mmc]->getNumberOfQuantities() ||
908 8012 : myder.getNumberOfDerivatives()!=mybasemulticolvars[mmc]->getNumberOfDerivatives() ) {
909 14 : myder.resize( mybasemulticolvars[mmc]->getNumberOfQuantities(), mybasemulticolvars[mmc]->getNumberOfDerivatives() );
910 : }
911 8026 : mybasedata[mmc]->retrieveDerivatives( atom_lab[myatoms.getIndex(0)].second, false, myder );
912 : }
913 224939 : double val=compute( taskIndex, myatoms ); updateActiveAtoms( myatoms );
914 225152 : return val;
915 : }
916 :
917 520607 : void MultiColvarBase::updateActiveAtoms( AtomValuePack& myatoms ) const {
918 520607 : if( mybasemulticolvars.size()==0 ) myatoms.updateUsingIndices();
919 137920 : else myatoms.updateDynamicList();
920 520763 : }
921 :
922 2201754 : Vector MultiColvarBase::getCentralAtomPos( const unsigned& taskIndex ) {
923 2201754 : unsigned curr=getTaskCode( taskIndex );
924 :
925 2201796 : if( usespecies || isDensity() ) {
926 908927 : return getPositionOfAtomForLinkCells(curr);
927 1292878 : } else if( nblock>0 ) {
928 : // double factor=1.0/static_cast<double>( ablocks.size() );
929 2100 : Vector mypos; mypos.zero();
930 2100 : std::vector<unsigned> atoms( ablocks.size() ); decodeIndexToAtoms( curr, atoms );
931 6300 : for(unsigned i=0; i<ablocks.size(); ++i) {
932 4200 : if( use_for_central_atom[i] ) mypos+=numberForCentralAtom*getPositionOfAtomForLinkCells(atoms[i]);
933 : }
934 2100 : return mypos;
935 : } else {
936 1290778 : Vector mypos; mypos.zero();
937 5134501 : for(unsigned i=0; i<ablocks.size(); ++i) {
938 3843719 : if( use_for_central_atom[i] ) mypos+=numberForCentralAtom*getPositionOfAtomForLinkCells(ablocks[i][curr]);
939 : }
940 1290779 : return mypos;
941 : }
942 : }
943 :
944 216011 : CatomPack MultiColvarBase::getCentralAtomPack( const unsigned& basn, const unsigned& taskIndex ) {
945 216011 : unsigned curr=getTaskCode( taskIndex );
946 :
947 216035 : CatomPack mypack;
948 215949 : if(usespecies) {
949 76673 : mypack.resize(1);
950 76674 : mypack.setIndex( 0, basn + curr );
951 76673 : mypack.setDerivative( 0, Tensor::identity() );
952 139276 : } else if( nblock>0 ) {
953 0 : mypack.resize(ncentral); unsigned k=0;
954 0 : std::vector<unsigned> atoms( ablocks.size() ); decodeIndexToAtoms( curr, atoms );
955 0 : for(unsigned i=0; i<ablocks.size(); ++i) {
956 0 : if( use_for_central_atom[i] ) {
957 0 : mypack.setIndex( k, basn + atoms[i] );
958 0 : mypack.setDerivative( k, numberForCentralAtom*Tensor::identity() );
959 0 : k++;
960 : }
961 0 : }
962 : } else {
963 139276 : mypack.resize(ncentral); unsigned k=0;
964 311808 : for(unsigned i=0; i<ablocks.size(); ++i) {
965 172541 : if( use_for_central_atom[i] ) {
966 169927 : mypack.setIndex( k, basn + ablocks[i][curr] );
967 169996 : mypack.setDerivative( k, numberForCentralAtom*Tensor::identity() );
968 169919 : k++;
969 : }
970 : }
971 : }
972 215993 : return mypack;
973 : }
974 :
975 2127473 : Vector MultiColvarBase::getSeparation( const Vector& vec1, const Vector& vec2 ) const {
976 2127473 : if(usepbc) { return pbcDistance( vec1, vec2 ); }
977 0 : else { return delta( vec1, vec2 ); }
978 : }
979 :
980 191420 : void MultiColvarBase::applyPbc(std::vector<Vector>& dlist, unsigned int max_index) const {
981 191420 : if (usepbc) pbcApply(dlist, max_index);
982 191417 : }
983 :
984 1536 : void MultiColvarBase::apply() {
985 1536 : if( getForcesFromVessels( forcesToApply ) ) setForcesOnAtoms( forcesToApply );
986 1536 : }
987 :
988 : }
989 2523 : }
|