Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2012-2019 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 223564 : 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 329370 : 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 1942684190 : 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 41822 : void DynamicList<T>::clear() {
219 41822 : all.resize(0);
220 41822 : onoff.resize(0); active.resize(0);
221 41822 : }
222 :
223 : template <typename T>
224 421254144 : bool DynamicList<T>::isActive( const unsigned& i ) const {
225 842508288 : 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 54367155 : unsigned DynamicList<T>::getNumberActive() const {
235 54367155 : 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 151611 : void DynamicList<T>::createIndexListFromVector( const std::vector<T>& myind ) {
245 303222 : plumed_dbg_assert( all.size()==0 ); onoff.resize( myind.size(), 0 );
246 303222 : active.resize( myind.size() );
247 303222 : all.insert( all.end(), myind.begin(), myind.end() );
248 151611 : }
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 2103230 : void DynamicList<T>::deactivateAll() {
276 2103230 : allWereDeactivated=true; allWereActivated=false;
277 245479928 : for(unsigned i=0; i<nactive; ++i) onoff[ active[i] ]= 0;
278 2103230 : nactive=0;
279 : #ifndef NDEBUG
280 : for(unsigned i=0; i<onoff.size(); ++i) plumed_dbg_assert( onoff[i]==0 );
281 : #endif
282 2103230 : }
283 :
284 : template <typename T>
285 1493117139 : 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 2986234278 : onoff[ii]=nprocessors;
289 1493117139 : }
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 61844 : void DynamicList<T>::updateActiveMembers() {
309 : plumed_dbg_assert( allWereActivated || allWereDeactivated );
310 61844 : unsigned kk=0; allWereActivated=allWereDeactivated=false;
311 56900269 : for(unsigned i=0; i<all.size(); ++i) {
312 21440679 : if( onoff[i]>0 && onoff[i]%nprocessors==0 ) { active[kk]=i; kk++; }
313 : }
314 61844 : nactive=kk;
315 61844 : }
316 :
317 : template <typename T>
318 946777 : void DynamicList<T>::emptyActiveMembers() {
319 : plumed_dbg_assert( allWereActivated || allWereDeactivated );
320 946777 : nactive=0;
321 946777 : }
322 :
323 : template <typename T>
324 78558980 : 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 157117960 : active[nactive]=ii; nactive++;
328 78558980 : }
329 :
330 : template <typename T>
331 890647 : void DynamicList<T>::completeUpdate() {
332 : plumed_dbg_assert( allWereActivated || allWereDeactivated );
333 890647 : allWereActivated=allWereDeactivated=false;
334 890647 : }
335 :
336 : template <typename T>
337 56130 : void DynamicList<T>::sortActiveList() {
338 : plumed_dbg_assert( allWereActivated || allWereDeactivated );
339 56130 : allWereActivated=allWereDeactivated=false;
340 56130 : std::sort( active.begin(), active.begin()+nactive );
341 56130 : }
342 :
343 : template <typename T>
344 4683178 : bool DynamicList<T>::updateComplete() const {
345 4683178 : if( !allWereActivated && !allWereDeactivated ) return true;
346 945342 : 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 4839 : }
378 :
379 : #endif
380 :
|