f33a958d0bf63e59b0544d7697db6aa75d80ba62
[alexxy/gromacs.git] / src / gromacs / mdlib / nbnxn_grid.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014,2015,2016,2017,2018, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35
36 #include "gmxpre.h"
37
38 #include "nbnxn_grid.h"
39
40 #include <cmath>
41 #include <cstring>
42
43 #include <algorithm>
44
45 #include "gromacs/domdec/domdec_struct.h"
46 #include "gromacs/math/utilities.h"
47 #include "gromacs/math/vec.h"
48 #include "gromacs/mdlib/gmx_omp_nthreads.h"
49 #include "gromacs/mdlib/nb_verlet.h"
50 #include "gromacs/mdlib/nbnxn_atomdata.h"
51 #include "gromacs/mdlib/nbnxn_consts.h"
52 #include "gromacs/mdlib/nbnxn_internal.h"
53 #include "gromacs/mdlib/nbnxn_search.h"
54 #include "gromacs/mdlib/nbnxn_util.h"
55 #include "gromacs/mdlib/updategroupscog.h"
56 #include "gromacs/mdtypes/forcerec.h" // only for GET_CGINFO_*
57 #include "gromacs/simd/simd.h"
58 #include "gromacs/simd/vector_operations.h"
59 #include "gromacs/utility/exceptions.h"
60 #include "gromacs/utility/smalloc.h"
61
62 struct gmx_domdec_zones_t;
63
64 static real grid_atom_density(int        numAtoms,
65                               const rvec lowerCorner,
66                               const rvec upperCorner)
67 {
68     rvec size;
69
70     if (numAtoms == 0)
71     {
72         /* To avoid zero density we use a minimum of 1 atom */
73         numAtoms = 1;
74     }
75
76     rvec_sub(upperCorner, lowerCorner, size);
77
78     return numAtoms/(size[XX]*size[YY]*size[ZZ]);
79 }
80
81 static void set_grid_size_xy(const nbnxn_search   *nbs,
82                              nbnxn_grid_t         *grid,
83                              int                   ddZone,
84                              int                   numAtoms,
85                              const rvec            lowerCorner,
86                              const rvec            upperCorner,
87                              real                  atomDensity)
88 {
89     rvec size;
90     real tlen, tlen_x, tlen_y;
91
92     rvec_sub(upperCorner, lowerCorner, size);
93
94     if (numAtoms > grid->na_sc)
95     {
96         GMX_ASSERT(atomDensity > 0, "With one or more atoms, the density should be positive");
97
98         /* target cell length */
99         if (grid->bSimple)
100         {
101             /* To minimize the zero interactions, we should make
102              * the largest of the i/j cell cubic.
103              */
104             int numAtomsInCell = std::max(grid->na_c, grid->na_cj);
105
106             /* Approximately cubic cells */
107             tlen   = std::cbrt(numAtomsInCell/atomDensity);
108             tlen_x = tlen;
109             tlen_y = tlen;
110         }
111         else
112         {
113             /* Approximately cubic sub cells */
114             tlen   = std::cbrt(grid->na_c/atomDensity);
115             tlen_x = tlen*c_gpuNumClusterPerCellX;
116             tlen_y = tlen*c_gpuNumClusterPerCellY;
117         }
118         /* We round ncx and ncy down, because we get less cell pairs
119          * in the nbsist when the fixed cell dimensions (x,y) are
120          * larger than the variable one (z) than the other way around.
121          */
122         grid->numCells[XX] = std::max(1, static_cast<int>(size[XX]/tlen_x));
123         grid->numCells[YY] = std::max(1, static_cast<int>(size[YY]/tlen_y));
124     }
125     else
126     {
127         grid->numCells[XX] = 1;
128         grid->numCells[YY] = 1;
129     }
130
131     for (int d = 0; d < DIM - 1; d++)
132     {
133         grid->cellSize[d]    = size[d]/grid->numCells[d];
134         grid->invCellSize[d] = 1/grid->cellSize[d];
135     }
136
137     if (ddZone > 0)
138     {
139         /* This is a non-home zone, add an extra row of cells
140          * for particles communicated for bonded interactions.
141          * These can be beyond the cut-off. It doesn't matter where
142          * they end up on the grid, but for performance it's better
143          * if they don't end up in cells that can be within cut-off range.
144          */
145         grid->numCells[XX]++;
146         grid->numCells[YY]++;
147     }
148
149     /* We need one additional cell entry for particles moved by DD */
150     int numCellsXY = grid->numCells[XX]*grid->numCells[YY];
151     grid->cxy_na.resize(numCellsXY + 1);
152     grid->cxy_ind.resize(numCellsXY + 2);
153
154     for (nbnxn_search_work_t &work : nbs->work)
155     {
156         work.cxy_na.resize(numCellsXY + 1);
157     }
158
159     /* Worst case scenario of 1 atom in each last cell */
160     int maxNumCells;
161     if (grid->na_cj <= grid->na_c)
162     {
163         maxNumCells = numAtoms/grid->na_sc + numCellsXY;
164     }
165     else
166     {
167         maxNumCells = numAtoms/grid->na_sc + numCellsXY*grid->na_cj/grid->na_c;
168     }
169
170     grid->nsubc.resize(maxNumCells);
171     grid->bbcz.resize(maxNumCells*NNBSBB_D);
172
173     /* This resize also zeros the contents, this avoid possible
174      * floating exceptions in SIMD with the unused bb elements.
175      */
176     if (grid->bSimple)
177     {
178         grid->bb.resize(maxNumCells);
179     }
180     else
181     {
182 #if NBNXN_BBXXXX
183         grid->pbb.resize(maxNumCells*c_gpuNumClusterPerCell/STRIDE_PBB*NNBSBB_XXXX);
184 #else
185         grid->bb.resize(maxNumCells*c_gpuNumClusterPerCell);
186 #endif
187     }
188
189     if (grid->bSimple)
190     {
191         if (grid->na_cj == grid->na_c)
192         {
193             grid->bbj = grid->bb;
194         }
195         else
196         {
197             grid->bbjStorage.resize(maxNumCells*grid->na_c/grid->na_cj);
198             grid->bbj = grid->bbjStorage;
199         }
200     }
201
202     grid->flags.resize(maxNumCells);
203     if (nbs->bFEP)
204     {
205         grid->fep.resize(maxNumCells*grid->na_sc/grid->na_c);
206     }
207
208     copy_rvec(lowerCorner, grid->c0);
209     copy_rvec(upperCorner, grid->c1);
210     copy_rvec(size,        grid->size);
211 }
212
213 /* We need to sort paricles in grid columns on z-coordinate.
214  * As particle are very often distributed homogeneously, we a sorting
215  * algorithm similar to pigeonhole sort. We multiply the z-coordinate
216  * by a factor, cast to an int and try to store in that hole. If the hole
217  * is full, we move this or another particle. A second pass is needed to make
218  * contiguous elements. SORT_GRID_OVERSIZE is the ratio of holes to particles.
219  * 4 is the optimal value for homogeneous particle distribution and allows
220  * for an O(#particles) sort up till distributions were all particles are
221  * concentrated in 1/4 of the space. No NlogN fallback is implemented,
222  * as it can be expensive to detect imhomogeneous particle distributions.
223  * SGSF is the maximum ratio of holes used, in the worst case all particles
224  * end up in the last hole and we need #particles extra holes at the end.
225  */
226 #define SORT_GRID_OVERSIZE 4
227 #define SGSF (SORT_GRID_OVERSIZE + 1)
228
229 /* Sort particle index a on coordinates x along dim.
230  * Backwards tells if we want decreasing iso increasing coordinates.
231  * h0 is the minimum of the coordinate range.
232  * invh is the 1/length of the sorting range.
233  * n_per_h (>=n) is the expected average number of particles per 1/invh
234  * sort is the sorting work array.
235  * sort should have a size of at least n_per_h*SORT_GRID_OVERSIZE + n,
236  * or easier, allocate at least n*SGSF elements.
237  */
238 static void sort_atoms(int dim, gmx_bool Backwards,
239                        int gmx_unused dd_zone,
240                        bool gmx_unused relevantAtomsAreWithinGridBounds,
241                        int *a, int n,
242                        gmx::ArrayRef<const gmx::RVec> x,
243                        real h0, real invh, int n_per_h,
244                        gmx::ArrayRef<int> sort)
245 {
246     if (n <= 1)
247     {
248         /* Nothing to do */
249         return;
250     }
251
252     GMX_ASSERT(n <= n_per_h, "We require n <= n_per_h");
253
254     /* Transform the inverse range height into the inverse hole height */
255     invh *= n_per_h*SORT_GRID_OVERSIZE;
256
257     /* Set nsort to the maximum possible number of holes used.
258      * In worst case all n elements end up in the last bin.
259      */
260     int nsort = n_per_h*SORT_GRID_OVERSIZE + n;
261
262     /* Determine the index range used, so we can limit it for the second pass */
263     int zi_min = INT_MAX;
264     int zi_max = -1;
265
266     /* Sort the particles using a simple index sort */
267     for (int i = 0; i < n; i++)
268     {
269         /* The cast takes care of float-point rounding effects below zero.
270          * This code assumes particles are less than 1/SORT_GRID_OVERSIZE
271          * times the box height out of the box.
272          */
273         int zi = static_cast<int>((x[a[i]][dim] - h0)*invh);
274
275 #ifndef NDEBUG
276         /* As we can have rounding effect, we use > iso >= here */
277         if (relevantAtomsAreWithinGridBounds &&
278             (zi < 0 || (dd_zone == 0 && zi > n_per_h*SORT_GRID_OVERSIZE)))
279         {
280             gmx_fatal(FARGS, "(int)((x[%d][%c]=%f - %f)*%f) = %d, not in 0 - %d*%d\n",
281                       a[i], 'x'+dim, x[a[i]][dim], h0, invh, zi,
282                       n_per_h, SORT_GRID_OVERSIZE);
283         }
284 #endif
285         if (zi < 0)
286         {
287             zi = 0;
288         }
289
290         /* In a non-local domain, particles communcated for bonded interactions
291          * can be far beyond the grid size, which is set by the non-bonded
292          * cut-off distance. We sort such particles into the last cell.
293          */
294         if (zi > n_per_h*SORT_GRID_OVERSIZE)
295         {
296             zi = n_per_h*SORT_GRID_OVERSIZE;
297         }
298
299         /* Ideally this particle should go in sort cell zi,
300          * but that might already be in use,
301          * in that case find the first empty cell higher up
302          */
303         if (sort[zi] < 0)
304         {
305             sort[zi] = a[i];
306             zi_min   = std::min(zi_min, zi);
307             zi_max   = std::max(zi_max, zi);
308         }
309         else
310         {
311             /* We have multiple atoms in the same sorting slot.
312              * Sort on real z for minimal bounding box size.
313              * There is an extra check for identical z to ensure
314              * well-defined output order, independent of input order
315              * to ensure binary reproducibility after restarts.
316              */
317             while (sort[zi] >= 0 && ( x[a[i]][dim] >  x[sort[zi]][dim] ||
318                                       (x[a[i]][dim] == x[sort[zi]][dim] &&
319                                        a[i] > sort[zi])))
320             {
321                 zi++;
322             }
323
324             if (sort[zi] >= 0)
325             {
326                 /* Shift all elements by one slot until we find an empty slot */
327                 int cp  = sort[zi];
328                 int zim = zi + 1;
329                 while (sort[zim] >= 0)
330                 {
331                     int tmp   = sort[zim];
332                     sort[zim] = cp;
333                     cp        = tmp;
334                     zim++;
335                 }
336                 sort[zim] = cp;
337                 zi_max    = std::max(zi_max, zim);
338             }
339             sort[zi] = a[i];
340             zi_max   = std::max(zi_max, zi);
341         }
342     }
343
344     int c = 0;
345     if (!Backwards)
346     {
347         for (int zi = 0; zi < nsort; zi++)
348         {
349             if (sort[zi] >= 0)
350             {
351                 a[c++]   = sort[zi];
352                 sort[zi] = -1;
353             }
354         }
355     }
356     else
357     {
358         for (int zi = zi_max; zi >= zi_min; zi--)
359         {
360             if (sort[zi] >= 0)
361             {
362                 a[c++]   = sort[zi];
363                 sort[zi] = -1;
364             }
365         }
366     }
367     if (c < n)
368     {
369         gmx_incons("Lost particles while sorting");
370     }
371 }
372
373 #if GMX_DOUBLE
374 #define R2F_D(x) ((float)((x) >= 0 ? ((1-GMX_FLOAT_EPS)*(x)) : ((1+GMX_FLOAT_EPS)*(x))))
375 #define R2F_U(x) ((float)((x) >= 0 ? ((1+GMX_FLOAT_EPS)*(x)) : ((1-GMX_FLOAT_EPS)*(x))))
376 #else
377 #define R2F_D(x) (x)
378 #define R2F_U(x) (x)
379 #endif
380
381 /* Coordinate order x,y,z, bb order xyz0 */
382 static void calc_bounding_box(int na, int stride, const real *x, nbnxn_bb_t *bb)
383 {
384     int  i;
385     real xl, xh, yl, yh, zl, zh;
386
387     i  = 0;
388     xl = x[i+XX];
389     xh = x[i+XX];
390     yl = x[i+YY];
391     yh = x[i+YY];
392     zl = x[i+ZZ];
393     zh = x[i+ZZ];
394     i += stride;
395     for (int j = 1; j < na; j++)
396     {
397         xl = std::min(xl, x[i+XX]);
398         xh = std::max(xh, x[i+XX]);
399         yl = std::min(yl, x[i+YY]);
400         yh = std::max(yh, x[i+YY]);
401         zl = std::min(zl, x[i+ZZ]);
402         zh = std::max(zh, x[i+ZZ]);
403         i += stride;
404     }
405     /* Note: possible double to float conversion here */
406     bb->lower[BB_X] = R2F_D(xl);
407     bb->lower[BB_Y] = R2F_D(yl);
408     bb->lower[BB_Z] = R2F_D(zl);
409     bb->upper[BB_X] = R2F_U(xh);
410     bb->upper[BB_Y] = R2F_U(yh);
411     bb->upper[BB_Z] = R2F_U(zh);
412 }
413
414 /* Packed coordinates, bb order xyz0 */
415 static void calc_bounding_box_x_x4(int na, const real *x, nbnxn_bb_t *bb)
416 {
417     real xl, xh, yl, yh, zl, zh;
418
419     xl = x[XX*c_packX4];
420     xh = x[XX*c_packX4];
421     yl = x[YY*c_packX4];
422     yh = x[YY*c_packX4];
423     zl = x[ZZ*c_packX4];
424     zh = x[ZZ*c_packX4];
425     for (int j = 1; j < na; j++)
426     {
427         xl = std::min(xl, x[j+XX*c_packX4]);
428         xh = std::max(xh, x[j+XX*c_packX4]);
429         yl = std::min(yl, x[j+YY*c_packX4]);
430         yh = std::max(yh, x[j+YY*c_packX4]);
431         zl = std::min(zl, x[j+ZZ*c_packX4]);
432         zh = std::max(zh, x[j+ZZ*c_packX4]);
433     }
434     /* Note: possible double to float conversion here */
435     bb->lower[BB_X] = R2F_D(xl);
436     bb->lower[BB_Y] = R2F_D(yl);
437     bb->lower[BB_Z] = R2F_D(zl);
438     bb->upper[BB_X] = R2F_U(xh);
439     bb->upper[BB_Y] = R2F_U(yh);
440     bb->upper[BB_Z] = R2F_U(zh);
441 }
442
443 /* Packed coordinates, bb order xyz0 */
444 static void calc_bounding_box_x_x8(int na, const real *x, nbnxn_bb_t *bb)
445 {
446     real xl, xh, yl, yh, zl, zh;
447
448     xl = x[XX*c_packX8];
449     xh = x[XX*c_packX8];
450     yl = x[YY*c_packX8];
451     yh = x[YY*c_packX8];
452     zl = x[ZZ*c_packX8];
453     zh = x[ZZ*c_packX8];
454     for (int j = 1; j < na; j++)
455     {
456         xl = std::min(xl, x[j+XX*c_packX8]);
457         xh = std::max(xh, x[j+XX*c_packX8]);
458         yl = std::min(yl, x[j+YY*c_packX8]);
459         yh = std::max(yh, x[j+YY*c_packX8]);
460         zl = std::min(zl, x[j+ZZ*c_packX8]);
461         zh = std::max(zh, x[j+ZZ*c_packX8]);
462     }
463     /* Note: possible double to float conversion here */
464     bb->lower[BB_X] = R2F_D(xl);
465     bb->lower[BB_Y] = R2F_D(yl);
466     bb->lower[BB_Z] = R2F_D(zl);
467     bb->upper[BB_X] = R2F_U(xh);
468     bb->upper[BB_Y] = R2F_U(yh);
469     bb->upper[BB_Z] = R2F_U(zh);
470 }
471
472 /* Packed coordinates, bb order xyz0 */
473 gmx_unused static void calc_bounding_box_x_x4_halves(int na, const real *x,
474                                                      nbnxn_bb_t *bb, nbnxn_bb_t *bbj)
475 {
476     // TODO: During SIMDv2 transition only some archs use namespace (remove when done)
477     using namespace gmx;
478
479     calc_bounding_box_x_x4(std::min(na, 2), x, bbj);
480
481     if (na > 2)
482     {
483         calc_bounding_box_x_x4(std::min(na-2, 2), x+(c_packX4>>1), bbj+1);
484     }
485     else
486     {
487         /* Set the "empty" bounding box to the same as the first one,
488          * so we don't need to treat special cases in the rest of the code.
489          */
490 #if NBNXN_SEARCH_BB_SIMD4
491         store4(&bbj[1].lower[0], load4(&bbj[0].lower[0]));
492         store4(&bbj[1].upper[0], load4(&bbj[0].upper[0]));
493 #else
494         bbj[1] = bbj[0];
495 #endif
496     }
497
498 #if NBNXN_SEARCH_BB_SIMD4
499     store4(&bb->lower[0], min(load4(&bbj[0].lower[0]), load4(&bbj[1].lower[0])));
500     store4(&bb->upper[0], max(load4(&bbj[0].upper[0]), load4(&bbj[1].upper[0])));
501 #else
502     {
503         int i;
504
505         for (i = 0; i < NNBSBB_C; i++)
506         {
507             bb->lower[i] = std::min(bbj[0].lower[i], bbj[1].lower[i]);
508             bb->upper[i] = std::max(bbj[0].upper[i], bbj[1].upper[i]);
509         }
510     }
511 #endif
512 }
513
514 #if NBNXN_SEARCH_BB_SIMD4
515
516 /* Coordinate order xyz, bb order xxxxyyyyzzzz */
517 static void calc_bounding_box_xxxx(int na, int stride, const real *x, float *bb)
518 {
519     int  i;
520     real xl, xh, yl, yh, zl, zh;
521
522     i  = 0;
523     xl = x[i+XX];
524     xh = x[i+XX];
525     yl = x[i+YY];
526     yh = x[i+YY];
527     zl = x[i+ZZ];
528     zh = x[i+ZZ];
529     i += stride;
530     for (int j = 1; j < na; j++)
531     {
532         xl = std::min(xl, x[i+XX]);
533         xh = std::max(xh, x[i+XX]);
534         yl = std::min(yl, x[i+YY]);
535         yh = std::max(yh, x[i+YY]);
536         zl = std::min(zl, x[i+ZZ]);
537         zh = std::max(zh, x[i+ZZ]);
538         i += stride;
539     }
540     /* Note: possible double to float conversion here */
541     bb[0*STRIDE_PBB] = R2F_D(xl);
542     bb[1*STRIDE_PBB] = R2F_D(yl);
543     bb[2*STRIDE_PBB] = R2F_D(zl);
544     bb[3*STRIDE_PBB] = R2F_U(xh);
545     bb[4*STRIDE_PBB] = R2F_U(yh);
546     bb[5*STRIDE_PBB] = R2F_U(zh);
547 }
548
549 #endif /* NBNXN_SEARCH_BB_SIMD4 */
550
551 #if NBNXN_SEARCH_SIMD4_FLOAT_X_BB
552
553 /* Coordinate order xyz?, bb order xyz0 */
554 static void calc_bounding_box_simd4(int na, const float *x, nbnxn_bb_t *bb)
555 {
556     // TODO: During SIMDv2 transition only some archs use namespace (remove when done)
557     using namespace gmx;
558
559     Simd4Float bb_0_S, bb_1_S;
560     Simd4Float x_S;
561
562     bb_0_S = load4(x);
563     bb_1_S = bb_0_S;
564
565     for (int i = 1; i < na; i++)
566     {
567         x_S    = load4(x+i*NNBSBB_C);
568         bb_0_S = min(bb_0_S, x_S);
569         bb_1_S = max(bb_1_S, x_S);
570     }
571
572     store4(&bb->lower[0], bb_0_S);
573     store4(&bb->upper[0], bb_1_S);
574 }
575
576 /* Coordinate order xyz?, bb order xxxxyyyyzzzz */
577 static void calc_bounding_box_xxxx_simd4(int na, const float *x,
578                                          nbnxn_bb_t *bb_work_aligned,
579                                          real *bb)
580 {
581     calc_bounding_box_simd4(na, x, bb_work_aligned);
582
583     bb[0*STRIDE_PBB] = bb_work_aligned->lower[BB_X];
584     bb[1*STRIDE_PBB] = bb_work_aligned->lower[BB_Y];
585     bb[2*STRIDE_PBB] = bb_work_aligned->lower[BB_Z];
586     bb[3*STRIDE_PBB] = bb_work_aligned->upper[BB_X];
587     bb[4*STRIDE_PBB] = bb_work_aligned->upper[BB_Y];
588     bb[5*STRIDE_PBB] = bb_work_aligned->upper[BB_Z];
589 }
590
591 #endif /* NBNXN_SEARCH_SIMD4_FLOAT_X_BB */
592
593
594 /* Combines pairs of consecutive bounding boxes */
595 static void combine_bounding_box_pairs(nbnxn_grid_t                    *grid,
596                                        gmx::ArrayRef<const nbnxn_bb_t>  bb)
597 {
598     // TODO: During SIMDv2 transition only some archs use namespace (remove when done)
599     using namespace gmx;
600
601     for (int i = 0; i < grid->numCells[XX]*grid->numCells[YY]; i++)
602     {
603         /* Starting bb in a column is expected to be 2-aligned */
604         int sc2 = grid->cxy_ind[i]>>1;
605         /* For odd numbers skip the last bb here */
606         int nc2 = (grid->cxy_na[i]+3)>>(2+1);
607         int c2;
608         for (c2 = sc2; c2 < sc2+nc2; c2++)
609         {
610 #if NBNXN_SEARCH_BB_SIMD4
611             Simd4Float min_S, max_S;
612
613             min_S = min(load4(&bb[c2*2+0].lower[0]),
614                         load4(&bb[c2*2+1].lower[0]));
615             max_S = max(load4(&bb[c2*2+0].upper[0]),
616                         load4(&bb[c2*2+1].upper[0]));
617             store4(&grid->bbj[c2].lower[0], min_S);
618             store4(&grid->bbj[c2].upper[0], max_S);
619 #else
620             for (int j = 0; j < NNBSBB_C; j++)
621             {
622                 grid->bbj[c2].lower[j] = std::min(bb[c2*2+0].lower[j],
623                                                   bb[c2*2+1].lower[j]);
624                 grid->bbj[c2].upper[j] = std::max(bb[c2*2+0].upper[j],
625                                                   bb[c2*2+1].upper[j]);
626             }
627 #endif
628         }
629         if (((grid->cxy_na[i]+3)>>2) & 1)
630         {
631             /* The bb count in this column is odd: duplicate the last bb */
632             for (int j = 0; j < NNBSBB_C; j++)
633             {
634                 grid->bbj[c2].lower[j] = bb[c2*2].lower[j];
635                 grid->bbj[c2].upper[j] = bb[c2*2].upper[j];
636             }
637         }
638     }
639 }
640
641
642 /* Prints the average bb size, used for debug output */
643 static void print_bbsizes_simple(FILE                *fp,
644                                  const nbnxn_grid_t  *grid)
645 {
646     dvec ba;
647
648     clear_dvec(ba);
649     for (int c = 0; c < grid->nc; c++)
650     {
651         for (int d = 0; d < DIM; d++)
652         {
653             ba[d] += grid->bb[c].upper[d] - grid->bb[c].lower[d];
654         }
655     }
656     dsvmul(1.0/grid->nc, ba, ba);
657
658     real avgCellSizeZ = (grid->atom_density > 0 ? grid->na_c/(grid->atom_density*grid->cellSize[XX]*grid->cellSize[YY]) : 0.0);
659
660     fprintf(fp, "ns bb: grid %4.2f %4.2f %4.2f abs %4.2f %4.2f %4.2f rel %4.2f %4.2f %4.2f\n",
661             grid->cellSize[XX],
662             grid->cellSize[YY],
663             avgCellSizeZ,
664             ba[XX], ba[YY], ba[ZZ],
665             ba[XX]*grid->invCellSize[XX],
666             ba[YY]*grid->invCellSize[YY],
667             grid->atom_density > 0 ? ba[ZZ]/avgCellSizeZ : 0.0);
668 }
669
670 /* Prints the average bb size, used for debug output */
671 static void print_bbsizes_supersub(FILE                *fp,
672                                    const nbnxn_grid_t  *grid)
673 {
674     int  ns;
675     dvec ba;
676
677     clear_dvec(ba);
678     ns = 0;
679     for (int c = 0; c < grid->nc; c++)
680     {
681 #if NBNXN_BBXXXX
682         for (int s = 0; s < grid->nsubc[c]; s += STRIDE_PBB)
683         {
684             int cs_w = (c*c_gpuNumClusterPerCell + s)/STRIDE_PBB;
685             for (int i = 0; i < STRIDE_PBB; i++)
686             {
687                 for (int d = 0; d < DIM; d++)
688                 {
689                     ba[d] +=
690                         grid->pbb[cs_w*NNBSBB_XXXX+(DIM+d)*STRIDE_PBB+i] -
691                         grid->pbb[cs_w*NNBSBB_XXXX+     d *STRIDE_PBB+i];
692                 }
693             }
694         }
695 #else
696         for (int s = 0; s < grid->nsubc[c]; s++)
697         {
698             int cs = c*c_gpuNumClusterPerCell + s;
699             for (int d = 0; d < DIM; d++)
700             {
701                 ba[d] += grid->bb[cs].upper[d] - grid->bb[cs].lower[d];
702             }
703         }
704 #endif
705         ns += grid->nsubc[c];
706     }
707     dsvmul(1.0/ns, ba, ba);
708
709     real avgClusterSizeZ = (grid->atom_density > 0 ? grid->na_sc/(grid->atom_density*grid->cellSize[XX]*grid->cellSize[YY]*c_gpuNumClusterPerCellZ) : 0.0);
710
711     fprintf(fp, "ns bb: grid %4.2f %4.2f %4.2f abs %4.2f %4.2f %4.2f rel %4.2f %4.2f %4.2f\n",
712             grid->cellSize[XX]/c_gpuNumClusterPerCellX,
713             grid->cellSize[YY]/c_gpuNumClusterPerCellY,
714             avgClusterSizeZ,
715             ba[XX], ba[YY], ba[ZZ],
716             ba[XX]*c_gpuNumClusterPerCellX*grid->invCellSize[XX],
717             ba[YY]*c_gpuNumClusterPerCellY*grid->invCellSize[YY],
718             grid->atom_density > 0 ? ba[ZZ]/avgClusterSizeZ : 0.0);
719 }
720
721 /* Set non-bonded interaction flags for the current cluster.
722  * Sorts atoms on LJ coefficients: !=0 first, ==0 at the end.
723  */
724 static void sort_cluster_on_flag(int                 numAtomsInCluster,
725                                  int                 atomStart,
726                                  int                 atomEnd,
727                                  const int          *atinfo,
728                                  gmx::ArrayRef<int>  order,
729                                  int                *flags)
730 {
731     constexpr int c_maxNumAtomsInCluster = 8;
732     int           sort1[c_maxNumAtomsInCluster];
733     int           sort2[c_maxNumAtomsInCluster];
734
735     GMX_ASSERT(numAtomsInCluster <= c_maxNumAtomsInCluster, "Need to increase c_maxNumAtomsInCluster to support larger clusters");
736
737     *flags = 0;
738
739     int subc = 0;
740     for (int s = atomStart; s < atomEnd; s += numAtomsInCluster)
741     {
742         /* Make lists for this (sub-)cell on atoms with and without LJ */
743         int      n1         = 0;
744         int      n2         = 0;
745         gmx_bool haveQ      = FALSE;
746         int      a_lj_max   = -1;
747         for (int a = s; a < std::min(s + numAtomsInCluster, atomEnd); a++)
748         {
749             haveQ = haveQ || GET_CGINFO_HAS_Q(atinfo[order[a]]);
750
751             if (GET_CGINFO_HAS_VDW(atinfo[order[a]]))
752             {
753                 sort1[n1++] = order[a];
754                 a_lj_max    = a;
755             }
756             else
757             {
758                 sort2[n2++] = order[a];
759             }
760         }
761
762         /* If we don't have atoms with LJ, there's nothing to sort */
763         if (n1 > 0)
764         {
765             *flags |= NBNXN_CI_DO_LJ(subc);
766
767             if (2*n1 <= numAtomsInCluster)
768             {
769                 /* Only sort when strictly necessary. Ordering particles
770                  * Ordering particles can lead to less accurate summation
771                  * due to rounding, both for LJ and Coulomb interactions.
772                  */
773                 if (2*(a_lj_max - s) >= numAtomsInCluster)
774                 {
775                     for (int i = 0; i < n1; i++)
776                     {
777                         order[atomStart + i]      = sort1[i];
778                     }
779                     for (int j = 0; j < n2; j++)
780                     {
781                         order[atomStart + n1 + j] = sort2[j];
782                     }
783                 }
784
785                 *flags |= NBNXN_CI_HALF_LJ(subc);
786             }
787         }
788         if (haveQ)
789         {
790             *flags |= NBNXN_CI_DO_COUL(subc);
791         }
792         subc++;
793     }
794 }
795
796 /* Fill a pair search cell with atoms.
797  * Potentially sorts atoms and sets the interaction flags.
798  */
799 static void fill_cell(nbnxn_search                  *nbs,
800                       nbnxn_grid_t                  *grid,
801                       nbnxn_atomdata_t              *nbat,
802                       int                            atomStart,
803                       int                            atomEnd,
804                       const int                     *atinfo,
805                       gmx::ArrayRef<const gmx::RVec> x,
806                       nbnxn_bb_t gmx_unused         *bb_work_aligned)
807 {
808     const int numAtoms = atomEnd - atomStart;
809
810     if (grid->bSimple)
811     {
812         /* Note that non-local grids are already sorted.
813          * Then sort_cluster_on_flag will only set the flags and the sorting
814          * will not affect the atom order.
815          */
816         sort_cluster_on_flag(grid->na_c, atomStart, atomEnd, atinfo, nbs->a,
817                              grid->flags.data() + (atomStart >> grid->na_c_2log) - grid->cell0);
818     }
819
820     if (nbs->bFEP)
821     {
822         /* Set the fep flag for perturbed atoms in this (sub-)cell */
823
824         /* The grid-local cluster/(sub-)cell index */
825         int cell = (atomStart >> grid->na_c_2log) - grid->cell0*(grid->bSimple ? 1 : c_gpuNumClusterPerCell);
826         grid->fep[cell] = 0;
827         for (int at = atomStart; at < atomEnd; at++)
828         {
829             if (nbs->a[at] >= 0 && GET_CGINFO_FEP(atinfo[nbs->a[at]]))
830             {
831                 grid->fep[cell] |= (1 << (at - atomStart));
832             }
833         }
834     }
835
836     /* Now we have sorted the atoms, set the cell indices */
837     for (int at = atomStart; at < atomEnd; at++)
838     {
839         nbs->cell[nbs->a[at]] = at;
840     }
841
842     copy_rvec_to_nbat_real(nbs->a.data() + atomStart, numAtoms, grid->na_c,
843                            as_rvec_array(x.data()),
844                            nbat->XFormat, nbat->x, atomStart);
845
846     if (nbat->XFormat == nbatX4)
847     {
848         /* Store the bounding boxes as xyz.xyz. */
849         size_t      offset = (atomStart - grid->cell0*grid->na_sc) >> grid->na_c_2log;
850         nbnxn_bb_t *bb_ptr = grid->bb.data() + offset;
851
852 #if GMX_SIMD && GMX_SIMD_REAL_WIDTH == 2
853         if (2*grid->na_cj == grid->na_c)
854         {
855             calc_bounding_box_x_x4_halves(numAtoms, nbat->x + atom_to_x_index<c_packX4>(atomStart), bb_ptr,
856                                           grid->bbj.data() + offset*2);
857         }
858         else
859 #endif
860         {
861             calc_bounding_box_x_x4(numAtoms, nbat->x + atom_to_x_index<c_packX4>(atomStart), bb_ptr);
862         }
863     }
864     else if (nbat->XFormat == nbatX8)
865     {
866         /* Store the bounding boxes as xyz.xyz. */
867         size_t      offset = (atomStart - grid->cell0*grid->na_sc) >> grid->na_c_2log;
868         nbnxn_bb_t *bb_ptr = grid->bb.data() + offset;
869
870         calc_bounding_box_x_x8(numAtoms, nbat->x +  atom_to_x_index<c_packX8>(atomStart), bb_ptr);
871     }
872 #if NBNXN_BBXXXX
873     else if (!grid->bSimple)
874     {
875         /* Store the bounding boxes in a format convenient
876          * for SIMD4 calculations: xxxxyyyyzzzz...
877          */
878         float *pbb_ptr =
879             grid->pbb.data() +
880             ((atomStart - grid->cell0*grid->na_sc) >> (grid->na_c_2log + STRIDE_PBB_2LOG))*NNBSBB_XXXX +
881             (((atomStart - grid->cell0*grid->na_sc) >> grid->na_c_2log) & (STRIDE_PBB - 1));
882
883 #if NBNXN_SEARCH_SIMD4_FLOAT_X_BB
884         if (nbat->XFormat == nbatXYZQ)
885         {
886             calc_bounding_box_xxxx_simd4(numAtoms, nbat->x + atomStart*nbat->xstride,
887                                          bb_work_aligned, pbb_ptr);
888         }
889         else
890 #endif
891         {
892             calc_bounding_box_xxxx(numAtoms, nbat->xstride, nbat->x + atomStart*nbat->xstride,
893                                    pbb_ptr);
894         }
895         if (gmx_debug_at)
896         {
897             fprintf(debug, "cell %4d bb %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\n",
898                     atomStart >> grid->na_c_2log,
899                     pbb_ptr[0*STRIDE_PBB], pbb_ptr[3*STRIDE_PBB],
900                     pbb_ptr[1*STRIDE_PBB], pbb_ptr[4*STRIDE_PBB],
901                     pbb_ptr[2*STRIDE_PBB], pbb_ptr[5*STRIDE_PBB]);
902         }
903     }
904 #endif
905     else
906     {
907         /* Store the bounding boxes as xyz.xyz. */
908         nbnxn_bb_t *bb_ptr = grid->bb.data() + ((atomStart - grid->cell0*grid->na_sc) >> grid->na_c_2log);
909
910         calc_bounding_box(numAtoms, nbat->xstride, nbat->x + atomStart*nbat->xstride,
911                           bb_ptr);
912
913         if (gmx_debug_at)
914         {
915             int bbo = (atomStart - grid->cell0*grid->na_sc)/grid->na_c;
916             fprintf(debug, "cell %4d bb %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\n",
917                     atomStart >> grid->na_c_2log,
918                     grid->bb[bbo].lower[BB_X],
919                     grid->bb[bbo].lower[BB_Y],
920                     grid->bb[bbo].lower[BB_Z],
921                     grid->bb[bbo].upper[BB_X],
922                     grid->bb[bbo].upper[BB_Y],
923                     grid->bb[bbo].upper[BB_Z]);
924         }
925     }
926 }
927
928 /* Spatially sort the atoms within one grid column */
929 static void sort_columns_simple(nbnxn_search *nbs,
930                                 int dd_zone,
931                                 nbnxn_grid_t *grid,
932                                 int atomStart, int atomEnd,
933                                 const int *atinfo,
934                                 gmx::ArrayRef<const gmx::RVec> x,
935                                 nbnxn_atomdata_t *nbat,
936                                 int cxy_start, int cxy_end,
937                                 gmx::ArrayRef<int> sort_work)
938 {
939     if (debug)
940     {
941         fprintf(debug, "cell0 %d sorting columns %d - %d, atoms %d - %d\n",
942                 grid->cell0, cxy_start, cxy_end, atomStart, atomEnd);
943     }
944
945     const bool relevantAtomsAreWithinGridBounds = (grid->maxAtomGroupRadius == 0);
946
947     /* Sort the atoms within each x,y column in 3 dimensions */
948     for (int cxy = cxy_start; cxy < cxy_end; cxy++)
949     {
950         int na  = grid->cxy_na[cxy];
951         int ncz = grid->cxy_ind[cxy+1] - grid->cxy_ind[cxy];
952         int ash = (grid->cell0 + grid->cxy_ind[cxy])*grid->na_sc;
953
954         /* Sort the atoms within each x,y column on z coordinate */
955         sort_atoms(ZZ, FALSE, dd_zone,
956                    relevantAtomsAreWithinGridBounds,
957                    nbs->a.data() + ash, na, x,
958                    grid->c0[ZZ],
959                    1.0/grid->size[ZZ], ncz*grid->na_sc,
960                    sort_work);
961
962         /* Fill the ncz cells in this column */
963         int cfilled = grid->cxy_ind[cxy];
964         for (int cz = 0; cz < ncz; cz++)
965         {
966             int c     = grid->cxy_ind[cxy] + cz;
967
968             int ash_c = ash + cz*grid->na_sc;
969             int na_c  = std::min(grid->na_sc, na-(ash_c-ash));
970
971             fill_cell(nbs, grid, nbat,
972                       ash_c, ash_c+na_c, atinfo, x,
973                       nullptr);
974
975             /* This copy to bbcz is not really necessary.
976              * But it allows to use the same grid search code
977              * for the simple and supersub cell setups.
978              */
979             if (na_c > 0)
980             {
981                 cfilled = c;
982             }
983             grid->bbcz[c*NNBSBB_D  ] = grid->bb[cfilled].lower[BB_Z];
984             grid->bbcz[c*NNBSBB_D+1] = grid->bb[cfilled].upper[BB_Z];
985         }
986
987         /* Set the unused atom indices to -1 */
988         for (int ind = na; ind < ncz*grid->na_sc; ind++)
989         {
990             nbs->a[ash+ind] = -1;
991         }
992     }
993 }
994
995 /* Spatially sort the atoms within one grid column */
996 static void sort_columns_supersub(nbnxn_search *nbs,
997                                   int dd_zone,
998                                   nbnxn_grid_t *grid,
999                                   int atomStart, int atomEnd,
1000                                   const int *atinfo,
1001                                   gmx::ArrayRef<const gmx::RVec> x,
1002                                   nbnxn_atomdata_t *nbat,
1003                                   int cxy_start, int cxy_end,
1004                                   gmx::ArrayRef<int> sort_work)
1005 {
1006     nbnxn_bb_t  bb_work_array[2];
1007     nbnxn_bb_t *bb_work_aligned = reinterpret_cast<nbnxn_bb_t *>((reinterpret_cast<std::size_t>(bb_work_array + 1)) & (~(static_cast<std::size_t>(15))));
1008
1009     if (debug)
1010     {
1011         fprintf(debug, "cell0 %d sorting columns %d - %d, atoms %d - %d\n",
1012                 grid->cell0, cxy_start, cxy_end, atomStart, atomEnd);
1013     }
1014
1015     const bool relevantAtomsAreWithinGridBounds = (grid->maxAtomGroupRadius == 0);
1016
1017     int        subdiv_x = grid->na_c;
1018     int        subdiv_y = c_gpuNumClusterPerCellX*subdiv_x;
1019     int        subdiv_z = c_gpuNumClusterPerCellY*subdiv_y;
1020
1021     /* Sort the atoms within each x,y column in 3 dimensions.
1022      * Loop over all columns on the x/y grid.
1023      */
1024     for (int cxy = cxy_start; cxy < cxy_end; cxy++)
1025     {
1026         int gridX            = cxy/grid->numCells[YY];
1027         int gridY            = cxy - gridX*grid->numCells[YY];
1028
1029         int numAtomsInColumn = grid->cxy_na[cxy];
1030         int numCellsInColumn = grid->cxy_ind[cxy + 1] - grid->cxy_ind[cxy];
1031         int ash              = (grid->cell0 + grid->cxy_ind[cxy])*grid->na_sc;
1032
1033         /* Sort the atoms within each x,y column on z coordinate */
1034         sort_atoms(ZZ, FALSE, dd_zone,
1035                    relevantAtomsAreWithinGridBounds,
1036                    nbs->a.data() + ash, numAtomsInColumn, x,
1037                    grid->c0[ZZ],
1038                    1.0/grid->size[ZZ], numCellsInColumn*grid->na_sc,
1039                    sort_work);
1040
1041         /* This loop goes over the cells and clusters along z at once */
1042         for (int sub_z = 0; sub_z < numCellsInColumn*c_gpuNumClusterPerCellZ; sub_z++)
1043         {
1044             int ash_z = ash + sub_z*subdiv_z;
1045             int na_z  = std::min(subdiv_z, numAtomsInColumn - (ash_z - ash));
1046             int cz    = -1;
1047             /* We have already sorted on z */
1048
1049             if (sub_z % c_gpuNumClusterPerCellZ == 0)
1050             {
1051                 cz = sub_z/c_gpuNumClusterPerCellZ;
1052                 int cell = grid->cxy_ind[cxy] + cz;
1053
1054                 /* The number of atoms in this cell/super-cluster */
1055                 int numAtoms = std::min(grid->na_sc, numAtomsInColumn - (ash_z - ash));
1056
1057                 grid->nsubc[cell] = std::min(c_gpuNumClusterPerCell,
1058                                              (numAtoms + grid->na_c - 1)/grid->na_c);
1059
1060                 /* Store the z-boundaries of the bounding box of the cell */
1061                 grid->bbcz[cell*NNBSBB_D  ] = x[nbs->a[ash_z]][ZZ];
1062                 grid->bbcz[cell*NNBSBB_D+1] = x[nbs->a[ash_z + numAtoms - 1]][ZZ];
1063             }
1064
1065             if (c_gpuNumClusterPerCellY > 1)
1066             {
1067                 /* Sort the atoms along y */
1068                 sort_atoms(YY, (sub_z & 1) != 0, dd_zone,
1069                            relevantAtomsAreWithinGridBounds,
1070                            nbs->a.data() + ash_z, na_z, x,
1071                            grid->c0[YY] + gridY*grid->cellSize[YY],
1072                            grid->invCellSize[YY], subdiv_z,
1073                            sort_work);
1074             }
1075
1076             for (int sub_y = 0; sub_y < c_gpuNumClusterPerCellY; sub_y++)
1077             {
1078                 int ash_y = ash_z + sub_y*subdiv_y;
1079                 int na_y  = std::min(subdiv_y, numAtomsInColumn - (ash_y - ash));
1080
1081                 if (c_gpuNumClusterPerCellX > 1)
1082                 {
1083                     /* Sort the atoms along x */
1084                     sort_atoms(XX, ((cz*c_gpuNumClusterPerCellY + sub_y) & 1) != 0, dd_zone,
1085                                relevantAtomsAreWithinGridBounds,
1086                                nbs->a.data() + ash_y, na_y, x,
1087                                grid->c0[XX] + gridX*grid->cellSize[XX],
1088                                grid->invCellSize[XX], subdiv_y,
1089                                sort_work);
1090                 }
1091
1092                 for (int sub_x = 0; sub_x < c_gpuNumClusterPerCellX; sub_x++)
1093                 {
1094                     int ash_x = ash_y + sub_x*subdiv_x;
1095                     int na_x  = std::min(subdiv_x, numAtomsInColumn - (ash_x - ash));
1096
1097                     fill_cell(nbs, grid, nbat,
1098                               ash_x, ash_x + na_x, atinfo, x,
1099                               bb_work_aligned);
1100                 }
1101             }
1102         }
1103
1104         /* Set the unused atom indices to -1 */
1105         for (int ind = numAtomsInColumn; ind < numCellsInColumn*grid->na_sc; ind++)
1106         {
1107             nbs->a[ash + ind] = -1;
1108         }
1109     }
1110 }
1111
1112 /* Sets the cell index in the cell array for atom \p atomIndex and increments the atom count for the grid column */
1113 static void setCellAndAtomCount(gmx::ArrayRef<int>  cell,
1114                                 int                 cellIndex,
1115                                 gmx::ArrayRef<int>  cxy_na,
1116                                 int                 atomIndex)
1117 {
1118     cell[atomIndex]    = cellIndex;
1119     cxy_na[cellIndex] += 1;
1120 }
1121
1122 /* Determine in which grid column atoms should go */
1123 static void calc_column_indices(nbnxn_grid_t *grid,
1124                                 const gmx::UpdateGroupsCog *updateGroupsCog,
1125                                 int atomStart, int atomEnd,
1126                                 gmx::ArrayRef<const gmx::RVec> x,
1127                                 int dd_zone, const int *move,
1128                                 int thread, int nthread,
1129                                 gmx::ArrayRef<int> cell,
1130                                 gmx::ArrayRef<int> cxy_na)
1131 {
1132     /* We add one extra cell for particles which moved during DD */
1133     for (int i = 0; i < grid->numCells[XX]*grid->numCells[YY] + 1; i++)
1134     {
1135         cxy_na[i] = 0;
1136     }
1137
1138     int taskAtomStart = atomStart + static_cast<int>((thread + 0)*(atomEnd - atomStart))/nthread;
1139     int taskAtomEnd   = atomStart + static_cast<int>((thread + 1)*(atomEnd - atomStart))/nthread;
1140     if (dd_zone == 0)
1141     {
1142         /* Home zone */
1143         for (int i = taskAtomStart; i < taskAtomEnd; i++)
1144         {
1145             if (move == nullptr || move[i] >= 0)
1146             {
1147
1148                 const gmx::RVec &coord = (updateGroupsCog ? updateGroupsCog->cogForAtom(i) : x[i]);
1149
1150                 /* We need to be careful with rounding,
1151                  * particles might be a few bits outside the local zone.
1152                  * The int cast takes care of the lower bound,
1153                  * we will explicitly take care of the upper bound.
1154                  */
1155                 int cx = static_cast<int>((coord[XX] - grid->c0[XX])*grid->invCellSize[XX]);
1156                 int cy = static_cast<int>((coord[YY] - grid->c0[YY])*grid->invCellSize[YY]);
1157
1158 #ifndef NDEBUG
1159                 if (cx < 0 || cx > grid->numCells[XX] ||
1160                     cy < 0 || cy > grid->numCells[YY])
1161                 {
1162                     gmx_fatal(FARGS,
1163                               "grid cell cx %d cy %d out of range (max %d %d)\n"
1164                               "atom %f %f %f, grid->c0 %f %f",
1165                               cx, cy, grid->numCells[XX], grid->numCells[YY],
1166                               x[i][XX], x[i][YY], x[i][ZZ], grid->c0[XX], grid->c0[YY]);
1167                 }
1168 #endif
1169                 /* Take care of potential rouding issues */
1170                 cx = std::min(cx, grid->numCells[XX] - 1);
1171                 cy = std::min(cy, grid->numCells[YY] - 1);
1172
1173                 /* For the moment cell will contain only the, grid local,
1174                  * x and y indices, not z.
1175                  */
1176                 setCellAndAtomCount(cell, cx*grid->numCells[YY] + cy, cxy_na, i);
1177             }
1178             else
1179             {
1180                 /* Put this moved particle after the end of the grid,
1181                  * so we can process it later without using conditionals.
1182                  */
1183                 setCellAndAtomCount(cell, grid->numCells[XX]*grid->numCells[YY], cxy_na, i);
1184             }
1185         }
1186     }
1187     else
1188     {
1189         /* Non-home zone */
1190         for (int i = taskAtomStart; i < taskAtomEnd; i++)
1191         {
1192             int cx = static_cast<int>((x[i][XX] - grid->c0[XX])*grid->invCellSize[XX]);
1193             int cy = static_cast<int>((x[i][YY] - grid->c0[YY])*grid->invCellSize[YY]);
1194
1195             /* For non-home zones there could be particles outside
1196              * the non-bonded cut-off range, which have been communicated
1197              * for bonded interactions only. For the result it doesn't
1198              * matter where these end up on the grid. For performance
1199              * we put them in an extra row at the border.
1200              */
1201             cx = std::max(cx, 0);
1202             cx = std::min(cx, grid->numCells[XX] - 1);
1203             cy = std::max(cy, 0);
1204             cy = std::min(cy, grid->numCells[YY] - 1);
1205
1206             /* For the moment cell will contain only the, grid local,
1207              * x and y indices, not z.
1208              */
1209             setCellAndAtomCount(cell, cx*grid->numCells[YY] + cy, cxy_na, i);
1210         }
1211     }
1212 }
1213
1214 /* Resizes grid and atom data which depend on the number of cells */
1215 static void resizeForNumberOfCells(const nbnxn_grid_t &grid,
1216                                    int                 numAtomsMoved,
1217                                    nbnxn_search       *nbs,
1218                                    nbnxn_atomdata_t   *nbat)
1219 {
1220     int numNbnxnAtoms = (grid.cell0 + grid.nc)*grid.na_sc;
1221
1222     /* Note: nbs->cell was already resized before */
1223
1224     /* To avoid conditionals we store the moved particles at the end of a,
1225      * make sure we have enough space.
1226      */
1227     nbs->a.resize(numNbnxnAtoms + numAtomsMoved);
1228
1229     /* We need padding up to a multiple of the buffer flag size: simply add */
1230     if (numNbnxnAtoms + NBNXN_BUFFERFLAG_SIZE > nbat->nalloc)
1231     {
1232         nbnxn_atomdata_realloc(nbat, numNbnxnAtoms + NBNXN_BUFFERFLAG_SIZE);
1233     }
1234
1235     nbat->natoms = numNbnxnAtoms;
1236 }
1237
1238 /* Determine in which grid cells the atoms should go */
1239 static void
1240 calc_cell_indices(nbnxn_search                   *nbs,
1241                   int                             ddZone,
1242                   nbnxn_grid_t                   *grid,
1243                   const gmx::UpdateGroupsCog     *updateGroupsCog,
1244                   int                             atomStart,
1245                   int                             atomEnd,
1246                   const int                      *atinfo,
1247                   gmx::ArrayRef<const gmx::RVec>  x,
1248                   int                             numAtomsMoved,
1249                   const int                      *move,
1250                   nbnxn_atomdata_t               *nbat)
1251 {
1252     /* First compute all grid/column indices and store them in nbs->cell */
1253     nbs->cell.resize(atomEnd);
1254
1255     const int nthread = gmx_omp_nthreads_get(emntPairsearch);
1256
1257 #pragma omp parallel for num_threads(nthread) schedule(static)
1258     for (int thread = 0; thread < nthread; thread++)
1259     {
1260         try
1261         {
1262             calc_column_indices(grid, updateGroupsCog,
1263                                 atomStart, atomEnd, x,
1264                                 ddZone, move, thread, nthread,
1265                                 nbs->cell, nbs->work[thread].cxy_na);
1266         }
1267         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
1268     }
1269
1270     /* Make the cell index as a function of x and y */
1271     int ncz_max      = 0;
1272     int ncz          = 0;
1273     grid->cxy_ind[0] = 0;
1274     for (int i = 0; i < grid->numCells[XX]*grid->numCells[YY] + 1; i++)
1275     {
1276         /* We set ncz_max at the beginning of the loop iso at the end
1277          * to skip i=grid->ncx*grid->numCells[YY] which are moved particles
1278          * that do not need to be ordered on the grid.
1279          */
1280         if (ncz > ncz_max)
1281         {
1282             ncz_max = ncz;
1283         }
1284         int cxy_na_i = nbs->work[0].cxy_na[i];
1285         for (int thread = 1; thread < nthread; thread++)
1286         {
1287             cxy_na_i += nbs->work[thread].cxy_na[i];
1288         }
1289         ncz = (cxy_na_i + grid->na_sc - 1)/grid->na_sc;
1290         if (nbat->XFormat == nbatX8)
1291         {
1292             /* Make the number of cell a multiple of 2 */
1293             ncz = (ncz + 1) & ~1;
1294         }
1295         grid->cxy_ind[i+1] = grid->cxy_ind[i] + ncz;
1296         /* Clear cxy_na, so we can reuse the array below */
1297         grid->cxy_na[i] = 0;
1298     }
1299     grid->nc = grid->cxy_ind[grid->numCells[XX]*grid->numCells[YY]] - grid->cxy_ind[0];
1300
1301     resizeForNumberOfCells(*grid, numAtomsMoved, nbs, nbat);
1302
1303     if (debug)
1304     {
1305         fprintf(debug, "ns na_sc %d na_c %d super-cells: %d x %d y %d z %.1f maxz %d\n",
1306                 grid->na_sc, grid->na_c, grid->nc,
1307                 grid->numCells[XX], grid->numCells[YY], grid->nc/(static_cast<double>(grid->numCells[XX]*grid->numCells[YY])),
1308                 ncz_max);
1309         if (gmx_debug_at)
1310         {
1311             int i = 0;
1312             for (int cy = 0; cy < grid->numCells[YY]; cy++)
1313             {
1314                 for (int cx = 0; cx < grid->numCells[XX]; cx++)
1315                 {
1316                     fprintf(debug, " %2d", grid->cxy_ind[i+1]-grid->cxy_ind[i]);
1317                     i++;
1318                 }
1319                 fprintf(debug, "\n");
1320             }
1321         }
1322     }
1323
1324     /* Make sure the work array for sorting is large enough */
1325     if (ncz_max*grid->na_sc*SGSF > gmx::index(nbs->work[0].sortBuffer.size()))
1326     {
1327         for (nbnxn_search_work_t &work : nbs->work)
1328         {
1329             /* Elements not in use should be -1 */
1330             work.sortBuffer.resize(ncz_max*grid->na_sc*SGSF, -1);
1331         }
1332     }
1333
1334     /* Now we know the dimensions we can fill the grid.
1335      * This is the first, unsorted fill. We sort the columns after this.
1336      */
1337     for (int i = atomStart; i < atomEnd; i++)
1338     {
1339         /* At this point nbs->cell contains the local grid x,y indices */
1340         int cxy = nbs->cell[i];
1341         nbs->a[(grid->cell0 + grid->cxy_ind[cxy])*grid->na_sc + grid->cxy_na[cxy]++] = i;
1342     }
1343
1344     if (ddZone == 0)
1345     {
1346         /* Set the cell indices for the moved particles */
1347         int n0 = grid->nc*grid->na_sc;
1348         int n1 = grid->nc*grid->na_sc+grid->cxy_na[grid->numCells[XX]*grid->numCells[YY]];
1349         if (ddZone == 0)
1350         {
1351             for (int i = n0; i < n1; i++)
1352             {
1353                 nbs->cell[nbs->a[i]] = i;
1354             }
1355         }
1356     }
1357
1358     /* Sort the super-cell columns along z into the sub-cells. */
1359 #pragma omp parallel for num_threads(nthread) schedule(static)
1360     for (int thread = 0; thread < nthread; thread++)
1361     {
1362         try
1363         {
1364             int columnStart = ((thread + 0)*grid->numCells[XX]*grid->numCells[YY])/nthread;
1365             int columnEnd   = ((thread + 1)*grid->numCells[XX]*grid->numCells[YY])/nthread;
1366             if (grid->bSimple)
1367             {
1368                 sort_columns_simple(nbs, ddZone, grid, atomStart, atomEnd, atinfo, x, nbat,
1369                                     columnStart, columnEnd,
1370                                     nbs->work[thread].sortBuffer);
1371             }
1372             else
1373             {
1374                 sort_columns_supersub(nbs, ddZone, grid, atomStart, atomEnd, atinfo, x, nbat,
1375                                       columnStart, columnEnd,
1376                                       nbs->work[thread].sortBuffer);
1377             }
1378         }
1379         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
1380     }
1381
1382     if (grid->bSimple && nbat->XFormat == nbatX8)
1383     {
1384         combine_bounding_box_pairs(grid, grid->bb);
1385     }
1386
1387     if (!grid->bSimple)
1388     {
1389         grid->nsubc_tot = 0;
1390         for (int i = 0; i < grid->nc; i++)
1391         {
1392             grid->nsubc_tot += grid->nsubc[i];
1393         }
1394     }
1395
1396     if (debug)
1397     {
1398         if (grid->bSimple)
1399         {
1400             print_bbsizes_simple(debug, grid);
1401         }
1402         else
1403         {
1404             fprintf(debug, "ns non-zero sub-cells: %d average atoms %.2f\n",
1405                     grid->nsubc_tot, (atomEnd - atomStart)/static_cast<double>(grid->nsubc_tot));
1406
1407             print_bbsizes_supersub(debug, grid);
1408         }
1409     }
1410 }
1411
1412 /* Sets up a grid and puts the atoms on the grid.
1413  * This function only operates on one domain of the domain decompostion.
1414  * Note that without domain decomposition there is only one domain.
1415  */
1416 void nbnxn_put_on_grid(nbnxn_search_t                  nbs,
1417                        int                             ePBC,
1418                        const matrix                    box,
1419                        int                             ddZone,
1420                        const rvec                      lowerCorner,
1421                        const rvec                      upperCorner,
1422                        const gmx::UpdateGroupsCog     *updateGroupsCog,
1423                        int                             atomStart,
1424                        int                             atomEnd,
1425                        real                            atomDensity,
1426                        const int                      *atinfo,
1427                        gmx::ArrayRef<const gmx::RVec>  x,
1428                        int                             numAtomsMoved,
1429                        const int                      *move,
1430                        int                             nb_kernel_type,
1431                        nbnxn_atomdata_t               *nbat)
1432 {
1433     nbnxn_grid_t *grid = &nbs->grid[ddZone];
1434
1435     nbs_cycle_start(&nbs->cc[enbsCCgrid]);
1436
1437     grid->bSimple = nbnxn_kernel_pairlist_simple(nb_kernel_type);
1438
1439     grid->na_c      = nbnxn_kernel_to_cluster_i_size(nb_kernel_type);
1440     grid->na_cj     = nbnxn_kernel_to_cluster_j_size(nb_kernel_type);
1441     grid->na_sc     = (grid->bSimple ? 1 : c_gpuNumClusterPerCell)*grid->na_c;
1442     grid->na_c_2log = get_2log(grid->na_c);
1443
1444     nbat->na_c = grid->na_c;
1445
1446     if (ddZone == 0)
1447     {
1448         grid->cell0 = 0;
1449     }
1450     else
1451     {
1452         grid->cell0 =
1453             (nbs->grid[ddZone - 1].cell0 + nbs->grid[ddZone - 1].nc)*
1454             nbs->grid[ddZone- 1].na_sc/grid->na_sc;
1455     }
1456
1457     const int n = atomEnd - atomStart;
1458
1459     if (ddZone == 0)
1460     {
1461         nbs->ePBC = ePBC;
1462         copy_mat(box, nbs->box);
1463
1464         /* Avoid zero density */
1465         if (atomDensity > 0)
1466         {
1467             grid->atom_density = atomDensity;
1468         }
1469         else
1470         {
1471             grid->atom_density = grid_atom_density(n - numAtomsMoved, lowerCorner, upperCorner);
1472         }
1473
1474         grid->cell0 = 0;
1475
1476         nbs->natoms_local    = atomEnd - numAtomsMoved;
1477         /* We assume that nbnxn_put_on_grid is called first
1478          * for the local atoms (ddZone=0).
1479          */
1480         nbs->natoms_nonlocal = atomEnd - numAtomsMoved;
1481
1482         /* When not using atom groups, all atoms should be within the grid bounds */
1483         grid->maxAtomGroupRadius = (updateGroupsCog ? updateGroupsCog->maxUpdateGroupRadius() : 0);
1484         /* For the non-local grids the situation is the same as for the local */
1485         for (size_t g = 1; g < nbs->grid.size(); g++)
1486         {
1487             nbs->grid[g].maxAtomGroupRadius = grid->maxAtomGroupRadius;
1488         }
1489
1490         if (debug)
1491         {
1492             fprintf(debug, "natoms_local = %5d atom_density = %5.1f\n",
1493                     nbs->natoms_local, grid->atom_density);
1494         }
1495     }
1496     else
1497     {
1498         nbs->natoms_nonlocal = std::max(nbs->natoms_nonlocal, atomEnd);
1499     }
1500
1501     /* We always use the home zone (grid[0]) for setting the cell size,
1502      * since determining densities for non-local zones is difficult.
1503      */
1504     set_grid_size_xy(nbs, grid,
1505                      ddZone, n - numAtomsMoved,
1506                      lowerCorner, upperCorner,
1507                      nbs->grid[0].atom_density);
1508
1509     calc_cell_indices(nbs, ddZone, grid, updateGroupsCog, atomStart, atomEnd, atinfo, x, numAtomsMoved, move, nbat);
1510
1511     if (ddZone == 0)
1512     {
1513         nbat->natoms_local = nbat->natoms;
1514     }
1515
1516     nbs_cycle_stop(&nbs->cc[enbsCCgrid]);
1517 }
1518
1519 /* Calls nbnxn_put_on_grid for all non-local domains */
1520 void nbnxn_put_on_grid_nonlocal(nbnxn_search_t                   nbs,
1521                                 const struct gmx_domdec_zones_t *zones,
1522                                 const int                       *atinfo,
1523                                 gmx::ArrayRef<const gmx::RVec>   x,
1524                                 int                              nb_kernel_type,
1525                                 nbnxn_atomdata_t                *nbat)
1526 {
1527     for (int zone = 1; zone < zones->n; zone++)
1528     {
1529         rvec c0, c1;
1530         for (int d = 0; d < DIM; d++)
1531         {
1532             c0[d] = zones->size[zone].bb_x0[d];
1533             c1[d] = zones->size[zone].bb_x1[d];
1534         }
1535
1536         nbnxn_put_on_grid(nbs, nbs->ePBC, nullptr,
1537                           zone, c0, c1,
1538                           nullptr,
1539                           zones->cg_range[zone],
1540                           zones->cg_range[zone+1],
1541                           -1,
1542                           atinfo,
1543                           x,
1544                           0, nullptr,
1545                           nb_kernel_type,
1546                           nbat);
1547     }
1548 }
1549
1550 void nbnxn_get_ncells(nbnxn_search_t nbs, int *ncx, int *ncy)
1551 {
1552     *ncx = nbs->grid[0].numCells[XX];
1553     *ncy = nbs->grid[0].numCells[YY];
1554 }
1555
1556 gmx::ArrayRef<const int> nbnxn_get_atomorder(const nbnxn_search *nbs)
1557 {
1558     /* Return the atom order for the home cell (index 0) */
1559     const nbnxn_grid_t &grid       = nbs->grid[0];
1560
1561     int                 numIndices = grid.cxy_ind[grid.numCells[XX]*grid.numCells[YY]]*grid.na_sc;
1562
1563     return gmx::constArrayRefFromArray(nbs->a.data(), numIndices);
1564 }
1565
1566 void nbnxn_set_atomorder(nbnxn_search_t nbs)
1567 {
1568     /* Set the atom order for the home cell (index 0) */
1569     nbnxn_grid_t *grid = &nbs->grid[0];
1570
1571     int           ao = 0;
1572     for (int cx = 0; cx < grid->numCells[XX]; cx++)
1573     {
1574         for (int cy = 0; cy < grid->numCells[YY]; cy++)
1575         {
1576             int cxy = cx*grid->numCells[YY] + cy;
1577             int j   = grid->cxy_ind[cxy]*grid->na_sc;
1578             for (int cz = 0; cz < grid->cxy_na[cxy]; cz++)
1579             {
1580                 nbs->a[j]     = ao;
1581                 nbs->cell[ao] = j;
1582                 ao++;
1583                 j++;
1584             }
1585         }
1586     }
1587 }
1588
1589 gmx::ArrayRef<const int> nbnxn_get_gridindices(const nbnxn_search *nbs)
1590 {
1591     return nbs->cell;
1592 }