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 :