LCOV - code coverage report
Current view: top level - tools - DynamicList.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 56 56 100.0 %
Date: 2026-03-30 13:16:06 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      360662 :   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   686682664 :     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) {
     214             :     this_active[k]=all[ active[k] ];
     215             :   }
     216             :   return this_active;
     217             : }
     218             : 
     219             : template <typename T>
     220       41860 : void DynamicList<T>::clear() {
     221       41860 :   all.resize(0);
     222       41860 :   onoff.resize(0);
     223       41860 :   active.resize(0);
     224       41860 : }
     225             : 
     226             : template <typename T>
     227   425653400 : bool DynamicList<T>::isActive( const unsigned& i ) const {
     228   425653400 :   return (onoff[i]>0 && onoff[i]%nprocessors==0);
     229             : }
     230             : 
     231             : template <typename T>
     232             : unsigned DynamicList<T>::fullSize() const {
     233             :   return all.size();
     234             : }
     235             : 
     236             : template <typename T>
     237    60114156 : unsigned DynamicList<T>::getNumberActive() const {
     238    60114156 :   return nactive;
     239             : }
     240             : 
     241             : template <typename T>
     242          91 : void DynamicList<T>::addIndexToList( const T & ii ) {
     243          91 :   all.push_back(ii);
     244          91 :   active.resize( all.size() );
     245          91 :   onoff.push_back(0);
     246          91 : }
     247             : 
     248             : template <typename T>
     249      402521 : void DynamicList<T>::createIndexListFromVector( const std::vector<T>& myind ) {
     250             :   plumed_dbg_assert( all.size()==0 );
     251      402521 :   onoff.resize( myind.size(), 0 );
     252      402521 :   active.resize( myind.size() );
     253      402521 :   all.insert( all.end(), myind.begin(), myind.end() );
     254      402521 : }
     255             : 
     256             : template <typename T>
     257             : void DynamicList<T>::setupMPICommunication( Communicator& comm ) {
     258             :   nprocessors=comm.Get_size();
     259             :   rank=comm.Get_rank();
     260             : }
     261             : 
     262             : template <typename T>
     263             : int DynamicList<T>::getIndexOfElement( const T& t ) const {
     264             :   for(unsigned i=0; i<all.size(); ++i) {
     265             :     if( t==all[i] ) {
     266             :       return i;
     267             :     }
     268             :   }
     269             :   plumed_merror("Could not find an element in the dynamic list");
     270             : }
     271             : 
     272             : template <typename T>
     273             : void DynamicList<T>::deactivate( const T& t ) {
     274             :   plumed_dbg_assert( allWereActivated );
     275             :   unsigned ii=getIndexOfElement( t );
     276             :   if( onoff[ii]==0 || onoff[ii]%nprocessors!=0 ) {
     277             :     return;
     278             :   }
     279             :   // Deactivates the component
     280             :   if( rank==0 ) {
     281             :     onoff[ii]=nprocessors-1;
     282             :   } else {
     283             :     onoff[ii]=nprocessors-rank;
     284             :   }
     285             : }
     286             : 
     287             : template <typename T>
     288     2753510 : void DynamicList<T>::deactivateAll() {
     289     2753510 :   allWereDeactivated=true;
     290     2753510 :   allWereActivated=false;
     291    90512778 :   for(unsigned i=0; i<nactive; ++i) {
     292    87759268 :     onoff[ active[i] ]= 0;
     293             :   }
     294     2753510 :   nactive=0;
     295             : #ifndef NDEBUG
     296             :   for(unsigned i=0; i<onoff.size(); ++i) {
     297             :     plumed_dbg_assert( onoff[i]==0 );
     298             :   }
     299             : #endif
     300     2753510 : }
     301             : 
     302             : template <typename T>
     303  1685646055 : void DynamicList<T>::activate( const unsigned ii ) {
     304             :   plumed_dbg_massert(ii<all.size(),"ii is out of bounds");
     305             :   plumed_dbg_assert( !allWereActivated );
     306  1685646055 :   onoff[ii]=nprocessors;
     307  1685646055 : }
     308             : 
     309             : template <typename T>
     310             : void DynamicList<T>::activateAll() {
     311             :   for(unsigned i=0; i<onoff.size(); ++i) {
     312             :     onoff[i]=nprocessors;
     313             :   }
     314             :   allWereActivated=true;
     315             :   updateActiveMembers();
     316             :   allWereActivated=true;
     317             : 
     318             : }
     319             : 
     320             : template <typename T>
     321             : void DynamicList<T>::mpi_gatherActiveMembers(Communicator& comm) {
     322             :   plumed_massert( comm.Get_size()==nprocessors, "error missing a call to DynamicList::setupMPICommunication");
     323             :   comm.Sum(&onoff[0],onoff.size());
     324             :   // When we mpi gather onoff to be on it should be active on ALL nodes
     325             :   for(unsigned i=0; i<all.size(); ++i)
     326             :     if( onoff[i]>0 && onoff[i]%nprocessors==0 ) {
     327             :       onoff[i]=nprocessors;
     328             :     }
     329             :   updateActiveMembers();
     330             : }
     331             : 
     332             : template <typename T>
     333       62072 : void DynamicList<T>::updateActiveMembers() {
     334             :   plumed_dbg_assert( allWereActivated || allWereDeactivated );
     335             :   unsigned kk=0;
     336       62072 :   allWereActivated=allWereDeactivated=false;
     337    19025522 :   for(unsigned i=0; i<all.size(); ++i) {
     338    18963450 :     if( onoff[i]>0 && onoff[i]%nprocessors==0 ) {
     339     2549625 :       active[kk]=i;
     340     2549625 :       kk++;
     341             :     }
     342             :   }
     343       62072 :   nactive=kk;
     344       62072 : }
     345             : 
     346             : template <typename T>
     347     1055630 : void DynamicList<T>::emptyActiveMembers() {
     348             :   plumed_dbg_assert( allWereActivated || allWereDeactivated );
     349     1055630 :   nactive=0;
     350     1055630 : }
     351             : 
     352             : template <typename T>
     353    85160918 : void DynamicList<T>::putIndexInActiveArray( const unsigned& ii ) {
     354             :   plumed_dbg_assert( allWereActivated || allWereDeactivated );
     355             :   plumed_dbg_assert( onoff[ii]>0 && onoff[ii]%nprocessors==0 );
     356    85160918 :   active[nactive]=ii;
     357    85160918 :   nactive++;
     358    85160918 : }
     359             : 
     360             : template <typename T>
     361      999325 : void DynamicList<T>::completeUpdate() {
     362             :   plumed_dbg_assert( allWereActivated || allWereDeactivated );
     363      999325 :   allWereActivated=allWereDeactivated=false;
     364      999325 : }
     365             : 
     366             : template <typename T>
     367       56305 : void DynamicList<T>::sortActiveList() {
     368             :   plumed_dbg_assert( allWereActivated || allWereDeactivated );
     369       56305 :   allWereActivated=allWereDeactivated=false;
     370       56305 :   std::sort( active.begin(), active.begin()+nactive );
     371       56305 : }
     372             : 
     373             : template <typename T>
     374     5247007 : bool DynamicList<T>::updateComplete() const {
     375     5247007 :   if( !allWereActivated && !allWereDeactivated ) {
     376     4192631 :     return true;
     377             :   }
     378             :   return false;
     379             : }
     380             : 
     381             : template <typename U>
     382             : void mpi_gatherActiveMembers(Communicator& comm, std::vector< DynamicList<U> >& ll ) {
     383             :   // Setup an array to hold all data
     384             :   unsigned bufsize=0;
     385             :   unsigned size=comm.Get_size();
     386             :   for(unsigned i=0; i<ll.size(); ++i) {
     387             :     plumed_dbg_massert( ll[i].nprocessors==size, "missing a call to DynamicList::setupMPICommunication" );
     388             :     bufsize+=ll[i].onoff.size();
     389             :   }
     390             :   std::vector<unsigned> buffer( bufsize );
     391             :   // Gather all onoff data into a single array
     392             :   bufsize=0;
     393             :   for(unsigned i=0; i<ll.size(); ++i) {
     394             :     for(unsigned j=0; j<ll[i].onoff.size(); ++j) {
     395             :       buffer[bufsize]=ll[i].onoff[j];
     396             :       bufsize++;
     397             :     }
     398             :   }
     399             :   // GATHER from all nodes
     400             :   comm.Sum(&buffer[0],buffer.size());
     401             :   // distribute back to original lists
     402             :   bufsize=0;
     403             :   for(unsigned i=0; i<ll.size(); ++i) {
     404             :     for(unsigned j=0; j<ll[i].onoff.size(); ++j) {
     405             :       if( buffer[bufsize]>0 && buffer[bufsize]%size==0 ) {
     406             :         ll[i].onoff[j]=size;
     407             :       } else {
     408             :         ll[i].onoff[j]=size-1;
     409             :       }
     410             :       bufsize++;
     411             :     }
     412             :   }
     413             :   for(unsigned i=0; i<ll.size(); ++i) {
     414             :     ll[i].updateActiveMembers();
     415             :   }
     416             : }
     417             : 
     418             : }
     419             : 
     420             : #endif
     421             : 

Generated by: LCOV version 1.16