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