PRISMS-PF  v2.1
nucleation.cc
Go to the documentation of this file.
1 // Methods in MatrixFreePDE to update the list of nuclei
2 #include "../../include/matrixFreePDE.h"
3 #include "../../include/parallelNucleationList.h"
4 #include "../../include/varBCs.h"
5 #include <random>
6 #include <time.h>
7 
8 // =======================================================================================================
9 // Function called in solve to update the global list of nuclei
10 // =======================================================================================================
11 template <int dim, int degree>
13 
14  if (userInputs.nucleation_occurs){
15 
16 
17  if (currentIncrement % userInputs.steps_between_nucleation_attempts == 0 || currentIncrement == 1){
18  computing_timer.enter_section("matrixFreePDE: nucleation");
19 
20  // Apply constraints
21  for(unsigned int fieldIndex=0; fieldIndex<fields.size(); fieldIndex++){
22  constraintsDirichletSet[fieldIndex]->distribute(*solutionSet[fieldIndex]);
23  constraintsOtherSet[fieldIndex]->distribute(*solutionSet[fieldIndex]);
24  solutionSet[fieldIndex]->update_ghost_values();
25  }
26 
27  std::vector<nucleus<dim> > new_nuclei;
28 
29  if (currentIncrement == 1){
30  while (new_nuclei.size() == 0){
31  currentTime+=userInputs.dtValue*(double)userInputs.steps_between_nucleation_attempts;
32  currentIncrement+=userInputs.steps_between_nucleation_attempts;
33 
34  while (userInputs.outputTimeStepList.size() > 0 && userInputs.outputTimeStepList[currentOutput] < currentIncrement){
35  currentOutput++;
36  }
37 
38  while (userInputs.checkpointTimeStepList.size() > 0 && userInputs.checkpointTimeStepList[currentCheckpoint] < currentIncrement){
39  currentCheckpoint++;
40  }
41 
42  new_nuclei = getNewNuclei();
43  }
44  }
45  else {
46  new_nuclei = getNewNuclei();
47  }
48 
49  nuclei.insert(nuclei.end(),new_nuclei.begin(),new_nuclei.end());
50 
51  if (new_nuclei.size() > 0 && userInputs.h_adaptivity == true){
52  refineMeshNearNuclei(new_nuclei);
53  }
54 
55  computing_timer.exit_section("matrixFreePDE: nucleation");
56  }
57  }
58 
59 }
60 
61 // =======================================================================================================
62 // Core method to perform a nucleation check
63 // =======================================================================================================
64 template <int dim, int degree>
65 std::vector<nucleus<dim> > MatrixFreePDE<dim,degree>::getNewNuclei(){
66 
67  // Declare a vector of all the NEW nuclei seeded in this time step
68  std::vector<nucleus<dim> > newnuclei;
69 
70  // Get list of prospective new nuclei for the local processor
71  pcout << "Nucleation attempt for increment " << currentIncrement << std::endl;
72 
73  getLocalNucleiList(newnuclei);
74  pcout << "nucleation attempt! " << currentTime << " " << currentIncrement << std::endl;
75 
76  // Generate global list of new nuclei and resolve conflicts between new nuclei
77  parallelNucleationList<dim> new_nuclei_parallel(newnuclei);
78  newnuclei = new_nuclei_parallel.buildGlobalNucleiList(userInputs.min_distance_between_nuclei, nuclei.size());
79 
80  // Final check to resolve overlap conflicts with existing precipitates
81  std::vector<unsigned int> conflict_ids;
82  safetyCheckNewNuclei(newnuclei, conflict_ids);
83 
84  newnuclei = new_nuclei_parallel.removeSubsetOfNuclei(conflict_ids, nuclei.size());
85 
86  return newnuclei;
87 }
88 
89 // =================================================================================
90 // Get list of prospective new nuclei for the local processor
91 // =================================================================================
92 template <int dim, int degree>
93 void MatrixFreePDE<dim,degree>::getLocalNucleiList(std::vector<nucleus<dim> > &newnuclei) const
94 {
95  // Nickname for current time and time step
96  double t=currentTime;
97  unsigned int inc=currentIncrement;
98 
99  //QGauss<dim> quadrature(degree+1);
100  QGaussLobatto<dim> quadrature(degree+1);
101  FEValues<dim> fe_values (*(FESet[0]), quadrature, update_values|update_quadrature_points|update_JxW_values);
102  const unsigned int num_quad_points = quadrature.size();
103  std::vector<std::vector<double> > var_values(userInputs.nucleation_need_value.size(),std::vector<double>(num_quad_points));
104  std::vector<dealii::Point<dim> > q_point_list(num_quad_points);
105 
106  std::vector<dealii::Point<dim> > q_point_list_overlap(num_quad_points);
107 
108  typename DoFHandler<dim>::active_cell_iterator di = dofHandlersSet_nonconst[0]->begin_active();
109 
110  // What used to be in nuc_attempt
111  double rand_val;
112  //Better random no. generator
113  std::random_device rd;
114  std::mt19937 gen(rd());
115  std::uniform_real_distribution<> distr(0.0,1.0);
116 
117  //Element cycle
118  while (di != this->dofHandlersSet_nonconst[0]->end())
119  {
120  if (di->is_locally_owned()){
121  //Obtaining average element concentration by averaging over element's quadrature points
122  fe_values.reinit(di);
123  for (unsigned int var = 0; var < userInputs.nucleation_need_value.size(); var++){
124  fe_values.get_function_values(*(solutionSet[userInputs.nucleation_need_value[var]]), var_values[var]);
125  }
126  q_point_list = fe_values.get_quadrature_points();
127 
128  // ---------------------------
129  // NOTE: This might not be the best way to do this. This is missing the loop of the DoFs from Step-3
130 
131  double element_volume = 0.0;
132  dealii::Point<dim> ele_center;
133  // Loop over the quadrature points to find the element volume (or area in 2D) and the average q point location
134  for (unsigned int q_point=0; q_point<num_quad_points; ++q_point){
135  element_volume = element_volume + fe_values.JxW(q_point);
136  for (unsigned int i=0; i<dim; i++)
137  ele_center[i]=ele_center[i]+q_point_list[q_point](i)/((double)num_quad_points);
138  }
139 
140  // Loop over each variable and each quadrature point to get the average variable value for the element
141  variableValueContainer variable_values;
142  for (unsigned int var = 0; var < userInputs.nucleation_need_value.size(); var++){
143  double ele_val = 0.0;
144  for (unsigned int q_point=0; q_point<num_quad_points; ++q_point){
145  ele_val = ele_val + var_values[var][q_point]*fe_values.JxW(q_point);
146  }
147  ele_val /= element_volume;
148  variable_values.set(userInputs.nucleation_need_value[var],ele_val);
149  }
150 
151  // Loop through each nucleating order parameter
152  for (unsigned int i = 0; i < userInputs.nucleating_variable_indices.size(); i++){
153  unsigned int variable_index = userInputs.nucleating_variable_indices.at(i);
154 
155  //Compute random no. between 0 and 1 (new method)
156  rand_val=distr(gen);
157  //Nucleation probability
158  double Prob=getNucleationProbability(variable_values,element_volume,ele_center,variable_index);
159 
160  // ----------------------------
161 
162  if (rand_val <= Prob){
163 
164  //Initializing random vector in "dim" dimensions
165  std::vector<double> randvec(dim,0.0);
166  dealii::Point<dim> nuc_ele_pos;
167 
168  //Finding coordinates of quadrature point closest to and furthest away from the origin
169  std::vector<double> ele_origin(dim);
170  for (unsigned int i=0; i<dim; i++)
171  ele_origin[i] = q_point_list[0](i);
172  std::vector<double> ele_max(dim);
173  for (unsigned int i=0; i<dim; i++)
174  ele_max[i] = q_point_list[0](i);
175  for (unsigned int i=0; i<dim; i++){
176  for (unsigned int q_point=0; q_point<num_quad_points; ++q_point){
177  for (unsigned int i=0; i<dim; i++){
178  if (q_point_list[q_point](i) < ele_origin[i])
179  ele_origin[i]=q_point_list[q_point](i);
180  if (q_point_list[q_point](i) > ele_max[i])
181  ele_max[i]=q_point_list[q_point](i);
182  }
183  }
184  }
185 
186  //Find a random point within the element
187  for (unsigned int j=0; j<dim; j++){
188  randvec[j]=distr(gen);
189  nuc_ele_pos[j]=ele_origin[j] + (ele_max[j]-ele_origin[j])*randvec[j];
190  }
191 
192  //Make sure point is in safety zone
193  bool insafetyzone = true;
194  for (unsigned int j=0; j < dim; j++){
195  bool periodic_j = (userInputs.BC_list[1].var_BC_type[2*j]==PERIODIC);
196  bool insafetyzone_j = (periodic_j || ((nuc_ele_pos[j] > userInputs.get_no_nucleation_border_thickness(variable_index)) && (nuc_ele_pos[j] < userInputs.domain_size[j]-userInputs.get_no_nucleation_border_thickness(variable_index))));
197  insafetyzone = insafetyzone && insafetyzone_j;
198  }
199 
200  if (insafetyzone){
201 
202  // Check to see if the order parameter anywhere within the element is above the threshold
203  bool anyqp_OK = false;
204  for (unsigned int q_point=0; q_point<num_quad_points; ++q_point){
205  double sum_op = 0.0;
206  for (unsigned int var = 0; var < userInputs.nucleation_need_value.size(); var++){
207  for (unsigned int op = 0; op < userInputs.nucleating_variable_indices.size(); op++){
208  if (userInputs.nucleation_need_value[var] == userInputs.nucleating_variable_indices[op]){
209  sum_op += var_values[var][q_point];
210  }
211  }
212  }
213  if (sum_op < userInputs.nucleation_order_parameter_cutoff){
214  anyqp_OK =true;
215  }
216  }
217 
218  if (anyqp_OK){
219  // Pick the order parameter (not needed anymore since the probability is now calculated on a per OP basis)
220  /*
221  std::random_device rd2;
222  std::mt19937 gen2(rd2());
223  std::uniform_int_distribution<unsigned int> int_distr(0,userInputs.nucleating_variable_indices.size()-1);
224  unsigned int op_for_nucleus = userInputs.nucleating_variable_indices[int_distr(gen2)];
225  std::cout << "Nucleation order parameter: " << op_for_nucleus << " " << rand_val << std::endl;
226  */
227 
228  //Add nucleus to prospective list
229  std::cout << "Prospective nucleation event. Nucleus no. " << nuclei.size()+1 << std::endl;
230  std::cout << "Nucleus center: " << nuc_ele_pos << std::endl;
231  std::cout << "Nucleus order parameter: " << variable_index << std::endl;
232  nucleus<dim>* temp = new nucleus<dim>;
233  temp->index=nuclei.size();
234  temp->center=nuc_ele_pos;
235  temp->semiaxes = userInputs.get_nucleus_semiaxes(variable_index);
236  temp->seededTime=t;
237  temp->seedingTime = userInputs.get_nucleus_hold_time(variable_index);
238  temp->seedingTimestep = inc;
239  temp->orderParameterIndex = variable_index;
240  newnuclei.push_back(*temp);
241  }
242  }
243  }
244  }
245  }
246  // Increment the cell iterators
247  ++di;
248  }
249 }
250 
251 // =======================================================================================================
252 // Making sure all new nuclei from complete prospective list do not overlap with existing precipitates
253 // =======================================================================================================
254 template <int dim, int degree>
255 void MatrixFreePDE<dim,degree>::safetyCheckNewNuclei(std::vector<nucleus<dim> > newnuclei, std::vector<unsigned int> &conflict_ids)
256 {
257  //QGauss<dim> quadrature(degree+1);
258  QGaussLobatto<dim> quadrature(degree+1);
259  FEValues<dim> fe_values (*(FESet[0]), quadrature, update_values|update_quadrature_points|update_JxW_values);
260  const unsigned int num_quad_points = quadrature.size();
261  std::vector<std::vector<double> > op_values(userInputs.nucleating_variable_indices.size(),std::vector<double>(num_quad_points));
262  std::vector<dealii::Point<dim> > q_point_list(num_quad_points);
263 
264  //Nucleus cycle
265  for (typename std::vector<nucleus<dim> >::iterator thisNucleus=newnuclei.begin(); thisNucleus!=newnuclei.end(); ++thisNucleus){
266  bool isClose=false;
267 
268  //Element cycle
269  typename DoFHandler<dim>::active_cell_iterator di = dofHandlersSet_nonconst[0]->begin_active();
270  while (di != dofHandlersSet_nonconst[0]->end())
271  {
272  if (di->is_locally_owned()){
273  fe_values.reinit(di);
274  for (unsigned int var = 0; var < userInputs.nucleating_variable_indices.size(); var++){
275  fe_values.get_function_values(*(solutionSet[userInputs.nucleating_variable_indices[var]]), op_values[var]);
276  }
277  q_point_list = fe_values.get_quadrature_points();
278 
279  //Quadrature points cycle
280  for (unsigned int q_point=0; q_point<num_quad_points; ++q_point){
281  // Calculate the ellipsoidal distance to the center of the nucleus
282  double weighted_dist = weightedDistanceFromNucleusCenter(thisNucleus->center, userInputs.get_nucleus_freeze_semiaxes(thisNucleus->orderParameterIndex), q_point_list[q_point], thisNucleus->orderParameterIndex);
283 
284  if (weighted_dist < 1.0){
285  double sum_op = 0.0;
286  for (unsigned int num_op = 0; num_op < userInputs.nucleating_variable_indices.size(); num_op++){
287  sum_op += op_values[num_op][q_point];
288  }
289  if (sum_op > 0.1){
290  isClose=true;
291  std::cout << "Attempted nucleation failed due to overlap w/ existing particle!!!!!!" << std::endl;
292  conflict_ids.push_back(thisNucleus->index);
293  break;
294  }
295  }
296  }
297  if (isClose) break;
298  }
299  // Increment the cell iterators
300  ++di;
301  }
302  }
303 }
304 
305 // =================================================================================
306 // Refine mesh near the new nuclei
307 // =================================================================================
308 template <int dim, int degree>
310 {
311  //QGauss<dim> quadrature(degree+1);
312  QGaussLobatto<dim> quadrature(degree+1);
313  FEValues<dim> fe_values (*(FESet[0]), quadrature, update_values|update_quadrature_points|update_JxW_values);
314  const unsigned int num_quad_points = quadrature.size();
315  std::vector<dealii::Point<dim> > q_point_list(num_quad_points);
316 
317  typename Triangulation<dim>::active_cell_iterator ti;
318  typename DoFHandler<dim>::active_cell_iterator di;
319 
320  unsigned int numDoF_preremesh = totalDOFs;
321 
322  for (unsigned int remesh_index=0; remesh_index < (userInputs.max_refinement_level-userInputs.min_refinement_level); remesh_index++){
323  ti = triangulation.begin_active();
324  di = dofHandlersSet_nonconst[0]->begin_active();
325  while (di != dofHandlersSet_nonconst[0]->end()){
326  if (di->is_locally_owned()){
327 
328  bool mark_refine = false;
329 
330  fe_values.reinit (di);
331  q_point_list = fe_values.get_quadrature_points();
332 
333  // Calculate the distance from the corner of the cell to the middle of the cell
334  double diag_dist = 0.0;
335  for (unsigned int i=0; i<dim; i++){
336  diag_dist += (userInputs.domain_size[i]*userInputs.domain_size[i])/(userInputs.subdivisions[i]*userInputs.subdivisions[i]);
337  }
338  diag_dist = sqrt(diag_dist);
339  diag_dist /= 2.0*pow(2.0,ti->level());
340 
341  for (unsigned int q_point=0; q_point<num_quad_points; ++q_point){
342  for (typename std::vector<nucleus<dim> >::iterator thisNucleus=newnuclei.begin(); thisNucleus!=newnuclei.end(); ++thisNucleus){
343 
344  // Calculate the ellipsoidal distance to the center of the nucleus
345  double weighted_dist = weightedDistanceFromNucleusCenter(thisNucleus->center, userInputs.get_nucleus_freeze_semiaxes(thisNucleus->orderParameterIndex), q_point_list[q_point], thisNucleus->orderParameterIndex);
346 
347  if (weighted_dist < 1.0 || thisNucleus->center.distance(q_point_list[q_point]) < diag_dist){
348  if ((unsigned int)ti->level() < userInputs.max_refinement_level){
349  mark_refine = true;
350  break;
351  }
352  }
353  if (mark_refine) break;
354  }
355  if (mark_refine) break;
356  }
357  if (mark_refine) di->set_refine_flag();
358  }
359  ++di;
360  ++ti;
361  }
362  // The bulk of all of modifySolutionFields is spent in the following two function calls
363  refineGrid();
364  reinit();
365 
366  // If the mesh hasn't changed from the previous cycle, stop remeshing
367  if (totalDOFs == numDoF_preremesh) break;
368  numDoF_preremesh = totalDOFs;
369  }
370 }
371 
372 // First of two versions of this function to calculated the weighted distance from the center of a nucleus_semiaxes
373 // This version is for when the points are given as doubles
374 template <int dim, int degree>
376  const dealii::Point<dim,double> center,
377  const std::vector<double> semiaxes,
378  const dealii::Point<dim,double> q_point_loc,
379  const unsigned int var_index) const
380 {
381  double weighted_dist = 0.0;
382  dealii::Tensor<1,dim,double> shortest_edist_tensor = center - q_point_loc;
383  for (unsigned int i=0; i<dim; i++){
384  if (userInputs.BC_list[var_index].var_BC_type[2*i]==PERIODIC){
385  shortest_edist_tensor[i] = shortest_edist_tensor[i]-round(shortest_edist_tensor[i]/userInputs.domain_size[i])*userInputs.domain_size[i];
386  }
387  }
388  shortest_edist_tensor = userInputs.get_nucleus_rotation_matrix(var_index) * shortest_edist_tensor;
389  for (unsigned int i=0; i<dim; i++){
390  shortest_edist_tensor[i] /= semiaxes[i];
391  }
392  weighted_dist = shortest_edist_tensor.norm();
393  return weighted_dist;
394 }
395 
396 // Second of two versions of this function to calculated the weighted distance from the center of a nucleus_semiaxes
397 // This version is for when the points are given as vectorized arrays
398 template <int dim, int degree>
400  const dealii::Point<dim,double> center,
401  const std::vector<double> semiaxes,
402  const dealii::Point<dim,dealii::VectorizedArray<double> > q_point_loc,
403  const unsigned int var_index) const
404 {
405  dealii::VectorizedArray<double> weighted_dist = constV(0.0);
406  dealii::Tensor<1,dim,dealii::VectorizedArray<double> > shortest_edist_tensor;
407  for (unsigned int j=0; j<dim; j++){
408  shortest_edist_tensor[j] = center(j) - q_point_loc(j); // Can I do this outside the loop?
409 
410  if (userInputs.BC_list[var_index].var_BC_type[2*j]==PERIODIC){
411  for (unsigned k=0; k<q_point_loc(0).n_array_elements;k++){
412  shortest_edist_tensor[j][k] = shortest_edist_tensor[j][k]-round(shortest_edist_tensor[j][k]/userInputs.domain_size[j])*userInputs.domain_size[j];
413  }
414  }
415  }
416  shortest_edist_tensor = userInputs.get_nucleus_rotation_matrix(var_index) * shortest_edist_tensor;
417  for (unsigned int j=0; j<dim; j++){
418  shortest_edist_tensor[j] /= constV(semiaxes[j]);
419  }
420  weighted_dist = shortest_edist_tensor.norm_square();
421  for (unsigned k=0; k<q_point_loc(0).n_array_elements;k++){
422  weighted_dist[k] = sqrt(weighted_dist[k]);
423  }
424  return weighted_dist;
425 }
426 
427 // Template instantiations
428 #include "../../include/matrixFreePDE_template_instantiations.h"
void getLocalNucleiList(std::vector< nucleus< dim > > &newnuclei) const
Definition: nucleation.cc:93
double seededTime
Definition: nucleus.h:21
double seedingTime
Definition: nucleus.h:21
#define constV(a)
Definition: matrixFreePDE.h:51
double weightedDistanceFromNucleusCenter(const dealii::Point< dim, double > center, const std::vector< double > semiaxes, const dealii::Point< dim, double > q_point_loc, const unsigned int var_index) const
Definition: nucleation.cc:375
dealii::Point< dim > center
Definition: nucleus.h:18
std::vector< nucleus< dim > > getNewNuclei()
Definition: nucleation.cc:65
std::vector< nucleus< dim > > removeSubsetOfNuclei(std::vector< unsigned int > nuclei_to_remove, unsigned int nuclei_size)
void set(unsigned int global_variable_index, double variable_value)
std::vector< double > semiaxes
Definition: nucleus.h:20
void updateNucleiList()
Definition: nucleation.cc:12
unsigned int seedingTimestep
Definition: nucleus.h:22
unsigned int index
Definition: nucleus.h:17
unsigned int orderParameterIndex
Definition: nucleus.h:23
std::vector< nucleus< dim > > buildGlobalNucleiList(double min_dist_between_nuclei, unsigned int old_num_nuclei)
void safetyCheckNewNuclei(std::vector< nucleus< dim > > newnuclei, std::vector< unsigned int > &conflict_ids)
Definition: nucleation.cc:255
void refineMeshNearNuclei(std::vector< nucleus< dim > > newnuclei)
Definition: nucleation.cc:309