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