LCOV - code coverage report
Current view: top level - tools - DynamicList.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 47 47 100.0 %
Date: 2024-10-11 08:09:47 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-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             : #ifndef __PLUMED_tools_DynamicList_h
      23             : #define __PLUMED_tools_DynamicList_h
      24             : 
      25             : #include <vector>
      26             : #include "Communicator.h"
      27             : 
      28             : namespace PLMD {
      29             : 
      30             : /**
      31             : \ingroup TOOLBOX
      32             : A class for storing a list that changes which members are active as a function of time.  It also
      33             : contains friends method that allows you to link two dynamic lists so that you can request
      34             : stuff from list2 in list1
      35             : A PLMD::DynamicList can be used to change what elements in a list should be looped over at any given
      36             : time. This class is, for the most part, used in tandem with PLMD::NeighbourList.  For complex reasons
      37             : related to the PLMD::MultiColvar object the dynamic list class is separate from PLMD::NeighbourList.
      38             : This is no bad thing though as there may be occasions where one needs to change the elements currently
      39             : involved in a calculation using some non neighbour list based method.  To be clear though PLMD::NeighbourList
      40             : will look after everything connected with PLMD::DynamicList other than the initial setup of PLMD::DynamicList
      41             : and the loops over the active elements of the list.
      42             : 
      43             : The essence of a dynamic list is as follows.  Consider the following loop:
      44             : 
      45             : \verbatim
      46             : std::vector<something> aa;
      47             : for(unsigned i=0;i<aa.size();++i){ aa[i].doSomething(); }
      48             : \endverbatim
      49             : 
      50             : This takes all the members in aa and does something or other to them - simple.  Now it may well
      51             : be that the precise set of things from aa that you want to do in any given time or place is not
      52             : always the same.  We can thus use dynamic lists to control what particular things are done are done
      53             : at a given time.  That is to say we can use PLMD::DynamicList to specify a subset of things from
      54             : aa to do at a given time.  This is done by:
      55             : 
      56             : \verbatim
      57             : DynamicList list; std::vector<something> aa; unsigned kk;
      58             : for(unsigned i=0;i<list.getNumberActive();++i){ kk=list[i]; aa[kk].doSomething(); }
      59             : \endverbatim
      60             : 
      61             : where we somewhere set up the list and make some decisions (in PLMD::NeighbourList for example) as to what elements
      62             : from aa are currently active.
      63             : 
      64             : \section Setup
      65             : 
      66             : Setting up a dynamic list is a matter of declaring it and passing a set of indices to it.  For the example
      67             : above with aa one can do this using:
      68             : 
      69             : \verbatim
      70             : DynamicList list;
      71             : for(unsigned i=0;i<aa.size();++i) list.addIndexToList( i );
      72             : \endverbatim
      73             : 
      74             : Doing this creates the list of all members.
      75             : 
      76             : \section arse1 Cycling over the full set of members
      77             : 
      78             : To cycle over the full set of members in the list one should do:
      79             : 
      80             : \verbatim
      81             : for(unsigned i=0;i<list.fullSize();++i){ kk=list(i); aa[kk].doSomething(); }
      82             : \endverbatim
      83             : 
      84             : If the DynamicList was set up as per the example above then this code is equivalent to:
      85             : 
      86             : \verbatim
      87             : for(unsigned i=0;i<aa.size();++i){ aa[i].doSomething(); }
      88             : \endverbatim
      89             : 
      90             : \section arse2 Activating and deactivating members
      91             : 
      92             : The important business comes when we start activating and deactivating members.  When we create
      93             : a dynamic list none of the members are active for business.  Hence, getNumberActive() returns 0.
      94             : There are four routines that we can use to change this situation.
      95             : 
      96             : <table align="center" frame="void" width="95%" cellpadding="5%">
      97             : <tr>
      98             : <td width="5%"> activateAll() </td> <td> make all members active </td>
      99             : </tr><tr>
     100             : <td> activate(i) </td> <td> make the ith element of the list active (in the example above this mean we doSomething() for element i of aa) </td>
     101             : </tr><tr>
     102             : <td> deactivateAll() </td> <td> make all members inactive </td>
     103             : </tr><tr>
     104             : <td> deactivate(i) </td> <td> make th ith element of the list active (in the example above this mean we dont doSomething() for element i of aa) </td>
     105             : </tr>
     106             : </table>
     107             : 
     108             : Once you have activated and deactivated members to your hearts content you can then update the dynamic list using
     109             : PLMD::DynamicList::updateActiveMembers().  Once this is done you can loop over only the members you have specifically
     110             : made active using:
     111             : 
     112             : \verbatim
     113             : DynamicList list;
     114             : for(unsigned i=0;i<list.getNumberActive();++i){ kk=list[i]; aa[kk].doSomething(); }
     115             : \endverbatim
     116             : 
     117             : as was described above.
     118             : 
     119             : \section arse3 Using MPI
     120             : 
     121             : If your loop is distributed over processesors you can still use dynamic lists to activate and deactivate members.
     122             : When running with mpi however you must call PLMD::DynamicList::setupMPICommunication during initialization.  To
     123             : gather the members that have been activated/deactivated during the running of all the processes on all the nodes
     124             : you must call PLMD::DynamicList::mpi_gatherActiveMembers in place of PLMD::DynamicList::updateActiveMembers.
     125             : 
     126             : \section arse4 A final note
     127             : 
     128             : When using dynamic_lists we strongly recommend that you first compile without the -DNDEBUG flag.  When this
     129             : flag is not present many checks are performed inside the dynamic list class, which will help you ensure that
     130             : the dynamic list is used correctly.
     131             : 
     132             : */
     133             : 
     134             : template <typename T>
     135             : class DynamicList {
     136             : /// This gathers data split across nodes list of Dynamic lists
     137             :   template <typename U>
     138             :   friend void mpi_gatherActiveMembers(Communicator&, std::vector< DynamicList<U> >& );
     139             : private:
     140             : /// This is the list of all the relevant members
     141             :   std::vector<T> all;
     142             : /// This tells us what members of all are on/off at any given time
     143             :   std::vector<unsigned> onoff;
     144             : /// The current number of active members
     145             :   unsigned nactive;
     146             : /// This is the list of active members
     147             :   std::vector<unsigned> active;
     148             : /// the number of processors the jobs in the Dynamic list are distributed across
     149             :   unsigned nprocessors;
     150             : /// The rank of the node we are on
     151             :   unsigned rank;
     152             : /// These are flags that are used internally to ensure that dynamic lists are being used properly
     153             :   bool allWereActivated, allWereDeactivated;
     154             : public:
     155             : /// Constructor
     156      360232 :   DynamicList():nactive(0),nprocessors(1),rank(0),allWereActivated(false),allWereDeactivated(false) {}
     157             : /// An operator that returns the element from the current active list
     158             :   inline T operator [] (const unsigned& i) const {
     159             :     plumed_dbg_assert( i<nactive );
     160   642995410 :     return all[ active[i] ];
     161             :   }
     162             : /// An operator that returns the element from the full list (used in neighbour lists)
     163             :   inline T operator () (const unsigned& i) const {
     164             :     plumed_dbg_assert( i<all.size() );
     165             :     return all[i];
     166             :   }
     167             : /// Clear the list
     168             :   void clear();
     169             : /// Return the total number of elements in the list
     170             :   unsigned fullSize() const;
     171             : /// Return the number of elements that are currently active
     172             :   unsigned getNumberActive() const;
     173             : /// Find out if a member is active
     174             :   bool isActive(const unsigned& ) const;
     175             : /// Setup MPI communication if things are activated/deactivated on different nodes
     176             :   void setupMPICommunication( Communicator& comm );
     177             : /// Add something to the active list
     178             :   void addIndexToList( const T & ii );
     179             : /// Create the list from a vector
     180             :   void createIndexListFromVector( const std::vector<T>& myind );
     181             : /// Find the index of in the list which has value t
     182             :   int getIndexOfElement( const T& t ) const ;
     183             : /// Make a particular element inactive
     184             :   void deactivate( const T& t );
     185             : /// Make everything in the list inactive
     186             :   void deactivateAll();
     187             : /// Make something active
     188             :   void activate( const unsigned ii );
     189             : /// Make everything in the list active
     190             :   void activateAll();
     191             : /// Do updateActiveMembers for a loop that has been distributed over multiple nodes
     192             :   void mpi_gatherActiveMembers(Communicator& comm);
     193             : /// Get the list of active members
     194             :   void updateActiveMembers();
     195             : /// Empty the list of active members
     196             :   void emptyActiveMembers();
     197             : /// This can be used for a fast version of updateActiveMembers in which only a subset of the
     198             : /// indexes are checked
     199             :   void putIndexInActiveArray( const unsigned& ii );
     200             : /// This can be called on once update is complete
     201             :   void completeUpdate();
     202             : /// This tells one if an update has been completed
     203             :   bool updateComplete() const ;
     204             : /// This sorts the elements in the active list
     205             :   void sortActiveList();
     206             : /// Retriee the list of active objects
     207             :   std::vector<T> retrieveActiveList();
     208             : };
     209             : 
     210             : template <typename T>
     211             : std::vector<T> DynamicList<T>::retrieveActiveList() {
     212             :   std::vector<T> this_active(nactive);
     213             :   for(unsigned k=0; k<nactive; ++k) this_active[k]=all[ active[k] ];
     214             :   return this_active;
     215             : }
     216             : 
     217             : template <typename T>
     218       41854 : void DynamicList<T>::clear() {
     219       41854 :   all.resize(0);
     220       41854 :   onoff.resize(0); active.resize(0);
     221       41854 : }
     222             : 
     223             : template <typename T>
     224   424151148 : bool DynamicList<T>::isActive( const unsigned& i ) const {
     225   424151148 :   return (onoff[i]>0 && onoff[i]%nprocessors==0);
     226             : }
     227             : 
     228             : template <typename T>
     229             : unsigned DynamicList<T>::fullSize() const {
     230             :   return all.size();
     231             : }
     232             : 
     233             : template <typename T>
     234    54364606 : unsigned DynamicList<T>::getNumberActive() const {
     235    54364606 :   return nactive;
     236             : }
     237             : 
     238             : template <typename T>
     239          91 : void DynamicList<T>::addIndexToList( const T & ii ) {
     240          91 :   all.push_back(ii); active.resize( all.size() ); onoff.push_back(0);
     241          91 : }
     242             : 
     243             : template <typename T>
     244      402085 : void DynamicList<T>::createIndexListFromVector( const std::vector<T>& myind ) {
     245      402085 :   plumed_dbg_assert( all.size()==0 ); onoff.resize( myind.size(), 0 );
     246      402085 :   active.resize( myind.size() );
     247      402085 :   all.insert( all.end(), myind.begin(), myind.end() );
     248      402085 : }
     249             : 
     250             : template <typename T>
     251             : void DynamicList<T>::setupMPICommunication( Communicator& comm ) {
     252             :   nprocessors=comm.Get_size(); rank=comm.Get_rank();
     253             : }
     254             : 
     255             : template <typename T>
     256             : int DynamicList<T>::getIndexOfElement( const T& t ) const {
     257             :   for(unsigned i=0; i<all.size(); ++i) {
     258             :     if( t==all[i] ) {return i; }
     259             :   }
     260             :   plumed_merror("Could not find an element in the dynamic list");
     261             : }
     262             : 
     263             : template <typename T>
     264             : void DynamicList<T>::deactivate( const T& t ) {
     265             :   plumed_dbg_assert( allWereActivated );
     266             :   unsigned ii=getIndexOfElement( t );
     267             :   if( onoff[ii]==0 || onoff[ii]%nprocessors!=0 ) return;
     268             :   // Deactivates the component
     269             :   if( rank==0 ) onoff[ii]=nprocessors-1;
     270             :   else onoff[ii]=nprocessors-rank;
     271             : }
     272             : 
     273             : template <typename T>
     274     2098930 : void DynamicList<T>::deactivateAll() {
     275     2098930 :   allWereDeactivated=true; allWereActivated=false;
     276    82073774 :   for(unsigned i=0; i<nactive; ++i) onoff[ active[i] ]= 0;
     277     2098930 :   nactive=0;
     278             : #ifndef NDEBUG
     279             :   for(unsigned i=0; i<onoff.size(); ++i) plumed_dbg_assert( onoff[i]==0 );
     280             : #endif
     281     2098930 : }
     282             : 
     283             : template <typename T>
     284  1544105863 : void DynamicList<T>::activate( const unsigned ii ) {
     285             :   plumed_dbg_massert(ii<all.size(),"ii is out of bounds");
     286             :   plumed_dbg_assert( !allWereActivated );
     287  1544105863 :   onoff[ii]=nprocessors;
     288  1544105863 : }
     289             : 
     290             : template <typename T>
     291             : void DynamicList<T>::activateAll() {
     292             :   for(unsigned i=0; i<onoff.size(); ++i) onoff[i]=nprocessors;
     293             :   allWereActivated=true; updateActiveMembers(); allWereActivated=true;
     294             : 
     295             : }
     296             : 
     297             : template <typename T>
     298             : void DynamicList<T>::mpi_gatherActiveMembers(Communicator& comm) {
     299             :   plumed_massert( comm.Get_size()==nprocessors, "error missing a call to DynamicList::setupMPICommunication");
     300             :   comm.Sum(&onoff[0],onoff.size());
     301             :   // When we mpi gather onoff to be on it should be active on ALL nodes
     302             :   for(unsigned i=0; i<all.size(); ++i) if( onoff[i]>0 && onoff[i]%nprocessors==0 ) { onoff[i]=nprocessors; }
     303             :   updateActiveMembers();
     304             : }
     305             : 
     306             : template <typename T>
     307       62072 : void DynamicList<T>::updateActiveMembers() {
     308             :   plumed_dbg_assert( allWereActivated || allWereDeactivated );
     309       62072 :   unsigned kk=0; allWereActivated=allWereDeactivated=false;
     310    19025522 :   for(unsigned i=0; i<all.size(); ++i) {
     311    18963450 :     if( onoff[i]>0 && onoff[i]%nprocessors==0 ) { active[kk]=i; kk++; }
     312             :   }
     313       62072 :   nactive=kk;
     314       62072 : }
     315             : 
     316             : template <typename T>
     317      932792 : void DynamicList<T>::emptyActiveMembers() {
     318             :   plumed_dbg_assert( allWereActivated || allWereDeactivated );
     319      932792 :   nactive=0;
     320      932792 : }
     321             : 
     322             : template <typename T>
     323    77407190 : void DynamicList<T>::putIndexInActiveArray( const unsigned& ii ) {
     324             :   plumed_dbg_assert( allWereActivated || allWereDeactivated );
     325             :   plumed_dbg_assert( onoff[ii]>0 && onoff[ii]%nprocessors==0 );
     326    77407190 :   active[nactive]=ii; nactive++;
     327    77407190 : }
     328             : 
     329             : template <typename T>
     330      876487 : void DynamicList<T>::completeUpdate() {
     331             :   plumed_dbg_assert( allWereActivated || allWereDeactivated );
     332      876487 :   allWereActivated=allWereDeactivated=false;
     333      876487 : }
     334             : 
     335             : template <typename T>
     336       56305 : void DynamicList<T>::sortActiveList() {
     337             :   plumed_dbg_assert( allWereActivated || allWereDeactivated );
     338       56305 :   allWereActivated=allWereDeactivated=false;
     339       56305 :   std::sort( active.begin(), active.begin()+nactive );
     340       56305 : }
     341             : 
     342             : template <typename T>
     343     4878569 : bool DynamicList<T>::updateComplete() const {
     344     4878569 :   if( !allWereActivated && !allWereDeactivated ) return true;
     345             :   return false;
     346             : }
     347             : 
     348             : template <typename U>
     349             : void mpi_gatherActiveMembers(Communicator& comm, std::vector< DynamicList<U> >& ll ) {
     350             :   // Setup an array to hold all data
     351             :   unsigned bufsize=0; unsigned size=comm.Get_size();
     352             :   for(unsigned i=0; i<ll.size(); ++i) {
     353             :     plumed_dbg_massert( ll[i].nprocessors==size, "missing a call to DynamicList::setupMPICommunication" );
     354             :     bufsize+=ll[i].onoff.size();
     355             :   }
     356             :   std::vector<unsigned> buffer( bufsize );
     357             :   // Gather all onoff data into a single array
     358             :   bufsize=0;
     359             :   for(unsigned i=0; i<ll.size(); ++i) {
     360             :     for(unsigned j=0; j<ll[i].onoff.size(); ++j) { buffer[bufsize]=ll[i].onoff[j]; bufsize++; }
     361             :   }
     362             :   // GATHER from all nodes
     363             :   comm.Sum(&buffer[0],buffer.size());
     364             :   // distribute back to original lists
     365             :   bufsize=0;
     366             :   for(unsigned i=0; i<ll.size(); ++i) {
     367             :     for(unsigned j=0; j<ll[i].onoff.size(); ++j) {
     368             :       if( buffer[bufsize]>0 && buffer[bufsize]%size==0 ) ll[i].onoff[j]=size;
     369             :       else ll[i].onoff[j]=size-1;
     370             :       bufsize++;
     371             :     }
     372             :   }
     373             :   for(unsigned i=0; i<ll.size(); ++i) ll[i].updateActiveMembers();
     374             : }
     375             : 
     376             : }
     377             : 
     378             : #endif
     379             : 

Generated by: LCOV version 1.15