Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2012-2020 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 722095 : 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 1076994 : 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 1915883972 : 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 421156870 : bool DynamicList<T>::isActive( const unsigned& i ) const { 225 842313740 : 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 54122356 : unsigned DynamicList<T>::getNumberActive() const { 235 54122356 : 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 400851 : void DynamicList<T>::createIndexListFromVector( const std::vector<T>& myind ) { 245 801702 : plumed_dbg_assert( all.size()==0 ); onoff.resize( myind.size(), 0 ); 246 801702 : active.resize( myind.size() ); 247 801702 : all.insert( all.end(), myind.begin(), myind.end() ); 248 400851 : } 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 2031257 : void DynamicList<T>::deactivateAll() { 276 2031257 : allWereDeactivated=true; allWereActivated=false; 277 238685639 : for(unsigned i=0; i<nactive; ++i) onoff[ active[i] ]= 0; 278 2031257 : nactive=0; 279 : #ifndef NDEBUG 280 : for(unsigned i=0; i<onoff.size(); ++i) plumed_dbg_assert( onoff[i]==0 ); 281 : #endif 282 2031257 : } 283 : 284 : template <typename T> 285 1484902003 : 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 2969804006 : onoff[ii]=nprocessors; 289 1484902003 : } 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 62067 : void DynamicList<T>::updateActiveMembers() { 309 : plumed_dbg_assert( allWereActivated || allWereDeactivated ); 310 62067 : unsigned kk=0; allWereActivated=allWereDeactivated=false; 311 57014124 : for(unsigned i=0; i<all.size(); ++i) { 312 21479871 : if( onoff[i]>0 && onoff[i]%nprocessors==0 ) { active[kk]=i; kk++; } 313 : } 314 62067 : nactive=kk; 315 62067 : } 316 : 317 : template <typename T> 318 902714 : void DynamicList<T>::emptyActiveMembers() { 319 : plumed_dbg_assert( allWereActivated || allWereDeactivated ); 320 902714 : nactive=0; 321 902714 : } 322 : 323 : template <typename T> 324 76317260 : 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 152634520 : active[nactive]=ii; nactive++; 328 76317260 : } 329 : 330 : template <typename T> 331 846434 : void DynamicList<T>::completeUpdate() { 332 : plumed_dbg_assert( allWereActivated || allWereDeactivated ); 333 846434 : allWereActivated=allWereDeactivated=false; 334 846434 : } 335 : 336 : template <typename T> 337 56280 : void DynamicList<T>::sortActiveList() { 338 : plumed_dbg_assert( allWereActivated || allWereDeactivated ); 339 56280 : allWereActivated=allWereDeactivated=false; 340 56280 : std::sort( active.begin(), active.begin()+nactive ); 341 56280 : } 342 : 343 : template <typename T> 344 4790593 : bool DynamicList<T>::updateComplete() const { 345 4790593 : if( !allWereActivated && !allWereDeactivated ) return true; 346 901480 : 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 5517 : } 378 : 379 : #endif 380 :