LCOV - code coverage report
Current view: top level - tools - DynamicList.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 50 50 100.0 %
Date: 2020-11-18 11:20:57 Functions: 18 18 100.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-2019 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 bussiness comes when we start activating and deactivating members.  When we create
      93             : a dynamic list none of the members are active for bussiness.  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      223564 : 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 relevent 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      329370 :   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  1942684190 :     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       41822 : void DynamicList<T>::clear() {
     219       41822 :   all.resize(0);
     220       41822 :   onoff.resize(0); active.resize(0);
     221       41822 : }
     222             : 
     223             : template <typename T>
     224   421254144 : bool DynamicList<T>::isActive( const unsigned& i ) const {
     225   842508288 :   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    54367155 : unsigned DynamicList<T>::getNumberActive() const {
     235    54367155 :   return nactive;
     236             : }
     237             : 
     238             : template <typename T>
     239          91 : void DynamicList<T>::addIndexToList( const T & ii ) {
     240         273 :   all.push_back(ii); active.resize( all.size() ); onoff.push_back(0);
     241          91 : }
     242             : 
     243             : template <typename T>
     244      151611 : void DynamicList<T>::createIndexListFromVector( const std::vector<T>& myind ) {
     245      303222 :   plumed_dbg_assert( all.size()==0 ); onoff.resize( myind.size(), 0 );
     246      303222 :   active.resize( myind.size() );
     247      303222 :   all.insert( all.end(), myind.begin(), myind.end() );
     248      151611 : }
     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             :   return 0;
     262             : }
     263             : 
     264             : template <typename T>
     265             : void DynamicList<T>::deactivate( const T& t ) {
     266             :   plumed_dbg_assert( allWereActivated );
     267             :   unsigned ii=getIndexOfElement( t );
     268             :   if( onoff[ii]==0 || onoff[ii]%nprocessors!=0 ) return;
     269             :   // Deactivates the component
     270             :   if( rank==0 ) onoff[ii]=nprocessors-1;
     271             :   else onoff[ii]=nprocessors-rank;
     272             : }
     273             : 
     274             : template <typename T>
     275     2103230 : void DynamicList<T>::deactivateAll() {
     276     2103230 :   allWereDeactivated=true; allWereActivated=false;
     277   245479928 :   for(unsigned i=0; i<nactive; ++i) onoff[ active[i] ]= 0;
     278     2103230 :   nactive=0;
     279             : #ifndef NDEBUG
     280             :   for(unsigned i=0; i<onoff.size(); ++i) plumed_dbg_assert( onoff[i]==0 );
     281             : #endif
     282     2103230 : }
     283             : 
     284             : template <typename T>
     285  1493117139 : void DynamicList<T>::activate( const unsigned ii ) {
     286             :   plumed_dbg_massert(ii<all.size(),"ii is out of bounds");
     287             :   plumed_dbg_assert( !allWereActivated );
     288  2986234278 :   onoff[ii]=nprocessors;
     289  1493117139 : }
     290             : 
     291             : template <typename T>
     292             : void DynamicList<T>::activateAll() {
     293             :   for(unsigned i=0; i<onoff.size(); ++i) onoff[i]=nprocessors;
     294             :   allWereActivated=true; updateActiveMembers(); allWereActivated=true;
     295             : 
     296             : }
     297             : 
     298             : template <typename T>
     299             : void DynamicList<T>::mpi_gatherActiveMembers(Communicator& comm) {
     300             :   plumed_massert( comm.Get_size()==nprocessors, "error missing a call to DynamicList::setupMPICommunication");
     301             :   comm.Sum(&onoff[0],onoff.size());
     302             :   // When we mpi gather onoff to be on it should be active on ALL nodes
     303             :   for(unsigned i=0; i<all.size(); ++i) if( onoff[i]>0 && onoff[i]%nprocessors==0 ) { onoff[i]=nprocessors; }
     304             :   updateActiveMembers();
     305             : }
     306             : 
     307             : template <typename T>
     308       61844 : void DynamicList<T>::updateActiveMembers() {
     309             :   plumed_dbg_assert( allWereActivated || allWereDeactivated );
     310       61844 :   unsigned kk=0; allWereActivated=allWereDeactivated=false;
     311    56900269 :   for(unsigned i=0; i<all.size(); ++i) {
     312    21440679 :     if( onoff[i]>0 && onoff[i]%nprocessors==0 ) { active[kk]=i; kk++; }
     313             :   }
     314       61844 :   nactive=kk;
     315       61844 : }
     316             : 
     317             : template <typename T>
     318      946777 : void DynamicList<T>::emptyActiveMembers() {
     319             :   plumed_dbg_assert( allWereActivated || allWereDeactivated );
     320      946777 :   nactive=0;
     321      946777 : }
     322             : 
     323             : template <typename T>
     324    78558980 : void DynamicList<T>::putIndexInActiveArray( const unsigned& ii ) {
     325             :   plumed_dbg_assert( allWereActivated || allWereDeactivated );
     326             :   plumed_dbg_assert( onoff[ii]>0 && onoff[ii]%nprocessors==0 );
     327   157117960 :   active[nactive]=ii; nactive++;
     328    78558980 : }
     329             : 
     330             : template <typename T>
     331      890647 : void DynamicList<T>::completeUpdate() {
     332             :   plumed_dbg_assert( allWereActivated || allWereDeactivated );
     333      890647 :   allWereActivated=allWereDeactivated=false;
     334      890647 : }
     335             : 
     336             : template <typename T>
     337       56130 : void DynamicList<T>::sortActiveList() {
     338             :   plumed_dbg_assert( allWereActivated || allWereDeactivated );
     339       56130 :   allWereActivated=allWereDeactivated=false;
     340       56130 :   std::sort( active.begin(), active.begin()+nactive );
     341       56130 : }
     342             : 
     343             : template <typename T>
     344     4683178 : bool DynamicList<T>::updateComplete() const {
     345     4683178 :   if( !allWereActivated && !allWereDeactivated ) return true;
     346      945342 :   return false;
     347             : }
     348             : 
     349             : template <typename U>
     350             : void mpi_gatherActiveMembers(Communicator& comm, std::vector< DynamicList<U> >& ll ) {
     351             :   // Setup an array to hold all data
     352             :   unsigned bufsize=0; unsigned size=comm.Get_size();
     353             :   for(unsigned i=0; i<ll.size(); ++i) {
     354             :     plumed_dbg_massert( ll[i].nprocessors==size, "missing a call to DynamicList::setupMPICommunication" );
     355             :     bufsize+=ll[i].onoff.size();
     356             :   }
     357             :   std::vector<unsigned> buffer( bufsize );
     358             :   // Gather all onoff data into a single array
     359             :   bufsize=0;
     360             :   for(unsigned i=0; i<ll.size(); ++i) {
     361             :     for(unsigned j=0; j<ll[i].onoff.size(); ++j) { buffer[bufsize]=ll[i].onoff[j]; bufsize++; }
     362             :   }
     363             :   // GATHER from all nodes
     364             :   comm.Sum(&buffer[0],buffer.size());
     365             :   // distribute back to original lists
     366             :   bufsize=0;
     367             :   for(unsigned i=0; i<ll.size(); ++i) {
     368             :     for(unsigned j=0; j<ll[i].onoff.size(); ++j) {
     369             :       if( buffer[bufsize]>0 && buffer[bufsize]%size==0 ) ll[i].onoff[j]=size;
     370             :       else ll[i].onoff[j]=size-1;
     371             :       bufsize++;
     372             :     }
     373             :   }
     374             :   for(unsigned i=0; i<ll.size(); ++i) ll[i].updateActiveMembers();
     375             : }
     376             : 
     377        4839 : }
     378             : 
     379             : #endif
     380             : 

Generated by: LCOV version 1.13