LCOV - code coverage report
Current view: top level - core - DomainDecomposition.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 264 280 94.3 %
Date: 2024-10-18 13:59:31 Functions: 31 33 93.9 %

          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         734 : Option interpretEnvString(const char* env,const char* str) {
      48         734 :   if(!str) return Option::automatic;
      49           0 :   if(!std::strcmp(str,"yes"))return Option::yes;
      50           0 :   if(!std::strcmp(str,"no"))return Option::no;
      51           0 :   if(!std::strcmp(str,"auto"))return Option::automatic;
      52           0 :   plumed_error()<<"Cannot understand env var "<<env<<"\nPossible values: yes/no/auto\nActual value: "<<str;
      53             : }
      54             : 
      55             : /// Use unique list of atoms to manipulate forces and positions.
      56             : /// A unique list of atoms is used to manipulate forces and positions in MPI parallel runs.
      57             : /// In serial runs, this is done if convenient. The code currently contain
      58             : /// some heuristic to decide if the unique list should be used or not.
      59             : /// An env var can be used to override this decision.
      60             : /// export PLUMED_FORCE_UNIQUE=yes  # enforce using the unique list in serial runs
      61             : /// export PLUMED_FORCE_UNIQUE=no   # enforce not using the unique list in serial runs
      62             : /// export PLUMED_FORCE_UNIQUE=auto # choose heuristically
      63             : /// default: auto
      64       69919 : Option getenvForceUnique() {
      65             :   static const char* name="PLUMED_FORCE_UNIQUE";
      66       69919 :   static const auto opt = interpretEnvString(name,std::getenv(name));
      67       69919 :   return opt;
      68             : }
      69             : 
      70             : }
      71             : 
      72             : PLUMED_REGISTER_ACTION(DomainDecomposition,"DOMAIN_DECOMPOSITION")
      73             : 
      74         393 : void DomainDecomposition::DomainComms::enable(Communicator& c) {
      75         393 :   on=true;
      76         393 :   Set_comm(c.Get_comm());
      77         393 :   async=Get_size()<10;
      78         393 :   if(std::getenv("PLUMED_ASYNC_SHARE")) {
      79           4 :     std::string s(std::getenv("PLUMED_ASYNC_SHARE"));
      80           4 :     if(s=="yes") async=true;
      81           4 :     else if(s=="no") async=false;
      82           0 :     else plumed_merror("PLUMED_ASYNC_SHARE variable is set to " + s + "; should be yes or no");
      83             :   }
      84         393 : }
      85             : 
      86        1197 : void DomainDecomposition::registerKeywords(Keywords& keys) {
      87        1197 :   ActionForInterface::registerKeywords( keys ); keys.remove("ROLE");
      88        2394 :   keys.add("compulsory","NATOMS","the number of atoms across all the domains");
      89        2394 :   keys.add("numbered","VALUE","value to create for the domain decomposition");
      90        2394 :   keys.add("numbered","UNIT","unit of the ith value that is being passed through the domain decomposition");
      91        2394 :   keys.add("numbered","CONSTANT","is the ith value that is being passed through the domain decomposition constant");
      92        2394 :   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 "
      93             :            "is not periodic you must state this using PERIODIC=NO.  Positions are passed with PERIODIC=NO even though special methods are used "
      94             :            "to deal with pbc");
      95        2394 :   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");
      96        2394 :   keys.add("compulsory","PBCLABEL","Box","the label to use for the PBC action that will be created");
      97        2394 :   keys.setValueDescription("vector","the domain that each atom is within");
      98        1197 : }
      99             : 
     100        1195 : DomainDecomposition::DomainDecomposition(const ActionOptions&ao):
     101             :   Action(ao),
     102             :   ActionForInterface(ao),
     103        1195 :   ddStep(0),
     104        1195 :   shuffledAtoms(0),
     105        1195 :   asyncSent(false),
     106        1195 :   unique_serial(false)
     107             : {
     108             :   // Read in the number of atoms
     109        2390 :   int natoms; parse("NATOMS",natoms);
     110        1195 :   std::string str_natoms; Tools::convert( natoms, str_natoms );
     111             :   // Setup the gat index
     112     9190732 :   gatindex.resize(natoms); for(unsigned i=0; i<gatindex.size(); i++) gatindex[i]=i;
     113             :   // Now read in the values we are creating here
     114        5975 :   for(unsigned i=1;; ++i) {
     115             :     std::string valname;
     116       14340 :     if( !parseNumbered("VALUE",i,valname) ) break;
     117       11950 :     std::string unit; parseNumbered("UNIT",i,unit);
     118       11950 :     std::string constant; parseNumbered("CONSTANT",i,constant);
     119       11950 :     std::string period; parseNumbered("PERIODIC",i,period);
     120       11950 :     std::string role; parseNumbered("ROLE",i,role);
     121        8365 :     if( constant=="True") plumed.readInputLine( valname + ": PUT FROM_DOMAINS CONSTANT SHAPE=" + str_natoms + " UNIT=" + unit + " PERIODIC=" + period + " ROLE=" + role, true );
     122        7170 :     else if( constant=="False") plumed.readInputLine( valname + ": PUT FROM_DOMAINS SHAPE=" + str_natoms + " UNIT=" + unit + " PERIODIC=" + period + " ROLE=" + role, true );
     123           0 :     else plumed_merror("missing information on whether value is constant");
     124             :     // And save the list of values that are set from here
     125        5975 :     ActionToPutData* ap=plumed.getActionSet().selectWithLabel<ActionToPutData*>(valname); ap->addDependency( this ); inputs.push_back( ap );
     126        5975 :   }
     127        2390 :   std::string pbclabel; parse("PBCLABEL",pbclabel); plumed.readInputLine(pbclabel + ": PBC",true);
     128             :   // Turn on the domain decomposition
     129        1195 :   if( Communicator::initialized() ) Set_comm(comm);
     130        1195 : }
     131             : 
     132         393 : void DomainDecomposition::Set_comm(Communicator& comm) {
     133         393 :   dd.enable(comm); setAtomsNlocal(getNumberOfAtoms()); setAtomsContiguous(0);
     134         393 :   if( dd.Get_rank()!=0 ) {
     135         284 :     ActionToPutData* ap=plumed.getActionSet().selectWithLabel<ActionToPutData*>("Box"); ap->noforce=true;
     136             :   }
     137         393 : }
     138             : 
     139      536882 : unsigned DomainDecomposition::getNumberOfAtoms() const {
     140      536882 :   if( inputs.size()==0 ) return 0;
     141      536882 :   return (inputs[0]->getPntrToValue())->getShape()[0];
     142             : }
     143             : 
     144       72638 : void DomainDecomposition::resetForStepStart() {
     145      435828 :   for(const auto & pp : inputs) pp->resetForStepStart();
     146       72638 : }
     147             : 
     148      355141 : void DomainDecomposition::setStart( const std::string& name, const unsigned& sss) {
     149     1049156 :   for(const auto & pp : inputs) {
     150     1049156 :     if( pp->getLabel()==name ) { pp->setStart(name, sss); return; }
     151             :   }
     152           0 :   plumed_error();
     153             : }
     154             : 
     155      355141 : void DomainDecomposition::setStride( const std::string& name, const unsigned& sss) {
     156     1049156 :   for(const auto & pp : inputs) {
     157     1049156 :     if( pp->getLabel()==name ) { pp->setStride(name, sss); return; }
     158             :   }
     159           0 :   plumed_error();
     160             : }
     161             : 
     162      359163 : bool DomainDecomposition::setValuePointer( const std::string& name, const TypesafePtr & val ) {
     163      359163 :   wasset=true;  // Once the domain decomposition stuff is transferred moved the setting of this to where the g2l vector is setup
     164     1073282 :   for(const auto & pp : inputs) {
     165     1069263 :     if( pp->setValuePointer( name, val ) ) return true;
     166             :   }
     167             :   return false;
     168             : }
     169             : 
     170      217908 : bool DomainDecomposition::setForcePointer( const std::string& name, const TypesafePtr & val ) {
     171      435936 :   for(const auto & pp : inputs) {
     172      435906 :     if( pp->setForcePointer( name, val ) ) return true;
     173             :   }
     174             :   return false;
     175             : }
     176             : 
     177        1533 : void DomainDecomposition::setAtomsNlocal(int n) {
     178        1533 :   gatindex.resize(n);
     179        1533 :   g2l.resize(getNumberOfAtoms(),-1);
     180        1533 :   if(dd) {
     181             : // Since these vectors are sent with MPI by using e.g.
     182             : // &dd.positionsToBeSent[0]
     183             : // we make sure they are non-zero-sized so as to
     184             : // avoid errors when doing boundary check
     185        1509 :     if(n==0) n++;
     186        1509 :     std::size_t nvals = inputs.size(), natoms = getNumberOfAtoms();
     187        1509 :     dd.positionsToBeSent.resize(n*nvals,0.0);
     188        1509 :     dd.positionsToBeReceived.resize(natoms*nvals,0.0);
     189        1509 :     dd.indexToBeSent.resize(n,0);
     190        1509 :     dd.indexToBeReceived.resize(natoms,0);
     191             :   }
     192        1533 : }
     193             : 
     194         988 : void DomainDecomposition::setAtomsGatindex(const TypesafePtr & g,bool fortran) {
     195         988 :   plumed_massert( g || gatindex.size()==0, "NULL gatindex pointer with non-zero local atoms");
     196         988 :   auto gg=g.get<const int*>({gatindex.size()});
     197         988 :   ddStep=getStep();
     198         988 :   if(fortran) {
     199           0 :     for(unsigned i=0; i<gatindex.size(); i++) gatindex[i]=gg[i]-1;
     200             :   } else {
     201       22140 :     for(unsigned i=0; i<gatindex.size(); i++) gatindex[i]=gg[i];
     202             :   }
     203       57530 :   for(unsigned i=0; i<g2l.size(); i++) g2l[i]=-1;
     204         988 :   if( gatindex.size()==getNumberOfAtoms() ) {
     205           9 :     shuffledAtoms=0;
     206        1005 :     for(unsigned i=0; i<gatindex.size(); i++) {
     207         996 :       if( gatindex[i]!=i ) { shuffledAtoms=1; break; }
     208             :     }
     209             :   } else {
     210         979 :     shuffledAtoms=1;
     211             :   }
     212         988 :   if(dd) dd.Sum(shuffledAtoms);
     213       22140 :   for(unsigned i=0; i<gatindex.size(); i++) g2l[gatindex[i]]=i;
     214             :   // keep in unique only those atoms that are local
     215        9906 :   for(unsigned i=0; i<actions.size(); i++) actions[i]->unique_local_needs_update=true;
     216             :   unique.clear(); forced_unique.clear();
     217         988 : }
     218             : 
     219         545 : void DomainDecomposition::setAtomsContiguous(int start) {
     220         545 :   ddStep=plumed.getStep();
     221      211635 :   for(unsigned i=0; i<gatindex.size(); i++) gatindex[i]=start+i;
     222      223947 :   for(unsigned i=0; i<g2l.size(); i++) g2l[i]=-1;
     223      211635 :   for(unsigned i=0; i<gatindex.size(); i++) g2l[gatindex[i]]=i;
     224         545 :   if(gatindex.size()<getNumberOfAtoms()) shuffledAtoms=1;
     225             :   // keep in unique only those atoms that are local
     226         757 :   for(unsigned i=0; i<actions.size(); i++) actions[i]->unique_local_needs_update=true;
     227             :   unique.clear(); forced_unique.clear();
     228         545 : }
     229             : 
     230        1221 : void DomainDecomposition::shareAll() {
     231        1449 :   unique.clear(); forced_unique.clear(); int natoms = getNumberOfAtoms();
     232        1221 :   if( dd && shuffledAtoms>0 ) {
     233       17616 :     for(int i=0; i<natoms; ++i) if( g2l[i]>=0 ) unique.push_back( AtomNumber::index(i) );
     234             :   } else {
     235        1035 :     unique.resize(natoms);
     236     5132697 :     for(int i=0; i<natoms; i++) unique[i]=AtomNumber::index(i);
     237             :   }
     238        1221 :   forced_unique.resize( unique.size() );
     239     5140886 :   for(unsigned i=0; i<unique.size(); ++i) forced_unique[i] = unique[i];
     240        1221 :   share(unique);
     241        1205 : }
     242             : 
     243       71026 : void DomainDecomposition::share() {
     244             :   // We can no longer set the pointers after the share
     245      426156 :   bool atomsNeeded=false; for(const auto & pp : inputs) pp->share();
     246             :   // At first step I scatter all the atoms so as to store their mass and charge
     247             :   // Notice that this works with the assumption that charges and masses are
     248             :   // not changing during the simulation!
     249       71026 :   if( firststep ) {
     250        1107 :     actions = plumed.getActionSet().select<ActionAtomistic*>();
     251        1107 :     shareAll(); return;
     252             :   }
     253             : 
     254       69919 :   if(getenvForceUnique()==Option::automatic) {
     255             :     unsigned largest=0;
     256      721000 :     for(unsigned i=0; i<actions.size(); i++) {
     257      651081 :       if(actions[i]->isActive()) {
     258             :         auto l=actions[i]->getUnique().size();
     259      444659 :         if(l>largest) largest=l;
     260             :       }
     261             :     }
     262       69919 :     if(largest*2<getNumberOfAtoms()) unique_serial=true;
     263       46317 :     else unique_serial=false;
     264           0 :   } else if(getenvForceUnique()==Option::yes) {
     265           0 :     unique_serial=true;
     266           0 :   } else if(getenvForceUnique()==Option::no) {
     267           0 :     unique_serial=false;
     268             :   } else {
     269           0 :     plumed_error();
     270             :   }
     271             : 
     272       69919 :   if(unique_serial || !(int(gatindex.size())==getNumberOfAtoms() && shuffledAtoms==0)) {
     273      288804 :     for(unsigned i=0; i<actions.size(); i++) {
     274      273968 :       if( actions[i]->unique_local_needs_update ) actions[i]->updateUniqueLocal( !(dd && shuffledAtoms>0), g2l );
     275             :     }
     276             :     // Now reset unique for the new step
     277             :     gch::small_vector<const std::vector<AtomNumber>*,32> forced_vectors;
     278             :     gch::small_vector<const std::vector<AtomNumber>*,32> nonforced_vectors;
     279             :     forced_vectors.reserve(actions.size()); nonforced_vectors.reserve(actions.size());
     280      288804 :     for(unsigned i=0; i<actions.size(); i++) {
     281      264814 :       if(actions[i]->isActive()) {
     282      191803 :         if(!actions[i]->getUnique().empty()) {
     283             :           // unique are the local atoms
     284      202027 :           if( actions[i]->actionHasForces() ) forced_vectors.push_back(&actions[i]->getUniqueLocal());
     285       22362 :           else nonforced_vectors.push_back(&actions[i]->getUniqueLocal());
     286             :         }
     287             :       }
     288             :     }
     289       23990 :     if( !(forced_vectors.empty() && nonforced_vectors.empty()) ) atomsNeeded=true;
     290             :     // Merge the atoms from the atoms that have a force on
     291       46231 :     unique.clear(); forced_unique.clear();
     292       23990 :     mergeVectorTools::mergeSortedVectors(forced_vectors,forced_unique);
     293             :     // Merge all the atoms
     294       23990 :     nonforced_vectors.push_back( &forced_unique );
     295       23990 :     mergeVectorTools::mergeSortedVectors(nonforced_vectors,unique);
     296             :   } else {
     297      432196 :     for(unsigned i=0; i<actions.size(); i++) {
     298      386267 :       if(actions[i]->isActive()) {
     299      252856 :         if(!actions[i]->getUnique().empty()) { atomsNeeded=true; }
     300             :       }
     301             :     }
     302             :   }
     303             : 
     304             :   // Now we retrieve the atom numbers we need
     305       69919 :   if( atomsNeeded ) share( unique );
     306             : }
     307             : 
     308       70435 : void DomainDecomposition::share(const std::vector<AtomNumber>& unique) {
     309             :   // This retrieves what values we need to get
     310             :   int ndata=0; std::vector<Value*> values_to_get;
     311       70435 :   if(!(int(gatindex.size())==getNumberOfAtoms() && shuffledAtoms==0)) {
     312        2212 :     uniq_index.resize(unique.size());
     313       32705 :     for(unsigned i=0; i<unique.size(); i++) uniq_index[i]=g2l[unique[i].index()];
     314       13272 :     for(const auto & ip : inputs) {
     315       11060 :       if( (!ip->fixed || firststep) && ip->wasset ) { (ip->mydata)->share_data( unique, uniq_index, ip->copyOutput(0) ); values_to_get.push_back(ip->copyOutput(0)); ndata++; }
     316             :     }
     317       68223 :   } else if( unique_serial) {
     318       21259 :     uniq_index.resize(unique.size());
     319      789222 :     for(unsigned i=0; i<unique.size(); i++) uniq_index[i]=unique[i].index();
     320      127554 :     for(const auto & ip : inputs) {
     321      106295 :       if( (!ip->fixed || firststep) && ip->wasset ) { (ip->mydata)->share_data( unique, uniq_index, ip->copyOutput(0) ); values_to_get.push_back(ip->copyOutput(0)); ndata++; }
     322             :     }
     323             :   } else {
     324             : // faster version, which retrieves all atoms
     325      281704 :     for(const auto & ip : inputs) {
     326      234756 :       if( (!ip->fixed || firststep) && ip->wasset ) { (ip->mydata)->share_data( 0, getNumberOfAtoms(), ip->copyOutput(0) ); values_to_get.push_back(ip->copyOutput(0)); ndata++; }
     327             :     }
     328             :   }
     329             : 
     330       70419 :   if(dd && shuffledAtoms>0) {
     331        2188 :     if(dd.async) {
     332        9598 :       for(unsigned i=0; i<dd.mpi_request_positions.size(); i++) dd.mpi_request_positions[i].wait();
     333        9598 :       for(unsigned i=0; i<dd.mpi_request_index.size(); i++)     dd.mpi_request_index[i].wait();
     334             :     }
     335             : 
     336        2188 :     int count=0;
     337       32623 :     for(const auto & p : unique) {
     338       30435 :       dd.indexToBeSent[count]=p.index(); int dpoint=0;
     339      126694 :       for(unsigned i=0; i<values_to_get.size(); ++i) {
     340       96259 :         dd.positionsToBeSent[ndata*count+dpoint]=values_to_get[i]->get(p.index());
     341       96259 :         dpoint++;
     342             :       }
     343       30435 :       count++;
     344             :     }
     345             : 
     346        2188 :     if(dd.async) {
     347        2168 :       asyncSent=true;
     348        2168 :       dd.mpi_request_positions.resize(dd.Get_size());
     349        2168 :       dd.mpi_request_index.resize(dd.Get_size());
     350        9802 :       for(int i=0; i<dd.Get_size(); i++) {
     351        7634 :         dd.mpi_request_index[i]=dd.Isend(&dd.indexToBeSent[0],count,i,666);
     352        7634 :         dd.mpi_request_positions[i]=dd.Isend(&dd.positionsToBeSent[0],ndata*count,i,667);
     353             :       }
     354             :     } else {
     355          20 :       const int n=(dd.Get_size());
     356          36 :       std::vector<int> counts(n);
     357          20 :       std::vector<int> displ(n);
     358          20 :       std::vector<int> counts5(n);
     359          20 :       std::vector<int> displ5(n);
     360          20 :       dd.Allgather(count,counts);
     361          20 :       displ[0]=0;
     362          80 :       for(int i=1; i<n; ++i) displ[i]=displ[i-1]+counts[i-1];
     363         100 :       for(int i=0; i<n; ++i) counts5[i]=counts[i]*ndata;
     364         100 :       for(int i=0; i<n; ++i) displ5[i]=displ[i]*ndata;
     365          20 :       dd.Allgatherv(&dd.indexToBeSent[0],count,&dd.indexToBeReceived[0],&counts[0],&displ[0]);
     366          20 :       dd.Allgatherv(&dd.positionsToBeSent[0],ndata*count,&dd.positionsToBeReceived[0],&counts5[0],&displ5[0]);
     367          20 :       int tot=displ[n-1]+counts[n-1];
     368        1620 :       for(int i=0; i<tot; i++) {
     369             :         int dpoint=0;
     370        7264 :         for(unsigned j=0; j<values_to_get.size(); ++j) {
     371        5664 :           values_to_get[j]->data[dd.indexToBeReceived[i]] = dd.positionsToBeReceived[ndata*i+dpoint]; dpoint++;
     372             :         }
     373             :       }
     374             :     }
     375             :   }
     376       70419 : }
     377             : 
     378       70411 : void DomainDecomposition::wait() {
     379      422466 :   for(const auto & ip : inputs) ip->dataCanBeSet=false;
     380             : 
     381       70411 :   if(dd && shuffledAtoms>0) {
     382             :     int ndata=0; std::vector<Value*> values_to_set;
     383       13128 :     for(const auto & ip : inputs) {
     384       10940 :       if( (!ip->fixed || firststep) && ip->wasset ) { values_to_set.push_back(ip->copyOutput(0)); ndata++; }
     385             :     }
     386             : 
     387             : // receive toBeReceived
     388        2188 :     if(asyncSent) {
     389             :       Communicator::Status status;
     390             :       std::size_t count=0;
     391        9802 :       for(int i=0; i<dd.Get_size(); i++) {
     392        7634 :         dd.Recv(&dd.indexToBeReceived[count],dd.indexToBeReceived.size()-count,i,666,status);
     393        7634 :         int c=status.Get_count<int>();
     394        7634 :         dd.Recv(&dd.positionsToBeReceived[ndata*count],dd.positionsToBeReceived.size()-ndata*count,i,667);
     395        7634 :         count+=c;
     396             :       }
     397       68220 :       for(int i=0; i<count; i++) {
     398             :         int dpoint=0;
     399      276100 :         for(unsigned j=0; j<values_to_set.size(); ++j) {
     400      210048 :           values_to_set[j]->set(dd.indexToBeReceived[i], dd.positionsToBeReceived[ndata*i+dpoint] );
     401      210048 :           dpoint++;
     402             :         }
     403             :       }
     404        2168 :       asyncSent=false;
     405             :     }
     406             :   }
     407       70411 : }
     408             : 
     409           0 : unsigned DomainDecomposition::getNumberOfForcesToRescale() const {
     410           0 :   return gatindex.size();
     411             : }
     412             : 
     413       70287 : void DomainDecomposition::apply() {
     414       70287 :   std::vector<unsigned> forced_uniq_index(forced_unique.size());
     415       70287 :   if(!(int(gatindex.size())==getNumberOfAtoms() && shuffledAtoms==0)) {
     416       24159 :     for(unsigned i=0; i<forced_unique.size(); i++) forced_uniq_index[i]=g2l[forced_unique[i].index()];
     417             :   } else {
     418     3901524 :     for(unsigned i=0; i<forced_unique.size(); i++) forced_uniq_index[i]=forced_unique[i].index();
     419             :   }
     420      421712 :   for(const auto & ip : inputs) {
     421      351427 :     if( !(ip->getPntrToValue())->forcesWereAdded() || ip->noforce ) {
     422      209330 :       continue;
     423      142097 :     } else if( ip->wasscaled || (!unique_serial && int(gatindex.size())==getNumberOfAtoms() && shuffledAtoms==0) ) {
     424       79589 :       (ip->mydata)->add_force( gatindex, ip->getPntrToValue() );
     425       62508 :     } else { (ip->mydata)->add_force( forced_unique, forced_uniq_index, ip->getPntrToValue() ); }
     426             :   }
     427       70285 : }
     428             : 
     429       70285 : void DomainDecomposition::reset() {
     430       70285 :   if( !unique_serial && int(gatindex.size())==getNumberOfAtoms() && shuffledAtoms==0 ) return;
     431             :   // This is an optimisation to ensure that we don't call std::fill over the whole forces
     432             :   // array if there are a small number of atoms passed between the MD code and PLUMED
     433       23347 :   if( dd && shuffledAtoms>0 ) getAllActiveAtoms( unique );
     434      140082 :   for(const auto & ip : inputs) (ip->copyOutput(0))->clearInputForce( unique );
     435             : }
     436             : 
     437         114 : void DomainDecomposition::writeBinary(std::ostream&o) {
     438         684 :   for(const auto & ip : inputs) ip->writeBinary(o);
     439         114 : }
     440             : 
     441         114 : void DomainDecomposition::readBinary(std::istream&i) {
     442         684 :   for(const auto & ip : inputs) ip->readBinary(i);
     443         114 : }
     444             : 
     445       66603 : void DomainDecomposition::broadcastToDomains( Value* val ) {
     446       66603 :   if( dd ) dd.Bcast( val->data, 0 );
     447       66603 : }
     448             : 
     449        3989 : void DomainDecomposition::sumOverDomains( Value* val ) {
     450        3989 :   if( dd && shuffledAtoms>0 ) dd.Sum( val->data );
     451        3989 : }
     452             : 
     453         900 : const long int& DomainDecomposition::getDdStep() const {
     454         900 :   return ddStep;
     455             : }
     456             : 
     457         459 : const std::vector<int>& DomainDecomposition::getGatindex() const {
     458         459 :   return gatindex;
     459             : }
     460             : 
     461        2183 : void DomainDecomposition::getAllActiveAtoms( std::vector<AtomNumber>& u ) {
     462             :   gch::small_vector<const std::vector<AtomNumber>*,32> vectors;
     463             :   vectors.reserve(actions.size());
     464       21016 :   for(unsigned i=0; i<actions.size(); i++) {
     465       37666 :     if(actions[i]->isActive()) {
     466       13033 :       if(!actions[i]->getUnique().empty()) {
     467             :         // unique are the local atoms
     468       22912 :         vectors.push_back(&actions[i]->getUnique());
     469             :       }
     470             :     }
     471             :   }
     472             :   u.clear();
     473        2183 :   mergeVectorTools::mergeSortedVectors(vectors,u);
     474        2183 : }
     475             : 
     476         116 : void DomainDecomposition::createFullList(const TypesafePtr & n) {
     477         116 :   if( firststep ) {
     478           7 :     int natoms = getNumberOfAtoms();
     479           7 :     n.set(int(natoms)); fullList.resize(natoms);
     480         803 :     for(unsigned i=0; i<natoms; i++) fullList[i]=i;
     481             :   } else {
     482             : // We update here the unique list defined at Atoms::unique.
     483             : // This is not very clear, and probably should be coded differently.
     484             : // Hopefully this fix the longstanding issue with NAMD.
     485         109 :     getAllActiveAtoms( unique );
     486         109 :     fullList.clear(); fullList.reserve(unique.size());
     487        5012 :     for(const auto & p : unique) fullList.push_back(p.index());
     488         109 :     n.set(int(fullList.size()));
     489             :   }
     490         116 : }
     491             : 
     492         116 : void DomainDecomposition::getFullList(const TypesafePtr & x) {
     493             :   auto xx=x.template get<const int**>();
     494         116 :   if(!fullList.empty()) *xx=&fullList[0];
     495           6 :   else *xx=NULL;
     496         116 : }
     497             : 
     498         116 : void DomainDecomposition::clearFullList() {
     499         116 :   fullList.resize(0);
     500         116 : }
     501             : 
     502             : }

Generated by: LCOV version 1.16