All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
DynamicList.h
Go to the documentation of this file.
1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2  Copyright (c) 2013 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-code.org for more information.
6 
7  This file is part of plumed, version 2.0.
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 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 /// This translates a position from all to active
145  std::vector<int> translator;
146 /// The current number of active members
147  unsigned nactive;
148 /// This is the list of active members
149  std::vector<unsigned> active;
150 /// the number of processors the jobs in the Dynamic list are distributed across
151  unsigned nprocessors;
152 /// The rank of the node we are on
153  unsigned rank;
154 /// This is a flag that is used internally to ensure that dynamic lists are being used properly
156 public:
157 /// Constructor
158  DynamicList():nactive(0),nprocessors(1),rank(0),allWereActivated(false) {}
159 /// An operator that returns the element from the current active list
160  inline T operator [] (const unsigned& i) const {
161  plumed_dbg_assert( i<nactive );
162  return all[ active[i] ];
163  }
164 /// An operator that returns the element from the full list (used in neighbour lists)
165  inline T operator () (const unsigned& i) const {
166  plumed_dbg_assert( i<all.size() );
167  return all[i];
168  }
169 /// Clear the list
170  void clear();
171 /// Return the total number of elements in the list
172  unsigned fullSize() const;
173 /// Return the number of elements that are currently active
174  unsigned getNumberActive() const;
175 /// Find out if a member is active
176  bool isActive(const unsigned& ) const;
177 /// Setup MPI communication if things are activated/deactivated on different nodes
178  void setupMPICommunication( Communicator& comm );
179 /// Add something to the active list
180  void addIndexToList( const T & ii );
181 /// Make a particular element inactive
182  void deactivate( const unsigned ii );
183 /// Make everything in the list inactive
184  void deactivateAll();
185 /// Make something active
186  void activate( const unsigned ii );
187 /// Make everything in the list active
188  void activateAll();
189 /// Do updateActiveMembers for a loop that has been distributed over multiple nodes
191 /// Get the list of active members
192  void updateActiveMembers();
193 /// Empty the list of active members
194  void emptyActiveMembers();
195 /// This can be used for a fast version of updateActiveMembers in which only a subset of the
196 /// indexes are checked
197  void updateIndex( const unsigned& ii );
198 /// This sorts the elements in the active list
199  void sortActiveList();
200 /// Retriee the list of active objects
201  std::vector<T> retrieveActiveList();
202 /// Return the index of this atom
203  unsigned linkIndex( const unsigned& ii ) const ;
204 };
205 
206 template <typename T>
208  std::vector<T> this_active(nactive);
209  for(unsigned k=0;k<nactive;++k) this_active[k]=all[ active[k] ];
210  return this_active;
211 }
212 
213 template <typename T>
215  all.resize(0); translator.resize(0);
216  onoff.resize(0); active.resize(0);
217 }
218 
219 template <typename T>
220 bool DynamicList<T>::isActive( const unsigned& i ) const {
221  return (onoff[i]>0 && onoff[i]%nprocessors==0);
222 }
223 
224 template <typename T>
225 unsigned DynamicList<T>::fullSize() const {
226  return all.size();
227 }
228 
229 template <typename T>
231  return nactive;
232 }
233 
234 template <typename T>
235 void DynamicList<T>::addIndexToList( const T & ii ){
236  all.push_back(ii); active.resize( all.size() );
237  translator.push_back( all.size()-1 ); onoff.push_back(0);
238 }
239 
240 template <typename T>
242  nprocessors=comm.Get_size(); rank=comm.Get_rank();
243 }
244 
245 template <typename T>
246 void DynamicList<T>::deactivate( const unsigned ii ){
247  plumed_dbg_massert(ii<all.size(),"ii is out of bounds");
248  plumed_dbg_assert( allWereActivated );
249  if( onoff[ii]==0 || onoff[ii]%nprocessors!=0 ) return ;
250  if( rank==0 ) onoff[ii]=nprocessors-1;
251  else onoff[ii]=nprocessors-rank;
252 }
253 
254 template <typename T>
256  for(unsigned i=0;i<nactive;++i) onoff[ active[i] ]= 0;
257 #ifndef NDEBUG
258  for(unsigned i=0;i<onoff.size();++i) plumed_dbg_assert( onoff[i]==0 );
259 #endif
260 }
261 
262 template <typename T>
263 void DynamicList<T>::activate( const unsigned ii ){
264  plumed_dbg_massert(ii<all.size(),"ii is out of bounds");
265  plumed_dbg_assert( !allWereActivated );
266  onoff[ii]=nprocessors;
267 }
268 
269 template <typename T>
271  for(unsigned i=0;i<onoff.size();++i) onoff[i]=nprocessors;
272  updateActiveMembers(); allWereActivated=true;
273 }
274 
275 template <typename T>
277  plumed_massert( comm.Get_size()==nprocessors, "error missing a call to DynamicList::setupMPICommunication");
278  comm.Sum(&onoff[0],onoff.size());
279  // When we mpi gather onoff to be on it should be active on ALL nodes
280  for(unsigned i=0;i<all.size();++i) if( onoff[i]>0 && onoff[i]%nprocessors==0 ){ onoff[i]=nprocessors; }
281  updateActiveMembers();
282 }
283 
284 template <typename T>
286  unsigned kk=0;
287  for(unsigned i=0;i<all.size();++i){
288  if( onoff[i]>0 && onoff[i]%nprocessors==0 ){ translator[i]=kk; active[kk]=i; kk++; }
289  }
290  nactive=kk; allWereActivated=false;
291 }
292 
293 template <typename T>
295  nactive=0;
296 }
297 
298 template <typename T>
299 void DynamicList<T>::updateIndex( const unsigned& ii ){
300  if( onoff[ii]>0 && onoff[ii]%nprocessors==0 ){
301  translator[nactive]=nactive; active[nactive]=ii; nactive++;
302  }
303 }
304 
305 template <typename T>
307  std::sort( active.begin(), active.begin()+nactive );
308  for(unsigned i=0;i<nactive;++i) translator[ active[i] ]=i;
309 }
310 
311 template <typename T>
312 unsigned DynamicList<T>::linkIndex( const unsigned& ii ) const {
313  plumed_dbg_assert( onoff[ii]>0 && onoff[ii]%nprocessors==0 );
314  return translator[ii];
315 }
316 
317 template <typename U>
318 void mpi_gatherActiveMembers(Communicator& comm, std::vector< DynamicList<U> >& ll ){
319  // Setup an array to hold all data
320  unsigned bufsize=0; unsigned size=comm.Get_size();
321  for(unsigned i=0;i<ll.size();++i){
322  plumed_dbg_massert( ll[i].nprocessors==size, "missing a call to DynamicList::setupMPICommunication" );
323  bufsize+=ll[i].onoff.size();
324  }
325  std::vector<unsigned> buffer( bufsize );
326  // Gather all onoff data into a single array
327  bufsize=0;
328  for(unsigned i=0;i<ll.size();++i){
329  for(unsigned j=0;j<ll[i].onoff.size();++j){ buffer[bufsize]=ll[i].onoff[j]; bufsize++; }
330  }
331  // GATHER from all nodes
332  comm.Sum(&buffer[0],buffer.size());
333  // distribute back to original lists
334  bufsize=0;
335  for(unsigned i=0;i<ll.size();++i){
336  for(unsigned j=0;j<ll[i].onoff.size();++j){
337  if( buffer[bufsize]>0 && buffer[bufsize]%size==0 ) ll[i].onoff[j]=size;
338  else ll[i].onoff[j]=size-1;
339  bufsize++;
340  }
341  }
342  for(unsigned i=0;i<ll.size();++i) ll[i].updateActiveMembers();
343 }
344 
345 }
346 
347 #endif
348 
void setupMPICommunication(Communicator &comm)
Setup MPI communication if things are activated/deactivated on different nodes.
Definition: DynamicList.h:241
std::vector< T > all
This is the list of all the relevent members.
Definition: DynamicList.h:141
std::vector< int > translator
This translates a position from all to active.
Definition: DynamicList.h:145
void mpi_gatherActiveMembers(Communicator &comm, std::vector< DynamicList< U > > &ll)
Definition: DynamicList.h:318
std::vector< T > retrieveActiveList()
Retriee the list of active objects.
Definition: DynamicList.h:207
bool allWereActivated
This is a flag that is used internally to ensure that dynamic lists are being used properly...
Definition: DynamicList.h:155
void deactivate(const unsigned ii)
Make a particular element inactive.
Definition: DynamicList.h:246
T operator[](const unsigned &i) const
An operator that returns the element from the current active list.
Definition: DynamicList.h:160
A class for storing a list that changes which members are active as a function of time...
Definition: DynamicList.h:135
Class containing wrappers to MPI.
Definition: Communicator.h:44
T operator()(const unsigned &i) const
An operator that returns the element from the full list (used in neighbour lists) ...
Definition: DynamicList.h:165
void activate(const unsigned ii)
Make something active.
Definition: DynamicList.h:263
void updateActiveMembers()
Get the list of active members.
Definition: DynamicList.h:285
DynamicList()
Constructor.
Definition: DynamicList.h:158
void sortActiveList()
This sorts the elements in the active list.
Definition: DynamicList.h:306
unsigned linkIndex(const unsigned &ii) const
Return the index of this atom.
Definition: DynamicList.h:312
void updateIndex(const unsigned &ii)
This can be used for a fast version of updateActiveMembers in which only a subset of the indexes are ...
Definition: DynamicList.h:299
void clear()
Clear the list.
Definition: DynamicList.h:214
int Get_rank() const
Obtain the rank of the present process.
void emptyActiveMembers()
Empty the list of active members.
Definition: DynamicList.h:294
unsigned nactive
The current number of active members.
Definition: DynamicList.h:147
void addIndexToList(const T &ii)
Add something to the active list.
Definition: DynamicList.h:235
bool isActive(const unsigned &) const
Find out if a member is active.
Definition: DynamicList.h:220
std::vector< unsigned > onoff
This tells us what members of all are on/off at any given time.
Definition: DynamicList.h:143
std::vector< unsigned > active
This is the list of active members.
Definition: DynamicList.h:149
friend void mpi_gatherActiveMembers(Communicator &, std::vector< DynamicList< U > > &)
This gathers data split across nodes list of Dynamic lists.
Definition: DynamicList.h:318
unsigned rank
The rank of the node we are on.
Definition: DynamicList.h:153
unsigned fullSize() const
Return the total number of elements in the list.
Definition: DynamicList.h:225
int Get_size() const
Obtain the number of processes.
void deactivateAll()
Make everything in the list inactive.
Definition: DynamicList.h:255
void activateAll()
Make everything in the list active.
Definition: DynamicList.h:270
void Sum(T *, int)
Wrapper for MPI_Allreduce with MPI_SUM.
Definition: Communicator.h:124
unsigned getNumberActive() const
Return the number of elements that are currently active.
Definition: DynamicList.h:230
unsigned nprocessors
the number of processors the jobs in the Dynamic list are distributed across
Definition: DynamicList.h:151