SSAGES  0.9.3
Software Suite for Advanced General Ensemble Simulations
DirectForwardFlux.cpp
1 
23 #include "DirectForwardFlux.h"
24 #include "FileContents.h"
25 #include "CVs/CVManager.h"
26 #include <iostream>
27 #include <random>
28 #include <queue>
29 
30 namespace SSAGES
31 {
32  void DirectForwardFlux::PostIntegration(Snapshot* snapshot, const CVManager& cvmanager)
33  {
34  //check if we want to check FFS interfaces this timestep
35  //for now, do it every time step
36  if (iteration_ % 1 != 0) return;
37 
38  auto cvs = cvmanager.GetCVs(cvmask_);
39  // check the structure at the beginning of the simulation
40  if (iteration_ == 0)
42 
43  // if _computefluxA0
44  if (_initialFluxFlag)
45  {
46  ComputeInitialFlux(snapshot,cvs);
47  if (!_initialFluxFlag)
48  { //only enter here once
49  InitializeQueue(snapshot,cvs);
50  PrintQueue();
51  }
52  }
53  else if (initializeQueueFlag)
54  {
55  InitializeQueue(snapshot,cvs);
56  PrintQueue();
57  }
58  // Else check the FFS interfaces
59  else
60  {
61  CheckForInterfaceCrossings(snapshot, cvmanager);
62  //FluxBruteForce(snapshot,cvs);
63  }
64  // Other modes?
65  }
66 
68  {
69  //This is the main FFS method. The magic happens here!
70  auto cvs = cvmanager.GetCVs(cvmask_);
71 
72  //QUESTION: Whats the difference between world_ and _comm?
73  //For now I'll use world_ for everything. But if each driver uses multiple procs then I suspect that this will be wrong.
74  _cvvalue = cvs[0]->GetValue();
75 
77  //if ((myFFSConfigID.l == 2) && (myFFSConfigID.n == 20) && (myFFSConfigID.a ==1)){
78  // std::cout << "stop here\n";
79  //}
80  //if (myFFSConfigID.l != _current_interface){
81  // std::cout << "stop here\n";
82  //}
83 
84  //check if I've crossed the next interface
85  bool hasreturned = HasReturnedToA(_cvvalue);
87  unsigned int fail_local=false,success_local=false;
88  //std::vector<bool> in MPI were strange, ended up using std::vector<unsigned int>
89  std::vector<unsigned int> successes (world_.size());
90  std::vector<unsigned int> failures (world_.size());
91 
93  {
94  // make sure this isnt a zombie trajectory that previously failed or succeeded and is just waiting for the queue to get more jobs
95  if (hasreturned)
96  fail_local=true;
97  else if (hascrossed == 1)
98  success_local=true;
99  else if (hascrossed == -1)
100  {
101  //this should never happen if the interfaces are non-intersecting, it would be wise to throw an error here though
102  std::cerr << "Trajectory l"<<myFFSConfigID.l<<"-n"<<myFFSConfigID.n<<"-a"<<myFFSConfigID.a<<" crossed backwards! This shouldnt happen!" << std::endl;
103  //world_.abort(EXIT_FAILURE);
104  }
105  else
106  {
107  //not sure if anything should needs to be done here
108  }
109  }
110 
111  //for each traj that crossed to lambda+1 need to write it to disk (FFSConfigurationFile)
112  //MPIAllgather success_local into successes
113  MPI_Allgather(&success_local,1,MPI_UNSIGNED,successes.data(),1,MPI_UNSIGNED,world_);
114  MPI_Allgather(&fail_local,1,MPI_UNSIGNED,failures.data(),1,MPI_UNSIGNED,world_);
115 
116  int success_count = 0, fail_count = 0;
117  // I dont pass the queue information between procs but I do syncronize 'successes' and 'failures'
118  // as a reuslt all proc should have the same queue throughout the simulation
119  for (int i=0;i<world_.size();i++)
120  {
121  int l,n,a,lprev,nprev,aprev;
122  // write config to lambda+1
123  lprev = myFFSConfigID.l;
124  nprev = myFFSConfigID.n;
125  aprev = myFFSConfigID.a;
126 
127  if (successes[i] == true)
128  {
129  if (i == world_.rank())
130  {
131  //update ffsconfigid's l,n,a
132  l = _current_interface + 1;
133  n = _S[_current_interface] + success_count;
134  a = 0;
135  FFSConfigID newid = FFSConfigID(l,n,a,lprev,nprev,aprev);
136  WriteFFSConfiguration(snapshot,newid,1);
137  }
138  success_count++;
139  }
140  if (failures[i] == true)
141  {
142  if (i == world_.rank())
143  {
144  //update ffsconfigid's l,n,a
145  l = 0; //only fail at lambda 0,
146  n = _nfailure_total + fail_count;
147  a = 0;
148  FFSConfigID newid = FFSConfigID(l,n,a,lprev,nprev,aprev);
149  WriteFFSConfiguration(snapshot,newid,0);
150  }
151  fail_count++;
152  }
153  }
154 
155  //update trajectories
156  if (_saveTrajectories)
157  {
158  if (!_pop_tried_but_empty_queue) //dont update trajectory if zombie job
160  if (success_local || fail_local) //if finished then close it
161  _trajectory_file.close();
162  }
163 
164  //update the number of successes and attempts, same for all proc since Allgathered 'successes' and 'failures'
165  _S[_current_interface] += success_count;
166  _A[_current_interface] += success_count + fail_count;
167  // ^ I dont like storing attempts this way (as is its only when they finish). _A should also include jobs in progress (i.e. jobs currently running). THINK ABOUT THIS!.
168  _nfailure_total += fail_count;
169 
170  //------------------------------
171  //print info
172  if ((success_local) || (fail_local))
173  {
174  std::cout << "Iteration: "<< iteration_ << ", proc " << world_.rank() << "\n";
175  if (success_local)
176  {
177  std::cout << "Successful attempt from interface " << _current_interface
178  << " l"<<myFFSConfigID.l<<"-n"<<myFFSConfigID.n<<"-a"<<myFFSConfigID.a
179  << " (cvvalue_previous: " << _cvvalue_previous << " cvvalue " << _cvvalue << " interface["<<_current_interface+1<<"] = " << _interfaces[_current_interface+1] << "\n";
180  }
181  if (fail_local)
182  {
183  std::cout << "Failed attempt from interface " << _current_interface
184  << " l"<<myFFSConfigID.l<<"-n"<<myFFSConfigID.n<<"-a"<<myFFSConfigID.a
185  << " (cvvalue_previous: " << _cvvalue_previous << " cvvalue " << _cvvalue << " interface[0] = "<< _interfaces[0] << "\n"
186  << "nfailuretotal: " << _nfailure_total << "\n";
187  }
188 
189  std::cout << "A: ";
190  for (auto a : _A)
191  std::cout << a << " ";
192  std::cout << "\n";
193 
194  std::cout << "S: ";
195  for (auto s : _S)
196  std::cout << s << " ";
197  std::cout << "\n";
198 
199  std::cout << "M: ";
200  for (auto m : _M)
201  std::cout << m << " ";
202  std::cout << "\n";
203  }
204  //------------------------------
205 
206  //create new funciton here? (call it SetupNextInterface()
207  // Check if this interface is finished, if so add new tasks to queue, and increment _current_interface
209  {
210  //set N and P
213 
215  {
216  _current_interface += 1;
217  //_N[_current_interface] = _S[_current_interface-1];
218 
219  if (_N[_current_interface] == 0)
220  {
221  std::cerr << "Error! No successes from interface " << _current_interface-1 << " to " << _current_interface <<"! Try turning up M["<<_current_interface-1<<"] or spacing interfaces closer together.\n";
222  MPI_Abort(world_, EXIT_FAILURE);
223  }
224 
225  //for DFFS
226  unsigned int npicks = _M[_current_interface];
227  std::vector<unsigned int> picks;
228  picks.resize(npicks);
229  if (IsMasterRank(world_))
230  {
231  std::uniform_int_distribution<int> distribution(0,_N[_current_interface]-1);
232  for (unsigned int i=0; i < npicks ; i++)
233  picks[i] = distribution(_generator);
234  }
235  MPI_Bcast(picks.data(),npicks,MPI_UNSIGNED,0,world_);
236 
237  //each proc adds to the queue
238  //set correct attempt index if a given ID is picked twice
239  std::vector<unsigned int> attempt_count;
240  attempt_count.resize(_N[_current_interface],0);
241 
242  for (unsigned int i=0; i < npicks ; i++)
243  {
244  unsigned int mypick = picks[i];
245  int l,n,a,lprev,nprev,aprev;
246  lprev = myFFSConfigID.l;
247  nprev = myFFSConfigID.n;
248  aprev = myFFSConfigID.a;
249  //update ffsconfigid's l,n,a
250  l = _current_interface;
251  n = mypick;
252  a = attempt_count[mypick];
253  attempt_count[mypick]++; //this updates attempt number if same config is picked twice
254 
255  FFSConfigIDQueue.emplace_back(l,n,a,lprev,nprev,aprev);
256  }
257  }
258  else
259  {
260  std::cout << "DFFS should be finished here, do something special? like exit?\n";
261  //Hythem said this is "acceptable" until the code is changed
262  PostSimulation(snapshot, cvmanager);
263  }
264  }
265 
266  // if succeeded or failed (or zombie job), get a new config from the queue...but need to be careful that no two procs get the same config
267 
268  // Need to account for zombie jobs that are waiting for a new config
269  unsigned int shouldpop_local = false;
270  std::vector<unsigned int> shouldpop(world_.size(),0);
271 
272  if (success_local || fail_local || _pop_tried_but_empty_queue)
273  shouldpop_local = true;
274 
275  //Pop the queue
276  // Need to perform mpi call so that all proc pop the queue in the same way
277  PopQueueMPI(snapshot,cvs, shouldpop_local);
278 
279  //Anything else to update across mpi?
280  //clean up
282  iteration_++;
283  }
284 
286  {
287  unsigned int npicks = _M[0];
288  std::vector<unsigned int> picks;
289  picks.resize(npicks);
290 
291  if(IsMasterRank(world_))
292  {
293  std::uniform_int_distribution<int> distribution(0,_N[0]-1);
294  for (unsigned int i=0; i < npicks ; i++)
295  picks[i] = distribution(_generator);
296  }
297  MPI_Bcast(picks.data(),npicks,MPI_UNSIGNED,0,world_);
298 
299  //set correct attempt index if a given ID is picked twice
300  std::vector<unsigned int> attempt_count;
301  attempt_count.resize(_N[0],0);
302 
303  //each proc adds to the queue
304  for (unsigned int i=0; i < npicks ; i++)
305  {
306  unsigned int mypick = picks[i];
307  int l,n,a,lprev,nprev,aprev;
308  FFSConfigID *myconfig = &Lambda0ConfigLibrary[picks[i]];
309  lprev = myconfig->l;
310  nprev = myconfig->n;
311  aprev = myconfig->a;
312  //update ffsconfigid's l,n,a
313  // current = previous, thats how you know you're lambda0
314  l = lprev;
315  n = nprev;
316  a = attempt_count[mypick];
317  attempt_count[mypick]++; //this updates attempt number if same config is picked twice
318  FFSConfigIDQueue.emplace_back(l,n,a,lprev,nprev,aprev);
319  }
320  std::cout << "FFSConfigIDQueue has " << FFSConfigIDQueue.size() << " entries upon initialization\n";
321  initializeQueueFlag = false;
322 
323  // now that queue is populated initialize tasks for all processors
324  // ==============================
325  unsigned int shouldpop_local = true;
326  PopQueueMPI(snapshot,cvs,shouldpop_local);
327  }
328 }
329 
Collective variable manager.
Definition: CVManager.h:43
CVList GetCVs(const std::vector< unsigned int > &mask=std::vector< unsigned int >()) const
Get CV iterator.
Definition: CVManager.h:81
void CheckForInterfaceCrossings(Snapshot *, const class CVManager &) override
Function that checks if interfaces have been crossed (different for each FFS flavor)
void PostIntegration(Snapshot *snapshot, const class CVManager &cvmanager) override
Post-integration hook.
void InitializeQueue(Snapshot *, const CVList &) override
Initialize the Queue.
static bool IsMasterRank(const MPI_Comm &comm)
Check if current processor is master.
Nested class to store different FFS Config IDs.
Definition: ForwardFlux.h:43
unsigned int a
Attempt number.
Definition: ForwardFlux.h:47
unsigned int n
Configuration Number.
Definition: ForwardFlux.h:46
unsigned int l
Interface number.
Definition: ForwardFlux.h:45
std::vector< FFSConfigID > Lambda0ConfigLibrary
Data structure that holds a Library N0 configurations at lambda0.
Definition: ForwardFlux.h:96
bool _pop_tried_but_empty_queue
Definition: ForwardFlux.h:132
void AppendTrajectoryFile(Snapshot *, std::ofstream &)
void PostSimulation(Snapshot *snapshot, const class CVManager &cvmanager) override
Method call post simulation.
Definition: ForwardFlux.cpp:48
double _cvvalue_previous
Previous cv position, used to determine if you've crossed an interface since last time.
Definition: ForwardFlux.h:87
unsigned int _nfailure_total
Definition: ForwardFlux.h:149
std::vector< double > _interfaces
FFS Interfaces.
Definition: ForwardFlux.h:81
unsigned int iteration_
Method iteration counter/.
Definition: ForwardFlux.h:175
std::vector< unsigned int > _M
Definition: ForwardFlux.h:109
FFSConfigID myFFSConfigID
The current FFSConfigID of this MPI process.
Definition: ForwardFlux.h:141
bool _saveTrajectories
should the FFS trajectories be saved
Definition: ForwardFlux.h:144
void ComputeInitialFlux(Snapshot *, const CVList &)
Compute Initial Flux.
void PrintQueue()
Print the queue, useful for debugging.
bool _initialFluxFlag
if 1, compute initial flux
Definition: ForwardFlux.h:135
std::vector< unsigned int > _N
Definition: ForwardFlux.h:127
void WriteFFSConfiguration(Snapshot *snapshot, FFSConfigID &ffsconfig, bool wassuccess)
Write a file corresponding to FFSConfigID from current snapshot.
std::ofstream _trajectory_file
file to which the current trajectory is written to
Definition: ForwardFlux.h:169
void CheckInitialStructure(const CVList &)
Function that checks the initial structure that user provides.
Definition: ForwardFlux.cpp:61
std::vector< unsigned int > _S
Definition: ForwardFlux.h:122
std::vector< double > _P
Flag to determine wheter fluxA0 should be calculated, seems not using this.
Definition: ForwardFlux.h:118
bool initializeQueueFlag
if 1, initialize the Queue
Definition: ForwardFlux.h:138
std::default_random_engine _generator
random number generator
Definition: ForwardFlux.h:172
unsigned int _current_interface
Current Interface.
Definition: ForwardFlux.h:156
double _cvvalue
current cv position
Definition: ForwardFlux.h:90
void PopQueueMPI(Snapshot *, const CVList &, unsigned)
Pop the queue, do MPI so that all procs maintain the same queue.
std::deque< FFSConfigID > FFSConfigIDQueue
Definition: ForwardFlux.h:163
int HasCrossedInterface(double current, double prev, unsigned int i)
Function checks if configuration has crossed interface specified since the last check.
Definition: ForwardFlux.cpp:79
bool HasReturnedToA(double current)
Function checks if configuration has returned to A.
std::vector< unsigned int > _A
Number of attempts from interface i.
Definition: ForwardFlux.h:112
std::vector< unsigned int > cvmask_
Mask which identifies which CVs to act on.
Definition: Method.h:50
mxx::comm world_
Global MPI communicator.
Definition: Method.h:46
Class containing a snapshot of the current simulation in time.
Definition: Snapshot.h:48
std::vector< CollectiveVariable * > CVList
List of Collective Variables.
Definition: types.h:51