1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2    Copyright (c) 2012-2021 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
DynamicList()156   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     return all[ active[i] ];
161   }
162 /// An operator that returns the element from the full list (used in neighbour lists)
operator()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>
retrieveActiveList()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>
clear()218 void DynamicList<T>::clear() {
219   all.resize(0);
220   onoff.resize(0); active.resize(0);
221 }
222 
223 template <typename T>
isActive(const unsigned & i)224 bool DynamicList<T>::isActive( const unsigned& i ) const {
225   return (onoff[i]>0 && onoff[i]%nprocessors==0);
226 }
227 
228 template <typename T>
fullSize()229 unsigned DynamicList<T>::fullSize() const {
230   return all.size();
231 }
232 
233 template <typename T>
getNumberActive()234 unsigned DynamicList<T>::getNumberActive() const {
235   return nactive;
236 }
237 
238 template <typename T>
addIndexToList(const T & ii)239 void DynamicList<T>::addIndexToList( const T & ii ) {
240   all.push_back(ii); active.resize( all.size() ); onoff.push_back(0);
241 }
242 
243 template <typename T>
createIndexListFromVector(const std::vector<T> & myind)244 void DynamicList<T>::createIndexListFromVector( const std::vector<T>& myind ) {
245   plumed_dbg_assert( all.size()==0 ); onoff.resize( myind.size(), 0 );
246   active.resize( myind.size() );
247   all.insert( all.end(), myind.begin(), myind.end() );
248 }
249 
250 template <typename T>
setupMPICommunication(Communicator & comm)251 void DynamicList<T>::setupMPICommunication( Communicator& comm ) {
252   nprocessors=comm.Get_size(); rank=comm.Get_rank();
253 }
254 
255 template <typename T>
getIndexOfElement(const T & 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>
deactivate(const T & 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>
deactivateAll()275 void DynamicList<T>::deactivateAll() {
276   allWereDeactivated=true; allWereActivated=false;
277   for(unsigned i=0; i<nactive; ++i) onoff[ active[i] ]= 0;
278   nactive=0;
279 #ifndef NDEBUG
280   for(unsigned i=0; i<onoff.size(); ++i) plumed_dbg_assert( onoff[i]==0 );
281 #endif
282 }
283 
284 template <typename T>
activate(const unsigned ii)285 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   onoff[ii]=nprocessors;
289 }
290 
291 template <typename T>
activateAll()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>
mpi_gatherActiveMembers(Communicator & comm)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>
updateActiveMembers()308 void DynamicList<T>::updateActiveMembers() {
309   plumed_dbg_assert( allWereActivated || allWereDeactivated );
310   unsigned kk=0; allWereActivated=allWereDeactivated=false;
311   for(unsigned i=0; i<all.size(); ++i) {
312     if( onoff[i]>0 && onoff[i]%nprocessors==0 ) { active[kk]=i; kk++; }
313   }
314   nactive=kk;
315 }
316 
317 template <typename T>
emptyActiveMembers()318 void DynamicList<T>::emptyActiveMembers() {
319   plumed_dbg_assert( allWereActivated || allWereDeactivated );
320   nactive=0;
321 }
322 
323 template <typename T>
putIndexInActiveArray(const unsigned & ii)324 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   active[nactive]=ii; nactive++;
328 }
329 
330 template <typename T>
completeUpdate()331 void DynamicList<T>::completeUpdate() {
332   plumed_dbg_assert( allWereActivated || allWereDeactivated );
333   allWereActivated=allWereDeactivated=false;
334 }
335 
336 template <typename T>
sortActiveList()337 void DynamicList<T>::sortActiveList() {
338   plumed_dbg_assert( allWereActivated || allWereDeactivated );
339   allWereActivated=allWereDeactivated=false;
340   std::sort( active.begin(), active.begin()+nactive );
341 }
342 
343 template <typename T>
updateComplete()344 bool DynamicList<T>::updateComplete() const {
345   if( !allWereActivated && !allWereDeactivated ) return true;
346   return false;
347 }
348 
349 template <typename U>
mpi_gatherActiveMembers(Communicator & comm,std::vector<DynamicList<U>> & ll)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 }
378 
379 #endif
380 
381