Simpatico  v1.10
ddMd/neighbor/CellList.cpp
1 /*
2 * Simpatico - Simulation Package for Polymeric and Molecular Liquids
3 *
4 * Copyright 2010 - 2017, The Regents of the University of Minnesota
5 * Distributed under the terms of the GNU General Public License.
6 */
7 
8 #include "CellList.h"
9 #include <util/space/Vector.h>
10 #include <util/space/IntVector.h>
11 #include <util/containers/FArray.h>
12 
13 namespace DdMd
14 {
15 
16  using namespace Util;
17 
18  /*
19  * Constructor.
20  */
22  : begin_(0),
23  nAtom_(0),
24  nReject_(0),
25  #ifdef UTIL_DEBUG
26  maxNAtomCell_(0),
27  #endif
28  isBuilt_(false)
29  {
30  for (int i = 0; i < Dimension; ++i) {
31  cellLengths_[i] = 0.0;
32  }
33  }
34 
35  /*
36  * Destructor.
37  */
39  {}
40 
41  /*
42  * Allocate memory for this CellList (generalized coordinates).
43  */
44  void CellList::allocate(int atomCapacity, const Vector& lower,
45  const Vector& upper, const Vector& cutoffs, int nCellCut)
46  {
47 
48  // Allocate arrays of tag and handle objects
49  tags_.allocate(atomCapacity);
50  atoms_.allocate(atomCapacity);
51 
52  // Set grid dimensions and allocate an array of Cell objects
53  setGridDimensions(lower, upper, cutoffs, nCellCut);
54  }
55 
56  /*
57  * Calculate number of cells in each direction of grid, resize cells_ array if needed.
58  */
59  void CellList::setGridDimensions(const Vector& lower, const Vector& upper,
60  const Vector& cutoffs, int nCellCut)
61  {
62  if (nCellCut < 1) {
63  UTIL_THROW("Error: nCellCut < 1");
64  }
65  if (nCellCut > Cell::MaxNCellCut) {
66  UTIL_THROW("Error: nCellCut > Cell::MaxNCellCut");
67  }
68  upper_ = upper;
69  lower_ = lower;
70 
71  bool isNewGrid;
72  if (grid_.size() < 27) {
73  isNewGrid = true;
74  } else {
75  isNewGrid = false;
76  }
77 
78  Vector lengths;
79  IntVector gridDimensions;
80  for (int i = 0; i < Dimension; ++i) {
81 
82  lengths[i] = upper_[i] - lower_[i];
83  if (lengths[i] < 0) {
84  UTIL_THROW("Processor length[i] < 0.0");
85  }
86  if (lengths[i] < cutoffs[i]) {
87  UTIL_THROW("Processor length[i] < cutoff[i]");
88  }
89  gridDimensions[i] = int(lengths[i]*nCellCut/cutoffs[i]);
90  cellLengths_[i] = lengths[i]/double(gridDimensions[i]);
91  lowerOuter_[i] = lower_[i] - nCellCut*cellLengths_[i];
92  upperOuter_[i] = upper_[i] + nCellCut*cellLengths_[i];
93 
94  // Add two extra layers of cells for ghosts.
95  gridDimensions[i] += 2*nCellCut;
96 
97  if (gridDimensions[i] != grid_.dimension(i)) {
98  isNewGrid = true;
99  }
100  }
101 
102  // Set new grid dimensions if necessary
103  if (isNewGrid) {
104  grid_.setDimensions(gridDimensions);
105  }
106 
107  // Resize and initialize cells_ array, if necessary
108  int oldSize = cells_.size();
109  int newSize = grid_.size();
110  if (newSize != oldSize) {
111  cells_.resize(newSize);
112  if (newSize > oldSize) {
113  for (int i = 0; i < newSize; ++i) {
114  cells_[i].setOffsetArray(offsets_);
115  cells_[i].setId(i);
116  }
117  }
118  // Indicate that cell list must be rebuilt
119  isNewGrid = true;
120  }
121  assert(newSize >= 27);
122  assert(newSize == cells_.size());
123 
124  // Build linked cell list, if necessary
125  if (isNewGrid) {
126  int ic;
127  // Initially mark all cells as ghost cells
128  for (ic = 0; ic < newSize; ++ic) {
129  cells_[ic].setIsGhostCell(true);
130  }
131  // Loop over local cells, linking and marking each as a local cell.
132  IntVector p;
133  Cell* prevPtr = 0;
134  Cell* cellPtr = 0;
135  for (p[0] = nCellCut; p[0] < grid_.dimension(0) - nCellCut; ++p[0]) {
136  for (p[1] = nCellCut; p[1] < grid_.dimension(1) - nCellCut; ++p[1]) {
137  for (p[2] = nCellCut; p[2] < grid_.dimension(2) - nCellCut; ++p[2]) {
138  ic = grid_.rank(p);
139  cellPtr = &cells_[ic];
140  cellPtr->setIsGhostCell(false);
141  if (prevPtr) {
142  prevPtr->setNextCell(*cellPtr);
143  } else {
144  begin_ = cellPtr;
145  }
146  prevPtr = cellPtr;
147  }
148  }
149  }
150  cellPtr->setLastCell();
151  }
152 
153  }
154 
155  /*
156  * Construct grid of cells, build linked list and identify neighbors.
157  */
158  void CellList::makeGrid(const Vector& lower, const Vector& upper,
159  const Vector& cutoffs, int nCellCut)
160  {
161 
162  // Calculate required grid dimensions, reinitialize cells_ array if needed.
163  setGridDimensions(lower, upper, cutoffs, nCellCut);
164 
165  // Construct e array, to help identify cells within the cutoff.
166  // Definition: For i in the range -nCellCut <= i <= nCellCut,
167  // let e[i+nCellCut][j] = ( m[i]*celllengths_[j]/cutoffs[j] )**2,
168  // where m[i] = abs(i) - 1 for abs(i) > 0, and m[0] = 0.
170  {
171  double q, r;
172  int i, j;
173  for (j = 0; j < Dimension; ++j) {
174  q = cellLengths_[j]/cutoffs[j];
175  for (i = 1; i <= nCellCut; ++i) {
176  r = q*double(i-1);
177  r = r*r;
178  e[nCellCut + i][j] = r;
179  e[nCellCut - i][j] = r;
180  }
181  e[nCellCut][j] = 0.0;
182  }
183  }
184 
185  // Construct Cell::OffsetArray offsets_ of integer offset strips
186  // Each element strip contains the cell index for the first cell
187  // strip.first and the cell index strip.second for the last cell
188  // in a contiguous strip of cells for which at least some of the
189  // cell lies within a cutoff distance of the primary cell.
190  offsets_.clear();
191  std::pair<int, int> strip;
192 
193  // Add strip (0,0) (self) as the first element of offsets_ array.
194  // This guarantees that first nAtom elements in neighborArray are
195  // in the primary cell, allowing for simple self-interaction check.
196  strip.first = 0;
197  strip.second = 0;
198  offsets_.append(strip);
199 
200  // Loop over all cells within box -nCellCut <= i, j, k <= nCellCut
201  double e0, e1, e2; // Partial sums of distance^2/cutoff^2
202  int offset0, offset1, offset; // Partial sums for cell id offset
203  int i, j, k; // relative cell coordinates
204  const int span0 = grid_.dimension(2)*grid_.dimension(1);
205  const int span1 = grid_.dimension(2);
206  bool isActive = false; // True iff this cell is within a valid strip
207  for (i = -nCellCut; i <= nCellCut; ++i) {
208  e0 = e[i+nCellCut][0];
209  offset0 = i*span0;
210  for (j = -nCellCut; j <= nCellCut; ++j) {
211  e1 = e0 + e[j + nCellCut][1];
212  offset1 = offset0 + j*span1;
213  for (k = -nCellCut; k <= nCellCut; ++k) {
214  offset = offset1 + k;
215  e2 = e1 + e[k + nCellCut][2];
216  if (e2 <= 1.0) {
217  if (offset != 0) { // Exclude offset = 0 (already added)
218  if (isActive) {
219  if (offset == strip.second + 1) {
220  strip.second = offset;
221  } else {
222  offsets_.append(strip);
223  strip.first = offset;
224  strip.second = offset;
225  }
226  } else {
227  strip.first = offset;
228  strip.second = offset;
229  isActive = true;
230  }
231  } else {
232  if (isActive) {
233  offsets_.append(strip);
234  isActive = false;
235  }
236  }
237  } else {
238  if (isActive) {
239  offsets_.append(strip);
240  isActive = false;
241  }
242  }
243  }
244  }
245  }
246  // Append last strip to offsets_, if still active at end of loop.
247  if (isActive) {
248  offsets_.append(strip);
249  }
250 
251  }
252 
253  /*
254  * Resets all cells to empty state.
255  */
257  {
258  // Clear all Cell objects
259  if (grid_.size() > 0) {
260  for (int i = 0; i < grid_.size(); ++i) {
261  cells_[i].clear();
262  }
263  }
264 
265  nAtom_ = 0;
266  nReject_ = 0;
267  #ifdef UTIL_DEBUG
268  maxNAtomCell_ = 0;
269  #endif
270 
271  isBuilt_ = false;
272  }
273 
274  /*
275  * Initialize all cells and fill them with atoms.
276  */
278  {
279  // Initialize all cells, by associating each with a
280  // block of the atoms_ array.
281 
282  CellAtom* cellAtomPtr = &atoms_[0];
283  for (int i = 0; i < grid_.size(); ++i) {
284  cellAtomPtr = cells_[i].initialize(cellAtomPtr);
285  }
286 
287  // Add all atoms to cells.
288  for (int i = 0; i < nAtom_; ++i) {
289  cells_[tags_[i].cellRank].append(tags_[i].ptr);
290  }
291 
292  #ifdef UTIL_DEBUG
293  // Calculate maxNAtomCell_
294  int nAtomCell;
295  maxNAtomCell_ = 0;
296  for (int i = 0; i < grid_.size(); ++i) {
297  nAtomCell = cells_[i].nAtom();
298  if (nAtomCell > maxNAtomCell_) {
299  maxNAtomCell_ = nAtomCell;
300  }
301  }
302  #endif
303 
304  isBuilt_ = true;
305  }
306 
307  /*
308  * Update position information in all CellAtom objects.
309  */
311  {
312  for (int i = 0; i < nAtom_; ++i) {
313  atoms_[i].update();
314  }
315  }
316 
317  /*
318  * Get total number of atoms in this CellList.
319  */
320  int CellList::nAtom() const
321  { return nAtom_; }
322 
323  /*
324  * Get number of atoms that were rejected (not added to cells).
325  */
326  int CellList::nReject() const
327  { return nReject_; }
328 
329  #ifdef UTIL_DEBUG
330  /*
331  * Get number of atoms that were rejected (not added to cells).
332  */
333  int CellList::maxNAtomCell() const
334  { return maxNAtomCell_; }
335  #endif
336 
337  /*
338  * Get the maximum number of atoms for which space is allocated.
339  */
341  { return tags_.capacity(); }
342 
343  /*
344  * Get the number of cells for which space has been allocated.
345  */
347  { return cells_.capacity(); }
348 
349  /*
350  * Is this CellList built (i.e., full of atoms)?
351  */
352  bool CellList::isBuilt() const
353  { return isBuilt_; }
354 
355  /*
356  * Check validity of CellList, throw an Exception if an error is found.
357  */
358  bool CellList::isValid() const
359  {
360 
361  if (isAllocated()) {
362  if (tags_.capacity() <= 0) {
363  UTIL_THROW("CellList is allocated but tags_.capacity() <= 0");
364  }
365  if (atoms_.capacity() <= 0) {
366  UTIL_THROW("CellList is allocated but atoms_.capacity() <= 0");
367  }
368  }
369 
370  if (isBuilt_) {
371 
372  if (!isAllocated()) {
373  UTIL_THROW("CellList is built but not allocated");
374  }
375 
376  // Check validity of all cells individually.
377  // const CellAtom* atomPtr;
378  const Cell* cellPtr;
379  int nAtomCell;
380  int nAtomSum = 0;
381  for (int icell = 0; icell < grid_.size(); ++icell) {
382  cellPtr = &cells_[icell];
383  nAtomCell = cellPtr->nAtom();
384  if (nAtomCell != cellPtr->atomCapacity()) {
385  UTIL_THROW("Cell nAtom != atomCapacity");
386  }
387  #if 0
388  if (nAtomCell > 0) {
389  for (int i = 0; i < nAtomCell; ++i) {
390  atomPtr = cellPtr->atomPtr(i);
391  if (icell != cellIndexFromPosition(atomPtr->position())) {
392  UTIL_THROW("Inconsistent position");
393  }
394  }
395  }
396  #endif
397  nAtomSum += nAtomCell;
398  }
399 
400  // Check that total number of atoms in all cells equals nAtom.
401  // Note: nAtom is incremented by the placeAtom() method.
402  if (nAtom_ >= 0) {
403  if (nAtomSum != nAtom_) {
404  UTIL_THROW("Number of atoms in all cells != nAtom");
405  }
406  }
407 
408  } else { // if not isBuilt_
409 
410  if (nAtom_ != 0) {
411  UTIL_THROW("CellList is not built, but nAtom_ != 0");
412  }
413 
414  }
415 
416  return true;
417  }
418 
419 }
void makeGrid(const Vector &lower, const Vector &upper, const Vector &cutoffs, int nCellCut=1)
Make the cell grid (using generalized coordinates).
const int Dimension
Dimensionality of space.
Definition: Dimension.h:19
A Vector is a Cartesian vector.
Definition: Vector.h:75
void clear()
Set logical size to zero.
Definition: FSArray.h:271
Data for an atom in a CellList.
CellAtom * atomPtr(int i) const
Return a pointer to atom i.
virtual ~CellList()
Destructor.
static const int MaxNCellCut
Maximum number of cell per cutoff length.
void append(const Data &data)
Append data to the end of the array.
Definition: FSArray.h:258
int nAtom() const
Get total number of atoms (local and ghost) in this CellList.
Parallel domain decomposition (DD) MD simulation.
bool isValid() const
Return true if valid, or throw Exception.
bool isBuilt() const
Has this CellList been built?
int dimension(int i) const
Get grid dimension along Cartesian direction i.
Definition: Grid.h:159
int atomCapacity() const
Maximum number of atoms for which space is allocated.
int size() const
Get total number of grid points.
Definition: Grid.h:166
#define UTIL_THROW(msg)
Macro for throwing an Exception, reporting function, file and line number.
Definition: global.h:51
void setNextCell(Cell &nextCell)
Set the pointer to the next cell in the list.
void update()
Update the cell list.
int nAtom() const
Number of atoms in cell.
Utility classes for scientific computation.
Definition: accumulators.mod:1
bool isAllocated() const
Has memory been allocated for this CellList?
void clear()
Reset the cell list to its empty state (no Atoms).
void setIsGhostCell(bool isGhostCell=true)
Mark as a ghost or local cell.
A fixed size (static) contiguous array template.
Definition: FArray.h:46
int rank(const IntVector &position) const
Get the rank of a grid point with specified position.
Definition: Grid.cpp:49
void setLastCell()
Set this to be the last cell in the list.
int nReject() const
Get number of atoms that were rejected (not placed in cells)
An IntVector is an integer Cartesian vector.
Definition: IntVector.h:73
void allocate(int atomCapacity, const Vector &lower, const Vector &upper, const Vector &cutoffs, int nCellCut=1)
Allocate memory for this CellList (generalized coordinates).
int cellCapacity() const
Number of cells for which space has been allocated.
int capacity() const
Return allocated size.
Definition: Array.h:153
void allocate(int capacity)
Allocate the underlying C array.
Definition: DArray.h:191
void setDimensions(const IntVector &dimensions)
Set the grid dimensions in all directions.
Definition: Grid.cpp:32
int atomCapacity() const
Capacity of array segment.
int cellIndexFromPosition(const Vector &position) const
Return the index of the cell that contains a position Vector.
void build()
Build the cell list.
A single Cell in a CellList.