LCOV - code coverage report
Current view: top level - core - DomainDecomposition.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 344 361 95.3 %
Date: 2025-12-04 11:19:34 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             : Data is passed from the MD code to PLUMED by creating [PUT](PUT.md) actions.
      37             : These PUT actions take the data from a void pointer that is passed to PLUMED from the MD code and transfer it into a
      38             : PLMD::Value object. Passing a void pointer and using a PUT action to convert the data within it
      39             : to a PLMD::Value is also used when the atomic positions, masses and charges are sent to PLUMED. However,
      40             : there are some other subtleties for these quantities because MD codes use a domain decomposition and scatter the properties of atoms across
      41             : multiple domains. We thus need to use the action DOMAIN_DECOMPOSITION when passing these quantities to make sense of the
      42             : information in the void pointers that the MD code passes.
      43             : 
      44             : ## Creating a DOMAIN_DECOMPOSITION action
      45             : 
      46             : A DOMAIN_DECOMPOSITION can be created by using a call to plumed.cmd as follows:
      47             : 
      48             : ```c++
      49             : plumed.cmd("readInputLine dd: DOMAIN_DECOMPOSITION NATOMS=20 VALUE1=vv UNIT1=length PERIODIC1=NO CONSTANT1=False");
      50             : ```
      51             : 
      52             : The DOMAIN_DECOMPOSTION command above creates a PUT action with the label vv. The pointer to the data that needs to be transferred to the PLMD::Value
      53             : object that is created by the PUT action is then set by using the command below:
      54             : 
      55             : ```c++
      56             : plumed.cmd("setInputValue vv, &val);
      57             : ```
      58             : 
      59             : Meanwhile, the pointer to the forces that should be modified is passed as follows:
      60             : 
      61             : ```c++
      62             : plumed.cmd("setValueForces vv", force);
      63             : ```
      64             : 
      65             : In other words, pointers to values and forces in the MD code are passed to PUT actions that are created by the DOMAIN_DECOMPOSION in
      66             : [the way you pass data to other PUT actions](PUT.md).
      67             : 
      68             : The PLMD::Value objects created by a DOMAIN_DECOMPOSITION action are always vectors with the same number of components as atoms in the system. Furthermore, you can create multiple PUT
      69             : actions from a single DOMAIN_DECOMPOSITION action. To see why this is useful, consider the following DOMAIN_DECOMPOSITION command:
      70             : 
      71             : ```plumed
      72             : gromacs: DOMAIN_DECOMPOSITION ...
      73             :    NATOMS=2000
      74             :    VALUE1=myposx UNIT1=length PERIODIC1=NO CONSTANT1=False ROLE1=x
      75             :    VALUE2=myposy UNIT2=length PERIODIC2=NO CONSTANT2=False ROLE2=y
      76             :    VALUE3=myposz UNIT3=length PERIODIC3=NO CONSTANT3=False ROLE3=z
      77             :    VALUE4=myMasses UNIT4=mass PERIODIC4=NO CONSTANT4=True ROLE4=m
      78             :    VALUE5=myCharges UNIT5=charge PERIODIC5=NO CONSTANT5=True ROLE5=q
      79             :    PBCLABEL=mybox
      80             : ...
      81             : ```
      82             : 
      83             : This action is created when you call `plumed.cmd("setNatoms",&natoms)` from gromacs. It makes 5 PLMD::Value called posx, posy, posz, Masses and Charges.
      84             : These PLMD::Value objects then hold the x, y and z positions of the atoms and the masses and charges of the atoms. It is important to note that this command will
      85             : also, create a PBC_ACTION to hold the cell.
      86             : 
      87             : The ROLE keywords above are only needed because the five quantities passed by the command above play a unique role within PLUMED. If you pass
      88             : some other quantities, this instruction is not required. PLMD::ActionAtomistic searches for atomic positions, masses and charges by looking for PUT Actions
      89             : that have these five particular roles and for ActionWithVirtualAtom objects.
      90             : 
      91             : ## Differences from regular PUT actions
      92             : 
      93             : PUT actions created from a DOMAIN_DECOMPOSITION action behave differently from other PUT actions. In particular:
      94             : 
      95             : * If a DOMAIN_DECOMPOSITION action creates a PUT action, then the PUT action depends on the DOMAIN_DECOMPOSITION action. ActionToPutData::apply thus does nothing for these PUT actions.
      96             : * Similarly, when DOMAIN_DECOMPOSITION actions create PUT actions, data is transferred from the input pointer to the PLMD::Value object by the DOMAIN_DECOMPOSITION action. When ActionToPutData::wait is called for these PUT Actions `wasset=true`, ActionToPutData::wait does nothing.
      97             : * Lastly, if a constant PUT action is created by DOMAIN_DECOMPOSITION, the values in the vector are set during the first step of MD rather than during initialisation.
      98             : 
      99             : These differences are necessary because PUT actions cannot understand the information in the pointers that are passed from the MD code. These pointers are only understandable if you know
     100             : which atoms are on each processor. This information is only passed to the DOMAIN_DECOMPOSITION action. DOMAIN_DECOMPOSITION must translate the information passed from the MD code before it is
     101             : passed back on through PLMD::Value objects created by the PUT actions. DOMAIN_DECOMPOSITION thus keeps pointers to all the PUT actions that it creates. It sets the data in these action's PLMD::Value objects
     102             : within `DomainDecomposition::share(const std::vector<AtomNumber>& unique)` and `DomainDecomposition::wait().`  The forces on the PUT actions created by the DOMAIN_DECOMPOSITION action are added in `DomainDecomposition::apply()`.
     103             : 
     104             : */
     105             : //+ENDPLUMEDOC
     106             : 
     107             : namespace PLMD {
     108             : 
     109             : namespace {
     110             : 
     111             : enum class Option {automatic, no, yes };
     112             : 
     113         775 : Option interpretEnvString(const char* env,const char* str) {
     114         775 :   if(!str) {
     115             :     return Option::automatic;
     116             :   }
     117           0 :   if(!std::strcmp(str,"yes")) {
     118             :     return Option::yes;
     119             :   }
     120           0 :   if(!std::strcmp(str,"no")) {
     121             :     return Option::no;
     122             :   }
     123           0 :   if(!std::strcmp(str,"auto")) {
     124             :     return Option::automatic;
     125             :   }
     126           0 :   plumed_error()<<"Cannot understand env var "<<env<<"\nPossible values: yes/no/auto\nActual value: "<<str;
     127             : }
     128             : 
     129             : /// Use unique list of atoms to manipulate forces and positions.
     130             : /// A unique list of atoms is used to manipulate forces and positions in MPI parallel runs.
     131             : /// In serial runs, this is done if convenient. The code currently contain
     132             : /// some heuristic to decide if the unique list should be used or not.
     133             : /// An env var can be used to override this decision.
     134             : /// export PLUMED_FORCE_UNIQUE=yes  # enforce using the unique list in serial runs
     135             : /// export PLUMED_FORCE_UNIQUE=no   # enforce not using the unique list in serial runs
     136             : /// export PLUMED_FORCE_UNIQUE=auto # choose heuristically
     137             : /// default: auto
     138       75482 : Option getenvForceUnique() {
     139             :   static const char* name="PLUMED_FORCE_UNIQUE";
     140       75482 :   static const auto opt = interpretEnvString(name,std::getenv(name));
     141       75482 :   return opt;
     142             : }
     143             : 
     144             : }
     145             : 
     146             : PLUMED_REGISTER_ACTION(DomainDecomposition,"DOMAIN_DECOMPOSITION")
     147             : 
     148         402 : void DomainDecomposition::DomainComms::enable(Communicator& c) {
     149         402 :   on=true;
     150         402 :   Set_comm(c.Get_comm());
     151         402 :   async=Get_size()<10;
     152         402 :   if(std::getenv("PLUMED_ASYNC_SHARE")) {
     153           4 :     std::string s(std::getenv("PLUMED_ASYNC_SHARE"));
     154           4 :     if(s=="yes") {
     155           0 :       async=true;
     156           4 :     } else if(s=="no") {
     157           4 :       async=false;
     158             :     } else {
     159           0 :       plumed_merror("PLUMED_ASYNC_SHARE variable is set to " + s + "; should be yes or no");
     160             :     }
     161             :   }
     162         402 : }
     163             : 
     164        1247 : void DomainDecomposition::registerKeywords(Keywords& keys) {
     165        1247 :   ActionForInterface::registerKeywords( keys );
     166        2494 :   keys.remove("ROLE");
     167        1247 :   keys.add("compulsory","NATOMS","the number of atoms across all the domains");
     168        1247 :   keys.add("numbered","VALUE","value to create for the domain decomposition");
     169        1247 :   keys.add("numbered","UNIT","unit of the ith value that is being passed through the domain decomposition");
     170        1247 :   keys.add("numbered","CONSTANT","is the ith value that is being passed through the domain decomposition constant");
     171        1247 :   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 "
     172             :            "is not periodic you must state this using PERIODIC=NO.  Positions are passed with PERIODIC=NO even though special methods are used "
     173             :            "to deal with pbc");
     174        1247 :   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");
     175        1247 :   keys.add("compulsory","PBCLABEL","Box","the label to use for the PBC action that will be created");
     176        1247 :   keys.remove("NUMERICAL_DERIVATIVES");
     177        2494 :   keys.setValueDescription("vector","the domain that each atom is within");
     178        1247 : }
     179             : 
     180        1245 : DomainDecomposition::DomainDecomposition(const ActionOptions&ao):
     181             :   Action(ao),
     182             :   ActionForInterface(ao),
     183        1245 :   ddStep(0),
     184        1245 :   shuffledAtoms(0),
     185        1245 :   asyncSent(false),
     186        1245 :   unique_serial(false) {
     187             :   // Read in the number of atoms
     188             :   int natoms;
     189        2490 :   parse("NATOMS",natoms);
     190             :   std::string str_natoms;
     191        1245 :   Tools::convert( natoms, str_natoms );
     192             :   // Setup the gat index
     193        1245 :   gatindex.resize(natoms);
     194    11238653 :   for(unsigned i=0; i<gatindex.size(); i++) {
     195    11237408 :     gatindex[i]=i;
     196             :   }
     197             :   // Now read in the values we are creating here
     198        6225 :   for(unsigned i=1;; ++i) {
     199             :     std::string valname;
     200       14940 :     if( !parseNumbered("VALUE",i,valname) ) {
     201             :       break;
     202             :     }
     203             :     std::string unit;
     204       12450 :     parseNumbered("UNIT",i,unit);
     205             :     std::string constant;
     206       12450 :     parseNumbered("CONSTANT",i,constant);
     207             :     std::string period;
     208       12450 :     parseNumbered("PERIODIC",i,period);
     209             :     std::string ddrole;
     210       12450 :     parseNumbered("ROLE",i,ddrole);
     211        6225 :     if( constant=="True") {
     212        4980 :       plumed.readInputLine( valname + ": PUT FROM_DOMAINS CONSTANT SHAPE=" + str_natoms + " UNIT=" + unit + " PERIODIC=" + period + " ROLE=" + ddrole, !plumed.hasBeenInitialized() );
     213        3735 :     } else if( constant=="False") {
     214        7470 :       plumed.readInputLine( valname + ": PUT FROM_DOMAINS SHAPE=" + str_natoms + " UNIT=" + unit + " PERIODIC=" + period + " ROLE=" + ddrole, !plumed.hasBeenInitialized() );
     215             :     } else {
     216           0 :       plumed_merror("missing information on whether value is constant");
     217             :     }
     218             :     // And save the list of values that are set from here
     219        6225 :     ActionToPutData* ap=plumed.getActionSet().selectWithLabel<ActionToPutData*>(valname);
     220        6225 :     ap->addDependency( this );
     221        6225 :     inputs.push_back( ap );
     222        6225 :   }
     223             :   std::string pbclabel;
     224        1245 :   parse("PBCLABEL",pbclabel);
     225        1245 :   plumed.readInputLine(pbclabel + ": PBC",true);
     226             :   // Turn on the domain decomposition
     227        1245 :   if( Communicator::initialized() ) {
     228         401 :     Set_comm(comm);
     229             :   }
     230        1245 : }
     231             : 
     232         402 : void DomainDecomposition::Set_comm(Communicator& newcomm) {
     233         402 :   dd.enable(newcomm);
     234         402 :   setAtomsNlocal(getNumberOfAtoms());
     235         402 :   setAtomsContiguous(0);
     236         402 :   if( dd.Get_rank()!=0 ) {
     237         145 :     ActionToPutData* ap=plumed.getActionSet().selectWithLabel<ActionToPutData*>("Box");
     238         145 :     ap->noforce=true;
     239             :   }
     240         402 : }
     241             : 
     242      597901 : unsigned DomainDecomposition::getNumberOfAtoms() const {
     243      597901 :   if( inputs.size()==0 ) {
     244             :     return 0;
     245             :   }
     246      597901 :   return (inputs[0]->getPntrToValue())->getShape()[0];
     247             : }
     248             : 
     249       78188 : void DomainDecomposition::resetForStepStart() {
     250      469128 :   for(const auto & pp : inputs) {
     251      390940 :     pp->resetForStepStart();
     252             :   }
     253       78188 : }
     254             : 
     255      382884 : void DomainDecomposition::setStart( const std::string& argName, const unsigned& sss) {
     256     1132381 :   for(const auto & pp : inputs) {
     257     1132381 :     if( pp->getLabel()==argName ) {
     258      382884 :       pp->setStart(argName, sss);
     259      382884 :       return;
     260             :     }
     261             :   }
     262           0 :   plumed_error();
     263             : }
     264             : 
     265      382884 : void DomainDecomposition::setStride( const std::string& argName, const unsigned& sss) {
     266     1132381 :   for(const auto & pp : inputs) {
     267     1132381 :     if( pp->getLabel()==argName ) {
     268      382884 :       pp->setStride(argName, sss);
     269      382884 :       return;
     270             :     }
     271             :   }
     272           0 :   plumed_error();
     273             : }
     274             : 
     275      386906 : bool DomainDecomposition::setValuePointer( const std::string& argName, const TypesafePtr & val ) {
     276      386906 :   wasset=true;  // Once the domain decomposition stuff is transferred moved the setting of this to where the g2l vector is setup
     277     1156507 :   for(const auto & pp : inputs) {
     278     1152488 :     if( pp->setValuePointer( argName, val ) ) {
     279             :       return true;
     280             :     }
     281             :   }
     282             :   return false;
     283             : }
     284             : 
     285      234555 : bool DomainDecomposition::setForcePointer( const std::string& argName, const TypesafePtr & val ) {
     286      469230 :   for(const auto & pp : inputs) {
     287      469200 :     if( pp->setForcePointer( argName, val ) ) {
     288             :       return true;
     289             :     }
     290             :   }
     291             :   return false;
     292             : }
     293             : 
     294        1544 : void DomainDecomposition::setAtomsNlocal(int n) {
     295        1544 :   gatindex.resize(n);
     296        1544 :   g2l.resize(getNumberOfAtoms(),-1);
     297        1544 :   if(dd) {
     298             : // Since these vectors are sent with MPI by using e.g.
     299             : // &dd.positionsToBeSent[0]
     300             : // we make sure they are non-zero-sized so as to
     301             : // avoid errors when doing boundary check
     302        1518 :     if(n==0) {
     303           8 :       n++;
     304             :     }
     305        1518 :     std::size_t nvals = inputs.size(), natoms = getNumberOfAtoms();
     306        1518 :     dd.positionsToBeSent.resize(n*nvals,0.0);
     307        1518 :     dd.positionsToBeReceived.resize(natoms*nvals,0.0);
     308        1518 :     dd.indexToBeSent.resize(n,0);
     309        1518 :     dd.indexToBeReceived.resize(natoms,0);
     310             :   }
     311        1544 : }
     312             : 
     313         990 : void DomainDecomposition::setAtomsGatindex(const TypesafePtr & g,bool fortran) {
     314         990 :   plumed_massert( g || gatindex.size()==0, "NULL gatindex pointer with non-zero local atoms");
     315         990 :   auto gg=g.get<const int*>({gatindex.size()});
     316         990 :   ddStep=getStep();
     317         990 :   if(fortran) {
     318          22 :     for(unsigned i=0; i<gatindex.size(); i++) {
     319          20 :       gatindex[i]=gg[i]-1;
     320             :     }
     321             :   } else {
     322       22140 :     for(unsigned i=0; i<gatindex.size(); i++) {
     323       21152 :       gatindex[i]=gg[i];
     324             :     }
     325             :   }
     326       57552 :   for(unsigned i=0; i<g2l.size(); i++) {
     327       56562 :     g2l[i]=-1;
     328             :   }
     329         990 :   if( gatindex.size()==getNumberOfAtoms() ) {
     330          11 :     shuffledAtoms=0;
     331        1007 :     for(unsigned i=0; i<gatindex.size(); i++) {
     332         998 :       if( gatindex[i]!=static_cast<int>(i) ) {
     333           2 :         shuffledAtoms=1;
     334           2 :         break;
     335             :       }
     336             :     }
     337             :   } else {
     338         979 :     shuffledAtoms=1;
     339             :   }
     340         990 :   if(dd) {
     341         964 :     dd.Sum(shuffledAtoms);
     342             :   }
     343       22162 :   for(unsigned i=0; i<gatindex.size(); i++) {
     344       21172 :     g2l[gatindex[i]]=i;
     345             :   }
     346             :   // keep in unique only those atoms that are local
     347        9908 :   for(unsigned i=0; i<actions.size(); i++) {
     348        8918 :     actions[i]->unique_local_needs_update=true;
     349             :   }
     350             :   unique.clear();
     351             :   forced_unique.clear();
     352         990 : }
     353             : 
     354         554 : void DomainDecomposition::setAtomsContiguous(int start) {
     355         554 :   ddStep=plumed.getStep();
     356      218327 :   for(unsigned i=0; i<gatindex.size(); i++) {
     357      217773 :     gatindex[i]=start+i;
     358             :   }
     359      230639 :   for(unsigned i=0; i<g2l.size(); i++) {
     360      230085 :     g2l[i]=-1;
     361             :   }
     362      218327 :   for(unsigned i=0; i<gatindex.size(); i++) {
     363      217773 :     g2l[gatindex[i]]=i;
     364             :   }
     365         554 :   if(gatindex.size()<getNumberOfAtoms()) {
     366         152 :     shuffledAtoms=1;
     367             :   }
     368             :   // keep in unique only those atoms that are local
     369         766 :   for(unsigned i=0; i<actions.size(); i++) {
     370         212 :     actions[i]->unique_local_needs_update=true;
     371             :   }
     372             :   unique.clear();
     373             :   forced_unique.clear();
     374         554 : }
     375             : 
     376        1270 : void DomainDecomposition::shareAll() {
     377        1270 :   unique.clear();
     378        1270 :   forced_unique.clear();
     379        1270 :   int natoms = getNumberOfAtoms();
     380        1270 :   if( dd && shuffledAtoms>0 ) {
     381       17616 :     for(int i=0; i<natoms; ++i)
     382       17430 :       if( g2l[i]>=0 ) {
     383        8003 :         unique.push_back( AtomNumber::index(i) );
     384             :       }
     385             :   } else {
     386        1084 :     unique.resize(natoms);
     387     6180617 :     for(int i=0; i<natoms; i++) {
     388     6179533 :       unique[i]=AtomNumber::index(i);
     389             :     }
     390             :   }
     391        1270 :   forced_unique.resize( unique.size() );
     392     6188806 :   for(unsigned i=0; i<unique.size(); ++i) {
     393     6187536 :     forced_unique[i] = unique[i];
     394             :   }
     395        1270 :   share(unique);
     396        1254 : }
     397             : 
     398       76638 : void DomainDecomposition::share() {
     399             :   // We can no longer set the pointers after the share
     400             :   bool atomsNeeded=false;
     401      459828 :   for(const auto & pp : inputs) {
     402      383190 :     pp->share();
     403             :   }
     404             :   // At first step I scatter all the atoms so as to store their mass and charge
     405             :   // Notice that this works with the assumption that charges and masses are
     406             :   // not changing during the simulation!
     407       76638 :   if( firststep ) {
     408        1156 :     actions = plumed.getActionSet().select<ActionAtomistic*>();
     409        1156 :     shareAll();
     410        1140 :     return;
     411             :   }
     412             : 
     413       75482 :   if(getenvForceUnique()==Option::automatic) {
     414             :     unsigned largest=0;
     415      651204 :     for(unsigned i=0; i<actions.size(); i++) {
     416      575722 :       if(actions[i]->isActive()) {
     417             :         auto l=actions[i]->getUnique().size();
     418      416883 :         if(l>largest) {
     419       79969 :           largest=l;
     420             :         }
     421             :       }
     422             :     }
     423       75482 :     if(largest*2<getNumberOfAtoms()) {
     424       23709 :       unique_serial=true;
     425             :     } else {
     426       51773 :       unique_serial=false;
     427             :     }
     428           0 :   } else if(getenvForceUnique()==Option::yes) {
     429           0 :     unique_serial=true;
     430           0 :   } else if(getenvForceUnique()==Option::no) {
     431           0 :     unique_serial=false;
     432             :   } else {
     433           0 :     plumed_error();
     434             :   }
     435             : 
     436       75482 :   if(unique_serial || !(gatindex.size()==getNumberOfAtoms() && shuffledAtoms==0)) {
     437      272695 :     for(unsigned i=0; i<actions.size(); i++) {
     438      248598 :       if( actions[i]->unique_local_needs_update ) {
     439      249328 :         actions[i]->updateUniqueLocal( !(dd && shuffledAtoms>0), g2l );
     440             :       }
     441             :     }
     442             :     // Now reset unique for the new step
     443             :     gch::small_vector<const std::vector<AtomNumber>*,32> forced_vectors;
     444             :     gch::small_vector<const std::vector<AtomNumber>*,32> nonforced_vectors;
     445             :     forced_vectors.reserve(actions.size());
     446             :     nonforced_vectors.reserve(actions.size());
     447      272695 :     for(unsigned i=0; i<actions.size(); i++) {
     448      248598 :       if(actions[i]->isActive()) {
     449      180523 :         if(!actions[i]->getUnique().empty()) {
     450             :           // unique are the local atoms
     451      107066 :           if( actions[i]->actionHasForces() ) {
     452      191398 :             forced_vectors.push_back(&actions[i]->getUniqueLocal());
     453             :           } else {
     454       22734 :             nonforced_vectors.push_back(&actions[i]->getUniqueLocal());
     455             :           }
     456             :         }
     457             :       }
     458             :     }
     459       24097 :     if( !(forced_vectors.empty() && nonforced_vectors.empty()) ) {
     460             :       atomsNeeded=true;
     461             :     }
     462             :     // Merge the atoms from the atoms that have a force on
     463       24097 :     unique.clear();
     464       24097 :     forced_unique.clear();
     465       24097 :     mergeVectorTools::mergeSortedVectors(forced_vectors,forced_unique);
     466             :     // Merge all the atoms
     467       24097 :     nonforced_vectors.push_back( &forced_unique );
     468       24097 :     mergeVectorTools::mergeSortedVectors(nonforced_vectors,unique);
     469             :   } else {
     470      378509 :     for(unsigned i=0; i<actions.size(); i++) {
     471      327124 :       if(actions[i]->isActive()) {
     472      236360 :         if(!actions[i]->getUnique().empty()) {
     473             :           atomsNeeded=true;
     474             :         }
     475             :       }
     476             :     }
     477             :   }
     478             : 
     479             :   // Now we retrieve the atom numbers we need
     480       75482 :   if( atomsNeeded ) {
     481       74775 :     share( unique );
     482             :   }
     483             : }
     484             : 
     485       76045 : void DomainDecomposition::share(const std::vector<AtomNumber>& uniqueIdxIn) {
     486             :   // This retrieves what values we need to get
     487             :   int ndata=0;
     488             :   std::vector<Value*> values_to_get;
     489       76045 :   if(!(gatindex.size()==getNumberOfAtoms() && shuffledAtoms==0)) {
     490        2214 :     uniq_index.resize(uniqueIdxIn.size());
     491       32727 :     for(unsigned i=0; i<uniqueIdxIn.size(); i++) {
     492       30513 :       uniq_index[i]=g2l[uniqueIdxIn[i].index()];
     493             :     }
     494       13284 :     for(const auto & ip : inputs) {
     495       11070 :       if( (!ip->fixed || firststep) && ip->wasset ) {
     496        6788 :         (ip->mydata)->share_data( uniqueIdxIn, uniq_index, ip->copyOutput(0) );
     497        6788 :         values_to_get.push_back(ip->copyOutput(0));
     498        6788 :         ndata++;
     499             :       }
     500             :     }
     501       73831 :   } else if( unique_serial) {
     502       21364 :     uniq_index.resize(uniqueIdxIn.size());
     503      966615 :     for(unsigned i=0; i<uniqueIdxIn.size(); i++) {
     504      945251 :       uniq_index[i]=uniqueIdxIn[i].index();
     505             :     }
     506      128184 :     for(const auto & ip : inputs) {
     507      106820 :       if( (!ip->fixed || firststep) && ip->wasset ) {
     508       64092 :         (ip->mydata)->share_data( uniqueIdxIn, uniq_index, ip->copyOutput(0) );
     509       64092 :         values_to_get.push_back(ip->copyOutput(0));
     510       64092 :         ndata++;
     511             :       }
     512             :     }
     513             :   } else {
     514             : // faster version, which retrieves all atoms
     515      314722 :     for(const auto & ip : inputs) {
     516      262271 :       if( (!ip->fixed || firststep) && ip->wasset ) {
     517      159280 :         (ip->mydata)->share_data( 0, getNumberOfAtoms(), ip->copyOutput(0) );
     518      159264 :         values_to_get.push_back(ip->copyOutput(0));
     519      159264 :         ndata++;
     520             :       }
     521             :     }
     522             :   }
     523             : 
     524       76029 :   if(dd && shuffledAtoms>0) {
     525        2188 :     if(dd.async) {
     526        9598 :       for(unsigned i=0; i<dd.mpi_request_positions.size(); i++) {
     527        7430 :         dd.mpi_request_positions[i].wait();
     528             :       }
     529        9598 :       for(unsigned i=0; i<dd.mpi_request_index.size(); i++) {
     530        7430 :         dd.mpi_request_index[i].wait();
     531             :       }
     532             :     }
     533             : 
     534        2188 :     int count=0;
     535       32623 :     for(const auto & p : uniqueIdxIn) {
     536       30435 :       dd.indexToBeSent[count]=p.index();
     537             :       int dpoint=0;
     538      126694 :       for(unsigned i=0; i<values_to_get.size(); ++i) {
     539       96259 :         dd.positionsToBeSent[ndata*count+dpoint]=values_to_get[i]->get(p.index());
     540       96259 :         dpoint++;
     541             :       }
     542       30435 :       count++;
     543             :     }
     544             : 
     545        2188 :     if(dd.async) {
     546        2168 :       asyncSent=true;
     547        2168 :       dd.mpi_request_positions.resize(dd.Get_size());
     548        2168 :       dd.mpi_request_index.resize(dd.Get_size());
     549        9802 :       for(int i=0; i<dd.Get_size(); i++) {
     550        7634 :         dd.mpi_request_index[i]=dd.Isend(&dd.indexToBeSent[0],count,i,666);
     551        7634 :         dd.mpi_request_positions[i]=dd.Isend(&dd.positionsToBeSent[0],ndata*count,i,667);
     552             :       }
     553             :     } else {
     554          20 :       const int n=(dd.Get_size());
     555          36 :       std::vector<int> counts(n);
     556          20 :       std::vector<int> displ(n);
     557          20 :       std::vector<int> counts5(n);
     558          20 :       std::vector<int> displ5(n);
     559          20 :       dd.Allgather(count,counts);
     560          20 :       displ[0]=0;
     561          80 :       for(int i=1; i<n; ++i) {
     562          60 :         displ[i]=displ[i-1]+counts[i-1];
     563             :       }
     564         100 :       for(int i=0; i<n; ++i) {
     565          80 :         counts5[i]=counts[i]*ndata;
     566             :       }
     567         100 :       for(int i=0; i<n; ++i) {
     568          80 :         displ5[i]=displ[i]*ndata;
     569             :       }
     570          20 :       dd.Allgatherv(&dd.indexToBeSent[0],count,&dd.indexToBeReceived[0],&counts[0],&displ[0]);
     571          20 :       dd.Allgatherv(&dd.positionsToBeSent[0],ndata*count,&dd.positionsToBeReceived[0],&counts5[0],&displ5[0]);
     572          20 :       int tot=displ[n-1]+counts[n-1];
     573        1620 :       for(int i=0; i<tot; i++) {
     574             :         int dpoint=0;
     575        7264 :         for(unsigned j=0; j<values_to_get.size(); ++j) {
     576        5664 :           values_to_get[j]->data[dd.indexToBeReceived[i]] = dd.positionsToBeReceived[ndata*i+dpoint];
     577        5664 :           dpoint++;
     578             :         }
     579             :       }
     580             :     }
     581             :   }
     582       76029 : }
     583             : 
     584       76019 : void DomainDecomposition::wait() {
     585      456114 :   for(const auto & ip : inputs) {
     586      380095 :     ip->dataCanBeSet=false;
     587             :   }
     588             : 
     589       76019 :   if(dd && shuffledAtoms>0) {
     590             :     int ndata=0;
     591             :     std::vector<Value*> values_to_set;
     592       13128 :     for(const auto & ip : inputs) {
     593       10940 :       if( (!ip->fixed || firststep) && ip->wasset ) {
     594        6708 :         values_to_set.push_back(ip->copyOutput(0));
     595        6708 :         ndata++;
     596             :       }
     597             :     }
     598             : 
     599             : // receive toBeReceived
     600        2188 :     if(asyncSent) {
     601             :       Communicator::Status status;
     602             :       std::size_t count=0;
     603        9802 :       for(int i=0; i<dd.Get_size(); i++) {
     604        7634 :         dd.Recv(&dd.indexToBeReceived[count],dd.indexToBeReceived.size()-count,i,666,status);
     605        7634 :         int c=status.Get_count<int>();
     606        7634 :         dd.Recv(&dd.positionsToBeReceived[ndata*count],dd.positionsToBeReceived.size()-ndata*count,i,667);
     607        7634 :         count+=c;
     608             :       }
     609       68220 :       for(unsigned i=0; i<count; i++) {
     610             :         int dpoint=0;
     611      276100 :         for(unsigned j=0; j<values_to_set.size(); ++j) {
     612      210048 :           values_to_set[j]->set(dd.indexToBeReceived[i], dd.positionsToBeReceived[ndata*i+dpoint] );
     613      210048 :           dpoint++;
     614             :         }
     615             :       }
     616        2168 :       asyncSent=false;
     617             :     }
     618             :   }
     619       76019 : }
     620             : 
     621           0 : unsigned DomainDecomposition::getNumberOfForcesToRescale() const {
     622           0 :   return gatindex.size();
     623             : }
     624             : 
     625       75895 : void DomainDecomposition::apply() {
     626       75895 :   std::vector<unsigned> forced_uniq_index(forced_unique.size());
     627       75895 :   if(!(gatindex.size()==getNumberOfAtoms() && shuffledAtoms==0)) {
     628       24181 :     for(unsigned i=0; i<forced_unique.size(); i++) {
     629       22081 :       forced_uniq_index[i]=g2l[forced_unique[i].index()];
     630             :     }
     631             :   } else {
     632     4496297 :     for(unsigned i=0; i<forced_unique.size(); i++) {
     633     4422502 :       forced_uniq_index[i]=forced_unique[i].index();
     634             :     }
     635             :   }
     636      455360 :   for(const auto & ip : inputs) {
     637      379467 :     if( !(ip->getPntrToValue())->forcesWereAdded() || ip->noforce ) {
     638      220681 :       continue;
     639      158786 :     } else if( ip->wasscaled || (!unique_serial && gatindex.size()==getNumberOfAtoms() && shuffledAtoms==0) ) {
     640       96131 :       (ip->mydata)->add_force( gatindex, ip->getPntrToValue() );
     641             :     } else {
     642       62655 :       (ip->mydata)->add_force( forced_unique, forced_uniq_index, ip->getPntrToValue() );
     643             :     }
     644             :   }
     645       75893 : }
     646             : 
     647       75893 : void DomainDecomposition::reset() {
     648       75893 :   if( !unique_serial && gatindex.size()==getNumberOfAtoms() && shuffledAtoms==0 ) {
     649             :     return;
     650             :   }
     651             :   // This is an optimisation to ensure that we don't call std::fill over the whole forces
     652             :   // array if there are a small number of atoms passed between the MD code and PLUMED
     653       23454 :   if( dd && shuffledAtoms>0 ) {
     654        2074 :     getAllActiveAtoms( unique );
     655             :   }
     656      140724 :   for(const auto & ip : inputs) {
     657      117270 :     (ip->copyOutput(0))->clearInputForce( unique );
     658             :   }
     659             : }
     660             : 
     661         114 : void DomainDecomposition::writeBinary(std::ostream&o) {
     662         684 :   for(const auto & ip : inputs) {
     663         570 :     ip->writeBinary(o);
     664             :   }
     665         114 : }
     666             : 
     667         114 : void DomainDecomposition::readBinary(std::istream&i) {
     668         684 :   for(const auto & ip : inputs) {
     669         570 :     ip->readBinary(i);
     670             :   }
     671         114 : }
     672             : 
     673       72211 : void DomainDecomposition::broadcastToDomains( Value* val ) {
     674       72211 :   if( dd ) {
     675       20496 :     dd.Bcast( val->data, 0 );
     676             :   }
     677       72211 : }
     678             : 
     679        3989 : void DomainDecomposition::sumOverDomains( Value* val ) {
     680        3989 :   if( dd && shuffledAtoms>0 ) {
     681           0 :     dd.Sum( val->data );
     682             :   }
     683        3989 : }
     684             : 
     685         900 : const long int& DomainDecomposition::getDdStep() const {
     686         900 :   return ddStep;
     687             : }
     688             : 
     689         459 : const std::vector<int>& DomainDecomposition::getGatindex() const {
     690         459 :   return gatindex;
     691             : }
     692             : 
     693        2183 : void DomainDecomposition::getAllActiveAtoms( std::vector<AtomNumber>& u ) {
     694             :   gch::small_vector<const std::vector<AtomNumber>*,32> vectors;
     695             :   vectors.reserve(actions.size());
     696       21016 :   for(unsigned i=0; i<actions.size(); i++) {
     697       18833 :     if(actions[i]->isActive()) {
     698       13033 :       if(!actions[i]->getUnique().empty()) {
     699             :         // unique are the local atoms
     700       22912 :         vectors.push_back(&actions[i]->getUnique());
     701             :       }
     702             :     }
     703             :   }
     704             :   u.clear();
     705        2183 :   mergeVectorTools::mergeSortedVectors(vectors,u);
     706        2183 : }
     707             : 
     708         116 : void DomainDecomposition::createFullList(const TypesafePtr & n) {
     709         116 :   if( firststep ) {
     710           7 :     int natoms = getNumberOfAtoms();
     711           7 :     n.set(natoms);
     712           7 :     fullList.resize(natoms);
     713         803 :     for(int i=0; i<natoms; i++) {
     714         796 :       fullList[i]=i;
     715             :     }
     716             :   } else {
     717             : // We update here the unique list defined at Atoms::unique.
     718             : // This is not very clear, and probably should be coded differently.
     719             : // Hopefully this fix the longstanding issue with NAMD.
     720         109 :     getAllActiveAtoms( unique );
     721         109 :     fullList.clear();
     722         109 :     fullList.reserve(unique.size());
     723        5012 :     for(const auto & p : unique) {
     724        4903 :       fullList.push_back(p.index());
     725             :     }
     726         109 :     n.set(int(fullList.size()));
     727             :   }
     728         116 : }
     729             : 
     730         116 : void DomainDecomposition::getFullList(const TypesafePtr & x) {
     731             :   auto xx=x.template get<const int**>();
     732         116 :   if(!fullList.empty()) {
     733         110 :     *xx=&fullList[0];
     734             :   } else {
     735           6 :     *xx=NULL;
     736             :   }
     737         116 : }
     738             : 
     739         116 : void DomainDecomposition::clearFullList() {
     740         116 :   fullList.resize(0);
     741         116 : }
     742             : 
     743             : }

Generated by: LCOV version 1.16