PRISMS-PF  v2.1
parallelNucleationList.cc
Go to the documentation of this file.
1 #include "../../include/parallelNucleationList.h"
2 #include "../../include/nucleus.h"
3 #include <deal.II/base/mpi.h>
4 #include <deal.II/base/utilities.h>
5 #include <iostream>
6 
7 // =================================================================================
8 // Constructor
9 // =================================================================================
10 template <int dim>
11 parallelNucleationList<dim>::parallelNucleationList (std::vector<nucleus<dim> > _newnuclei): newnuclei(_newnuclei) {}
12 
13 // =================================================================================
14 // Generate global list of new nuclei and resolve conflicts between new nuclei
15 // =================================================================================
16 template <int dim>
17 std::vector<nucleus<dim> > parallelNucleationList<dim>::buildGlobalNucleiList(double min_dist_between_nuclei, unsigned int old_num_nuclei)
18 {
19  //MPI INITIALIZATON
20  int numProcs=dealii::Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD);
21  int thisProc=dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);
22  if (numProcs > 1) {
23  // Cycle through each processor, sending and receiving, to append the list of new nuclei
24  for (int proc_index=0; proc_index < numProcs-1; proc_index++){
25  if (thisProc == proc_index){
26  sendUpdate(thisProc+1);
27  }
28  else if (thisProc == proc_index+1){
29  receiveUpdate(thisProc-1);
30  }
31  MPI_Barrier(MPI_COMM_WORLD);
32  }
33  // The final processor now has all of the new nucleation attempts
34  // Check for conflicts on the final processor before broadcasting the list
35  if (thisProc == numProcs-1){
36  resolveNucleationConflicts(min_dist_between_nuclei, old_num_nuclei);
37  }
38 
39  // The final processor now has the final list of the new nuclei, broadcast it to all the other processors
40  broadcastUpdate(numProcs-1, thisProc);
41  }
42  else {
43  // Check for conflicts between nucleation attempts this time step
44  resolveNucleationConflicts(min_dist_between_nuclei, old_num_nuclei);
45  }
46 
47  return newnuclei;
48 }
49 
50 // =================================================================================
51 // Sends the list of new nuclei to the next processor
52 // =================================================================================
53 template <int dim>
55 {
56  int currnonucs=newnuclei.size();
57  //MPI SECTION TO SEND INFORMATION TO THE PROCESSOR procno
58  //Sending local no. of nuclei
59  MPI_Send(&currnonucs, 1, MPI_INT, procno, 0, MPI_COMM_WORLD);
60  if (currnonucs > 0){
61  //Creating vectors of each quantity in nuclei. Each numbered acording to the tags used for MPI_Send/MPI_Recv
62  //1 - index
63  std::vector<unsigned int> s_index;
64  //2 - "x" componenet of center
65  std::vector<double> s_center_x;
66  //3 - "y" componenet of center
67  std::vector<double> s_center_y;
68  //4 - "z" componenet of center
69  std::vector<double> s_center_z;
70  //5 - radius
71  std::vector<double> s_semiaxis_a;
72  std::vector<double> s_semiaxis_b;
73  std::vector<double> s_semiaxis_c;
74  //6 - seededTime
75  std::vector<double> s_seededTime;
76  //7 - seedingTime
77  std::vector<double> s_seedingTime;
78  //8 - seedingTimestep
79  std::vector<unsigned int> s_seedingTimestep;
80  //9 - orderParameterIndex
81  std::vector<unsigned int> s_orderParameterIndex;
82 
83  //Loop to store info of all nuclei into vectors
84  for (typename std::vector<nucleus<dim> >::const_iterator thisNuclei=newnuclei.begin(); thisNuclei!=newnuclei.end(); ++thisNuclei){
85  s_index.push_back(thisNuclei->index);
86  dealii::Point<dim> s_center=thisNuclei->center;
87  s_center_x.push_back(s_center[0]);
88  s_center_y.push_back(s_center[1]);
89  if (dim ==3)
90  s_center_z.push_back(s_center[2]);
91 
92  s_semiaxis_a.push_back(thisNuclei->semiaxes[0]);
93  s_semiaxis_b.push_back(thisNuclei->semiaxes[1]);
94  if (dim == 3)
95  s_semiaxis_c.push_back(thisNuclei->semiaxes[2]);
96 
97  s_seededTime.push_back(thisNuclei->seededTime);
98  s_seedingTime.push_back(thisNuclei->seedingTime);
99  s_seedingTimestep.push_back(thisNuclei->seedingTimestep);
100  s_orderParameterIndex.push_back(thisNuclei->orderParameterIndex);
101  }
102  //Send vectors to next processor
103  MPI_Send(&s_index[0], currnonucs, MPI_UNSIGNED, procno, 1, MPI_COMM_WORLD);
104  MPI_Send(&s_center_x[0], currnonucs, MPI_DOUBLE, procno, 2, MPI_COMM_WORLD);
105  MPI_Send(&s_center_y[0], currnonucs, MPI_DOUBLE, procno, 3, MPI_COMM_WORLD);
106  if (dim ==3){
107  MPI_Send(&s_center_z[0], currnonucs, MPI_DOUBLE, procno, 4, MPI_COMM_WORLD);
108  MPI_Send(&s_semiaxis_c[0], currnonucs, MPI_DOUBLE, procno, 7, MPI_COMM_WORLD);
109  }
110 
111  MPI_Send(&s_semiaxis_a[0], currnonucs, MPI_DOUBLE, procno, 5, MPI_COMM_WORLD);
112  MPI_Send(&s_semiaxis_b[0], currnonucs, MPI_DOUBLE, procno, 6, MPI_COMM_WORLD);
113 
114  MPI_Send(&s_seededTime[0], currnonucs, MPI_DOUBLE, procno, 8, MPI_COMM_WORLD);
115  MPI_Send(&s_seedingTime[0], currnonucs, MPI_DOUBLE, procno, 9, MPI_COMM_WORLD);
116  MPI_Send(&s_seedingTimestep[0], currnonucs, MPI_UNSIGNED, procno, 10, MPI_COMM_WORLD);
117  MPI_Send(&s_orderParameterIndex[0], currnonucs, MPI_UNSIGNED, procno, 11, MPI_COMM_WORLD);
118  }
119  //END OF MPI SECTION
120 }
121 
122 // =================================================================================
123 // Recieves the list of new nuclei to the next processor
124 // =================================================================================
125 template <int dim>
127 {
128  //MPI PROCEDURE TO RECIEVE INFORMATION FROM ANOTHER PROCESSOR AND UPDATE LOCAL NUCLEI INFORMATION
129  int recvnonucs = 0;
130  MPI_Recv(&recvnonucs, 1, MPI_INT, procno, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
131  if (recvnonucs > 0){
132 
133  //Creating vectors of each quantity in nuclei. Each numbered acording to the tags used for MPI_Send/MPI_Recv
134  //1 - index
135  std::vector<unsigned int> r_index(recvnonucs,0);
136  //2 - "x" componenet of center
137  std::vector<double> r_center_x(recvnonucs,0.0);
138  //3 - "y" componenet of center
139  std::vector<double> r_center_y(recvnonucs,0.0);
140  //4 - "z" componenet of center
141  std::vector<double> r_center_z(recvnonucs,0.0);
142  //5 - semiaxes
143  std::vector<double> r_semiaxis_a(recvnonucs,0.0);
144  std::vector<double> r_semiaxis_b(recvnonucs,0.0);
145  std::vector<double> r_semiaxis_c(recvnonucs,0.0);
146  //6 - seededTime
147  std::vector<double> r_seededTime(recvnonucs,0.0);
148  //7 - seedingTime
149  std::vector<double> r_seedingTime(recvnonucs,0.0);
150  //8 - seedingTimestep
151  std::vector<unsigned int> r_seedingTimestep(recvnonucs,0);
152  //9 - orderParameterIndex
153  std::vector<unsigned int> r_orderParameterIndex(recvnonucs,0);
154 
155  //Recieve vectors from processor procno
156  MPI_Recv(&r_index[0], recvnonucs, MPI_UNSIGNED, procno, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
157  MPI_Recv(&r_center_x[0], recvnonucs, MPI_DOUBLE, procno, 2, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
158  MPI_Recv(&r_center_y[0], recvnonucs, MPI_DOUBLE, procno, 3, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
159  if (dim ==3){
160  MPI_Recv(&r_center_z[0], recvnonucs, MPI_DOUBLE, procno, 4, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
161  MPI_Recv(&r_semiaxis_c[0], recvnonucs, MPI_DOUBLE, procno, 7, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
162  }
163 
164  MPI_Recv(&r_semiaxis_a[0], recvnonucs, MPI_DOUBLE, procno, 5, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
165  MPI_Recv(&r_semiaxis_b[0], recvnonucs, MPI_DOUBLE, procno, 6, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
166 
167  MPI_Recv(&r_seededTime[0], recvnonucs, MPI_DOUBLE, procno, 8, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
168  MPI_Recv(&r_seedingTime[0], recvnonucs, MPI_DOUBLE, procno, 9, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
169  MPI_Recv(&r_seedingTimestep[0], recvnonucs, MPI_UNSIGNED, procno, 10, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
170  MPI_Recv(&r_orderParameterIndex[0], recvnonucs, MPI_UNSIGNED, procno, 11, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
171 
172  //Loop to store info in vectors onto the nuclei structure
173  for (int jnuc=0; jnuc<=recvnonucs-1; jnuc++){
174  nucleus<dim>* temp = new nucleus<dim>;
175  temp->index=r_index[jnuc];
176  dealii::Point<dim> r_center;
177  r_center[0]=r_center_x[jnuc];
178  r_center[1]=r_center_y[jnuc];
179  if (dim ==3)
180  r_center[2]=r_center_z[jnuc];
181  temp->center=r_center;
182  temp->semiaxes.push_back(r_semiaxis_a[jnuc]);
183  temp->semiaxes.push_back(r_semiaxis_b[jnuc]);
184  if (dim ==3)
185  temp->semiaxes.push_back(r_semiaxis_c[jnuc]);
186  temp->seededTime=r_seededTime[jnuc];
187  temp->seedingTime = r_seedingTime[jnuc];
188  temp->seedingTimestep = r_seedingTimestep[jnuc];
189  temp->orderParameterIndex = r_orderParameterIndex[jnuc];
190  newnuclei.push_back(*temp);
191  }
192 
193  }
194 }
195 
196 // =================================================================================
197 // Broadcast the final list of new nuclei from the last processor to the rest
198 // =================================================================================
199 template <int dim>
200 void parallelNucleationList<dim>::broadcastUpdate (int broadcastProc, int thisProc)
201 {
202  //MPI PROCEDURE TO SEND THE LIST OF NEW NUCLEI FROM ONE PROCESSOR TO ALL THE OTHERS
203  int currnonucs = newnuclei.size();
204  MPI_Bcast(&currnonucs, 1, MPI_INT, broadcastProc, MPI_COMM_WORLD);
205  if (currnonucs > 0){
206 
207  //Creating vectors of each quantity in nuclei. Each numbered acording to the tags used for MPI_Send/MPI_Recv
208  unsigned int initial_vec_size;
209  if (thisProc == broadcastProc){
210  initial_vec_size = 0;
211  }
212  else{
213  initial_vec_size = currnonucs;
214  }
215 
216  //1 - index
217  std::vector<unsigned int> r_index(initial_vec_size,0);
218  //2 - "x" componenet of center
219  std::vector<double> r_center_x(initial_vec_size,0.0);
220  //3 - "y" componenet of center
221  std::vector<double> r_center_y(initial_vec_size,0.0);
222  //4 - "z" componenet of center
223  std::vector<double> r_center_z(initial_vec_size,0.0);
224  //5 - radius
225  std::vector<double> r_semiaxis_a(initial_vec_size,0.0);
226  std::vector<double> r_semiaxis_b(initial_vec_size,0.0);
227  std::vector<double> r_semiaxis_c(initial_vec_size,0.0);
228  //6 - seededTime
229  std::vector<double> r_seededTime(initial_vec_size,0.0);
230  //7 - seedingTime
231  std::vector<double> r_seedingTime(initial_vec_size,0.0);
232  //8 - seedingTimestep
233  std::vector<unsigned int> r_seedingTimestep(initial_vec_size,0);
234  //9 - orderParameterIndex
235  std::vector<unsigned int> r_orderParameterIndex(initial_vec_size,0);
236 
237  if (thisProc == broadcastProc){
238  for (typename std::vector<nucleus<dim> >::iterator thisNuclei=newnuclei.begin(); thisNuclei!=newnuclei.end(); ++thisNuclei){
239  r_index.push_back(thisNuclei->index);
240  dealii::Point<dim> s_center=thisNuclei->center;
241  r_center_x.push_back(s_center[0]);
242  r_center_y.push_back(s_center[1]);
243  if (dim ==3){
244  r_center_z.push_back(s_center[2]);
245  }
246 
247  r_semiaxis_a.push_back(thisNuclei->semiaxes[0]);
248  r_semiaxis_b.push_back(thisNuclei->semiaxes[1]);
249  if (dim ==3){
250  r_semiaxis_c.push_back(thisNuclei->semiaxes[2]);
251  }
252 
253  r_seededTime.push_back(thisNuclei->seededTime);
254  r_seedingTime.push_back(thisNuclei->seedingTime);
255  r_seedingTimestep.push_back(thisNuclei->seedingTimestep);
256  r_orderParameterIndex.push_back(thisNuclei->orderParameterIndex);
257  }
258  }
259 
260  //Recieve vectors from processor procno
261  MPI_Bcast(&r_index[0], currnonucs, MPI_UNSIGNED, broadcastProc, MPI_COMM_WORLD);
262  MPI_Bcast(&r_center_x[0], currnonucs, MPI_DOUBLE, broadcastProc, MPI_COMM_WORLD);
263  MPI_Bcast(&r_center_y[0], currnonucs, MPI_DOUBLE, broadcastProc, MPI_COMM_WORLD);
264  if (dim ==3)
265  MPI_Bcast(&r_center_z[0], currnonucs, MPI_DOUBLE, broadcastProc, MPI_COMM_WORLD);
266 
267  MPI_Bcast(&r_semiaxis_a[0], currnonucs, MPI_DOUBLE, broadcastProc, MPI_COMM_WORLD);
268  MPI_Bcast(&r_semiaxis_b[0], currnonucs, MPI_DOUBLE, broadcastProc, MPI_COMM_WORLD);
269  if (dim == 3)
270  MPI_Bcast(&r_semiaxis_c[0], currnonucs, MPI_DOUBLE, broadcastProc, MPI_COMM_WORLD);
271 
272  MPI_Bcast(&r_seededTime[0], currnonucs, MPI_DOUBLE, broadcastProc, MPI_COMM_WORLD);
273  MPI_Bcast(&r_seedingTime[0], currnonucs, MPI_DOUBLE, broadcastProc, MPI_COMM_WORLD);
274  MPI_Bcast(&r_seedingTimestep[0], currnonucs, MPI_UNSIGNED, broadcastProc, MPI_COMM_WORLD);
275  MPI_Bcast(&r_orderParameterIndex[0], currnonucs, MPI_UNSIGNED, broadcastProc, MPI_COMM_WORLD);
276 
277  newnuclei.clear();
278 
279  //Loop to store info in vectors onto the nuclei structure
280  for (int jnuc=0; jnuc<=currnonucs-1; jnuc++){
281  nucleus<dim>* temp = new nucleus<dim>;
282  temp->index=r_index[jnuc];
283  dealii::Point<dim> r_center;
284  r_center[0]=r_center_x[jnuc];
285  r_center[1]=r_center_y[jnuc];
286  if (dim ==3)
287  r_center[2]=r_center_z[jnuc];
288  temp->center=r_center;
289  temp->semiaxes.push_back(r_semiaxis_a[jnuc]);
290  temp->semiaxes.push_back(r_semiaxis_b[jnuc]);
291  if (dim == 3){
292  temp->semiaxes.push_back(r_semiaxis_c[jnuc]);
293  }
294  temp->seededTime=r_seededTime[jnuc];
295  temp->seedingTime = r_seedingTime[jnuc];
296  temp->seedingTimestep = r_seedingTimestep[jnuc];
297  temp->orderParameterIndex = r_orderParameterIndex[jnuc];
298  newnuclei.push_back(*temp);
299  }
300 
301  }
302 }
303 
304 // =================================================================================
305 // Determine if any new nuclei are in conflict and resolve those conflicts
306 // =================================================================================
307 template <int dim>
308 void parallelNucleationList<dim>::resolveNucleationConflicts (double min_dist_between_nuclei, unsigned int old_num_nuclei)
309 {
310  std::vector<nucleus<dim> > newnuclei_cleaned;
311 
312  for (unsigned int nuc_index=0; nuc_index<newnuclei.size(); nuc_index++){
313  bool isClose=false;
314 
315  for (unsigned int prev_nuc_index=0; prev_nuc_index<nuc_index; prev_nuc_index++){
316 
317  // We may want to break this section into a separate function to allow different choices for when
318  // nucleation should be prevented
319  if (newnuclei[nuc_index].center.distance(newnuclei[prev_nuc_index].center) < min_dist_between_nuclei){
320  isClose = true;
321  std::cout << "Conflict between nuclei! Distance is: " << newnuclei[nuc_index].center.distance(newnuclei[prev_nuc_index].center)
322  << " Conflict removed."<< std::endl;
323  break;
324  }
325  }
326 
327  if (!isClose){
328  newnuclei[nuc_index].index = old_num_nuclei + newnuclei_cleaned.size();
329  newnuclei_cleaned.push_back(newnuclei[nuc_index]);
330  }
331  }
332 
333  newnuclei = newnuclei_cleaned;
334 }
335 
336 // =================================================================================
337 // Remove nuclei from the list of nuclei given a local list of nucleus indices
338 // =================================================================================
339 template <int dim>
340 std::vector<nucleus<dim> > parallelNucleationList<dim>::removeSubsetOfNuclei(std::vector<unsigned int> nuclei_to_remove, unsigned int nuclei_size){
341  // Note: This method is very similar to buildGlobalNucleiList in structure, and uses simplified versions of what is done
342  // in sendUpdate, receiveUpdate, and broadcastUpdate. There is likely a cleaner way to reorganize the methods to reduce
343  // duplication.
344 
345  //MPI INITIALIZATON
346  int numProcs=dealii::Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD);
347  int thisProc=dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD);
348 
349  // Build a global list of nuclei to delete, first sending the length of the vector of indices, then the vector itself
350  if (numProcs > 1) {
351  // Cycle through each processor, sending and receiving, to append the list of new nuclei
352  for (int proc_index=0; proc_index < numProcs-1; proc_index++){
353  if (thisProc == proc_index){
354  int currnonucs= nuclei_to_remove.size();
355  MPI_Send(&currnonucs, 1, MPI_INT, thisProc+1, 0, MPI_COMM_WORLD);
356  MPI_Send(&nuclei_to_remove[0], currnonucs, MPI_UNSIGNED, thisProc+1, 1, MPI_COMM_WORLD);
357  }
358  else if (thisProc == proc_index+1){
359  int recvnonucs = 0;
360  MPI_Recv(&recvnonucs, 1, MPI_INT, thisProc-1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
361  std::vector<unsigned int> recieved_nuclei_to_remove(recvnonucs);
362  MPI_Recv(&recieved_nuclei_to_remove[0], recvnonucs, MPI_UNSIGNED, thisProc-1, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
363  nuclei_to_remove.insert(nuclei_to_remove.end(), recieved_nuclei_to_remove.begin(), recieved_nuclei_to_remove.end());
364 
365  }
366  MPI_Barrier(MPI_COMM_WORLD);
367  }
368 
369  // The final processor now has the final list of the new nuclei, broadcast it to all the other processors
370  int currnonucs= nuclei_to_remove.size();
371  MPI_Bcast(&currnonucs, 1, MPI_INT, numProcs-1, MPI_COMM_WORLD);
372  std::vector<unsigned int> recieved_nuclei_to_remove(currnonucs);
373  if (thisProc == numProcs-1){
374  recieved_nuclei_to_remove = nuclei_to_remove;
375  }
376  MPI_Bcast(&recieved_nuclei_to_remove[0], currnonucs, MPI_UNSIGNED, numProcs-1, MPI_COMM_WORLD);
377  nuclei_to_remove = recieved_nuclei_to_remove;
378  }
379 
380  for (unsigned int i=0; i<nuclei_to_remove.size(); i++){
381  std::cout << thisProc << ": " << nuclei_to_remove[i] << std::endl;
382  }
383 
384  // Remove the nuclei from the list
385  std::vector<nucleus<dim> > pruned_list;
386  for (unsigned int nuc = 0; nuc < newnuclei.size(); nuc++){
387  bool pruneNucleus = false;
388  for (unsigned int i = 0; i < nuclei_to_remove.size(); i++){
389  if (nuclei_to_remove[i] == nuclei_size+nuc){
390  pruneNucleus = true;
391  break;
392  }
393  }
394  if (!pruneNucleus){
395  pruned_list.push_back(newnuclei[nuc]);
396  }
397  }
398  return pruned_list;
399 }
400 
401 // =================================================================================
402 // Template instantiations
403 // =================================================================================
404 template class parallelNucleationList<2>;
405 template class parallelNucleationList<3>;
double seededTime
Definition: nucleus.h:21
double seedingTime
Definition: nucleus.h:21
dealii::Point< dim > center
Definition: nucleus.h:18
void resolveNucleationConflicts(double min_dist_between_nuclei, unsigned int old_num_nuclei)
parallelNucleationList(std::vector< nucleus< dim > > _newnuclei)
std::vector< nucleus< dim > > removeSubsetOfNuclei(std::vector< unsigned int > nuclei_to_remove, unsigned int nuclei_size)
void broadcastUpdate(int broadcastProc, int thisProc)
std::vector< double > semiaxes
Definition: nucleus.h:20
unsigned int seedingTimestep
Definition: nucleus.h:22
unsigned int index
Definition: nucleus.h:17
void sendUpdate(int procno) const
unsigned int orderParameterIndex
Definition: nucleus.h:23
std::vector< nucleus< dim > > buildGlobalNucleiList(double min_dist_between_nuclei, unsigned int old_num_nuclei)