diff --git a/src/cltools/Driver.cpp b/src/cltools/Driver.cpp index 48ddf54b129290574629eae386d1a9c1601a4afe..aa8a9deaffc4b98d096eeabf4062268577a2c372 100644 --- a/src/cltools/Driver.cpp +++ b/src/cltools/Driver.cpp @@ -691,7 +691,7 @@ int Driver<real>::main(FILE* in,FILE*out,Communicator& pc) { } } if(intracomm.Get_rank()==0) { - fprintf(out,"\nDRIVER: Reassigning particle decomposition\n"); + fprintf(out,"\nDRIVER: Reassigning domain decomposition\n"); } p.cmd("setAtomsNlocal",&dd_nlocal); p.cmd("setAtomsGatindex",&dd_gatindex[0]); diff --git a/src/core/ActionAtomistic.cpp b/src/core/ActionAtomistic.cpp index 29ed09e3da598535da09afe9e7af4ac27014ce26..db3c257ff7ab8b4336258334bcded1aed1eb9b99 100644 --- a/src/core/ActionAtomistic.cpp +++ b/src/core/ActionAtomistic.cpp @@ -77,7 +77,8 @@ void ActionAtomistic::requestAtoms(const vector<AtomNumber> & a) { // only real atoms are requested to lower level Atoms class else unique.insert(indexes[i]); } - + updateUniqueLocal(); + atoms.unique.clear(); } Vector ActionAtomistic::pbcDistance(const Vector &v1,const Vector &v2)const { @@ -282,4 +283,15 @@ void ActionAtomistic::makeWhole() { } } +void ActionAtomistic::updateUniqueLocal() { + unique_local.clear(); + if(atoms.dd && atoms.shuffledAtoms>0) { + for(auto pp=unique.begin(); pp!=unique.end(); ++pp) { + if(atoms.dd.g2l[pp->index()]>=0) unique_local.insert(*pp); + } + } else { + unique_local.insert(unique.begin(),unique.end()); + } +} + } diff --git a/src/core/ActionAtomistic.h b/src/core/ActionAtomistic.h index cec10f26877788041f0e7931e5951b2e1ed437df..7269a0a1b0d2ae045fbfcfe7bbdd0fdc35501ee8 100644 --- a/src/core/ActionAtomistic.h +++ b/src/core/ActionAtomistic.h @@ -41,7 +41,10 @@ class ActionAtomistic : { std::vector<AtomNumber> indexes; // the set of needed atoms +/// unique should be an ordered set since we later create a vector containing the corresponding indexes std::set<AtomNumber> unique; +/// unique_local should be an ordered set since we later create a vector containing the corresponding indexes + std::set<AtomNumber> unique_local; std::vector<Vector> positions; // positions of the needed atoms double energy; Pbc& pbc; @@ -146,6 +149,8 @@ public: void makeWhole(); /// Allow calls to modifyGlobalForce() void allowToAccessGlobalForces() {atoms.zeroallforces=true;} +/// updates local unique atoms + void updateUniqueLocal(); public: // virtual functions: @@ -169,6 +174,7 @@ public: void lockRequests(); void unlockRequests(); const std::set<AtomNumber> & getUnique()const; + const std::set<AtomNumber> & getUniqueLocal()const; /// Read in an input file containing atom positions and calculate the action for the atomic /// configuration therin void readAtomsFromPDB( const PDB& pdb ); @@ -270,6 +276,11 @@ const std::set<AtomNumber> & ActionAtomistic::getUnique()const { return unique; } +inline +const std::set<AtomNumber> & ActionAtomistic::getUniqueLocal()const { + return unique_local; +} + inline unsigned ActionAtomistic::getTotAtoms()const { return atoms.positions.size(); diff --git a/src/core/Atoms.cpp b/src/core/Atoms.cpp index 674fad203228e3071294c10808081f1b9f4db125..ac71203737b445d1bc2725d2984a7301c809a015 100644 --- a/src/core/Atoms.cpp +++ b/src/core/Atoms.cpp @@ -108,7 +108,6 @@ void Atoms::setCharges(void*p) { void Atoms::setVirial(void*p) { plumed_massert( dataCanBeSet,"setVirial must be called after setStep in MD code interface"); mdatoms->setVirial(p); virialHasBeenSet=true; - } void Atoms::setEnergy(void*p) { @@ -138,7 +137,6 @@ void Atoms::setForces(void*p,int i) { } void Atoms::share() { - std::set<AtomNumber> unique; // At first step I scatter all the atoms so as to store their mass and charge // Notice that this works with the assumption that charges and masses are // not changing during the simulation! @@ -146,46 +144,74 @@ void Atoms::share() { shareAll(); return; } - for(unsigned i=0; i<actions.size(); i++) if(actions[i]->isActive()) { - if(dd && shuffledAtoms>0) { - unique.insert(actions[i]->getUnique().begin(),actions[i]->getUnique().end()); + + if(!(int(gatindex.size())==natoms && shuffledAtoms==0)) { + for(unsigned i=0; i<actions.size(); i++) { + if(actions[i]->isActive()) { + if(!actions[i]->getUnique().empty()) { + atomsNeeded=true; + // unique are the local atoms + unique.insert(actions[i]->getUniqueLocal().begin(),actions[i]->getUniqueLocal().end()); + } + } + } + } else { + for(unsigned i=0; i<actions.size(); i++) { + if(actions[i]->isActive()) { + if(!actions[i]->getUnique().empty()) { + atomsNeeded=true; + } } - if(!actions[i]->getUnique().empty()) atomsNeeded=true; } + + } + share(unique); } void Atoms::shareAll() { - std::set<AtomNumber> unique; - if(dd && shuffledAtoms>0) + unique.clear(); + // keep in unique only those atoms that are local + if(dd && shuffledAtoms>0) { + for(int i=0; i<natoms; i++) if(dd.g2l[i]>=0) unique.insert(AtomNumber::index(i)); + } else { for(int i=0; i<natoms; i++) unique.insert(AtomNumber::index(i)); + } atomsNeeded=true; share(unique); } void Atoms::share(const std::set<AtomNumber>& unique) { plumed_assert( positionsHaveBeenSet==3 && massesHaveBeenSet ); + virial.zero(); if(zeroallforces || int(gatindex.size())==natoms) { for(int i=0; i<natoms; i++) forces[i].zero(); } else { - for(unsigned i=0; i<gatindex.size(); i++) forces[gatindex[i]].zero(); + for(const auto & p : unique) forces[p.index()].zero(); } for(unsigned i=getNatoms(); i<positions.size(); i++) forces[i].zero(); // virtual atoms forceOnEnergy=0.0; mdatoms->getBox(box); if(!atomsNeeded) return; - atomsNeeded=false; if(int(gatindex.size())==natoms && shuffledAtoms==0) { // faster version, which retrieves all atoms mdatoms->getPositions(0,natoms,positions); } else { -// version that picks only atoms that are available on this proc - mdatoms->getPositions(gatindex,positions); + uniq_index.clear(); + uniq_index.reserve(unique.size()); + if(dd && shuffledAtoms>0) { + for(const auto & p : unique) uniq_index.push_back(dd.g2l[p.index()]); + } else { + for(const auto & p : unique) uniq_index.push_back(p.index()); + } + mdatoms->getPositions(unique,uniq_index,positions); } + + // how many double per atom should be scattered: int ndata=3; if(!massAndChargeOK) { @@ -203,17 +229,15 @@ void Atoms::share(const std::set<AtomNumber>& unique) { } int count=0; for(const auto & p : unique) { - if(dd.g2l[p.index()]>=0) { - dd.indexToBeSent[count]=p.index(); - dd.positionsToBeSent[ndata*count+0]=positions[p.index()][0]; - dd.positionsToBeSent[ndata*count+1]=positions[p.index()][1]; - dd.positionsToBeSent[ndata*count+2]=positions[p.index()][2]; - if(!massAndChargeOK) { - dd.positionsToBeSent[ndata*count+3]=masses[p.index()]; - dd.positionsToBeSent[ndata*count+4]=charges[p.index()]; - } - count++; + dd.indexToBeSent[count]=p.index(); + dd.positionsToBeSent[ndata*count+0]=positions[p.index()][0]; + dd.positionsToBeSent[ndata*count+1]=positions[p.index()][1]; + dd.positionsToBeSent[ndata*count+2]=positions[p.index()][2]; + if(!massAndChargeOK) { + dd.positionsToBeSent[ndata*count+3]=masses[p.index()]; + dd.positionsToBeSent[ndata*count+4]=charges[p.index()]; } + count++; } if(dd.async) { asyncSent=true; @@ -297,8 +321,11 @@ void Atoms::updateForces() { if(forceOnEnergy*forceOnEnergy>epsilon) { double alpha=1.0-forceOnEnergy; mdatoms->rescaleForces(gatindex,alpha); + mdatoms->updateForces(gatindex,forces); + } else { + if(int(gatindex.size())==natoms && shuffledAtoms==0) mdatoms->updateForces(gatindex,forces); + else mdatoms->updateForces(unique,uniq_index,forces); } - mdatoms->updateForces(gatindex,forces); if( !plumed.novirial && dd.Get_rank()==0 ) { plumed_assert( virialHasBeenSet ); mdatoms->updateVirial(virial); @@ -316,11 +343,11 @@ void Atoms::setNatoms(int n) { } -void Atoms::add(const ActionAtomistic*a) { +void Atoms::add(ActionAtomistic*a) { actions.push_back(a); } -void Atoms::remove(const ActionAtomistic*a) { +void Atoms::remove(ActionAtomistic*a) { auto f=find(actions.begin(),actions.end(),a); plumed_massert(f!=actions.end(),"cannot remove an action registered to atoms"); actions.erase(f); @@ -376,6 +403,12 @@ void Atoms::setAtomsGatindex(int*g,bool fortran) { dd.Sum(shuffledAtoms); for(unsigned i=0; i<gatindex.size(); i++) dd.g2l[gatindex[i]]=i; } + + for(unsigned i=0; i<actions.size(); i++) { + // keep in unique only those atoms that are local + actions[i]->updateUniqueLocal(); + } + unique.clear(); } void Atoms::setAtomsContiguous(int start) { @@ -384,6 +417,11 @@ void Atoms::setAtomsContiguous(int start) { for(unsigned i=0; i<dd.g2l.size(); i++) dd.g2l[i]=-1; if(dd) for(unsigned i=0; i<gatindex.size(); i++) dd.g2l[gatindex[i]]=i; if(gatindex.size()<natoms) shuffledAtoms=1; + for(unsigned i=0; i<actions.size(); i++) { + // keep in unique only those atoms that are local + actions[i]->updateUniqueLocal(); + } + unique.clear(); } void Atoms::setRealPrecision(int p) { diff --git a/src/core/Atoms.h b/src/core/Atoms.h index e9cccf399fe87a7ac6b33f9a9e133da4a964c86f..85d3940f91607909619dc3bb882f3c7a966071ac 100644 --- a/src/core/Atoms.h +++ b/src/core/Atoms.h @@ -47,6 +47,8 @@ class Atoms friend class ActionAtomistic; friend class ActionWithVirtualAtom; int natoms; + std::set<AtomNumber> unique; + std::vector<unsigned> uniq_index; std::vector<Vector> positions; std::vector<Vector> forces; std::vector<double> masses; @@ -98,7 +100,7 @@ class Atoms double kbT; - std::vector<const ActionAtomistic*> actions; + std::vector<ActionAtomistic*> actions; std::vector<int> gatindex; bool asyncSent; @@ -191,8 +193,8 @@ public: void getFullList(int**); void clearFullList(); - void add(const ActionAtomistic*); - void remove(const ActionAtomistic*); + void add(ActionAtomistic*); + void remove(ActionAtomistic*); double getEnergy()const {plumed_assert(collectEnergy && energyHasBeenSet); return energy;} diff --git a/src/core/MDAtoms.cpp b/src/core/MDAtoms.cpp index 370527d2fbccffe344174ecf586990b6fa7f1b3a..b0de91532978e27b87d1b4f5b219de8e160c1b1d 100644 --- a/src/core/MDAtoms.cpp +++ b/src/core/MDAtoms.cpp @@ -70,12 +70,14 @@ public: } void getBox(Tensor &)const; void getPositions(const vector<int>&index,vector<Vector>&positions)const; + void getPositions(const std::set<AtomNumber>&index,const vector<unsigned>&i,vector<Vector>&positions)const; void getPositions(unsigned j,unsigned k,vector<Vector>&positions)const; void getLocalPositions(std::vector<Vector>&p)const; void getMasses(const vector<int>&index,vector<double>&)const; void getCharges(const vector<int>&index,vector<double>&)const; void updateVirial(const Tensor&)const; void updateForces(const vector<int>&index,const vector<Vector>&); + void updateForces(const std::set<AtomNumber>&index,const vector<unsigned>&i,const vector<Vector>&forces); void rescaleForces(const vector<int>&index,double factor); unsigned getRealPrecision()const; }; @@ -112,6 +114,18 @@ void MDAtomsTyped<T>::getPositions(const vector<int>&index,vector<Vector>&positi } } +template <class T> +void MDAtomsTyped<T>::getPositions(const std::set<AtomNumber>&index,const vector<unsigned>&i, vector<Vector>&positions)const { +// cannot be parallelized with omp because access to positions is not ordered + unsigned k=0; + for(const auto & p : index) { + positions[p.index()][0]=px[stride*i[k]]*scalep; + positions[p.index()][1]=py[stride*i[k]]*scalep; + positions[p.index()][2]=pz[stride*i[k]]*scalep; + k++; + } +} + template <class T> void MDAtomsTyped<T>::getPositions(unsigned j,unsigned k,vector<Vector>&positions)const { #pragma omp parallel for num_threads(OpenMP::getGoodNumThreads(&positions[j],(k-j))) @@ -151,6 +165,17 @@ void MDAtomsTyped<T>::updateVirial(const Tensor&virial)const { if(this->virial) for(int i=0; i<3; i++)for(int j=0; j<3; j++) this->virial[3*i+j]+=T(virial(i,j)*scalev); } +template <class T> +void MDAtomsTyped<T>::updateForces(const std::set<AtomNumber>&index,const vector<unsigned>&i,const vector<Vector>&forces) { + unsigned k=0; + for(const auto & p : index) { + fx[stride*i[k]]+=scalef*T(forces[p.index()][0]); + fy[stride*i[k]]+=scalef*T(forces[p.index()][1]); + fz[stride*i[k]]+=scalef*T(forces[p.index()][2]); + k++; + } +} + template <class T> void MDAtomsTyped<T>::updateForces(const vector<int>&index,const vector<Vector>&forces) { #pragma omp parallel for num_threads(OpenMP::getGoodNumThreads(fx,stride*index.size())) diff --git a/src/core/MDAtoms.h b/src/core/MDAtoms.h index 3926d47f13d004e8edb51dd1d14e9e3f3bbe9003..03e8c29c3648cdc13099a662d2bbf8a7d34f6e9a 100644 --- a/src/core/MDAtoms.h +++ b/src/core/MDAtoms.h @@ -24,7 +24,9 @@ #include "tools/Tensor.h" #include "tools/Vector.h" +#include "tools/AtomNumber.h" #include <vector> +#include <set> #include "tools/Units.h" namespace PLMD { @@ -85,6 +87,8 @@ public: virtual void getPositions(const std::vector<int>&index,std::vector<Vector>&p)const=0; /// Retrieve all atom positions from index i to index j. virtual void getPositions(unsigned i,unsigned j,std::vector<Vector>&p)const=0; +/// Retrieve all atom positions from atom indices and local indices. + virtual void getPositions(const std::set<AtomNumber>&index,const std::vector<unsigned>&i,std::vector<Vector>&p)const=0; /// Retrieve selected masses. /// The operation is done in such a way that m[index[i]] is equal to the mass of atom i virtual void getMasses(const std::vector<int>&index,std::vector<double>&m)const=0; @@ -98,6 +102,9 @@ public: /// Increment the force on selected atoms. /// The operation is done in such a way that f[index[i]] is added to the force on atom i virtual void updateForces(const std::vector<int>&index,const std::vector<Vector>&f)=0; +/// Increment the force on selected atoms. +/// The operation is done only for local atoms used in an action + virtual void updateForces(const std::set<AtomNumber>&index,const std::vector<unsigned>&i,const std::vector<Vector>&forces)=0; /// Rescale all the forces, including the virial. /// It is applied to all atoms with local index going from 0 to index.size()-1 virtual void rescaleForces(const std::vector<int>&index,double factor)=0; diff --git a/src/core/PlumedMain.cpp b/src/core/PlumedMain.cpp index dd96581db44c5e97ab805e445481a82d62ac8e8b..0c5ee14150f571c0d9985529af443466603de1f4 100644 --- a/src/core/PlumedMain.cpp +++ b/src/core/PlumedMain.cpp @@ -595,8 +595,6 @@ void PlumedMain::prepareDependencies() { // First switch off all actions for(const auto & p : actionSet) { p->deactivate(); - //I think this is already done inside deactivate - //(*p)->clearOptions(); } // for optimization, an "active" flag remains false if no action at all is active