Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2017-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 "DomainDecomposition.h"
23 : #include "ActionToPutData.h"
24 : #include "ActionAtomistic.h"
25 : #include "ActionRegister.h"
26 : #include "PlumedMain.h"
27 : #include "ActionSet.h"
28 :
29 : #include "small_vector/small_vector.h"
30 : #include "tools/MergeVectorTools.h"
31 :
32 : //+PLUMEDOC ANALYSIS DOMAIN_DECOMPOSITION
33 : /*
34 : Pass domain decomposed properties of atoms to PLUMED
35 :
36 : \par Examples
37 :
38 : */
39 : //+ENDPLUMEDOC
40 :
41 : namespace PLMD {
42 :
43 : namespace {
44 :
45 : enum class Option {automatic, no, yes };
46 :
47 756 : Option interpretEnvString(const char* env,const char* str) {
48 756 : if(!str) {
49 : return Option::automatic;
50 : }
51 0 : if(!std::strcmp(str,"yes")) {
52 : return Option::yes;
53 : }
54 0 : if(!std::strcmp(str,"no")) {
55 : return Option::no;
56 : }
57 0 : if(!std::strcmp(str,"auto")) {
58 : return Option::automatic;
59 : }
60 0 : plumed_error()<<"Cannot understand env var "<<env<<"\nPossible values: yes/no/auto\nActual value: "<<str;
61 : }
62 :
63 : /// Use unique list of atoms to manipulate forces and positions.
64 : /// A unique list of atoms is used to manipulate forces and positions in MPI parallel runs.
65 : /// In serial runs, this is done if convenient. The code currently contain
66 : /// some heuristic to decide if the unique list should be used or not.
67 : /// An env var can be used to override this decision.
68 : /// export PLUMED_FORCE_UNIQUE=yes # enforce using the unique list in serial runs
69 : /// export PLUMED_FORCE_UNIQUE=no # enforce not using the unique list in serial runs
70 : /// export PLUMED_FORCE_UNIQUE=auto # choose heuristically
71 : /// default: auto
72 72010 : Option getenvForceUnique() {
73 : static const char* name="PLUMED_FORCE_UNIQUE";
74 72010 : static const auto opt = interpretEnvString(name,std::getenv(name));
75 72010 : return opt;
76 : }
77 :
78 : }
79 :
80 : PLUMED_REGISTER_ACTION(DomainDecomposition,"DOMAIN_DECOMPOSITION")
81 :
82 402 : void DomainDecomposition::DomainComms::enable(Communicator& c) {
83 402 : on=true;
84 402 : Set_comm(c.Get_comm());
85 402 : async=Get_size()<10;
86 402 : if(std::getenv("PLUMED_ASYNC_SHARE")) {
87 4 : std::string s(std::getenv("PLUMED_ASYNC_SHARE"));
88 4 : if(s=="yes") {
89 0 : async=true;
90 4 : } else if(s=="no") {
91 4 : async=false;
92 : } else {
93 0 : plumed_merror("PLUMED_ASYNC_SHARE variable is set to " + s + "; should be yes or no");
94 : }
95 : }
96 402 : }
97 :
98 1226 : void DomainDecomposition::registerKeywords(Keywords& keys) {
99 1226 : ActionForInterface::registerKeywords( keys );
100 1226 : keys.remove("ROLE");
101 2452 : keys.add("compulsory","NATOMS","the number of atoms across all the domains");
102 2452 : keys.add("numbered","VALUE","value to create for the domain decomposition");
103 2452 : keys.add("numbered","UNIT","unit of the ith value that is being passed through the domain decomposition");
104 2452 : keys.add("numbered","CONSTANT","is the ith value that is being passed through the domain decomposition constant");
105 2452 : keys.add("numbered","PERIODIC","if the value being passed to plumed is periodic then you should specify the periodicity of the function. If the value "
106 : "is not periodic you must state this using PERIODIC=NO. Positions are passed with PERIODIC=NO even though special methods are used "
107 : "to deal with pbc");
108 2452 : keys.add("numbered","ROLE","Get the role this value plays in the code can be x/y/z/m/q to signify that this is x, y, z positions of atoms or masses or charges of atoms");
109 2452 : keys.add("compulsory","PBCLABEL","Box","the label to use for the PBC action that will be created");
110 1226 : keys.setValueDescription("the domain that each atom is within");
111 1226 : }
112 :
113 1222 : DomainDecomposition::DomainDecomposition(const ActionOptions&ao):
114 : Action(ao),
115 : ActionForInterface(ao),
116 1222 : ddStep(0),
117 1222 : shuffledAtoms(0),
118 1222 : asyncSent(false),
119 1222 : unique_serial(false) {
120 : // Read in the number of atoms
121 : int natoms;
122 2444 : parse("NATOMS",natoms);
123 : std::string str_natoms;
124 1222 : Tools::convert( natoms, str_natoms );
125 : // Setup the gat index
126 1222 : gatindex.resize(natoms);
127 9202518 : for(unsigned i=0; i<gatindex.size(); i++) {
128 9201296 : gatindex[i]=i;
129 : }
130 : // Now read in the values we are creating here
131 6110 : for(unsigned i=1;; ++i) {
132 : std::string valname;
133 14664 : if( !parseNumbered("VALUE",i,valname) ) {
134 : break;
135 : }
136 : std::string unit;
137 12220 : parseNumbered("UNIT",i,unit);
138 : std::string constant;
139 12220 : parseNumbered("CONSTANT",i,constant);
140 : std::string period;
141 12220 : parseNumbered("PERIODIC",i,period);
142 : std::string role;
143 12220 : parseNumbered("ROLE",i,role);
144 6110 : if( constant=="True") {
145 4888 : plumed.readInputLine( valname + ": PUT FROM_DOMAINS CONSTANT SHAPE=" + str_natoms + " UNIT=" + unit + " PERIODIC=" + period + " ROLE=" + role, true );
146 3666 : } else if( constant=="False") {
147 7332 : plumed.readInputLine( valname + ": PUT FROM_DOMAINS SHAPE=" + str_natoms + " UNIT=" + unit + " PERIODIC=" + period + " ROLE=" + role, true );
148 : } else {
149 0 : plumed_merror("missing information on whether value is constant");
150 : }
151 : // And save the list of values that are set from here
152 6110 : ActionToPutData* ap=plumed.getActionSet().selectWithLabel<ActionToPutData*>(valname);
153 6110 : ap->addDependency( this );
154 6110 : inputs.push_back( ap );
155 6110 : }
156 : std::string pbclabel;
157 1222 : parse("PBCLABEL",pbclabel);
158 1222 : plumed.readInputLine(pbclabel + ": PBC",true);
159 : // Turn on the domain decomposition
160 1222 : if( Communicator::initialized() ) {
161 401 : Set_comm(comm);
162 : }
163 1222 : }
164 :
165 402 : void DomainDecomposition::Set_comm(Communicator& comm) {
166 402 : dd.enable(comm);
167 402 : setAtomsNlocal(getNumberOfAtoms());
168 402 : setAtomsContiguous(0);
169 402 : if( dd.Get_rank()!=0 ) {
170 145 : ActionToPutData* ap=plumed.getActionSet().selectWithLabel<ActionToPutData*>("Box");
171 145 : ap->noforce=true;
172 : }
173 402 : }
174 :
175 559880 : unsigned DomainDecomposition::getNumberOfAtoms() const {
176 559880 : if( inputs.size()==0 ) {
177 : return 0;
178 : }
179 559880 : return (inputs[0]->getPntrToValue())->getShape()[0];
180 : }
181 :
182 74756 : void DomainDecomposition::resetForStepStart() {
183 448536 : for(const auto & pp : inputs) {
184 373780 : pp->resetForStepStart();
185 : }
186 74756 : }
187 :
188 365729 : void DomainDecomposition::setStart( const std::string& name, const unsigned& sss) {
189 1080916 : for(const auto & pp : inputs) {
190 1080916 : if( pp->getLabel()==name ) {
191 365729 : pp->setStart(name, sss);
192 365729 : return;
193 : }
194 : }
195 0 : plumed_error();
196 : }
197 :
198 365729 : void DomainDecomposition::setStride( const std::string& name, const unsigned& sss) {
199 1080916 : for(const auto & pp : inputs) {
200 1080916 : if( pp->getLabel()==name ) {
201 365729 : pp->setStride(name, sss);
202 365729 : return;
203 : }
204 : }
205 0 : plumed_error();
206 : }
207 :
208 369751 : bool DomainDecomposition::setValuePointer( const std::string& name, const TypesafePtr & val ) {
209 369751 : wasset=true; // Once the domain decomposition stuff is transferred moved the setting of this to where the g2l vector is setup
210 1105042 : for(const auto & pp : inputs) {
211 1101023 : if( pp->setValuePointer( name, val ) ) {
212 : return true;
213 : }
214 : }
215 : return false;
216 : }
217 :
218 224262 : bool DomainDecomposition::setForcePointer( const std::string& name, const TypesafePtr & val ) {
219 448644 : for(const auto & pp : inputs) {
220 448614 : if( pp->setForcePointer( name, val ) ) {
221 : return true;
222 : }
223 : }
224 : return false;
225 : }
226 :
227 1544 : void DomainDecomposition::setAtomsNlocal(int n) {
228 1544 : gatindex.resize(n);
229 1544 : g2l.resize(getNumberOfAtoms(),-1);
230 1544 : if(dd) {
231 : // Since these vectors are sent with MPI by using e.g.
232 : // &dd.positionsToBeSent[0]
233 : // we make sure they are non-zero-sized so as to
234 : // avoid errors when doing boundary check
235 1518 : if(n==0) {
236 8 : n++;
237 : }
238 1518 : std::size_t nvals = inputs.size(), natoms = getNumberOfAtoms();
239 1518 : dd.positionsToBeSent.resize(n*nvals,0.0);
240 1518 : dd.positionsToBeReceived.resize(natoms*nvals,0.0);
241 1518 : dd.indexToBeSent.resize(n,0);
242 1518 : dd.indexToBeReceived.resize(natoms,0);
243 : }
244 1544 : }
245 :
246 990 : void DomainDecomposition::setAtomsGatindex(const TypesafePtr & g,bool fortran) {
247 990 : plumed_massert( g || gatindex.size()==0, "NULL gatindex pointer with non-zero local atoms");
248 990 : auto gg=g.get<const int*>({gatindex.size()});
249 990 : ddStep=getStep();
250 990 : if(fortran) {
251 22 : for(unsigned i=0; i<gatindex.size(); i++) {
252 20 : gatindex[i]=gg[i]-1;
253 : }
254 : } else {
255 22140 : for(unsigned i=0; i<gatindex.size(); i++) {
256 21152 : gatindex[i]=gg[i];
257 : }
258 : }
259 57552 : for(unsigned i=0; i<g2l.size(); i++) {
260 56562 : g2l[i]=-1;
261 : }
262 990 : if( gatindex.size()==getNumberOfAtoms() ) {
263 11 : shuffledAtoms=0;
264 1007 : for(unsigned i=0; i<gatindex.size(); i++) {
265 998 : if( gatindex[i]!=i ) {
266 2 : shuffledAtoms=1;
267 2 : break;
268 : }
269 : }
270 : } else {
271 979 : shuffledAtoms=1;
272 : }
273 990 : if(dd) {
274 964 : dd.Sum(shuffledAtoms);
275 : }
276 22162 : for(unsigned i=0; i<gatindex.size(); i++) {
277 21172 : g2l[gatindex[i]]=i;
278 : }
279 : // keep in unique only those atoms that are local
280 9908 : for(unsigned i=0; i<actions.size(); i++) {
281 8918 : actions[i]->unique_local_needs_update=true;
282 : }
283 : unique.clear();
284 : forced_unique.clear();
285 990 : }
286 :
287 554 : void DomainDecomposition::setAtomsContiguous(int start) {
288 554 : ddStep=plumed.getStep();
289 218327 : for(unsigned i=0; i<gatindex.size(); i++) {
290 217773 : gatindex[i]=start+i;
291 : }
292 230639 : for(unsigned i=0; i<g2l.size(); i++) {
293 230085 : g2l[i]=-1;
294 : }
295 218327 : for(unsigned i=0; i<gatindex.size(); i++) {
296 217773 : g2l[gatindex[i]]=i;
297 : }
298 554 : if(gatindex.size()<getNumberOfAtoms()) {
299 152 : shuffledAtoms=1;
300 : }
301 : // keep in unique only those atoms that are local
302 766 : for(unsigned i=0; i<actions.size(); i++) {
303 212 : actions[i]->unique_local_needs_update=true;
304 : }
305 : unique.clear();
306 : forced_unique.clear();
307 554 : }
308 :
309 1248 : void DomainDecomposition::shareAll() {
310 1248 : unique.clear();
311 1248 : forced_unique.clear();
312 1248 : int natoms = getNumberOfAtoms();
313 1248 : if( dd && shuffledAtoms>0 ) {
314 17616 : for(int i=0; i<natoms; ++i)
315 17430 : if( g2l[i]>=0 ) {
316 8003 : unique.push_back( AtomNumber::index(i) );
317 : }
318 : } else {
319 1062 : unique.resize(natoms);
320 5144483 : for(int i=0; i<natoms; i++) {
321 5143421 : unique[i]=AtomNumber::index(i);
322 : }
323 : }
324 1248 : forced_unique.resize( unique.size() );
325 5152672 : for(unsigned i=0; i<unique.size(); ++i) {
326 5151424 : forced_unique[i] = unique[i];
327 : }
328 1248 : share(unique);
329 1232 : }
330 :
331 73144 : void DomainDecomposition::share() {
332 : // We can no longer set the pointers after the share
333 : bool atomsNeeded=false;
334 438864 : for(const auto & pp : inputs) {
335 365720 : pp->share();
336 : }
337 : // At first step I scatter all the atoms so as to store their mass and charge
338 : // Notice that this works with the assumption that charges and masses are
339 : // not changing during the simulation!
340 73144 : if( firststep ) {
341 1134 : actions = plumed.getActionSet().select<ActionAtomistic*>();
342 1134 : shareAll();
343 1118 : return;
344 : }
345 :
346 72010 : if(getenvForceUnique()==Option::automatic) {
347 : unsigned largest=0;
348 728346 : for(unsigned i=0; i<actions.size(); i++) {
349 656336 : if(actions[i]->isActive()) {
350 : auto l=actions[i]->getUnique().size();
351 451068 : if(l>largest) {
352 76455 : largest=l;
353 : }
354 : }
355 : }
356 72010 : if(largest*2<getNumberOfAtoms()) {
357 23647 : unique_serial=true;
358 : } else {
359 48363 : unique_serial=false;
360 : }
361 0 : } else if(getenvForceUnique()==Option::yes) {
362 0 : unique_serial=true;
363 0 : } else if(getenvForceUnique()==Option::no) {
364 0 : unique_serial=false;
365 : } else {
366 0 : plumed_error();
367 : }
368 :
369 72010 : if(unique_serial || !(int(gatindex.size())==getNumberOfAtoms() && shuffledAtoms==0)) {
370 289091 : for(unsigned i=0; i<actions.size(); i++) {
371 265056 : if( actions[i]->unique_local_needs_update ) {
372 265786 : actions[i]->updateUniqueLocal( !(dd && shuffledAtoms>0), g2l );
373 : }
374 : }
375 : // Now reset unique for the new step
376 : gch::small_vector<const std::vector<AtomNumber>*,32> forced_vectors;
377 : gch::small_vector<const std::vector<AtomNumber>*,32> nonforced_vectors;
378 : forced_vectors.reserve(actions.size());
379 : nonforced_vectors.reserve(actions.size());
380 289091 : for(unsigned i=0; i<actions.size(); i++) {
381 265056 : if(actions[i]->isActive()) {
382 192031 : if(!actions[i]->getUnique().empty()) {
383 : // unique are the local atoms
384 106738 : if( actions[i]->actionHasForces() ) {
385 191054 : forced_vectors.push_back(&actions[i]->getUniqueLocal());
386 : } else {
387 22422 : nonforced_vectors.push_back(&actions[i]->getUniqueLocal());
388 : }
389 : }
390 : }
391 : }
392 24035 : if( !(forced_vectors.empty() && nonforced_vectors.empty()) ) {
393 : atomsNeeded=true;
394 : }
395 : // Merge the atoms from the atoms that have a force on
396 24035 : unique.clear();
397 24035 : forced_unique.clear();
398 24035 : mergeVectorTools::mergeSortedVectors(forced_vectors,forced_unique);
399 : // Merge all the atoms
400 24035 : nonforced_vectors.push_back( &forced_unique );
401 24035 : mergeVectorTools::mergeSortedVectors(nonforced_vectors,unique);
402 : } else {
403 439255 : for(unsigned i=0; i<actions.size(); i++) {
404 391280 : if(actions[i]->isActive()) {
405 259037 : if(!actions[i]->getUnique().empty()) {
406 : atomsNeeded=true;
407 : }
408 : }
409 : }
410 : }
411 :
412 : // Now we retrieve the atom numbers we need
413 72010 : if( atomsNeeded ) {
414 71305 : share( unique );
415 : }
416 : }
417 :
418 72553 : void DomainDecomposition::share(const std::vector<AtomNumber>& unique) {
419 : // This retrieves what values we need to get
420 : int ndata=0;
421 : std::vector<Value*> values_to_get;
422 72553 : if(!(int(gatindex.size())==getNumberOfAtoms() && shuffledAtoms==0)) {
423 2214 : uniq_index.resize(unique.size());
424 32727 : for(unsigned i=0; i<unique.size(); i++) {
425 30513 : uniq_index[i]=g2l[unique[i].index()];
426 : }
427 13284 : for(const auto & ip : inputs) {
428 11070 : if( (!ip->fixed || firststep) && ip->wasset ) {
429 6788 : (ip->mydata)->share_data( unique, uniq_index, ip->copyOutput(0) );
430 6788 : values_to_get.push_back(ip->copyOutput(0));
431 6788 : ndata++;
432 : }
433 : }
434 70339 : } else if( unique_serial) {
435 21304 : uniq_index.resize(unique.size());
436 801395 : for(unsigned i=0; i<unique.size(); i++) {
437 780091 : uniq_index[i]=unique[i].index();
438 : }
439 127824 : for(const auto & ip : inputs) {
440 106520 : if( (!ip->fixed || firststep) && ip->wasset ) {
441 63912 : (ip->mydata)->share_data( unique, uniq_index, ip->copyOutput(0) );
442 63912 : values_to_get.push_back(ip->copyOutput(0));
443 63912 : ndata++;
444 : }
445 : }
446 : } else {
447 : // faster version, which retrieves all atoms
448 294130 : for(const auto & ip : inputs) {
449 245111 : if( (!ip->fixed || firststep) && ip->wasset ) {
450 148945 : (ip->mydata)->share_data( 0, getNumberOfAtoms(), ip->copyOutput(0) );
451 148929 : values_to_get.push_back(ip->copyOutput(0));
452 148929 : ndata++;
453 : }
454 : }
455 : }
456 :
457 72537 : if(dd && shuffledAtoms>0) {
458 2188 : if(dd.async) {
459 9598 : for(unsigned i=0; i<dd.mpi_request_positions.size(); i++) {
460 7430 : dd.mpi_request_positions[i].wait();
461 : }
462 9598 : for(unsigned i=0; i<dd.mpi_request_index.size(); i++) {
463 7430 : dd.mpi_request_index[i].wait();
464 : }
465 : }
466 :
467 2188 : int count=0;
468 32623 : for(const auto & p : unique) {
469 30435 : dd.indexToBeSent[count]=p.index();
470 : int dpoint=0;
471 126694 : for(unsigned i=0; i<values_to_get.size(); ++i) {
472 96259 : dd.positionsToBeSent[ndata*count+dpoint]=values_to_get[i]->get(p.index());
473 96259 : dpoint++;
474 : }
475 30435 : count++;
476 : }
477 :
478 2188 : if(dd.async) {
479 2168 : asyncSent=true;
480 2168 : dd.mpi_request_positions.resize(dd.Get_size());
481 2168 : dd.mpi_request_index.resize(dd.Get_size());
482 9802 : for(int i=0; i<dd.Get_size(); i++) {
483 7634 : dd.mpi_request_index[i]=dd.Isend(&dd.indexToBeSent[0],count,i,666);
484 7634 : dd.mpi_request_positions[i]=dd.Isend(&dd.positionsToBeSent[0],ndata*count,i,667);
485 : }
486 : } else {
487 20 : const int n=(dd.Get_size());
488 36 : std::vector<int> counts(n);
489 20 : std::vector<int> displ(n);
490 20 : std::vector<int> counts5(n);
491 20 : std::vector<int> displ5(n);
492 20 : dd.Allgather(count,counts);
493 20 : displ[0]=0;
494 80 : for(int i=1; i<n; ++i) {
495 60 : displ[i]=displ[i-1]+counts[i-1];
496 : }
497 100 : for(int i=0; i<n; ++i) {
498 80 : counts5[i]=counts[i]*ndata;
499 : }
500 100 : for(int i=0; i<n; ++i) {
501 80 : displ5[i]=displ[i]*ndata;
502 : }
503 20 : dd.Allgatherv(&dd.indexToBeSent[0],count,&dd.indexToBeReceived[0],&counts[0],&displ[0]);
504 20 : dd.Allgatherv(&dd.positionsToBeSent[0],ndata*count,&dd.positionsToBeReceived[0],&counts5[0],&displ5[0]);
505 20 : int tot=displ[n-1]+counts[n-1];
506 1620 : for(int i=0; i<tot; i++) {
507 : int dpoint=0;
508 7264 : for(unsigned j=0; j<values_to_get.size(); ++j) {
509 5664 : values_to_get[j]->data[dd.indexToBeReceived[i]] = dd.positionsToBeReceived[ndata*i+dpoint];
510 5664 : dpoint++;
511 : }
512 : }
513 : }
514 : }
515 72537 : }
516 :
517 72529 : void DomainDecomposition::wait() {
518 435174 : for(const auto & ip : inputs) {
519 362645 : ip->dataCanBeSet=false;
520 : }
521 :
522 72529 : if(dd && shuffledAtoms>0) {
523 : int ndata=0;
524 : std::vector<Value*> values_to_set;
525 13128 : for(const auto & ip : inputs) {
526 10940 : if( (!ip->fixed || firststep) && ip->wasset ) {
527 6708 : values_to_set.push_back(ip->copyOutput(0));
528 6708 : ndata++;
529 : }
530 : }
531 :
532 : // receive toBeReceived
533 2188 : if(asyncSent) {
534 : Communicator::Status status;
535 : std::size_t count=0;
536 9802 : for(int i=0; i<dd.Get_size(); i++) {
537 7634 : dd.Recv(&dd.indexToBeReceived[count],dd.indexToBeReceived.size()-count,i,666,status);
538 7634 : int c=status.Get_count<int>();
539 7634 : dd.Recv(&dd.positionsToBeReceived[ndata*count],dd.positionsToBeReceived.size()-ndata*count,i,667);
540 7634 : count+=c;
541 : }
542 68220 : for(int i=0; i<count; i++) {
543 : int dpoint=0;
544 276100 : for(unsigned j=0; j<values_to_set.size(); ++j) {
545 210048 : values_to_set[j]->set(dd.indexToBeReceived[i], dd.positionsToBeReceived[ndata*i+dpoint] );
546 210048 : dpoint++;
547 : }
548 : }
549 2168 : asyncSent=false;
550 : }
551 : }
552 72529 : }
553 :
554 0 : unsigned DomainDecomposition::getNumberOfForcesToRescale() const {
555 0 : return gatindex.size();
556 : }
557 :
558 72405 : void DomainDecomposition::apply() {
559 72405 : std::vector<unsigned> forced_uniq_index(forced_unique.size());
560 72405 : if(!(int(gatindex.size())==getNumberOfAtoms() && shuffledAtoms==0)) {
561 24181 : for(unsigned i=0; i<forced_unique.size(); i++) {
562 22081 : forced_uniq_index[i]=g2l[forced_unique[i].index()];
563 : }
564 : } else {
565 4189154 : for(unsigned i=0; i<forced_unique.size(); i++) {
566 4118849 : forced_uniq_index[i]=forced_unique[i].index();
567 : }
568 : }
569 434420 : for(const auto & ip : inputs) {
570 362017 : if( !(ip->getPntrToValue())->forcesWereAdded() || ip->noforce ) {
571 213644 : continue;
572 148373 : } else if( ip->wasscaled || (!unique_serial && int(gatindex.size())==getNumberOfAtoms() && shuffledAtoms==0) ) {
573 85784 : (ip->mydata)->add_force( gatindex, ip->getPntrToValue() );
574 : } else {
575 62589 : (ip->mydata)->add_force( forced_unique, forced_uniq_index, ip->getPntrToValue() );
576 : }
577 : }
578 72403 : }
579 :
580 72403 : void DomainDecomposition::reset() {
581 72403 : if( !unique_serial && int(gatindex.size())==getNumberOfAtoms() && shuffledAtoms==0 ) {
582 : return;
583 : }
584 : // This is an optimisation to ensure that we don't call std::fill over the whole forces
585 : // array if there are a small number of atoms passed between the MD code and PLUMED
586 23394 : if( dd && shuffledAtoms>0 ) {
587 2074 : getAllActiveAtoms( unique );
588 : }
589 140364 : for(const auto & ip : inputs) {
590 116970 : (ip->copyOutput(0))->clearInputForce( unique );
591 : }
592 : }
593 :
594 114 : void DomainDecomposition::writeBinary(std::ostream&o) {
595 684 : for(const auto & ip : inputs) {
596 570 : ip->writeBinary(o);
597 : }
598 114 : }
599 :
600 114 : void DomainDecomposition::readBinary(std::istream&i) {
601 684 : for(const auto & ip : inputs) {
602 570 : ip->readBinary(i);
603 : }
604 114 : }
605 :
606 68721 : void DomainDecomposition::broadcastToDomains( Value* val ) {
607 68721 : if( dd ) {
608 20496 : dd.Bcast( val->data, 0 );
609 : }
610 68721 : }
611 :
612 3989 : void DomainDecomposition::sumOverDomains( Value* val ) {
613 3989 : if( dd && shuffledAtoms>0 ) {
614 0 : dd.Sum( val->data );
615 : }
616 3989 : }
617 :
618 900 : const long int& DomainDecomposition::getDdStep() const {
619 900 : return ddStep;
620 : }
621 :
622 459 : const std::vector<int>& DomainDecomposition::getGatindex() const {
623 459 : return gatindex;
624 : }
625 :
626 2183 : void DomainDecomposition::getAllActiveAtoms( std::vector<AtomNumber>& u ) {
627 : gch::small_vector<const std::vector<AtomNumber>*,32> vectors;
628 : vectors.reserve(actions.size());
629 21016 : for(unsigned i=0; i<actions.size(); i++) {
630 18833 : if(actions[i]->isActive()) {
631 13033 : if(!actions[i]->getUnique().empty()) {
632 : // unique are the local atoms
633 22912 : vectors.push_back(&actions[i]->getUnique());
634 : }
635 : }
636 : }
637 : u.clear();
638 2183 : mergeVectorTools::mergeSortedVectors(vectors,u);
639 2183 : }
640 :
641 116 : void DomainDecomposition::createFullList(const TypesafePtr & n) {
642 116 : if( firststep ) {
643 7 : int natoms = getNumberOfAtoms();
644 7 : n.set(int(natoms));
645 7 : fullList.resize(natoms);
646 803 : for(unsigned i=0; i<natoms; i++) {
647 796 : fullList[i]=i;
648 : }
649 : } else {
650 : // We update here the unique list defined at Atoms::unique.
651 : // This is not very clear, and probably should be coded differently.
652 : // Hopefully this fix the longstanding issue with NAMD.
653 109 : getAllActiveAtoms( unique );
654 109 : fullList.clear();
655 109 : fullList.reserve(unique.size());
656 5012 : for(const auto & p : unique) {
657 4903 : fullList.push_back(p.index());
658 : }
659 109 : n.set(int(fullList.size()));
660 : }
661 116 : }
662 :
663 116 : void DomainDecomposition::getFullList(const TypesafePtr & x) {
664 : auto xx=x.template get<const int**>();
665 116 : if(!fullList.empty()) {
666 110 : *xx=&fullList[0];
667 : } else {
668 6 : *xx=NULL;
669 : }
670 116 : }
671 :
672 116 : void DomainDecomposition::clearFullList() {
673 116 : fullList.resize(0);
674 116 : }
675 :
676 : }
|