MDStressLab++
neighbor_list.cpp
Go to the documentation of this file.
1 /*
2  * neighbor_list.cpp
3  *
4  * Created on: Nov 9, 2019
5  * Author: Nikhil; Adapted from Mingjian Wen's kimpy code
6  */
7 
8 
9 
10 #include "neighbor_list.h"
11 #include "helper.hpp"
12 #include <cmath>
13 #include <cstring>
14 #include <sstream>
15 #include <vector>
16 
17 #define DIM 3
18 #define TOL 1.0e-10
19 
20 
21 void nbl_clean_content(NeighList * const nl)
22 {
23  if (nl != NULL)
24  {
25  for (int i = 0; i < nl->numberOfNeighborLists; i++)
26  {
27  NeighListOne * cnl = &(nl->lists[i]);
28  delete[] cnl->Nneighbors;
29  delete[] cnl->neighborList;
30  delete[] cnl->beginIndex;
31  cnl->numberOfParticles = 0;
32  cnl->cutoff = 0.0;
33  cnl->Nneighbors = NULL;
34  cnl->neighborList = NULL;
35  cnl->beginIndex = NULL;
36  }
37  delete[] nl->lists;
38  nl->lists = NULL;
39  nl->numberOfNeighborLists = 0;
40  }
41 }
42 
43 
45  int const numberOfCutoffs,
46  int const numberOfParticles)
47 {
48  nl->lists = new NeighListOne[numberOfCutoffs];
49  nl->numberOfNeighborLists = numberOfCutoffs;
50  for (int i = 0; i < numberOfCutoffs; i++)
51  {
52  NeighListOne * cnl = &(nl->lists[i]);
53  cnl->numberOfParticles = 0;
54  cnl->cutoff = 0.0;
55  cnl->Nneighbors = new int[numberOfParticles];
56  cnl->neighborList = NULL;
57  cnl->beginIndex = new int[numberOfParticles];
58  }
59 }
60 
61 
62 void nbl_initialize(NeighList ** const nl)
63 {
64  *nl = new NeighList;
65  (*nl)->numberOfNeighborLists = 0;
66  (*nl)->lists = NULL;
67 }
68 
69 
70 void nbl_clean(NeighList ** const nl)
71 {
72  nbl_clean_content(*nl);
73 
74  delete (*nl);
75 
76  // nullify pointer
77  (*nl) = NULL;
78 }
79 
80 
81 int nbl_build(NeighList * const nl,
82  int const numberOfParticles,
83  double const * coordinates,
84  double const influenceDistance,
85  int const numberOfCutoffs,
86  double const * cutoffs,
87  int const * needNeighbors)
88 {
89  // find max and min extend of coordinates
90  double min[DIM];
91  double max[DIM];
92 
93  // init max and min of coordinates to that of the first atom
94  for (int k = 0; k < DIM; k++)
95  {
96  min[k] = coordinates[k];
97  max[k] = coordinates[k] + 1.0; // +1 to prevent max==min for 1D and 2D case
98 // max[k] = coordinates[k] + TOL; // +1 to prevent max==min for 1D and 2D case
99  }
100  for (int i = 0; i < numberOfParticles; i++)
101  {
102  for (int j = 0; j < DIM; j++)
103  {
104  if (max[j] < coordinates[DIM * i + j])
105  { max[j] = coordinates[DIM * i + j]; }
106  if (min[j] > coordinates[DIM * i + j])
107  { min[j] = coordinates[DIM * i + j]; }
108  }
109  }
110 
111  // make the cell box
112  int size_total = 1;
113  int size[DIM];
114  for (int i = 0; i < DIM; i++)
115  {
116  size[i] = static_cast<int>((max[i] - min[i]) / influenceDistance);
117  size[i] = size[i] <= 0 ? 1 : size[i];
118  size_total *= size[i];
119  }
120  if (size_total > 1000000000)
121  {
122  MY_WARNING("Cell size too large. Check if you have partilces fly away.");
123  return 1;
124  }
125 
126  // assign atoms into cells
127  std::vector<std::vector<int> > cells(size_total);
128  for (int i = 0; i < numberOfParticles; i++)
129  {
130  int index[DIM];
131  coords_to_index(&coordinates[DIM * i], size, max, min, index);
132  int idx = index[0] + index[1] * size[0] + index[2] * size[0] * size[1];
133  cells[idx].push_back(i);
134  }
135 
136  // create neighbors
137 
138  // free previous neigh content and then create new
139  nbl_clean_content(nl);
140  nbl_allocate_memory(nl, numberOfCutoffs, numberOfParticles);
141 
142  double * cutsqs = new double[numberOfCutoffs];
143  for (int i = 0; i < numberOfCutoffs; i++)
144  { cutsqs[i] = cutoffs[i] * cutoffs[i]; }
145 
146  // temporary neigh container
147  std::vector<int> * tmp_neigh = new std::vector<int>[numberOfCutoffs];
148  int * total = new int[numberOfCutoffs];
149  int * num_neigh = new int[numberOfCutoffs];
150  for (int k = 0; k < numberOfCutoffs; k++) { total[k] = 0; }
151 
152  for (int i = 0; i < numberOfParticles; i++)
153  {
154  for (int k = 0; k < numberOfCutoffs; k++) { num_neigh[k] = 0; }
155 
156  if (needNeighbors[i])
157  {
158  int index[DIM];
159  coords_to_index(&coordinates[DIM * i], size, max, min, index);
160 
161  // loop over neighborling cells and the cell atom i resides
162  for (int ii = std::max(0, index[0] - 1);
163  ii <= std::min(index[0] + 1, size[0] - 1);
164  ii++)
165  {
166  for (int jj = std::max(0, index[1] - 1);
167  jj <= std::min(index[1] + 1, size[1] - 1);
168  jj++)
169  {
170  for (int kk = std::max(0, index[2] - 1);
171  kk <= std::min(index[2] + 1, size[2] - 1);
172  kk++)
173  {
174  int idx = ii + jj * size[0] + kk * size[0] * size[1];
175 
176  for (size_t m = 0; m < cells[idx].size(); m++)
177  {
178  int n = cells[idx][m];
179  if (n != i)
180  {
181  double rsq = 0.0;
182 
183  for (int k = 0; k < DIM; k++)
184  {
185  double del
186  = coordinates[DIM * n + k] - coordinates[DIM * i + k];
187  rsq += del * del;
188  }
189  if (rsq < TOL)
190  {
191  std::ostringstream stringStream;
192  stringStream << "Collision of atoms " << i + 1 << " and "
193  << n + 1 << ". ";
194  stringStream << "Their distance is " << std::sqrt(rsq) << "."
195  << std::endl;
196  std::string my_str = stringStream.str();
197  MY_WARNING(my_str);
198  return 1;
199  }
200  for (int k = 0; k < numberOfCutoffs; k++)
201  {
202  if (rsq < cutsqs[k])
203  {
204  tmp_neigh[k].push_back(n);
205  num_neigh[k]++;
206  }
207  }
208  }
209  }
210  }
211  }
212  }
213  }
214 
215  for (int k = 0; k < numberOfCutoffs; k++)
216  {
217  nl->lists[k].Nneighbors[i] = num_neigh[k];
218  nl->lists[k].beginIndex[i] = total[k];
219  total[k] += num_neigh[k];
220  }
221  }
222 
223  for (int k = 0; k < numberOfCutoffs; k++)
224  {
225  nl->lists[k].numberOfParticles = numberOfParticles;
226  nl->lists[k].cutoff = cutoffs[k];
227  nl->lists[k].neighborList = new int[total[k]];
228  std::memcpy(
229  nl->lists[k].neighborList, tmp_neigh[k].data(), sizeof(int) * total[k]);
230  }
231 
232  delete[] cutsqs;
233  delete[] tmp_neigh;
234  delete[] num_neigh;
235  delete[] total;
236 
237  return 0;
238 }
239 
240 
241 int nbl_get_neigh(void const * const dataObject,
242  int const numberOfCutoffs,
243  double const * const cutoffs,
244  int const neighborListIndex,
245  int const particleNumber,
246  int * const numberOfNeighbors,
247  int const ** const neighborsOfParticle)
248 {
249  int error = 1;
250  NeighList * nl = (NeighList *) dataObject;
251 
252  if (neighborListIndex >= nl->numberOfNeighborLists) { return error; }
253  NeighListOne * cnl = &(nl->lists[neighborListIndex]);
254 
255  if (cutoffs[neighborListIndex] > cnl->cutoff + TOL) { return error; }
256 
257  // invalid id
258  int numberOfParticles = cnl->numberOfParticles;
259  if ((particleNumber >= numberOfParticles) || (particleNumber < 0))
260  {
261  MY_WARNING("Invalid part ID in nbl_get_neigh");
262  return error;
263  }
264 
265  // number of neighbors
266  *numberOfNeighbors = cnl->Nneighbors[particleNumber];
267 
268  // neighbor list starting point
269  int idx = cnl->beginIndex[particleNumber];
270  *neighborsOfParticle = cnl->neighborList + idx;
271 
272  return 0;
273 }
274 
275 
276 int nbl_create_paddings(int const numberOfParticles,
277  double const cutoff,
278  double const * reference_cell,
279  double const * cell,
280  int const * PBC,
281  double const * reference_coordinates,
282  double const * coordinates,
283  const std::vector<std::string>& speciesCode,
284  int & numberOfPaddings,
285  std::vector<double> & reference_coordinatesOfPaddings,
286  std::vector<double> & coordinatesOfPaddings,
287  std::vector<std::string> & speciesCodeOfPaddings,
288  std::vector<int> & masterOfPaddings,
289  int referenceAndFinal)
290 {
291  // transform coordinates into fractional coordinates
292  double tcell[9];
293  double fcell[9];
294  transpose(cell, tcell);
295  {
296  int error = inverse(tcell, fcell);
297  if (error) { return error; }
298  }
299 
300 
301  double reference_tcell[9];
302  double reference_fcell[9];
303  if (referenceAndFinal)
304  {
305  transpose(reference_cell, reference_tcell);
306  {
307  int error = inverse(reference_tcell, reference_fcell);
308  if (error) { return error; }
309  }
310  }
311 
312  double* frac_coords= new double[DIM * numberOfParticles];
313  double* reference_frac_coords= nullptr;
314  if (referenceAndFinal) reference_frac_coords= new double[DIM * numberOfParticles];
315  double min[DIM] = {1e10, 1e10, 1e10};
316  double max[DIM] = {-1e10, -1e10, -1e10};
317  for (int i = 0; i < numberOfParticles; i++)
318  {
319  const double * atom_coords = coordinates + (DIM * i);
320  const double * reference_atom_coords = reference_coordinates + (DIM * i);
321  double x = dot(fcell, atom_coords);
322  double y = dot(fcell + 3, atom_coords);
323  double z = dot(fcell + 6, atom_coords);
324  frac_coords[DIM * i + 0] = x;
325  frac_coords[DIM * i + 1] = y;
326  frac_coords[DIM * i + 2] = z;
327  if (x < min[0]) { min[0] = x; }
328  if (y < min[1]) { min[1] = y; }
329  if (z < min[2]) { min[2] = z; }
330  if (x > max[0]) { max[0] = x; }
331  if (y > max[1]) { max[1] = y; }
332  if (z > max[2]) { max[2] = z; }
333  if(referenceAndFinal)
334  {
335  double reference_x = dot(reference_fcell, reference_atom_coords);
336  double reference_y = dot(reference_fcell + 3, reference_atom_coords);
337  double reference_z = dot(reference_fcell + 6, reference_atom_coords);
338  reference_frac_coords[DIM * i + 0]= reference_x;
339  reference_frac_coords[DIM * i + 1]= reference_y;
340  reference_frac_coords[DIM * i + 2]= reference_z;
341  }
342  }
343  // add some extra value to deal with edge case
344  for (int i = 0; i < DIM; i++)
345  {
346  min[i] -= 1e-10;
347  max[i] += 1e-10;
348  }
349 
350  // volume of cell
351  double xprod[DIM];
352  cross(cell + 3, cell + 6, xprod);
353  double volume = std::abs(dot(cell, xprod));
354 
355  // distance between parallelpiped cell faces
356  double dist[DIM];
357  cross(cell + 3, cell + 6, xprod);
358  dist[0] = volume / norm(xprod);
359  cross(cell + 6, cell + 0, xprod);
360  dist[1] = volume / norm(xprod);
361  cross(cell, cell + 3, xprod);
362  dist[2] = volume / norm(xprod);
363 
364  // number of cells in each direction
365  double ratio[DIM];
366  double size[DIM];
367  for (int i = 0; i < DIM; i++)
368  {
369  ratio[i] = cutoff / dist[i];
370  size[i] = static_cast<int>(std::ceil(ratio[i]));
371  }
372 
373  // creating padding atoms
374  for (int i = -size[0]; i <= size[0]; i++)
375  {
376  for (int j = -size[1]; j <= size[1]; j++)
377  {
378  for (int k = -size[2]; k <= size[2]; k++)
379  {
380  // skip contributing atoms
381  if (i == 0 && j == 0 && k == 0) { continue; }
382 
383  // apply BC
384  if (PBC[0] == 0 && i != 0) { continue; }
385  if (PBC[1] == 0 && j != 0) { continue; }
386  if (PBC[2] == 0 && k != 0) { continue; }
387 
388  for (int at = 0; at < numberOfParticles; at++)
389  {
390  double x = frac_coords[DIM * at + 0];
391  double y = frac_coords[DIM * at + 1];
392  double z = frac_coords[DIM * at + 2];
393  double reference_x, reference_y, reference_z;
394  if(referenceAndFinal)
395  {
396  reference_x= reference_frac_coords[DIM * at + 0];
397  reference_y= reference_frac_coords[DIM * at + 1];
398  reference_z= reference_frac_coords[DIM * at + 2];
399  }
400  // select the necessary atoms to repeate for the most outside bins
401  // the follwing few lines can be easily understood when assuming
402  // size=1
403  if (i == -size[0]
404  && x - min[0] < static_cast<double>(size[0]) - ratio[0])
405  { continue; }
406  if (i == size[0]
407  && max[0] - x < static_cast<double>(size[0]) - ratio[0])
408  { continue; }
409  if (j == -size[1]
410  && y - min[1] < static_cast<double>(size[1]) - ratio[1])
411  { continue; }
412  if (j == size[1]
413  && max[1] - y < static_cast<double>(size[1]) - ratio[1])
414  { continue; }
415  if (k == -size[2]
416  && z - min[2] < static_cast<double>(size[2]) - ratio[2])
417  { continue; }
418  if (k == size[2]
419  && max[2] - z < static_cast<double>(size[2]) - ratio[2])
420  { continue; }
421 
422  // fractional coordinates of padding atom at
423  double atom_coords[3] = {i + x, j + y, k + z};
424  double reference_atom_coords[3];
425  if (referenceAndFinal)
426  {
427  reference_atom_coords[0]= i + reference_x;
428  reference_atom_coords[1]= j + reference_y;
429  reference_atom_coords[2]= k + reference_z;
430  }
431 
432  // absolute coordinates of padding atoms
433  coordinatesOfPaddings.push_back(dot(tcell, atom_coords));
434  coordinatesOfPaddings.push_back(dot(tcell + 3, atom_coords));
435  coordinatesOfPaddings.push_back(dot(tcell + 6, atom_coords));
436  if (referenceAndFinal)
437  {
438  reference_coordinatesOfPaddings.push_back(dot(reference_tcell, reference_atom_coords));
439  reference_coordinatesOfPaddings.push_back(dot(reference_tcell + 3, reference_atom_coords));
440  reference_coordinatesOfPaddings.push_back(dot(reference_tcell + 6, reference_atom_coords));
441  }
442 
443  // padding speciesCode code and image
444  speciesCodeOfPaddings.push_back(speciesCode[at]);
445  masterOfPaddings.push_back(at);
446  }
447  }
448  }
449  }
450  delete[] frac_coords;
451  if(reference_frac_coords!= nullptr) delete[] reference_frac_coords;
452 
453  numberOfPaddings = masterOfPaddings.size();
454 
455  return 0;
456 }
457 
void transpose(double const *mat, double *const trans)
Definition: helper.hpp:59
int numberOfNeighborLists
Definition: neighbor_list.h:26
void nbl_initialize(NeighList **const nl)
double dot(double const *a, double const *b)
Definition: helper.hpp:27
int numberOfParticles
Definition: neighbor_list.h:16
#define MY_WARNING(message)
Definition: typedef.h:24
void nbl_clean(NeighList **const nl)
NeighListOne * lists
Definition: neighbor_list.h:27
void cross(double const *a, double const *b, double *const axb)
Definition: helper.hpp:34
int inverse(double const *mat, double *const inv)
Definition: helper.hpp:69
#define TOL
int * beginIndex
Definition: neighbor_list.h:20
double norm(double const *a)
Definition: helper.hpp:20
#define DIM
void nbl_clean_content(NeighList *const nl)
int * neighborList
Definition: neighbor_list.h:19
int nbl_get_neigh(void const *const dataObject, int const numberOfCutoffs, double const *const cutoffs, int const neighborListIndex, int const particleNumber, int *const numberOfNeighbors, int const **const neighborsOfParticle)
void nbl_allocate_memory(NeighList *const nl, int const numberOfCutoffs, int const numberOfParticles)
int nbl_create_paddings(int const numberOfParticles, double const cutoff, double const *reference_cell, double const *cell, int const *PBC, double const *reference_coordinates, double const *coordinates, const std::vector< std::string > &speciesCode, int &numberOfPaddings, std::vector< double > &reference_coordinatesOfPaddings, std::vector< double > &coordinatesOfPaddings, std::vector< std::string > &speciesCodeOfPaddings, std::vector< int > &masterOfPaddings, int referenceAndFinal)
int nbl_build(NeighList *const nl, int const numberOfParticles, double const *coordinates, double const influenceDistance, int const numberOfCutoffs, double const *cutoffs, int const *needNeighbors)
void coords_to_index(double const *x, int const *size, double const *max, double const *min, int *const index)
Definition: helper.hpp:92
int * Nneighbors
Definition: neighbor_list.h:18