Turn t_forcerec.shift_vec into an std::vector of gmx::RVec
[alexxy/gromacs.git] / src / gromacs / nbnxm / atomdata.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012-2018, The GROMACS development team.
5  * Copyright (c) 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 #include "gmxpre.h"
38
39 #include "atomdata.h"
40
41 #include <cmath>
42 #include <cstdlib>
43 #include <cstring>
44
45 #include <algorithm>
46
47 #include "gromacs/math/functions.h"
48 #include "gromacs/math/utilities.h"
49 #include "gromacs/math/vec.h"
50 #include "gromacs/mdlib/gmx_omp_nthreads.h"
51 #include "gromacs/mdtypes/forcerec.h" // only for GET_CGINFO_*
52 #include "gromacs/mdtypes/md_enums.h"
53 #include "gromacs/nbnxm/nbnxm.h"
54 #include "gromacs/pbcutil/ishift.h"
55 #include "gromacs/simd/simd.h"
56 #include "gromacs/utility/exceptions.h"
57 #include "gromacs/utility/fatalerror.h"
58 #include "gromacs/utility/gmxomp.h"
59 #include "gromacs/utility/logger.h"
60 #include "gromacs/utility/strconvert.h"
61 #include "gromacs/utility/stringutil.h"
62
63 #include "grid.h"
64 #include "gridset.h"
65 #include "nbnxm_geometry.h"
66 #include "nbnxm_gpu.h"
67 #include "pairlist.h"
68
69 using namespace gmx; // TODO: Remove when this file is moved into gmx namespace
70
71
72 const char* enumValueToString(LJCombinationRule enumValue)
73 {
74     static constexpr gmx::EnumerationArray<LJCombinationRule, const char*> s_ljCombinationRuleNames = {
75         "Geometric", "Lorentz-Berthelot", "None"
76     };
77     return s_ljCombinationRuleNames[enumValue];
78 }
79
80 void nbnxn_atomdata_t::resizeCoordinateBuffer(int numAtoms)
81 {
82     numAtoms_ = numAtoms;
83
84     x_.resize(numAtoms * xstride);
85 }
86
87 void nbnxn_atomdata_t::resizeForceBuffers()
88 {
89     /* Force buffers need padding up to a multiple of the buffer flag size */
90     const int paddedSize =
91             (numAtoms() + NBNXN_BUFFERFLAG_SIZE - 1) / NBNXN_BUFFERFLAG_SIZE * NBNXN_BUFFERFLAG_SIZE;
92
93     /* Should we let each thread allocate it's own data instead? */
94     for (nbnxn_atomdata_output_t& outBuffer : out)
95     {
96         outBuffer.f.resize(paddedSize * fstride);
97     }
98 }
99
100 /* Initializes an nbnxn_atomdata_output_t data structure */
101 nbnxn_atomdata_output_t::nbnxn_atomdata_output_t(Nbnxm::KernelType  kernelType,
102                                                  int                numEnergyGroups,
103                                                  int                simdEnergyBufferStride,
104                                                  gmx::PinningPolicy pinningPolicy) :
105     f({}, { pinningPolicy }),
106     fshift({}, { pinningPolicy }),
107     Vvdw({}, { pinningPolicy }),
108     Vc({}, { pinningPolicy })
109 {
110     fshift.resize(SHIFTS * DIM);
111     Vvdw.resize(numEnergyGroups * numEnergyGroups);
112     Vc.resize(numEnergyGroups * numEnergyGroups);
113
114     if (Nbnxm::kernelTypeIsSimd(kernelType))
115     {
116         int cj_size = Nbnxm::JClusterSizePerKernelType[kernelType];
117         int numElements =
118                 numEnergyGroups * numEnergyGroups * simdEnergyBufferStride * (cj_size / 2) * cj_size;
119         VSvdw.resize(numElements);
120         VSc.resize(numElements);
121     }
122 }
123
124 static void copy_int_to_nbat_int(const int* a, int na, int na_round, const int* in, int fill, int* innb)
125 {
126     int j = 0;
127     for (int i = 0; i < na; i++)
128     {
129         innb[j++] = in[a[i]];
130     }
131     /* Complete the partially filled last cell with fill */
132     for (int i = na; i < na_round; i++)
133     {
134         innb[j++] = fill;
135     }
136 }
137
138 void copy_rvec_to_nbat_real(const int* a, int na, int na_round, const rvec* x, int nbatFormat, real* xnb, int a0)
139 {
140     /* We complete partially filled cells, can only be the last one in each
141      * column, with coordinates farAway. The actual coordinate value does
142      * not influence the results, since these filler particles do not interact.
143      * Clusters with normal atoms + fillers have a bounding box based only
144      * on the coordinates of the atoms. Clusters with only fillers have as
145      * the bounding box the coordinates of the first filler. Such clusters
146      * are not considered as i-entries, but they are considered as j-entries.
147      * So for performance it is better to have their bounding boxes far away,
148      * such that filler only clusters don't end up in the pair list.
149      */
150     const real farAway = -1000000;
151
152     if (nbatFormat == nbatXYZ)
153     {
154         int i = 0;
155         int j = a0 * STRIDE_XYZ;
156         for (; i < na; i++)
157         {
158             xnb[j++] = x[a[i]][XX];
159             xnb[j++] = x[a[i]][YY];
160             xnb[j++] = x[a[i]][ZZ];
161         }
162         /* Complete the partially filled last cell with farAway elements */
163         for (; i < na_round; i++)
164         {
165             xnb[j++] = farAway;
166             xnb[j++] = farAway;
167             xnb[j++] = farAway;
168         }
169     }
170     else if (nbatFormat == nbatXYZQ)
171     {
172         int i = 0;
173         int j = a0 * STRIDE_XYZQ;
174         for (; i < na; i++)
175         {
176             xnb[j++] = x[a[i]][XX];
177             xnb[j++] = x[a[i]][YY];
178             xnb[j++] = x[a[i]][ZZ];
179             j++;
180         }
181         /* Complete the partially filled last cell with zeros */
182         for (; i < na_round; i++)
183         {
184             xnb[j++] = farAway;
185             xnb[j++] = farAway;
186             xnb[j++] = farAway;
187             j++;
188         }
189     }
190     else if (nbatFormat == nbatX4)
191     {
192         int i = 0;
193         int j = atom_to_x_index<c_packX4>(a0);
194         int c = a0 & (c_packX4 - 1);
195         for (; i < na; i++)
196         {
197             xnb[j + XX * c_packX4] = x[a[i]][XX];
198             xnb[j + YY * c_packX4] = x[a[i]][YY];
199             xnb[j + ZZ * c_packX4] = x[a[i]][ZZ];
200             j++;
201             c++;
202             if (c == c_packX4)
203             {
204                 j += (DIM - 1) * c_packX4;
205                 c = 0;
206             }
207         }
208         /* Complete the partially filled last cell with zeros */
209         for (; i < na_round; i++)
210         {
211             xnb[j + XX * c_packX4] = farAway;
212             xnb[j + YY * c_packX4] = farAway;
213             xnb[j + ZZ * c_packX4] = farAway;
214             j++;
215             c++;
216             if (c == c_packX4)
217             {
218                 j += (DIM - 1) * c_packX4;
219                 c = 0;
220             }
221         }
222     }
223     else if (nbatFormat == nbatX8)
224     {
225         int i = 0;
226         int j = atom_to_x_index<c_packX8>(a0);
227         int c = a0 & (c_packX8 - 1);
228         for (; i < na; i++)
229         {
230             xnb[j + XX * c_packX8] = x[a[i]][XX];
231             xnb[j + YY * c_packX8] = x[a[i]][YY];
232             xnb[j + ZZ * c_packX8] = x[a[i]][ZZ];
233             j++;
234             c++;
235             if (c == c_packX8)
236             {
237                 j += (DIM - 1) * c_packX8;
238                 c = 0;
239             }
240         }
241         /* Complete the partially filled last cell with zeros */
242         for (; i < na_round; i++)
243         {
244             xnb[j + XX * c_packX8] = farAway;
245             xnb[j + YY * c_packX8] = farAway;
246             xnb[j + ZZ * c_packX8] = farAway;
247             j++;
248             c++;
249             if (c == c_packX8)
250             {
251                 j += (DIM - 1) * c_packX8;
252                 c = 0;
253             }
254         }
255     }
256     else
257     {
258         gmx_incons("Unsupported nbnxn_atomdata_t format");
259     }
260 }
261
262 /* Stores the LJ parameter data in a format convenient for different kernels */
263 static void set_lj_parameter_data(nbnxn_atomdata_t::Params* params, gmx_bool bSIMD)
264 {
265     int nt = params->numTypes;
266
267     if (bSIMD)
268     {
269 #if GMX_SIMD
270         /* nbfp_aligned stores two parameters using the stride most suitable
271          * for the present SIMD architecture, as specified by the constant
272          * c_simdBestPairAlignment from the SIMD header.
273          * There's a slight inefficiency in allocating and initializing nbfp_aligned
274          * when it might not be used, but introducing the conditional code is not
275          * really worth it.
276          */
277         params->nbfp_aligned.resize(nt * nt * c_simdBestPairAlignment);
278
279         for (int i = 0; i < nt; i++)
280         {
281             for (int j = 0; j < nt; j++)
282             {
283                 params->nbfp_aligned[(i * nt + j) * c_simdBestPairAlignment + 0] =
284                         params->nbfp[(i * nt + j) * 2 + 0];
285                 params->nbfp_aligned[(i * nt + j) * c_simdBestPairAlignment + 1] =
286                         params->nbfp[(i * nt + j) * 2 + 1];
287                 if (c_simdBestPairAlignment > 2)
288                 {
289                     params->nbfp_aligned[(i * nt + j) * c_simdBestPairAlignment + 2] = 0;
290                     params->nbfp_aligned[(i * nt + j) * c_simdBestPairAlignment + 3] = 0;
291                 }
292             }
293         }
294 #endif
295     }
296
297     /* We use combination rule data for SIMD combination rule kernels
298      * and with LJ-PME kernels. We then only need parameters per atom type,
299      * not per pair of atom types.
300      */
301     params->nbfp_comb.resize(nt * 2);
302     switch (params->ljCombinationRule)
303     {
304         case LJCombinationRule::Geometric:
305             for (int i = 0; i < nt; i++)
306             {
307                 /* Store the sqrt of the diagonal from the nbfp matrix */
308                 params->nbfp_comb[i * 2]     = std::sqrt(params->nbfp[(i * nt + i) * 2]);
309                 params->nbfp_comb[i * 2 + 1] = std::sqrt(params->nbfp[(i * nt + i) * 2 + 1]);
310             }
311             break;
312         case LJCombinationRule::LorentzBerthelot:
313             for (int i = 0; i < nt; i++)
314             {
315                 /* Get 6*C6 and 12*C12 from the diagonal of the nbfp matrix */
316                 const real c6  = params->nbfp[(i * nt + i) * 2];
317                 const real c12 = params->nbfp[(i * nt + i) * 2 + 1];
318                 if (c6 > 0 && c12 > 0)
319                 {
320                     /* We store 0.5*2^1/6*sigma and sqrt(4*3*eps),
321                      * so we get 6*C6 and 12*C12 after combining.
322                      */
323                     params->nbfp_comb[i * 2]     = 0.5 * gmx::sixthroot(c12 / c6);
324                     params->nbfp_comb[i * 2 + 1] = std::sqrt(c6 * c6 / c12);
325                 }
326                 else
327                 {
328                     params->nbfp_comb[i * 2]     = 0;
329                     params->nbfp_comb[i * 2 + 1] = 0;
330                 }
331             }
332             break;
333         case LJCombinationRule::None:
334             /* We always store the full matrix (see code above) */
335             break;
336         default: gmx_incons("Unknown combination rule");
337     }
338 }
339
340 nbnxn_atomdata_t::SimdMasks::SimdMasks()
341 {
342 #if GMX_SIMD
343     constexpr int simd_width = GMX_SIMD_REAL_WIDTH;
344     /* Set the diagonal cluster pair exclusion mask setup data.
345      * In the kernel we check 0 < j - i to generate the masks.
346      * Here we store j - i for generating the mask for the first i,
347      * we subtract 0.5 to avoid rounding issues.
348      * In the kernel we can subtract 1 to generate the subsequent mask.
349      */
350     const int simd_4xn_diag_size = std::max(c_nbnxnCpuIClusterSize, simd_width);
351     diagonal_4xn_j_minus_i.resize(simd_4xn_diag_size);
352     for (int j = 0; j < simd_4xn_diag_size; j++)
353     {
354         diagonal_4xn_j_minus_i[j] = j - 0.5;
355     }
356
357     diagonal_2xnn_j_minus_i.resize(simd_width);
358     for (int j = 0; j < simd_width / 2; j++)
359     {
360         /* The j-cluster size is half the SIMD width */
361         diagonal_2xnn_j_minus_i[j] = j - 0.5;
362         /* The next half of the SIMD width is for i + 1 */
363         diagonal_2xnn_j_minus_i[simd_width / 2 + j] = j - 1 - 0.5;
364     }
365
366     /* We use up to 32 bits for exclusion masking.
367      * The same masks are used for the 4xN and 2x(N+N) kernels.
368      * The masks are read either into integer SIMD registers or into
369      * real SIMD registers (together with a cast).
370      * In single precision this means the real and integer SIMD registers
371      * are of equal size.
372      */
373     const int simd_excl_size = c_nbnxnCpuIClusterSize * simd_width;
374 #    if GMX_DOUBLE && !GMX_SIMD_HAVE_INT32_LOGICAL
375     exclusion_filter64.resize(simd_excl_size);
376 #    else
377     exclusion_filter.resize(simd_excl_size);
378 #    endif
379
380     for (int j = 0; j < simd_excl_size; j++)
381     {
382         /* Set the consecutive bits for masking pair exclusions */
383 #    if GMX_DOUBLE && !GMX_SIMD_HAVE_INT32_LOGICAL
384         exclusion_filter64[j] = (1U << j);
385 #    else
386         exclusion_filter[j] = (1U << j);
387 #    endif
388     }
389
390     if (!GMX_SIMD_HAVE_LOGICAL && !GMX_SIMD_HAVE_INT32_LOGICAL) // NOLINT(misc-redundant-expression)
391     {
392         // If the SIMD implementation has no bitwise logical operation support
393         // whatsoever we cannot use the normal masking. Instead,
394         // we generate a vector of all 2^4 possible ways an i atom
395         // interacts with its 4 j atoms. Each array entry contains
396         // GMX_SIMD_REAL_WIDTH values that are read with a single aligned SIMD load.
397         // Since there is no logical value representation in this case, we use
398         // any nonzero value to indicate 'true', while zero mean 'false'.
399         // This can then be converted to a SIMD boolean internally in the SIMD
400         // module by comparing to zero.
401         // Each array entry encodes how this i atom will interact with the 4 j atoms.
402         // Matching code exists in set_ci_top_excls() to generate indices into this array.
403         // Those indices are used in the kernels.
404
405         const int  simd_excl_size = c_nbnxnCpuIClusterSize * c_nbnxnCpuIClusterSize;
406         const real simdFalse      = 0.0;
407         const real simdTrue       = 1.0;
408
409         interaction_array.resize(simd_excl_size * GMX_SIMD_REAL_WIDTH);
410         for (int j = 0; j < simd_excl_size; j++)
411         {
412             const int index = j * GMX_SIMD_REAL_WIDTH;
413             for (int i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
414             {
415                 interaction_array[index + i] = (j & (1 << i)) ? simdTrue : simdFalse;
416             }
417         }
418     }
419 #endif
420 }
421
422 nbnxn_atomdata_t::Params::Params(gmx::PinningPolicy pinningPolicy) :
423     numTypes(0),
424     nbfp({}, { pinningPolicy }),
425     nbfp_comb({}, { pinningPolicy }),
426     type({}, { pinningPolicy }),
427     lj_comb({}, { pinningPolicy }),
428     q({}, { pinningPolicy }),
429     nenergrp(0),
430     neg_2log(0),
431     energrp({}, { pinningPolicy })
432 {
433 }
434
435 nbnxn_atomdata_t::nbnxn_atomdata_t(gmx::PinningPolicy pinningPolicy) :
436     params_(pinningPolicy),
437     numAtoms_(0),
438     natoms_local(0),
439     shift_vec({}, { pinningPolicy }),
440     x_({}, { pinningPolicy }),
441     simdMasks(),
442     bUseBufferFlags(FALSE)
443 {
444 }
445
446 /* Initializes an nbnxn_atomdata_t::Params data structure */
447 static void nbnxn_atomdata_params_init(const gmx::MDLogger&      mdlog,
448                                        nbnxn_atomdata_t::Params* params,
449                                        const Nbnxm::KernelType   kernelType,
450                                        int                       enbnxninitcombrule,
451                                        int                       ntype,
452                                        ArrayRef<const real>      nbfp,
453                                        int                       n_energygroups)
454 {
455     if (debug)
456     {
457         fprintf(debug, "There are %d atom types in the system, adding one for nbnxn_atomdata_t\n", ntype);
458     }
459     params->numTypes = ntype + 1;
460     params->nbfp.resize(params->numTypes * params->numTypes * 2);
461     params->nbfp_comb.resize(params->numTypes * 2);
462
463     /* A tolerance of 1e-5 seems reasonable for (possibly hand-typed)
464      * force-field floating point parameters.
465      */
466     real        tol               = 1e-5;
467     const char* tolOverrideString = getenv("GMX_LJCOMB_TOL");
468     if (tolOverrideString != nullptr)
469     {
470         double tolOverride = std::strtod(tolOverrideString, nullptr);
471         tol                = tolOverride;
472     }
473     bool bCombGeom = true;
474     bool bCombLB   = true;
475
476     /* Temporarily fill params->nbfp_comb with sigma and epsilon
477      * to check for the LB rule.
478      */
479     for (int i = 0; i < ntype; i++)
480     {
481         const real c6  = nbfp[(i * ntype + i) * 2] / 6.0;
482         const real c12 = nbfp[(i * ntype + i) * 2 + 1] / 12.0;
483         if (c6 > 0 && c12 > 0)
484         {
485             params->nbfp_comb[i * 2]     = gmx::sixthroot(c12 / c6);
486             params->nbfp_comb[i * 2 + 1] = 0.25 * c6 * c6 / c12;
487         }
488         else if (c6 == 0 && c12 == 0)
489         {
490             params->nbfp_comb[i * 2]     = 0;
491             params->nbfp_comb[i * 2 + 1] = 0;
492         }
493         else
494         {
495             /* Can not use LB rule with only dispersion or repulsion */
496             bCombLB = false;
497         }
498     }
499
500     for (int i = 0; i < params->numTypes; i++)
501     {
502         for (int j = 0; j < params->numTypes; j++)
503         {
504             if (i < ntype && j < ntype)
505             {
506                 /* fr->nbfp has been updated, so that array too now stores c6/c12 including
507                  * the 6.0/12.0 prefactors to save 2 flops in the most common case (force-only).
508                  */
509                 real c6  = nbfp[(i * ntype + j) * 2];
510                 real c12 = nbfp[(i * ntype + j) * 2 + 1];
511
512                 params->nbfp[(i * params->numTypes + j) * 2]     = c6;
513                 params->nbfp[(i * params->numTypes + j) * 2 + 1] = c12;
514
515                 /* Compare 6*C6 and 12*C12 for geometric cobination rule */
516                 bCombGeom =
517                         bCombGeom
518                         && gmx_within_tol(c6 * c6, nbfp[(i * ntype + i) * 2] * nbfp[(j * ntype + j) * 2], tol)
519                         && gmx_within_tol(c12 * c12,
520                                           nbfp[(i * ntype + i) * 2 + 1] * nbfp[(j * ntype + j) * 2 + 1],
521                                           tol);
522
523                 /* Compare C6 and C12 for Lorentz-Berthelot combination rule */
524                 c6 /= 6.0;
525                 c12 /= 12.0;
526                 bCombLB =
527                         bCombLB
528                         && ((c6 == 0 && c12 == 0
529                              && (params->nbfp_comb[i * 2 + 1] == 0 || params->nbfp_comb[j * 2 + 1] == 0))
530                             || (c6 > 0 && c12 > 0
531                                 && gmx_within_tol(gmx::sixthroot(c12 / c6),
532                                                   0.5 * (params->nbfp_comb[i * 2] + params->nbfp_comb[j * 2]),
533                                                   tol)
534                                 && gmx_within_tol(0.25 * c6 * c6 / c12,
535                                                   std::sqrt(params->nbfp_comb[i * 2 + 1]
536                                                             * params->nbfp_comb[j * 2 + 1]),
537                                                   tol)));
538             }
539             else
540             {
541                 /* Add zero parameters for the additional dummy atom type */
542                 params->nbfp[(i * params->numTypes + j) * 2]     = 0;
543                 params->nbfp[(i * params->numTypes + j) * 2 + 1] = 0;
544             }
545         }
546     }
547     if (debug)
548     {
549         fprintf(debug,
550                 "Combination rules: geometric %s Lorentz-Berthelot %s\n",
551                 gmx::boolToString(bCombGeom),
552                 gmx::boolToString(bCombLB));
553     }
554
555     const bool simple = Nbnxm::kernelTypeUsesSimplePairlist(kernelType);
556
557     switch (enbnxninitcombrule)
558     {
559         case enbnxninitcombruleDETECT:
560             /* We prefer the geometric combination rule,
561              * as that gives a slightly faster kernel than the LB rule.
562              */
563             if (bCombGeom)
564             {
565                 params->ljCombinationRule = LJCombinationRule::Geometric;
566             }
567             else if (bCombLB)
568             {
569                 params->ljCombinationRule = LJCombinationRule::LorentzBerthelot;
570             }
571             else
572             {
573                 params->ljCombinationRule = LJCombinationRule::None;
574
575                 params->nbfp_comb.clear();
576             }
577
578             {
579                 std::string mesg;
580                 if (params->ljCombinationRule == LJCombinationRule::None)
581                 {
582                     mesg = "Using full Lennard-Jones parameter combination matrix";
583                 }
584                 else
585                 {
586                     mesg = gmx::formatString("Using %s Lennard-Jones combination rule",
587                                              enumValueToString(params->ljCombinationRule));
588                 }
589                 GMX_LOG(mdlog.info).asParagraph().appendText(mesg);
590             }
591             break;
592         case enbnxninitcombruleGEOM:
593             params->ljCombinationRule = LJCombinationRule::Geometric;
594             break;
595         case enbnxninitcombruleLB:
596             params->ljCombinationRule = LJCombinationRule::LorentzBerthelot;
597             break;
598         case enbnxninitcombruleNONE:
599             params->ljCombinationRule = LJCombinationRule::None;
600
601             params->nbfp_comb.clear();
602             break;
603         default: gmx_incons("Unknown enbnxninitcombrule");
604     }
605
606     const bool bSIMD = Nbnxm::kernelTypeIsSimd(kernelType);
607
608     set_lj_parameter_data(params, bSIMD);
609
610     params->nenergrp = n_energygroups;
611     if (!simple)
612     {
613         // We now check for energy groups already when starting mdrun
614         GMX_RELEASE_ASSERT(n_energygroups == 1, "GPU kernels do not support energy groups");
615     }
616     /* Temporary storage goes as #grp^3*simd_width^2/2, so limit to 64 */
617     if (params->nenergrp > 64)
618     {
619         gmx_fatal(FARGS, "With NxN kernels not more than 64 energy groups are supported\n");
620     }
621     params->neg_2log = 1;
622     while (params->nenergrp > (1 << params->neg_2log))
623     {
624         params->neg_2log++;
625     }
626 }
627
628 /* Initializes an nbnxn_atomdata_t data structure */
629 void nbnxn_atomdata_init(const gmx::MDLogger&    mdlog,
630                          nbnxn_atomdata_t*       nbat,
631                          const Nbnxm::KernelType kernelType,
632                          int                     enbnxninitcombrule,
633                          int                     ntype,
634                          ArrayRef<const real>    nbfp,
635                          int                     n_energygroups,
636                          int                     nout)
637 {
638     nbnxn_atomdata_params_init(
639             mdlog, &nbat->paramsDeprecated(), kernelType, enbnxninitcombrule, ntype, nbfp, n_energygroups);
640
641     const bool simple = Nbnxm::kernelTypeUsesSimplePairlist(kernelType);
642     const bool bSIMD  = Nbnxm::kernelTypeIsSimd(kernelType);
643
644     if (simple)
645     {
646         if (bSIMD)
647         {
648             int pack_x = std::max(c_nbnxnCpuIClusterSize, Nbnxm::JClusterSizePerKernelType[kernelType]);
649             switch (pack_x)
650             {
651                 case 4: nbat->XFormat = nbatX4; break;
652                 case 8: nbat->XFormat = nbatX8; break;
653                 default: gmx_incons("Unsupported packing width");
654             }
655         }
656         else
657         {
658             nbat->XFormat = nbatXYZ;
659         }
660
661         nbat->FFormat = nbat->XFormat;
662     }
663     else
664     {
665         nbat->XFormat = nbatXYZQ;
666         nbat->FFormat = nbatXYZ;
667     }
668
669     nbat->shift_vec.resize(SHIFTS);
670
671     nbat->xstride = (nbat->XFormat == nbatXYZQ ? STRIDE_XYZQ : DIM);
672     nbat->fstride = (nbat->FFormat == nbatXYZQ ? STRIDE_XYZQ : DIM);
673
674     /* Initialize the output data structures */
675     for (int i = 0; i < nout; i++)
676     {
677         const auto& pinningPolicy = nbat->params().type.get_allocator().pinningPolicy();
678         nbat->out.emplace_back(
679                 kernelType, nbat->params().nenergrp, 1 << nbat->params().neg_2log, pinningPolicy);
680     }
681
682     nbat->buffer_flags.clear();
683 }
684
685 template<int packSize>
686 static void copy_lj_to_nbat_lj_comb(gmx::ArrayRef<const real> ljparam_type, const int* type, int na, real* ljparam_at)
687 {
688     /* The LJ params follow the combination rule:
689      * copy the params for the type array to the atom array.
690      */
691     for (int is = 0; is < na; is += packSize)
692     {
693         for (int k = 0; k < packSize; k++)
694         {
695             int i                             = is + k;
696             ljparam_at[is * 2 + k]            = ljparam_type[type[i] * 2];
697             ljparam_at[is * 2 + packSize + k] = ljparam_type[type[i] * 2 + 1];
698         }
699     }
700 }
701
702 /* Sets the atom type in nbnxn_atomdata_t */
703 static void nbnxn_atomdata_set_atomtypes(nbnxn_atomdata_t::Params* params,
704                                          const Nbnxm::GridSet&     gridSet,
705                                          ArrayRef<const int>       atomTypes)
706 {
707     params->type.resize(gridSet.numGridAtomsTotal());
708
709     for (const Nbnxm::Grid& grid : gridSet.grids())
710     {
711         /* Loop over all columns and copy and fill */
712         for (int i = 0; i < grid.numColumns(); i++)
713         {
714             const int numAtoms   = grid.paddedNumAtomsInColumn(i);
715             const int atomOffset = grid.firstAtomInColumn(i);
716
717             copy_int_to_nbat_int(gridSet.atomIndices().data() + atomOffset,
718                                  grid.numAtomsInColumn(i),
719                                  numAtoms,
720                                  atomTypes.data(),
721                                  params->numTypes - 1,
722                                  params->type.data() + atomOffset);
723         }
724     }
725 }
726
727 /* Sets the LJ combination rule parameters in nbnxn_atomdata_t */
728 static void nbnxn_atomdata_set_ljcombparams(nbnxn_atomdata_t::Params* params,
729                                             const int                 XFormat,
730                                             const Nbnxm::GridSet&     gridSet)
731 {
732     params->lj_comb.resize(gridSet.numGridAtomsTotal() * 2);
733
734     if (params->ljCombinationRule != LJCombinationRule::None)
735     {
736         for (const Nbnxm::Grid& grid : gridSet.grids())
737         {
738             /* Loop over all columns and copy and fill */
739             for (int i = 0; i < grid.numColumns(); i++)
740             {
741                 const int numAtoms   = grid.paddedNumAtomsInColumn(i);
742                 const int atomOffset = grid.firstAtomInColumn(i);
743
744                 if (XFormat == nbatX4)
745                 {
746                     copy_lj_to_nbat_lj_comb<c_packX4>(params->nbfp_comb,
747                                                       params->type.data() + atomOffset,
748                                                       numAtoms,
749                                                       params->lj_comb.data() + atomOffset * 2);
750                 }
751                 else if (XFormat == nbatX8)
752                 {
753                     copy_lj_to_nbat_lj_comb<c_packX8>(params->nbfp_comb,
754                                                       params->type.data() + atomOffset,
755                                                       numAtoms,
756                                                       params->lj_comb.data() + atomOffset * 2);
757                 }
758                 else if (XFormat == nbatXYZQ)
759                 {
760                     copy_lj_to_nbat_lj_comb<1>(params->nbfp_comb,
761                                                params->type.data() + atomOffset,
762                                                numAtoms,
763                                                params->lj_comb.data() + atomOffset * 2);
764                 }
765             }
766         }
767     }
768 }
769
770 /* Sets the charges in nbnxn_atomdata_t *nbat */
771 static void nbnxn_atomdata_set_charges(nbnxn_atomdata_t*     nbat,
772                                        const Nbnxm::GridSet& gridSet,
773                                        ArrayRef<const real>  charges)
774 {
775     if (nbat->XFormat != nbatXYZQ)
776     {
777         nbat->paramsDeprecated().q.resize(nbat->numAtoms());
778     }
779
780     for (const Nbnxm::Grid& grid : gridSet.grids())
781     {
782         /* Loop over all columns and copy and fill */
783         for (int cxy = 0; cxy < grid.numColumns(); cxy++)
784         {
785             const int atomOffset     = grid.firstAtomInColumn(cxy);
786             const int numAtoms       = grid.numAtomsInColumn(cxy);
787             const int paddedNumAtoms = grid.paddedNumAtomsInColumn(cxy);
788
789             if (nbat->XFormat == nbatXYZQ)
790             {
791                 real* q = nbat->x().data() + atomOffset * STRIDE_XYZQ + ZZ + 1;
792                 for (int i = 0; i < numAtoms; i++)
793                 {
794                     *q = charges[gridSet.atomIndices()[atomOffset + i]];
795                     q += STRIDE_XYZQ;
796                 }
797                 /* Complete the partially filled last cell with zeros */
798                 for (int i = numAtoms; i < paddedNumAtoms; i++)
799                 {
800                     *q = 0;
801                     q += STRIDE_XYZQ;
802                 }
803             }
804             else
805             {
806                 real* q = nbat->paramsDeprecated().q.data() + atomOffset;
807                 for (int i = 0; i < numAtoms; i++)
808                 {
809                     *q = charges[gridSet.atomIndices()[atomOffset + i]];
810                     q++;
811                 }
812                 /* Complete the partially filled last cell with zeros */
813                 for (int i = numAtoms; i < paddedNumAtoms; i++)
814                 {
815                     *q = 0;
816                     q++;
817                 }
818             }
819         }
820     }
821 }
822
823 /* Set the charges of perturbed atoms in nbnxn_atomdata_t to 0.
824  * This is to automatically remove the RF/PME self term in the nbnxn kernels.
825  * Part of the zero interactions are still calculated in the normal kernels.
826  * All perturbed interactions are calculated in the free energy kernel,
827  * using the original charge and LJ data, not nbnxn_atomdata_t.
828  */
829 static void nbnxn_atomdata_mask_fep(nbnxn_atomdata_t* nbat, const Nbnxm::GridSet& gridSet)
830 {
831     nbnxn_atomdata_t::Params& params = nbat->paramsDeprecated();
832
833     const bool formatIsXYZQ = (nbat->XFormat == nbatXYZQ);
834
835     real* q        = formatIsXYZQ ? (nbat->x().data() + ZZ + 1) : params.q.data();
836     int   stride_q = formatIsXYZQ ? STRIDE_XYZQ : 1;
837
838     for (const Nbnxm::Grid& grid : gridSet.grids())
839     {
840         const int nsubc = (grid.geometry().isSimple) ? 1 : c_gpuNumClusterPerCell;
841
842         const int c_offset = grid.firstAtomInColumn(0);
843
844         /* Loop over all columns and copy and fill */
845         for (int c = 0; c < grid.numCells() * nsubc; c++)
846         {
847             /* Does this cluster contain perturbed particles? */
848             if (grid.clusterIsPerturbed(c))
849             {
850                 const int numAtomsPerCluster = grid.geometry().numAtomsICluster;
851                 for (int i = 0; i < numAtomsPerCluster; i++)
852                 {
853                     /* Is this a perturbed particle? */
854                     if (grid.atomIsPerturbed(c, i))
855                     {
856                         int ind = c_offset + c * numAtomsPerCluster + i;
857                         /* Set atom type and charge to non-interacting */
858                         params.type[ind]  = params.numTypes - 1;
859                         q[ind * stride_q] = 0;
860                     }
861                 }
862             }
863         }
864     }
865 }
866
867 /* Copies the energy group indices to a reordered and packed array */
868 static void
869 copy_egp_to_nbat_egps(const int* a, int na, int na_round, int na_c, int bit_shift, const int* in, int* innb)
870 {
871     int i = 0, j = 0;
872     for (; i < na; i += na_c)
873     {
874         /* Store na_c energy group numbers into one int */
875         int comb = 0;
876         for (int sa = 0; sa < na_c; sa++)
877         {
878             int at = a[i + sa];
879             if (at >= 0)
880             {
881                 comb |= (GET_CGINFO_GID(in[at]) << (sa * bit_shift));
882             }
883         }
884         innb[j++] = comb;
885     }
886     /* Complete the partially filled last cell with fill */
887     for (; i < na_round; i += na_c)
888     {
889         innb[j++] = 0;
890     }
891 }
892
893 /* Set the energy group indices for atoms in nbnxn_atomdata_t */
894 static void nbnxn_atomdata_set_energygroups(nbnxn_atomdata_t::Params* params,
895                                             const Nbnxm::GridSet&     gridSet,
896                                             ArrayRef<const int>       atomInfo)
897 {
898     if (params->nenergrp == 1)
899     {
900         return;
901     }
902
903     params->energrp.resize(gridSet.numGridAtomsTotal());
904
905     for (const Nbnxm::Grid& grid : gridSet.grids())
906     {
907         /* Loop over all columns and copy and fill */
908         for (int i = 0; i < grid.numColumns(); i++)
909         {
910             const int numAtoms   = grid.paddedNumAtomsInColumn(i);
911             const int atomOffset = grid.firstAtomInColumn(i);
912
913             copy_egp_to_nbat_egps(gridSet.atomIndices().data() + atomOffset,
914                                   grid.numAtomsInColumn(i),
915                                   numAtoms,
916                                   c_nbnxnCpuIClusterSize,
917                                   params->neg_2log,
918                                   atomInfo.data(),
919                                   params->energrp.data() + grid.atomToCluster(atomOffset));
920         }
921     }
922 }
923
924 /* Sets all required atom parameter data in nbnxn_atomdata_t */
925 void nbnxn_atomdata_set(nbnxn_atomdata_t*     nbat,
926                         const Nbnxm::GridSet& gridSet,
927                         ArrayRef<const int>   atomTypes,
928                         ArrayRef<const real>  atomCharges,
929                         ArrayRef<const int>   atomInfo)
930 {
931     nbnxn_atomdata_t::Params& params = nbat->paramsDeprecated();
932
933     nbnxn_atomdata_set_atomtypes(&params, gridSet, atomTypes);
934
935     nbnxn_atomdata_set_charges(nbat, gridSet, atomCharges);
936
937     if (gridSet.haveFep())
938     {
939         nbnxn_atomdata_mask_fep(nbat, gridSet);
940     }
941
942     /* This must be done after masking types for FEP */
943     nbnxn_atomdata_set_ljcombparams(&params, nbat->XFormat, gridSet);
944
945     nbnxn_atomdata_set_energygroups(&params, gridSet, atomInfo);
946 }
947
948 /* Copies the shift vector array to nbnxn_atomdata_t */
949 void nbnxn_atomdata_copy_shiftvec(gmx_bool bDynamicBox, gmx::ArrayRef<gmx::RVec> shift_vec, nbnxn_atomdata_t* nbat)
950 {
951     nbat->bDynamicBox = bDynamicBox;
952     std::copy(shift_vec.begin(), shift_vec.end(), nbat->shift_vec.begin());
953 }
954
955 // This is slightly different from nbnxn_get_atom_range(...) at the end of the file
956 // TODO: Combine if possible
957 static void getAtomRanges(const Nbnxm::GridSet&   gridSet,
958                           const gmx::AtomLocality locality,
959                           int*                    gridBegin,
960                           int*                    gridEnd)
961 {
962     switch (locality)
963     {
964         case gmx::AtomLocality::All:
965             *gridBegin = 0;
966             *gridEnd   = gridSet.grids().size();
967             break;
968         case gmx::AtomLocality::Local:
969             *gridBegin = 0;
970             *gridEnd   = 1;
971             break;
972         case gmx::AtomLocality::NonLocal:
973             *gridBegin = 1;
974             *gridEnd   = gridSet.grids().size();
975             break;
976         case gmx::AtomLocality::Count:
977             GMX_ASSERT(false, "Count is invalid locality specifier");
978             break;
979     }
980 }
981
982 /* Copies (and reorders) the coordinates to nbnxn_atomdata_t */
983 void nbnxn_atomdata_copy_x_to_nbat_x(const Nbnxm::GridSet&   gridSet,
984                                      const gmx::AtomLocality locality,
985                                      const rvec*             coordinates,
986                                      nbnxn_atomdata_t*       nbat)
987 {
988
989     int gridBegin = 0;
990     int gridEnd   = 0;
991     getAtomRanges(gridSet, locality, &gridBegin, &gridEnd);
992
993     const int nth = gmx_omp_nthreads_get(emntPairsearch);
994 #pragma omp parallel for num_threads(nth) schedule(static)
995     for (int th = 0; th < nth; th++)
996     {
997         try
998         {
999             for (int g = gridBegin; g < gridEnd; g++)
1000             {
1001                 const Nbnxm::Grid& grid       = gridSet.grids()[g];
1002                 const int          numCellsXY = grid.numColumns();
1003
1004                 const int cxy0 = (numCellsXY * th + nth - 1) / nth;
1005                 const int cxy1 = (numCellsXY * (th + 1) + nth - 1) / nth;
1006
1007                 for (int cxy = cxy0; cxy < cxy1; cxy++)
1008                 {
1009                     const int na  = grid.numAtomsInColumn(cxy);
1010                     const int ash = grid.firstAtomInColumn(cxy);
1011
1012                     copy_rvec_to_nbat_real(gridSet.atomIndices().data() + ash,
1013                                            na,
1014                                            na,
1015                                            coordinates,
1016                                            nbat->XFormat,
1017                                            nbat->x().data(),
1018                                            ash);
1019                 }
1020             }
1021         }
1022         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
1023     }
1024 }
1025
1026 /* Copies (and reorders) the coordinates to nbnxn_atomdata_t on the GPU*/
1027 void nbnxn_atomdata_x_to_nbat_x_gpu(const Nbnxm::GridSet&   gridSet,
1028                                     const gmx::AtomLocality locality,
1029                                     NbnxmGpu*               gpu_nbv,
1030                                     DeviceBuffer<RVec>      d_x,
1031                                     GpuEventSynchronizer*   xReadyOnDevice)
1032 {
1033     GMX_ASSERT(xReadyOnDevice != nullptr, "Need a valid GpuEventSynchronizer object");
1034
1035     int gridBegin = 0;
1036     int gridEnd   = 0;
1037     getAtomRanges(gridSet, locality, &gridBegin, &gridEnd);
1038
1039     for (int g = gridBegin; g < gridEnd; g++)
1040     {
1041         nbnxn_gpu_x_to_nbat_x(gridSet.grids()[g],
1042                               gpu_nbv,
1043                               d_x,
1044                               (g == gridBegin) ? xReadyOnDevice : nullptr, // Sync on first iteration only
1045                               locality,
1046                               g,
1047                               gridSet.numColumnsMax(),
1048                               (g == gridEnd - 1));
1049     }
1050 }
1051
1052 static void nbnxn_atomdata_clear_reals(gmx::ArrayRef<real> dest, int i0, int i1)
1053 {
1054     for (int i = i0; i < i1; i++)
1055     {
1056         dest[i] = 0;
1057     }
1058 }
1059
1060 gmx_unused static void nbnxn_atomdata_reduce_reals(real* gmx_restrict dest,
1061                                                    gmx_bool           bDestSet,
1062                                                    const real** gmx_restrict src,
1063                                                    int                       nsrc,
1064                                                    int                       i0,
1065                                                    int                       i1)
1066 {
1067     if (bDestSet)
1068     {
1069         /* The destination buffer contains data, add to it */
1070         for (int i = i0; i < i1; i++)
1071         {
1072             for (int s = 0; s < nsrc; s++)
1073             {
1074                 dest[i] += src[s][i];
1075             }
1076         }
1077     }
1078     else
1079     {
1080         /* The destination buffer is uninitialized, set it first */
1081         for (int i = i0; i < i1; i++)
1082         {
1083             dest[i] = src[0][i];
1084             for (int s = 1; s < nsrc; s++)
1085             {
1086                 dest[i] += src[s][i];
1087             }
1088         }
1089     }
1090 }
1091
1092 gmx_unused static void nbnxn_atomdata_reduce_reals_simd(real gmx_unused* gmx_restrict dest,
1093                                                         gmx_bool gmx_unused bDestSet,
1094                                                         const gmx_unused real** gmx_restrict src,
1095                                                         int gmx_unused nsrc,
1096                                                         int gmx_unused i0,
1097                                                         int gmx_unused i1)
1098 {
1099 #if GMX_SIMD
1100     /* The SIMD width here is actually independent of that in the kernels,
1101      * but we use the same width for simplicity (usually optimal anyhow).
1102      */
1103     SimdReal dest_SSE, src_SSE;
1104
1105     if (bDestSet)
1106     {
1107         for (int i = i0; i < i1; i += GMX_SIMD_REAL_WIDTH)
1108         {
1109             dest_SSE = load<SimdReal>(dest + i);
1110             for (int s = 0; s < nsrc; s++)
1111             {
1112                 src_SSE  = load<SimdReal>(src[s] + i);
1113                 dest_SSE = dest_SSE + src_SSE;
1114             }
1115             store(dest + i, dest_SSE);
1116         }
1117     }
1118     else
1119     {
1120         for (int i = i0; i < i1; i += GMX_SIMD_REAL_WIDTH)
1121         {
1122             dest_SSE = load<SimdReal>(src[0] + i);
1123             for (int s = 1; s < nsrc; s++)
1124             {
1125                 src_SSE  = load<SimdReal>(src[s] + i);
1126                 dest_SSE = dest_SSE + src_SSE;
1127             }
1128             store(dest + i, dest_SSE);
1129         }
1130     }
1131 #endif
1132 }
1133
1134 /* Add part of the force array(s) from nbnxn_atomdata_t to f
1135  *
1136  * Note: Adding restrict to f makes this function 50% slower with gcc 7.3
1137  */
1138 static void nbnxn_atomdata_add_nbat_f_to_f_part(const Nbnxm::GridSet&          gridSet,
1139                                                 const nbnxn_atomdata_t&        nbat,
1140                                                 const nbnxn_atomdata_output_t& out,
1141                                                 const int                      a0,
1142                                                 const int                      a1,
1143                                                 rvec*                          f)
1144 {
1145     gmx::ArrayRef<const int> cell = gridSet.cells();
1146     // Note: Using ArrayRef instead makes this code 25% slower with gcc 7.3
1147     const real* fnb = out.f.data();
1148
1149     /* Loop over all columns and copy and fill */
1150     switch (nbat.FFormat)
1151     {
1152         case nbatXYZ:
1153         case nbatXYZQ:
1154             for (int a = a0; a < a1; a++)
1155             {
1156                 int i = cell[a] * nbat.fstride;
1157
1158                 f[a][XX] += fnb[i];
1159                 f[a][YY] += fnb[i + 1];
1160                 f[a][ZZ] += fnb[i + 2];
1161             }
1162             break;
1163         case nbatX4:
1164             for (int a = a0; a < a1; a++)
1165             {
1166                 int i = atom_to_x_index<c_packX4>(cell[a]);
1167
1168                 f[a][XX] += fnb[i + XX * c_packX4];
1169                 f[a][YY] += fnb[i + YY * c_packX4];
1170                 f[a][ZZ] += fnb[i + ZZ * c_packX4];
1171             }
1172             break;
1173         case nbatX8:
1174             for (int a = a0; a < a1; a++)
1175             {
1176                 int i = atom_to_x_index<c_packX8>(cell[a]);
1177
1178                 f[a][XX] += fnb[i + XX * c_packX8];
1179                 f[a][YY] += fnb[i + YY * c_packX8];
1180                 f[a][ZZ] += fnb[i + ZZ * c_packX8];
1181             }
1182             break;
1183         default: gmx_incons("Unsupported nbnxn_atomdata_t format");
1184     }
1185 }
1186
1187 static void nbnxn_atomdata_add_nbat_f_to_f_reduce(nbnxn_atomdata_t* nbat, int nth)
1188 {
1189 #pragma omp parallel for num_threads(nth) schedule(static)
1190     for (int th = 0; th < nth; th++)
1191     {
1192         try
1193         {
1194             const real* fptr[NBNXN_BUFFERFLAG_MAX_THREADS];
1195
1196             gmx::ArrayRef<const gmx_bitmask_t> flags = nbat->buffer_flags;
1197
1198             /* Calculate the cell-block range for our thread */
1199             const int b0 = (flags.size() * th) / nth;
1200             const int b1 = (flags.size() * (th + 1)) / nth;
1201
1202             for (int b = b0; b < b1; b++)
1203             {
1204                 const int i0 = b * NBNXN_BUFFERFLAG_SIZE * nbat->fstride;
1205                 const int i1 = (b + 1) * NBNXN_BUFFERFLAG_SIZE * nbat->fstride;
1206
1207                 int nfptr = 0;
1208                 for (gmx::index out = 1; out < gmx::ssize(nbat->out); out++)
1209                 {
1210                     if (bitmask_is_set(flags[b], out))
1211                     {
1212                         fptr[nfptr++] = nbat->out[out].f.data();
1213                     }
1214                 }
1215                 if (nfptr > 0)
1216                 {
1217 #if GMX_SIMD
1218                     nbnxn_atomdata_reduce_reals_simd
1219 #else
1220                     nbnxn_atomdata_reduce_reals
1221 #endif
1222                             (nbat->out[0].f.data(), bitmask_is_set(flags[b], 0), fptr, nfptr, i0, i1);
1223                 }
1224                 else if (!bitmask_is_set(flags[b], 0))
1225                 {
1226                     nbnxn_atomdata_clear_reals(nbat->out[0].f, i0, i1);
1227                 }
1228             }
1229         }
1230         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
1231     }
1232 }
1233
1234
1235 /* Add the force array(s) from nbnxn_atomdata_t to f */
1236 void reduceForces(nbnxn_atomdata_t* nbat, const gmx::AtomLocality locality, const Nbnxm::GridSet& gridSet, rvec* f)
1237 {
1238     int a0 = 0;
1239     int na = 0;
1240
1241     nbnxn_get_atom_range(locality, gridSet, &a0, &na);
1242
1243     if (na == 0)
1244     {
1245         /* The are no atoms for this reduction, avoid some overhead */
1246         return;
1247     }
1248
1249     int nth = gmx_omp_nthreads_get(emntNonbonded);
1250
1251     if (nbat->out.size() > 1)
1252     {
1253         if (locality != gmx::AtomLocality::All)
1254         {
1255             gmx_incons("add_f_to_f called with nout>1 and locality!=eatAll");
1256         }
1257
1258         /* Reduce the force thread output buffers into buffer 0, before adding
1259          * them to the, differently ordered, "real" force buffer.
1260          */
1261         nbnxn_atomdata_add_nbat_f_to_f_reduce(nbat, nth);
1262     }
1263 #pragma omp parallel for num_threads(nth) schedule(static)
1264     for (int th = 0; th < nth; th++)
1265     {
1266         try
1267         {
1268             nbnxn_atomdata_add_nbat_f_to_f_part(
1269                     gridSet, *nbat, nbat->out[0], a0 + ((th + 0) * na) / nth, a0 + ((th + 1) * na) / nth, f);
1270         }
1271         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
1272     }
1273 }
1274
1275 void nbnxn_atomdata_add_nbat_fshift_to_fshift(const nbnxn_atomdata_t& nbat, gmx::ArrayRef<gmx::RVec> fshift)
1276 {
1277     gmx::ArrayRef<const nbnxn_atomdata_output_t> outputBuffers = nbat.out;
1278
1279     for (int s = 0; s < SHIFTS; s++)
1280     {
1281         rvec sum;
1282         clear_rvec(sum);
1283         for (const nbnxn_atomdata_output_t& out : outputBuffers)
1284         {
1285             sum[XX] += out.fshift[s * DIM + XX];
1286             sum[YY] += out.fshift[s * DIM + YY];
1287             sum[ZZ] += out.fshift[s * DIM + ZZ];
1288         }
1289         fshift[s] += sum;
1290     }
1291 }
1292
1293 void nbnxn_get_atom_range(const gmx::AtomLocality atomLocality,
1294                           const Nbnxm::GridSet&   gridSet,
1295                           int*                    atomStart,
1296                           int*                    nAtoms)
1297 {
1298
1299     switch (atomLocality)
1300     {
1301         case gmx::AtomLocality::All:
1302             *atomStart = 0;
1303             *nAtoms    = gridSet.numRealAtomsTotal();
1304             break;
1305         case gmx::AtomLocality::Local:
1306             *atomStart = 0;
1307             *nAtoms    = gridSet.numRealAtomsLocal();
1308             break;
1309         case gmx::AtomLocality::NonLocal:
1310             *atomStart = gridSet.numRealAtomsLocal();
1311             *nAtoms    = gridSet.numRealAtomsTotal() - gridSet.numRealAtomsLocal();
1312             break;
1313         case gmx::AtomLocality::Count:
1314             GMX_ASSERT(false, "Count is invalid locality specifier");
1315             break;
1316     }
1317 }