Merge branch release-2019 into master
[alexxy/gromacs.git] / src / gromacs / mdlib / vsite.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #include "gmxpre.h"
38
39 #include "vsite.h"
40
41 #include <cstdio>
42
43 #include <algorithm>
44 #include <memory>
45 #include <vector>
46
47 #include "gromacs/domdec/domdec.h"
48 #include "gromacs/domdec/domdec_struct.h"
49 #include "gromacs/gmxlib/network.h"
50 #include "gromacs/gmxlib/nrnb.h"
51 #include "gromacs/math/functions.h"
52 #include "gromacs/math/vec.h"
53 #include "gromacs/mdlib/gmx_omp_nthreads.h"
54 #include "gromacs/mdtypes/commrec.h"
55 #include "gromacs/mdtypes/mdatom.h"
56 #include "gromacs/pbcutil/ishift.h"
57 #include "gromacs/pbcutil/mshift.h"
58 #include "gromacs/pbcutil/pbc.h"
59 #include "gromacs/timing/wallcycle.h"
60 #include "gromacs/topology/ifunc.h"
61 #include "gromacs/topology/mtop_util.h"
62 #include "gromacs/topology/topology.h"
63 #include "gromacs/utility/exceptions.h"
64 #include "gromacs/utility/fatalerror.h"
65 #include "gromacs/utility/gmxassert.h"
66 #include "gromacs/utility/gmxomp.h"
67
68 /* The strategy used here for assigning virtual sites to (thread-)tasks
69  * is as follows:
70  *
71  * We divide the atom range that vsites operate on (natoms_local with DD,
72  * 0 - last atom involved in vsites without DD) equally over all threads.
73  *
74  * Vsites in the local range constructed from atoms in the local range
75  * and/or other vsites that are fully local are assigned to a simple,
76  * independent task.
77  *
78  * Vsites that are not assigned after using the above criterion get assigned
79  * to a so called "interdependent" thread task when none of the constructing
80  * atoms is a vsite. These tasks are called interdependent, because one task
81  * accesses atoms assigned to a different task/thread.
82  * Note that this option is turned off with large (local) atom counts
83  * to avoid high memory usage.
84  *
85  * Any remaining vsites are assigned to a separate master thread task.
86  */
87
88 using gmx::RVec;
89
90 static void init_ilist(t_ilist *ilist)
91 {
92     for (int i = 0; i < F_NRE; i++)
93     {
94         ilist[i].nr     = 0;
95         ilist[i].nalloc = 0;
96         ilist[i].iatoms = nullptr;
97     }
98 }
99
100 /*! \brief List of atom indices belonging to a task */
101 struct AtomIndex {
102     //! List of atom indices
103     std::vector<int> atom;
104 };
105
106 /*! \brief Data structure for thread tasks that use constructing atoms outside their own atom range */
107 struct InterdependentTask
108 {
109     //! The interaction lists, only vsite entries are used
110     t_ilist                ilist[F_NRE];
111     //! Thread/task-local force buffer
112     std::vector<RVec>      force;
113     //! The atom indices of the vsites of our task
114     std::vector<int>       vsite;
115     //! Flags if elements in force are spread to or not
116     std::vector<bool>      use;
117     //! The number of entries set to true in use
118     int                    nuse;
119     //! Array of atoms indices, size nthreads, covering all nuse set elements in use
120     std::vector<AtomIndex> atomIndex;
121     //! List of tasks (force blocks) this task spread forces to
122     std::vector<int>       spreadTask;
123     //! List of tasks that write to this tasks force block range
124     std::vector<int>       reduceTask;
125
126     InterdependentTask()
127     {
128         init_ilist(ilist);
129         nuse = 0;
130     }
131 };
132
133 /*! \brief Vsite thread task data structure */
134 struct VsiteThread
135 {
136     //! Start of atom range of this task
137     int                rangeStart;
138     //! End of atom range of this task
139     int                rangeEnd;
140     //! The interaction lists, only vsite entries are used
141     t_ilist            ilist[F_NRE];
142     //! Local fshift accumulation buffer
143     rvec               fshift[SHIFTS];
144     //! Local virial dx*df accumulation buffer
145     matrix             dxdf;
146     //! Tells if interdependent task idTask should be used (in addition to the rest of this task), this bool has the same value on all threads
147     bool               useInterdependentTask;
148     //! Data for vsites that involve constructing atoms in the atom range of other threads/tasks
149     InterdependentTask idTask;
150
151     /*! \brief Constructor */
152     VsiteThread()
153     {
154         rangeStart            = -1;
155         rangeEnd              = -1;
156         init_ilist(ilist);
157         clear_rvecs(SHIFTS, fshift);
158         clear_mat(dxdf);
159         useInterdependentTask = false;
160     }
161 };
162
163 /*! \brief Returns the sum of the vsite ilist sizes over all vsite types
164  *
165  * \param[in] ilist  The interaction list
166  */
167 template <typename T>
168 static int vsiteIlistNrCount(const T *ilist)
169 {
170     int nr = 0;
171     for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
172     {
173         nr += ilist[ftype].size();
174     }
175
176     return nr;
177 }
178
179 static int pbc_rvec_sub(const t_pbc *pbc, const rvec xi, const rvec xj, rvec dx)
180 {
181     if (pbc)
182     {
183         return pbc_dx_aiuc(pbc, xi, xj, dx);
184     }
185     else
186     {
187         rvec_sub(xi, xj, dx);
188         return CENTRAL;
189     }
190 }
191
192 static inline real inverseNorm(const rvec x)
193 {
194     return gmx::invsqrt(iprod(x, x));
195 }
196
197 /* Vsite construction routines */
198
199 static void constr_vsite2(const rvec xi, const rvec xj, rvec x, real a, const t_pbc *pbc)
200 {
201     real b = 1 - a;
202     /* 1 flop */
203
204     if (pbc)
205     {
206         rvec dx;
207         pbc_dx_aiuc(pbc, xj, xi, dx);
208         x[XX] = xi[XX] + a*dx[XX];
209         x[YY] = xi[YY] + a*dx[YY];
210         x[ZZ] = xi[ZZ] + a*dx[ZZ];
211     }
212     else
213     {
214         x[XX] = b*xi[XX] + a*xj[XX];
215         x[YY] = b*xi[YY] + a*xj[YY];
216         x[ZZ] = b*xi[ZZ] + a*xj[ZZ];
217         /* 9 Flops */
218     }
219
220     /* TOTAL: 10 flops */
221 }
222
223 static void constr_vsite2FD(const rvec xi, const rvec xj, rvec x, real a,
224                             const t_pbc *pbc)
225 {
226     rvec xij;
227     pbc_rvec_sub(pbc, xj, xi, xij);
228     /* 3 flops */
229
230     const real b = a*inverseNorm(xij);
231     /* 6 + 10 flops */
232
233     x[XX] = xi[XX] + b*xij[XX];
234     x[YY] = xi[YY] + b*xij[YY];
235     x[ZZ] = xi[ZZ] + b*xij[ZZ];
236     /* 6 Flops */
237
238     /* TOTAL: 25 flops */
239 }
240
241 static void constr_vsite3(const rvec xi, const rvec xj, const rvec xk, rvec x, real a, real b,
242                           const t_pbc *pbc)
243 {
244     real c = 1 - a - b;
245     /* 2 flops */
246
247     if (pbc)
248     {
249         rvec dxj, dxk;
250
251         pbc_dx_aiuc(pbc, xj, xi, dxj);
252         pbc_dx_aiuc(pbc, xk, xi, dxk);
253         x[XX] = xi[XX] + a*dxj[XX] + b*dxk[XX];
254         x[YY] = xi[YY] + a*dxj[YY] + b*dxk[YY];
255         x[ZZ] = xi[ZZ] + a*dxj[ZZ] + b*dxk[ZZ];
256     }
257     else
258     {
259         x[XX] = c*xi[XX] + a*xj[XX] + b*xk[XX];
260         x[YY] = c*xi[YY] + a*xj[YY] + b*xk[YY];
261         x[ZZ] = c*xi[ZZ] + a*xj[ZZ] + b*xk[ZZ];
262         /* 15 Flops */
263     }
264
265     /* TOTAL: 17 flops */
266 }
267
268 static void constr_vsite3FD(const rvec xi, const rvec xj, const rvec xk, rvec x, real a, real b,
269                             const t_pbc *pbc)
270 {
271     rvec xij, xjk, temp;
272     real c;
273
274     pbc_rvec_sub(pbc, xj, xi, xij);
275     pbc_rvec_sub(pbc, xk, xj, xjk);
276     /* 6 flops */
277
278     /* temp goes from i to a point on the line jk */
279     temp[XX] = xij[XX] + a*xjk[XX];
280     temp[YY] = xij[YY] + a*xjk[YY];
281     temp[ZZ] = xij[ZZ] + a*xjk[ZZ];
282     /* 6 flops */
283
284     c = b*inverseNorm(temp);
285     /* 6 + 10 flops */
286
287     x[XX] = xi[XX] + c*temp[XX];
288     x[YY] = xi[YY] + c*temp[YY];
289     x[ZZ] = xi[ZZ] + c*temp[ZZ];
290     /* 6 Flops */
291
292     /* TOTAL: 34 flops */
293 }
294
295 static void constr_vsite3FAD(const rvec xi, const rvec xj, const rvec xk, rvec x, real a, real b, const t_pbc *pbc)
296 {
297     rvec xij, xjk, xp;
298     real a1, b1, c1, invdij;
299
300     pbc_rvec_sub(pbc, xj, xi, xij);
301     pbc_rvec_sub(pbc, xk, xj, xjk);
302     /* 6 flops */
303
304     invdij = inverseNorm(xij);
305     c1     = invdij * invdij * iprod(xij, xjk);
306     xp[XX] = xjk[XX] - c1*xij[XX];
307     xp[YY] = xjk[YY] - c1*xij[YY];
308     xp[ZZ] = xjk[ZZ] - c1*xij[ZZ];
309     a1     = a*invdij;
310     b1     = b*inverseNorm(xp);
311     /* 45 */
312
313     x[XX] = xi[XX] + a1*xij[XX] + b1*xp[XX];
314     x[YY] = xi[YY] + a1*xij[YY] + b1*xp[YY];
315     x[ZZ] = xi[ZZ] + a1*xij[ZZ] + b1*xp[ZZ];
316     /* 12 Flops */
317
318     /* TOTAL: 63 flops */
319 }
320
321 static void constr_vsite3OUT(const rvec xi, const rvec xj, const rvec xk, rvec x,
322                              real a, real b, real c, const t_pbc *pbc)
323 {
324     rvec xij, xik, temp;
325
326     pbc_rvec_sub(pbc, xj, xi, xij);
327     pbc_rvec_sub(pbc, xk, xi, xik);
328     cprod(xij, xik, temp);
329     /* 15 Flops */
330
331     x[XX] = xi[XX] + a*xij[XX] + b*xik[XX] + c*temp[XX];
332     x[YY] = xi[YY] + a*xij[YY] + b*xik[YY] + c*temp[YY];
333     x[ZZ] = xi[ZZ] + a*xij[ZZ] + b*xik[ZZ] + c*temp[ZZ];
334     /* 18 Flops */
335
336     /* TOTAL: 33 flops */
337 }
338
339 static void constr_vsite4FD(const rvec xi, const rvec xj, const rvec xk, const rvec xl, rvec x,
340                             real a, real b, real c, const t_pbc *pbc)
341 {
342     rvec xij, xjk, xjl, temp;
343     real d;
344
345     pbc_rvec_sub(pbc, xj, xi, xij);
346     pbc_rvec_sub(pbc, xk, xj, xjk);
347     pbc_rvec_sub(pbc, xl, xj, xjl);
348     /* 9 flops */
349
350     /* temp goes from i to a point on the plane jkl */
351     temp[XX] = xij[XX] + a*xjk[XX] + b*xjl[XX];
352     temp[YY] = xij[YY] + a*xjk[YY] + b*xjl[YY];
353     temp[ZZ] = xij[ZZ] + a*xjk[ZZ] + b*xjl[ZZ];
354     /* 12 flops */
355
356     d = c*inverseNorm(temp);
357     /* 6 + 10 flops */
358
359     x[XX] = xi[XX] + d*temp[XX];
360     x[YY] = xi[YY] + d*temp[YY];
361     x[ZZ] = xi[ZZ] + d*temp[ZZ];
362     /* 6 Flops */
363
364     /* TOTAL: 43 flops */
365 }
366
367 static void constr_vsite4FDN(const rvec xi, const rvec xj, const rvec xk, const rvec xl, rvec x,
368                              real a, real b, real c, const t_pbc *pbc)
369 {
370     rvec xij, xik, xil, ra, rb, rja, rjb, rm;
371     real d;
372
373     pbc_rvec_sub(pbc, xj, xi, xij);
374     pbc_rvec_sub(pbc, xk, xi, xik);
375     pbc_rvec_sub(pbc, xl, xi, xil);
376     /* 9 flops */
377
378     ra[XX] = a*xik[XX];
379     ra[YY] = a*xik[YY];
380     ra[ZZ] = a*xik[ZZ];
381
382     rb[XX] = b*xil[XX];
383     rb[YY] = b*xil[YY];
384     rb[ZZ] = b*xil[ZZ];
385
386     /* 6 flops */
387
388     rvec_sub(ra, xij, rja);
389     rvec_sub(rb, xij, rjb);
390     /* 6 flops */
391
392     cprod(rja, rjb, rm);
393     /* 9 flops */
394
395     d = c*inverseNorm(rm);
396     /* 5+5+1 flops */
397
398     x[XX] = xi[XX] + d*rm[XX];
399     x[YY] = xi[YY] + d*rm[YY];
400     x[ZZ] = xi[ZZ] + d*rm[ZZ];
401     /* 6 Flops */
402
403     /* TOTAL: 47 flops */
404 }
405
406
407 static int constr_vsiten(const t_iatom *ia, const t_iparams ip[],
408                          rvec *x, const t_pbc *pbc)
409 {
410     rvec x1, dx;
411     dvec dsum;
412     int  n3, av, ai;
413     real a;
414
415     n3 = 3*ip[ia[0]].vsiten.n;
416     av = ia[1];
417     ai = ia[2];
418     copy_rvec(x[ai], x1);
419     clear_dvec(dsum);
420     for (int i = 3; i < n3; i += 3)
421     {
422         ai = ia[i+2];
423         a  = ip[ia[i]].vsiten.a;
424         if (pbc)
425         {
426             pbc_dx_aiuc(pbc, x[ai], x1, dx);
427         }
428         else
429         {
430             rvec_sub(x[ai], x1, dx);
431         }
432         dsum[XX] += a*dx[XX];
433         dsum[YY] += a*dx[YY];
434         dsum[ZZ] += a*dx[ZZ];
435         /* 9 Flops */
436     }
437
438     x[av][XX] = x1[XX] + dsum[XX];
439     x[av][YY] = x1[YY] + dsum[YY];
440     x[av][ZZ] = x1[ZZ] + dsum[ZZ];
441
442     return n3;
443 }
444
445 /*! \brief PBC modes for vsite construction and spreading */
446 enum class PbcMode
447 {
448     all,         // Apply normal, simple PBC for all vsites
449     none         // No PBC treatment needed
450 };
451
452 /*! \brief Returns the PBC mode based on the system PBC and vsite properties
453  *
454  * \param[in] pbcPtr  A pointer to a PBC struct or nullptr when no PBC treatment is required
455  */
456 static PbcMode getPbcMode(const t_pbc *pbcPtr)
457 {
458     if (pbcPtr == nullptr)
459     {
460         return PbcMode::none;
461     }
462     else
463     {
464         return PbcMode::all;
465     }
466 }
467
468 static void construct_vsites_thread(rvec x[],
469                                     real dt, rvec *v,
470                                     const t_iparams ip[], const t_ilist ilist[],
471                                     const t_pbc *pbc_null)
472 {
473     real         inv_dt;
474     if (v != nullptr)
475     {
476         inv_dt = 1.0/dt;
477     }
478     else
479     {
480         inv_dt = 1.0;
481     }
482
483     const PbcMode            pbcMode   = getPbcMode(pbc_null);
484     /* We need another pbc pointer, as with charge groups we switch per vsite */
485     const t_pbc             *pbc_null2 = pbc_null;
486
487     for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
488     {
489         if (ilist[ftype].nr == 0)
490         {
491             continue;
492         }
493
494         {   // TODO remove me
495             int            nra = interaction_function[ftype].nratoms;
496             int            inc = 1 + nra;
497             int            nr  = ilist[ftype].nr;
498
499             const t_iatom *ia = ilist[ftype].iatoms;
500
501             for (int i = 0; i < nr; )
502             {
503                 int  tp     = ia[0];
504                 /* The vsite and constructing atoms */
505                 int  avsite = ia[1];
506                 int  ai     = ia[2];
507                 /* Constants for constructing vsites */
508                 real a1     = ip[tp].vsite.a;
509                 /* Copy the old position */
510                 rvec xv;
511                 copy_rvec(x[avsite], xv);
512
513                 /* Construct the vsite depending on type */
514                 int  aj, ak, al;
515                 real b1, c1;
516                 switch (ftype)
517                 {
518                     case F_VSITE2:
519                         aj = ia[3];
520                         constr_vsite2(x[ai], x[aj], x[avsite], a1, pbc_null2);
521                         break;
522                     case F_VSITE2FD:
523                         aj = ia[3];
524                         constr_vsite2FD(x[ai], x[aj], x[avsite], a1, pbc_null2);
525                         break;
526                     case F_VSITE3:
527                         aj = ia[3];
528                         ak = ia[4];
529                         b1 = ip[tp].vsite.b;
530                         constr_vsite3(x[ai], x[aj], x[ak], x[avsite], a1, b1, pbc_null2);
531                         break;
532                     case F_VSITE3FD:
533                         aj = ia[3];
534                         ak = ia[4];
535                         b1 = ip[tp].vsite.b;
536                         constr_vsite3FD(x[ai], x[aj], x[ak], x[avsite], a1, b1, pbc_null2);
537                         break;
538                     case F_VSITE3FAD:
539                         aj = ia[3];
540                         ak = ia[4];
541                         b1 = ip[tp].vsite.b;
542                         constr_vsite3FAD(x[ai], x[aj], x[ak], x[avsite], a1, b1, pbc_null2);
543                         break;
544                     case F_VSITE3OUT:
545                         aj = ia[3];
546                         ak = ia[4];
547                         b1 = ip[tp].vsite.b;
548                         c1 = ip[tp].vsite.c;
549                         constr_vsite3OUT(x[ai], x[aj], x[ak], x[avsite], a1, b1, c1, pbc_null2);
550                         break;
551                     case F_VSITE4FD:
552                         aj = ia[3];
553                         ak = ia[4];
554                         al = ia[5];
555                         b1 = ip[tp].vsite.b;
556                         c1 = ip[tp].vsite.c;
557                         constr_vsite4FD(x[ai], x[aj], x[ak], x[al], x[avsite], a1, b1, c1,
558                                         pbc_null2);
559                         break;
560                     case F_VSITE4FDN:
561                         aj = ia[3];
562                         ak = ia[4];
563                         al = ia[5];
564                         b1 = ip[tp].vsite.b;
565                         c1 = ip[tp].vsite.c;
566                         constr_vsite4FDN(x[ai], x[aj], x[ak], x[al], x[avsite], a1, b1, c1,
567                                          pbc_null2);
568                         break;
569                     case F_VSITEN:
570                         inc = constr_vsiten(ia, ip, x, pbc_null2);
571                         break;
572                     default:
573                         gmx_fatal(FARGS, "No such vsite type %d in %s, line %d",
574                                   ftype, __FILE__, __LINE__);
575                 }
576
577                 if (pbcMode == PbcMode::all)
578                 {
579                     /* Keep the vsite in the same periodic image as before */
580                     rvec dx;
581                     int  ishift = pbc_dx_aiuc(pbc_null, x[avsite], xv, dx);
582                     if (ishift != CENTRAL)
583                     {
584                         rvec_add(xv, dx, x[avsite]);
585                     }
586                 }
587                 if (v != nullptr)
588                 {
589                     /* Calculate velocity of vsite... */
590                     rvec vv;
591                     rvec_sub(x[avsite], xv, vv);
592                     svmul(inv_dt, vv, v[avsite]);
593                 }
594
595                 /* Increment loop variables */
596                 i  += inc;
597                 ia += inc;
598             }
599         }
600     }
601 }
602
603 void construct_vsites(const gmx_vsite_t *vsite,
604                       rvec x[],
605                       real dt, rvec *v,
606                       const t_iparams ip[], const t_ilist ilist[],
607                       int ePBC, gmx_bool bMolPBC,
608                       const t_commrec *cr,
609                       const matrix box)
610 {
611     const bool useDomdec = (vsite != nullptr && vsite->useDomdec);
612     GMX_ASSERT(!useDomdec || (cr != nullptr && DOMAINDECOMP(cr)), "When vsites are set up with domain decomposition, we need a valid commrec");
613     // TODO: Remove this assertion when we remove charge groups
614     GMX_ASSERT(vsite != nullptr || ePBC == epbcNONE, "Without a vsite struct we can not do PBC (in case we have charge groups)");
615
616     t_pbc     pbc, *pbc_null;
617
618     /* We only need to do pbc when we have inter-cg vsites.
619      * Note that with domain decomposition we do not need to apply PBC here
620      * when we have at least 3 domains along each dimension. Currently we
621      * do not optimize this case.
622      */
623     if (ePBC != epbcNONE && (useDomdec || bMolPBC) &&
624         !(vsite != nullptr && vsite->numInterUpdategroupVsites == 0))
625     {
626         /* This is wasting some CPU time as we now do this multiple times
627          * per MD step.
628          */
629         ivec null_ivec;
630         clear_ivec(null_ivec);
631         pbc_null = set_pbc_dd(&pbc, ePBC,
632                               useDomdec ? cr->dd->nc : null_ivec,
633                               FALSE, box);
634     }
635     else
636     {
637         pbc_null = nullptr;
638     }
639
640     if (useDomdec)
641     {
642         dd_move_x_vsites(cr->dd, box, x);
643     }
644
645     if (vsite == nullptr || vsite->nthreads == 1)
646     {
647         construct_vsites_thread(x, dt, v,
648                                 ip, ilist,
649                                 pbc_null);
650     }
651     else
652     {
653 #pragma omp parallel num_threads(vsite->nthreads)
654         {
655             try
656             {
657                 const int          th    = gmx_omp_get_thread_num();
658                 const VsiteThread &tData = *vsite->tData[th];
659                 GMX_ASSERT(tData.rangeStart >= 0, "The thread data should be initialized before calling construct_vsites");
660
661                 construct_vsites_thread(x, dt, v,
662                                         ip, tData.ilist,
663                                         pbc_null);
664                 if (tData.useInterdependentTask)
665                 {
666                     /* Here we don't need a barrier (unlike the spreading),
667                      * since both tasks only construct vsites from particles,
668                      * or local vsites, not from non-local vsites.
669                      */
670                     construct_vsites_thread(x, dt, v,
671                                             ip, tData.idTask.ilist,
672                                             pbc_null);
673                 }
674             }
675             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
676         }
677         /* Now we can construct the vsites that might depend on other vsites */
678         construct_vsites_thread(x, dt, v,
679                                 ip, vsite->tData[vsite->nthreads]->ilist,
680                                 pbc_null);
681     }
682 }
683
684 static void spread_vsite2(const t_iatom ia[], real a,
685                           const rvec x[],
686                           rvec f[], rvec fshift[],
687                           const t_pbc *pbc, const t_graph *g)
688 {
689     rvec    fi, fj, dx;
690     t_iatom av, ai, aj;
691     ivec    di;
692     int     siv, sij;
693
694     av = ia[1];
695     ai = ia[2];
696     aj = ia[3];
697
698     svmul(1 - a, f[av], fi);
699     svmul(    a, f[av], fj);
700     /* 7 flop */
701
702     rvec_inc(f[ai], fi);
703     rvec_inc(f[aj], fj);
704     /* 6 Flops */
705
706     if (g)
707     {
708         ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, av), di);
709         siv = IVEC2IS(di);
710         ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), di);
711         sij = IVEC2IS(di);
712     }
713     else if (pbc)
714     {
715         siv = pbc_dx_aiuc(pbc, x[ai], x[av], dx);
716         sij = pbc_dx_aiuc(pbc, x[ai], x[aj], dx);
717     }
718     else
719     {
720         siv = CENTRAL;
721         sij = CENTRAL;
722     }
723
724     if (fshift && (siv != CENTRAL || sij != CENTRAL))
725     {
726         rvec_inc(fshift[siv], f[av]);
727         rvec_dec(fshift[CENTRAL], fi);
728         rvec_dec(fshift[sij], fj);
729     }
730
731     /* TOTAL: 13 flops */
732 }
733
734 void constructVsitesGlobal(const gmx_mtop_t         &mtop,
735                            gmx::ArrayRef<gmx::RVec>  x)
736 {
737     GMX_ASSERT(x.ssize() >= mtop.natoms, "x should contain the whole system");
738     GMX_ASSERT(!mtop.moleculeBlockIndices.empty(), "molblock indices are needed in constructVsitesGlobal");
739
740     for (size_t mb = 0; mb < mtop.molblock.size(); mb++)
741     {
742         const gmx_molblock_t  &molb = mtop.molblock[mb];
743         const gmx_moltype_t   &molt = mtop.moltype[molb.type];
744         if (vsiteIlistNrCount(molt.ilist.data()) > 0)
745         {
746             int atomOffset = mtop.moleculeBlockIndices[mb].globalAtomStart;
747             for (int mol = 0; mol < molb.nmol; mol++)
748             {
749                 t_ilist ilist[F_NRE];
750                 for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
751                 {
752                     ilist[ftype].nr     = molt.ilist[ftype].size();
753                     ilist[ftype].iatoms = const_cast<t_iatom *>(molt.ilist[ftype].iatoms.data());
754                 }
755
756                 construct_vsites(nullptr, as_rvec_array(x.data()) + atomOffset,
757                                  0.0, nullptr,
758                                  mtop.ffparams.iparams.data(), ilist,
759                                  epbcNONE, TRUE, nullptr, nullptr);
760                 atomOffset += molt.atoms.nr;
761             }
762         }
763     }
764 }
765
766 static void spread_vsite2FD(const t_iatom ia[], real a,
767                             const rvec x[],
768                             rvec f[], rvec fshift[],
769                             gmx_bool VirCorr, matrix dxdf,
770                             const t_pbc *pbc, const t_graph *g)
771 {
772     const int av = ia[1];
773     const int ai = ia[2];
774     const int aj = ia[3];
775     rvec      fv;
776     copy_rvec(f[av], fv);
777
778     rvec xij;
779     int  sji = pbc_rvec_sub(pbc, x[aj], x[ai], xij);
780     /* 6 flops */
781
782     const real invDistance = inverseNorm(xij);
783     const real b           = a*invDistance;
784     /* 4 + ?10? flops */
785
786     const real fproj = iprod(xij, fv)*invDistance*invDistance;
787
788     rvec       fj;
789     fj[XX] = b*(fv[XX] - fproj*xij[XX]);
790     fj[YY] = b*(fv[YY] - fproj*xij[YY]);
791     fj[ZZ] = b*(fv[ZZ] - fproj*xij[ZZ]);
792     /* 9 */
793
794     /* b is already calculated in constr_vsite2FD
795        storing b somewhere will save flops.     */
796
797     f[ai][XX] += fv[XX] - fj[XX];
798     f[ai][YY] += fv[YY] - fj[YY];
799     f[ai][ZZ] += fv[ZZ] - fj[ZZ];
800     f[aj][XX] += fj[XX];
801     f[aj][YY] += fj[YY];
802     f[aj][ZZ] += fj[ZZ];
803     /* 9 Flops */
804
805     if (fshift)
806     {
807         int svi;
808         if (g)
809         {
810             ivec di;
811             ivec_sub(SHIFT_IVEC(g, ia[1]), SHIFT_IVEC(g, ai), di);
812             svi = IVEC2IS(di);
813             ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
814             sji = IVEC2IS(di);
815         }
816         else if (pbc)
817         {
818             rvec xvi;
819             svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
820         }
821         else
822         {
823             svi = CENTRAL;
824         }
825
826         if (svi != CENTRAL || sji != CENTRAL)
827         {
828             rvec_dec(fshift[svi], fv);
829             fshift[CENTRAL][XX] += fv[XX] - fj[XX];
830             fshift[CENTRAL][YY] += fv[YY] - fj[YY];
831             fshift[CENTRAL][ZZ] += fv[ZZ] - fj[ZZ];
832             fshift[    sji][XX] += fj[XX];
833             fshift[    sji][YY] += fj[YY];
834             fshift[    sji][ZZ] += fj[ZZ];
835         }
836     }
837
838     if (VirCorr)
839     {
840         /* When VirCorr=TRUE, the virial for the current forces is not
841          * calculated from the redistributed forces. This means that
842          * the effect of non-linear virtual site constructions on the virial
843          * needs to be added separately. This contribution can be calculated
844          * in many ways, but the simplest and cheapest way is to use
845          * the first constructing atom ai as a reference position in space:
846          * subtract (xv-xi)*fv and add (xj-xi)*fj.
847          */
848         rvec xiv;
849
850         pbc_rvec_sub(pbc, x[av], x[ai], xiv);
851
852         for (int i = 0; i < DIM; i++)
853         {
854             for (int j = 0; j < DIM; j++)
855             {
856                 /* As xix is a linear combination of j and k, use that here */
857                 dxdf[i][j] += -xiv[i]*fv[j] + xij[i]*fj[j];
858             }
859         }
860     }
861
862     /* TOTAL: 38 flops */
863 }
864
865 static void spread_vsite3(const t_iatom ia[], real a, real b,
866                           const rvec x[],
867                           rvec f[], rvec fshift[],
868                           const t_pbc *pbc, const t_graph *g)
869 {
870     rvec    fi, fj, fk, dx;
871     int     av, ai, aj, ak;
872     ivec    di;
873     int     siv, sij, sik;
874
875     av = ia[1];
876     ai = ia[2];
877     aj = ia[3];
878     ak = ia[4];
879
880     svmul(1 - a - b, f[av], fi);
881     svmul(        a, f[av], fj);
882     svmul(        b, f[av], fk);
883     /* 11 flops */
884
885     rvec_inc(f[ai], fi);
886     rvec_inc(f[aj], fj);
887     rvec_inc(f[ak], fk);
888     /* 9 Flops */
889
890     if (g)
891     {
892         ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, ia[1]), di);
893         siv = IVEC2IS(di);
894         ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), di);
895         sij = IVEC2IS(di);
896         ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, ak), di);
897         sik = IVEC2IS(di);
898     }
899     else if (pbc)
900     {
901         siv = pbc_dx_aiuc(pbc, x[ai], x[av], dx);
902         sij = pbc_dx_aiuc(pbc, x[ai], x[aj], dx);
903         sik = pbc_dx_aiuc(pbc, x[ai], x[ak], dx);
904     }
905     else
906     {
907         siv = CENTRAL;
908         sij = CENTRAL;
909         sik = CENTRAL;
910     }
911
912     if (fshift && (siv != CENTRAL || sij != CENTRAL || sik != CENTRAL))
913     {
914         rvec_inc(fshift[siv], f[av]);
915         rvec_dec(fshift[CENTRAL], fi);
916         rvec_dec(fshift[sij], fj);
917         rvec_dec(fshift[sik], fk);
918     }
919
920     /* TOTAL: 20 flops */
921 }
922
923 static void spread_vsite3FD(const t_iatom ia[], real a, real b,
924                             const rvec x[],
925                             rvec f[], rvec fshift[],
926                             gmx_bool VirCorr, matrix dxdf,
927                             const t_pbc *pbc, const t_graph *g)
928 {
929     real    fproj, a1;
930     rvec    xvi, xij, xjk, xix, fv, temp;
931     t_iatom av, ai, aj, ak;
932     int     svi, sji, skj;
933     ivec    di;
934
935     av = ia[1];
936     ai = ia[2];
937     aj = ia[3];
938     ak = ia[4];
939     copy_rvec(f[av], fv);
940
941     sji = pbc_rvec_sub(pbc, x[aj], x[ai], xij);
942     skj = pbc_rvec_sub(pbc, x[ak], x[aj], xjk);
943     /* 6 flops */
944
945     /* xix goes from i to point x on the line jk */
946     xix[XX] = xij[XX]+a*xjk[XX];
947     xix[YY] = xij[YY]+a*xjk[YY];
948     xix[ZZ] = xij[ZZ]+a*xjk[ZZ];
949     /* 6 flops */
950
951     const real invDistance = inverseNorm(xix);
952     const real c           = b*invDistance;
953     /* 4 + ?10? flops */
954
955     fproj = iprod(xix, fv)*invDistance*invDistance; /* = (xix . f)/(xix . xix) */
956
957     temp[XX] = c*(fv[XX]-fproj*xix[XX]);
958     temp[YY] = c*(fv[YY]-fproj*xix[YY]);
959     temp[ZZ] = c*(fv[ZZ]-fproj*xix[ZZ]);
960     /* 16 */
961
962     /* c is already calculated in constr_vsite3FD
963        storing c somewhere will save 26 flops!     */
964
965     a1         = 1 - a;
966     f[ai][XX] += fv[XX] - temp[XX];
967     f[ai][YY] += fv[YY] - temp[YY];
968     f[ai][ZZ] += fv[ZZ] - temp[ZZ];
969     f[aj][XX] += a1*temp[XX];
970     f[aj][YY] += a1*temp[YY];
971     f[aj][ZZ] += a1*temp[ZZ];
972     f[ak][XX] += a*temp[XX];
973     f[ak][YY] += a*temp[YY];
974     f[ak][ZZ] += a*temp[ZZ];
975     /* 19 Flops */
976
977     if (g)
978     {
979         ivec_sub(SHIFT_IVEC(g, ia[1]), SHIFT_IVEC(g, ai), di);
980         svi = IVEC2IS(di);
981         ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
982         sji = IVEC2IS(di);
983         ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, aj), di);
984         skj = IVEC2IS(di);
985     }
986     else if (pbc)
987     {
988         svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
989     }
990     else
991     {
992         svi = CENTRAL;
993     }
994
995     if (fshift && (svi != CENTRAL || sji != CENTRAL || skj != CENTRAL))
996     {
997         rvec_dec(fshift[svi], fv);
998         fshift[CENTRAL][XX] += fv[XX] - (1 + a)*temp[XX];
999         fshift[CENTRAL][YY] += fv[YY] - (1 + a)*temp[YY];
1000         fshift[CENTRAL][ZZ] += fv[ZZ] - (1 + a)*temp[ZZ];
1001         fshift[    sji][XX] += temp[XX];
1002         fshift[    sji][YY] += temp[YY];
1003         fshift[    sji][ZZ] += temp[ZZ];
1004         fshift[    skj][XX] += a*temp[XX];
1005         fshift[    skj][YY] += a*temp[YY];
1006         fshift[    skj][ZZ] += a*temp[ZZ];
1007     }
1008
1009     if (VirCorr)
1010     {
1011         /* When VirCorr=TRUE, the virial for the current forces is not
1012          * calculated from the redistributed forces. This means that
1013          * the effect of non-linear virtual site constructions on the virial
1014          * needs to be added separately. This contribution can be calculated
1015          * in many ways, but the simplest and cheapest way is to use
1016          * the first constructing atom ai as a reference position in space:
1017          * subtract (xv-xi)*fv and add (xj-xi)*fj + (xk-xi)*fk.
1018          */
1019         rvec xiv;
1020
1021         pbc_rvec_sub(pbc, x[av], x[ai], xiv);
1022
1023         for (int i = 0; i < DIM; i++)
1024         {
1025             for (int j = 0; j < DIM; j++)
1026             {
1027                 /* As xix is a linear combination of j and k, use that here */
1028                 dxdf[i][j] += -xiv[i]*fv[j] + xix[i]*temp[j];
1029             }
1030         }
1031     }
1032
1033     /* TOTAL: 61 flops */
1034 }
1035
1036 static void spread_vsite3FAD(const t_iatom ia[], real a, real b,
1037                              const rvec x[],
1038                              rvec f[], rvec fshift[],
1039                              gmx_bool VirCorr, matrix dxdf,
1040                              const t_pbc *pbc, const t_graph *g)
1041 {
1042     rvec    xvi, xij, xjk, xperp, Fpij, Fppp, fv, f1, f2, f3;
1043     real    a1, b1, c1, c2, invdij, invdij2, invdp, fproj;
1044     t_iatom av, ai, aj, ak;
1045     int     svi, sji, skj, d;
1046     ivec    di;
1047
1048     av = ia[1];
1049     ai = ia[2];
1050     aj = ia[3];
1051     ak = ia[4];
1052     copy_rvec(f[ia[1]], fv);
1053
1054     sji = pbc_rvec_sub(pbc, x[aj], x[ai], xij);
1055     skj = pbc_rvec_sub(pbc, x[ak], x[aj], xjk);
1056     /* 6 flops */
1057
1058     invdij    = inverseNorm(xij);
1059     invdij2   = invdij * invdij;
1060     c1        = iprod(xij, xjk) * invdij2;
1061     xperp[XX] = xjk[XX] - c1*xij[XX];
1062     xperp[YY] = xjk[YY] - c1*xij[YY];
1063     xperp[ZZ] = xjk[ZZ] - c1*xij[ZZ];
1064     /* xperp in plane ijk, perp. to ij */
1065     invdp = inverseNorm(xperp);
1066     a1    = a*invdij;
1067     b1    = b*invdp;
1068     /* 45 flops */
1069
1070     /* a1, b1 and c1 are already calculated in constr_vsite3FAD
1071        storing them somewhere will save 45 flops!     */
1072
1073     fproj = iprod(xij, fv)*invdij2;
1074     svmul(fproj,                      xij,  Fpij);    /* proj. f on xij */
1075     svmul(iprod(xperp, fv)*invdp*invdp, xperp, Fppp); /* proj. f on xperp */
1076     svmul(b1*fproj,                   xperp, f3);
1077     /* 23 flops */
1078
1079     rvec_sub(fv, Fpij, f1); /* f1 = f - Fpij */
1080     rvec_sub(f1, Fppp, f2); /* f2 = f - Fpij - Fppp */
1081     for (d = 0; (d < DIM); d++)
1082     {
1083         f1[d] *= a1;
1084         f2[d] *= b1;
1085     }
1086     /* 12 flops */
1087
1088     c2         = 1 + c1;
1089     f[ai][XX] += fv[XX] - f1[XX] + c1*f2[XX] + f3[XX];
1090     f[ai][YY] += fv[YY] - f1[YY] + c1*f2[YY] + f3[YY];
1091     f[ai][ZZ] += fv[ZZ] - f1[ZZ] + c1*f2[ZZ] + f3[ZZ];
1092     f[aj][XX] +=          f1[XX] - c2*f2[XX] - f3[XX];
1093     f[aj][YY] +=          f1[YY] - c2*f2[YY] - f3[YY];
1094     f[aj][ZZ] +=          f1[ZZ] - c2*f2[ZZ] - f3[ZZ];
1095     f[ak][XX] +=                      f2[XX];
1096     f[ak][YY] +=                      f2[YY];
1097     f[ak][ZZ] +=                      f2[ZZ];
1098     /* 30 Flops */
1099
1100     if (g)
1101     {
1102         ivec_sub(SHIFT_IVEC(g, ia[1]), SHIFT_IVEC(g, ai), di);
1103         svi = IVEC2IS(di);
1104         ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
1105         sji = IVEC2IS(di);
1106         ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, aj), di);
1107         skj = IVEC2IS(di);
1108     }
1109     else if (pbc)
1110     {
1111         svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
1112     }
1113     else
1114     {
1115         svi = CENTRAL;
1116     }
1117
1118     if (fshift && (svi != CENTRAL || sji != CENTRAL || skj != CENTRAL))
1119     {
1120         rvec_dec(fshift[svi], fv);
1121         fshift[CENTRAL][XX] += fv[XX] - f1[XX] - (1-c1)*f2[XX] + f3[XX];
1122         fshift[CENTRAL][YY] += fv[YY] - f1[YY] - (1-c1)*f2[YY] + f3[YY];
1123         fshift[CENTRAL][ZZ] += fv[ZZ] - f1[ZZ] - (1-c1)*f2[ZZ] + f3[ZZ];
1124         fshift[    sji][XX] +=          f1[XX] -    c1 *f2[XX] - f3[XX];
1125         fshift[    sji][YY] +=          f1[YY] -    c1 *f2[YY] - f3[YY];
1126         fshift[    sji][ZZ] +=          f1[ZZ] -    c1 *f2[ZZ] - f3[ZZ];
1127         fshift[    skj][XX] +=                          f2[XX];
1128         fshift[    skj][YY] +=                          f2[YY];
1129         fshift[    skj][ZZ] +=                          f2[ZZ];
1130     }
1131
1132     if (VirCorr)
1133     {
1134         rvec xiv;
1135         int  i, j;
1136
1137         pbc_rvec_sub(pbc, x[av], x[ai], xiv);
1138
1139         for (i = 0; i < DIM; i++)
1140         {
1141             for (j = 0; j < DIM; j++)
1142             {
1143                 /* Note that xik=xij+xjk, so we have to add xij*f2 */
1144                 dxdf[i][j] +=
1145                     -xiv[i]*fv[j]
1146                     + xij[i]*(f1[j] + (1 - c2)*f2[j] - f3[j])
1147                     + xjk[i]*f2[j];
1148             }
1149         }
1150     }
1151
1152     /* TOTAL: 113 flops */
1153 }
1154
1155 static void spread_vsite3OUT(const t_iatom ia[], real a, real b, real c,
1156                              const rvec x[],
1157                              rvec f[], rvec fshift[],
1158                              gmx_bool VirCorr, matrix dxdf,
1159                              const t_pbc *pbc, const t_graph *g)
1160 {
1161     rvec    xvi, xij, xik, fv, fj, fk;
1162     real    cfx, cfy, cfz;
1163     int     av, ai, aj, ak;
1164     ivec    di;
1165     int     svi, sji, ski;
1166
1167     av = ia[1];
1168     ai = ia[2];
1169     aj = ia[3];
1170     ak = ia[4];
1171
1172     sji = pbc_rvec_sub(pbc, x[aj], x[ai], xij);
1173     ski = pbc_rvec_sub(pbc, x[ak], x[ai], xik);
1174     /* 6 Flops */
1175
1176     copy_rvec(f[av], fv);
1177
1178     cfx = c*fv[XX];
1179     cfy = c*fv[YY];
1180     cfz = c*fv[ZZ];
1181     /* 3 Flops */
1182
1183     fj[XX] = a*fv[XX]     -  xik[ZZ]*cfy +  xik[YY]*cfz;
1184     fj[YY] =  xik[ZZ]*cfx + a*fv[YY]     -  xik[XX]*cfz;
1185     fj[ZZ] = -xik[YY]*cfx +  xik[XX]*cfy + a*fv[ZZ];
1186
1187     fk[XX] = b*fv[XX]     +  xij[ZZ]*cfy -  xij[YY]*cfz;
1188     fk[YY] = -xij[ZZ]*cfx + b*fv[YY]     +  xij[XX]*cfz;
1189     fk[ZZ] =  xij[YY]*cfx -  xij[XX]*cfy + b*fv[ZZ];
1190     /* 30 Flops */
1191
1192     f[ai][XX] += fv[XX] - fj[XX] - fk[XX];
1193     f[ai][YY] += fv[YY] - fj[YY] - fk[YY];
1194     f[ai][ZZ] += fv[ZZ] - fj[ZZ] - fk[ZZ];
1195     rvec_inc(f[aj], fj);
1196     rvec_inc(f[ak], fk);
1197     /* 15 Flops */
1198
1199     if (g)
1200     {
1201         ivec_sub(SHIFT_IVEC(g, ia[1]), SHIFT_IVEC(g, ai), di);
1202         svi = IVEC2IS(di);
1203         ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
1204         sji = IVEC2IS(di);
1205         ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, ai), di);
1206         ski = IVEC2IS(di);
1207     }
1208     else if (pbc)
1209     {
1210         svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
1211     }
1212     else
1213     {
1214         svi = CENTRAL;
1215     }
1216
1217     if (fshift && (svi != CENTRAL || sji != CENTRAL || ski != CENTRAL))
1218     {
1219         rvec_dec(fshift[svi], fv);
1220         fshift[CENTRAL][XX] += fv[XX] - fj[XX] - fk[XX];
1221         fshift[CENTRAL][YY] += fv[YY] - fj[YY] - fk[YY];
1222         fshift[CENTRAL][ZZ] += fv[ZZ] - fj[ZZ] - fk[ZZ];
1223         rvec_inc(fshift[sji], fj);
1224         rvec_inc(fshift[ski], fk);
1225     }
1226
1227     if (VirCorr)
1228     {
1229         rvec xiv;
1230
1231         pbc_rvec_sub(pbc, x[av], x[ai], xiv);
1232
1233         for (int i = 0; i < DIM; i++)
1234         {
1235             for (int j = 0; j < DIM; j++)
1236             {
1237                 dxdf[i][j] += -xiv[i]*fv[j] + xij[i]*fj[j] + xik[i]*fk[j];
1238             }
1239         }
1240     }
1241
1242     /* TOTAL: 54 flops */
1243 }
1244
1245 static void spread_vsite4FD(const t_iatom ia[], real a, real b, real c,
1246                             const rvec x[],
1247                             rvec f[], rvec fshift[],
1248                             gmx_bool VirCorr, matrix dxdf,
1249                             const t_pbc *pbc, const t_graph *g)
1250 {
1251     real    fproj, a1;
1252     rvec    xvi, xij, xjk, xjl, xix, fv, temp;
1253     int     av, ai, aj, ak, al;
1254     ivec    di;
1255     int     svi, sji, skj, slj, m;
1256
1257     av = ia[1];
1258     ai = ia[2];
1259     aj = ia[3];
1260     ak = ia[4];
1261     al = ia[5];
1262
1263     sji = pbc_rvec_sub(pbc, x[aj], x[ai], xij);
1264     skj = pbc_rvec_sub(pbc, x[ak], x[aj], xjk);
1265     slj = pbc_rvec_sub(pbc, x[al], x[aj], xjl);
1266     /* 9 flops */
1267
1268     /* xix goes from i to point x on the plane jkl */
1269     for (m = 0; m < DIM; m++)
1270     {
1271         xix[m] = xij[m] + a*xjk[m] + b*xjl[m];
1272     }
1273     /* 12 flops */
1274
1275     const real invDistance = inverseNorm(xix);
1276     const real d           = c*invDistance;
1277     /* 4 + ?10? flops */
1278
1279     copy_rvec(f[av], fv);
1280
1281     fproj = iprod(xix, fv)*invDistance*invDistance; /* = (xix . f)/(xix . xix) */
1282
1283     for (m = 0; m < DIM; m++)
1284     {
1285         temp[m] = d*(fv[m] - fproj*xix[m]);
1286     }
1287     /* 16 */
1288
1289     /* c is already calculated in constr_vsite3FD
1290        storing c somewhere will save 35 flops!     */
1291
1292     a1 = 1 - a - b;
1293     for (m = 0; m < DIM; m++)
1294     {
1295         f[ai][m] += fv[m] - temp[m];
1296         f[aj][m] += a1*temp[m];
1297         f[ak][m] += a*temp[m];
1298         f[al][m] += b*temp[m];
1299     }
1300     /* 26 Flops */
1301
1302     if (g)
1303     {
1304         ivec_sub(SHIFT_IVEC(g, ia[1]), SHIFT_IVEC(g, ai), di);
1305         svi = IVEC2IS(di);
1306         ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
1307         sji = IVEC2IS(di);
1308         ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, aj), di);
1309         skj = IVEC2IS(di);
1310         ivec_sub(SHIFT_IVEC(g, al), SHIFT_IVEC(g, aj), di);
1311         slj = IVEC2IS(di);
1312     }
1313     else if (pbc)
1314     {
1315         svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
1316     }
1317     else
1318     {
1319         svi = CENTRAL;
1320     }
1321
1322     if (fshift &&
1323         (svi != CENTRAL || sji != CENTRAL || skj != CENTRAL || slj != CENTRAL))
1324     {
1325         rvec_dec(fshift[svi], fv);
1326         for (m = 0; m < DIM; m++)
1327         {
1328             fshift[CENTRAL][m] += fv[m] - (1 + a + b)*temp[m];
1329             fshift[    sji][m] += temp[m];
1330             fshift[    skj][m] += a*temp[m];
1331             fshift[    slj][m] += b*temp[m];
1332         }
1333     }
1334
1335     if (VirCorr)
1336     {
1337         rvec xiv;
1338         int  i, j;
1339
1340         pbc_rvec_sub(pbc, x[av], x[ai], xiv);
1341
1342         for (i = 0; i < DIM; i++)
1343         {
1344             for (j = 0; j < DIM; j++)
1345             {
1346                 dxdf[i][j] += -xiv[i]*fv[j] + xix[i]*temp[j];
1347             }
1348         }
1349     }
1350
1351     /* TOTAL: 77 flops */
1352 }
1353
1354
1355 static void spread_vsite4FDN(const t_iatom ia[], real a, real b, real c,
1356                              const rvec x[],
1357                              rvec f[], rvec fshift[],
1358                              gmx_bool VirCorr, matrix dxdf,
1359                              const t_pbc *pbc, const t_graph *g)
1360 {
1361     rvec xvi, xij, xik, xil, ra, rb, rja, rjb, rab, rm, rt;
1362     rvec fv, fj, fk, fl;
1363     real invrm, denom;
1364     real cfx, cfy, cfz;
1365     ivec di;
1366     int  av, ai, aj, ak, al;
1367     int  svi, sij, sik, sil;
1368
1369     /* DEBUG: check atom indices */
1370     av = ia[1];
1371     ai = ia[2];
1372     aj = ia[3];
1373     ak = ia[4];
1374     al = ia[5];
1375
1376     copy_rvec(f[av], fv);
1377
1378     sij = pbc_rvec_sub(pbc, x[aj], x[ai], xij);
1379     sik = pbc_rvec_sub(pbc, x[ak], x[ai], xik);
1380     sil = pbc_rvec_sub(pbc, x[al], x[ai], xil);
1381     /* 9 flops */
1382
1383     ra[XX] = a*xik[XX];
1384     ra[YY] = a*xik[YY];
1385     ra[ZZ] = a*xik[ZZ];
1386
1387     rb[XX] = b*xil[XX];
1388     rb[YY] = b*xil[YY];
1389     rb[ZZ] = b*xil[ZZ];
1390
1391     /* 6 flops */
1392
1393     rvec_sub(ra, xij, rja);
1394     rvec_sub(rb, xij, rjb);
1395     rvec_sub(rb, ra, rab);
1396     /* 9 flops */
1397
1398     cprod(rja, rjb, rm);
1399     /* 9 flops */
1400
1401     invrm = inverseNorm(rm);
1402     denom = invrm*invrm;
1403     /* 5+5+2 flops */
1404
1405     cfx = c*invrm*fv[XX];
1406     cfy = c*invrm*fv[YY];
1407     cfz = c*invrm*fv[ZZ];
1408     /* 6 Flops */
1409
1410     cprod(rm, rab, rt);
1411     /* 9 flops */
1412
1413     rt[XX] *= denom;
1414     rt[YY] *= denom;
1415     rt[ZZ] *= denom;
1416     /* 3flops */
1417
1418     fj[XX] = (        -rm[XX]*rt[XX]) * cfx + ( rab[ZZ]-rm[YY]*rt[XX]) * cfy + (-rab[YY]-rm[ZZ]*rt[XX]) * cfz;
1419     fj[YY] = (-rab[ZZ]-rm[XX]*rt[YY]) * cfx + (        -rm[YY]*rt[YY]) * cfy + ( rab[XX]-rm[ZZ]*rt[YY]) * cfz;
1420     fj[ZZ] = ( rab[YY]-rm[XX]*rt[ZZ]) * cfx + (-rab[XX]-rm[YY]*rt[ZZ]) * cfy + (        -rm[ZZ]*rt[ZZ]) * cfz;
1421     /* 30 flops */
1422
1423     cprod(rjb, rm, rt);
1424     /* 9 flops */
1425
1426     rt[XX] *= denom*a;
1427     rt[YY] *= denom*a;
1428     rt[ZZ] *= denom*a;
1429     /* 3flops */
1430
1431     fk[XX] = (          -rm[XX]*rt[XX]) * cfx + (-a*rjb[ZZ]-rm[YY]*rt[XX]) * cfy + ( a*rjb[YY]-rm[ZZ]*rt[XX]) * cfz;
1432     fk[YY] = ( a*rjb[ZZ]-rm[XX]*rt[YY]) * cfx + (          -rm[YY]*rt[YY]) * cfy + (-a*rjb[XX]-rm[ZZ]*rt[YY]) * cfz;
1433     fk[ZZ] = (-a*rjb[YY]-rm[XX]*rt[ZZ]) * cfx + ( a*rjb[XX]-rm[YY]*rt[ZZ]) * cfy + (          -rm[ZZ]*rt[ZZ]) * cfz;
1434     /* 36 flops */
1435
1436     cprod(rm, rja, rt);
1437     /* 9 flops */
1438
1439     rt[XX] *= denom*b;
1440     rt[YY] *= denom*b;
1441     rt[ZZ] *= denom*b;
1442     /* 3flops */
1443
1444     fl[XX] = (          -rm[XX]*rt[XX]) * cfx + ( b*rja[ZZ]-rm[YY]*rt[XX]) * cfy + (-b*rja[YY]-rm[ZZ]*rt[XX]) * cfz;
1445     fl[YY] = (-b*rja[ZZ]-rm[XX]*rt[YY]) * cfx + (          -rm[YY]*rt[YY]) * cfy + ( b*rja[XX]-rm[ZZ]*rt[YY]) * cfz;
1446     fl[ZZ] = ( b*rja[YY]-rm[XX]*rt[ZZ]) * cfx + (-b*rja[XX]-rm[YY]*rt[ZZ]) * cfy + (          -rm[ZZ]*rt[ZZ]) * cfz;
1447     /* 36 flops */
1448
1449     f[ai][XX] += fv[XX] - fj[XX] - fk[XX] - fl[XX];
1450     f[ai][YY] += fv[YY] - fj[YY] - fk[YY] - fl[YY];
1451     f[ai][ZZ] += fv[ZZ] - fj[ZZ] - fk[ZZ] - fl[ZZ];
1452     rvec_inc(f[aj], fj);
1453     rvec_inc(f[ak], fk);
1454     rvec_inc(f[al], fl);
1455     /* 21 flops */
1456
1457     if (g)
1458     {
1459         ivec_sub(SHIFT_IVEC(g, av), SHIFT_IVEC(g, ai), di);
1460         svi = IVEC2IS(di);
1461         ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
1462         sij = IVEC2IS(di);
1463         ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, ai), di);
1464         sik = IVEC2IS(di);
1465         ivec_sub(SHIFT_IVEC(g, al), SHIFT_IVEC(g, ai), di);
1466         sil = IVEC2IS(di);
1467     }
1468     else if (pbc)
1469     {
1470         svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
1471     }
1472     else
1473     {
1474         svi = CENTRAL;
1475     }
1476
1477     if (fshift && (svi != CENTRAL || sij != CENTRAL || sik != CENTRAL || sil != CENTRAL))
1478     {
1479         rvec_dec(fshift[svi], fv);
1480         fshift[CENTRAL][XX] += fv[XX] - fj[XX] - fk[XX] - fl[XX];
1481         fshift[CENTRAL][YY] += fv[YY] - fj[YY] - fk[YY] - fl[YY];
1482         fshift[CENTRAL][ZZ] += fv[ZZ] - fj[ZZ] - fk[ZZ] - fl[ZZ];
1483         rvec_inc(fshift[sij], fj);
1484         rvec_inc(fshift[sik], fk);
1485         rvec_inc(fshift[sil], fl);
1486     }
1487
1488     if (VirCorr)
1489     {
1490         rvec xiv;
1491         int  i, j;
1492
1493         pbc_rvec_sub(pbc, x[av], x[ai], xiv);
1494
1495         for (i = 0; i < DIM; i++)
1496         {
1497             for (j = 0; j < DIM; j++)
1498             {
1499                 dxdf[i][j] += -xiv[i]*fv[j] + xij[i]*fj[j] + xik[i]*fk[j] + xil[i]*fl[j];
1500             }
1501         }
1502     }
1503
1504     /* Total: 207 flops (Yuck!) */
1505 }
1506
1507
1508 static int spread_vsiten(const t_iatom ia[], const t_iparams ip[],
1509                          const rvec x[],
1510                          rvec f[], rvec fshift[],
1511                          const t_pbc *pbc, const t_graph *g)
1512 {
1513     rvec xv, dx, fi;
1514     int  n3, av, i, ai;
1515     real a;
1516     ivec di;
1517     int  siv;
1518
1519     n3 = 3*ip[ia[0]].vsiten.n;
1520     av = ia[1];
1521     copy_rvec(x[av], xv);
1522
1523     for (i = 0; i < n3; i += 3)
1524     {
1525         ai = ia[i+2];
1526         if (g)
1527         {
1528             ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, av), di);
1529             siv = IVEC2IS(di);
1530         }
1531         else if (pbc)
1532         {
1533             siv = pbc_dx_aiuc(pbc, x[ai], xv, dx);
1534         }
1535         else
1536         {
1537             siv = CENTRAL;
1538         }
1539         a = ip[ia[i]].vsiten.a;
1540         svmul(a, f[av], fi);
1541         rvec_inc(f[ai], fi);
1542         if (fshift && siv != CENTRAL)
1543         {
1544             rvec_inc(fshift[siv], fi);
1545             rvec_dec(fshift[CENTRAL], fi);
1546         }
1547         /* 6 Flops */
1548     }
1549
1550     return n3;
1551 }
1552
1553
1554 static int vsite_count(const t_ilist *ilist, int ftype)
1555 {
1556     if (ftype == F_VSITEN)
1557     {
1558         return ilist[ftype].nr/3;
1559     }
1560     else
1561     {
1562         return ilist[ftype].nr/(1 + interaction_function[ftype].nratoms);
1563     }
1564 }
1565
1566 static void spread_vsite_f_thread(const rvec x[],
1567                                   rvec f[], rvec *fshift,
1568                                   gmx_bool VirCorr, matrix dxdf,
1569                                   t_iparams ip[], const t_ilist ilist[],
1570                                   const t_graph *g, const t_pbc *pbc_null)
1571 {
1572     const PbcMode            pbcMode   = getPbcMode(pbc_null);
1573     /* We need another pbc pointer, as with charge groups we switch per vsite */
1574     const t_pbc             *pbc_null2 = pbc_null;
1575     gmx::ArrayRef<const int> vsite_pbc;
1576
1577     /* this loop goes backwards to be able to build *
1578      * higher type vsites from lower types         */
1579     for (int ftype = c_ftypeVsiteEnd - 1; ftype >= c_ftypeVsiteStart; ftype--)
1580     {
1581         if (ilist[ftype].nr == 0)
1582         {
1583             continue;
1584         }
1585
1586         {   // TODO remove me
1587             int            nra = interaction_function[ftype].nratoms;
1588             int            inc = 1 + nra;
1589             int            nr  = ilist[ftype].nr;
1590
1591             const t_iatom *ia = ilist[ftype].iatoms;
1592
1593             if (pbcMode == PbcMode::all)
1594             {
1595                 pbc_null2 = pbc_null;
1596             }
1597
1598             for (int i = 0; i < nr; )
1599             {
1600                 int tp = ia[0];
1601
1602                 /* Constants for constructing */
1603                 real a1, b1, c1;
1604                 a1 = ip[tp].vsite.a;
1605                 /* Construct the vsite depending on type */
1606                 switch (ftype)
1607                 {
1608                     case F_VSITE2:
1609                         spread_vsite2(ia, a1, x, f, fshift, pbc_null2, g);
1610                         break;
1611                     case F_VSITE2FD:
1612                         spread_vsite2FD(ia, a1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
1613                         break;
1614                     case F_VSITE3:
1615                         b1 = ip[tp].vsite.b;
1616                         spread_vsite3(ia, a1, b1, x, f, fshift, pbc_null2, g);
1617                         break;
1618                     case F_VSITE3FD:
1619                         b1 = ip[tp].vsite.b;
1620                         spread_vsite3FD(ia, a1, b1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
1621                         break;
1622                     case F_VSITE3FAD:
1623                         b1 = ip[tp].vsite.b;
1624                         spread_vsite3FAD(ia, a1, b1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
1625                         break;
1626                     case F_VSITE3OUT:
1627                         b1 = ip[tp].vsite.b;
1628                         c1 = ip[tp].vsite.c;
1629                         spread_vsite3OUT(ia, a1, b1, c1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
1630                         break;
1631                     case F_VSITE4FD:
1632                         b1 = ip[tp].vsite.b;
1633                         c1 = ip[tp].vsite.c;
1634                         spread_vsite4FD(ia, a1, b1, c1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
1635                         break;
1636                     case F_VSITE4FDN:
1637                         b1 = ip[tp].vsite.b;
1638                         c1 = ip[tp].vsite.c;
1639                         spread_vsite4FDN(ia, a1, b1, c1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
1640                         break;
1641                     case F_VSITEN:
1642                         inc = spread_vsiten(ia, ip, x, f, fshift, pbc_null2, g);
1643                         break;
1644                     default:
1645                         gmx_fatal(FARGS, "No such vsite type %d in %s, line %d",
1646                                   ftype, __FILE__, __LINE__);
1647                 }
1648                 clear_rvec(f[ia[1]]);
1649
1650                 /* Increment loop variables */
1651                 i  += inc;
1652                 ia += inc;
1653             }
1654         }
1655     }
1656 }
1657
1658 /*! \brief Clears the task force buffer elements that are written by task idTask */
1659 static void clearTaskForceBufferUsedElements(InterdependentTask *idTask)
1660 {
1661     int ntask = idTask->spreadTask.size();
1662     for (int ti = 0; ti < ntask; ti++)
1663     {
1664         const AtomIndex *atomList = &idTask->atomIndex[idTask->spreadTask[ti]];
1665         int              natom    = atomList->atom.size();
1666         RVec            *force    = idTask->force.data();
1667         for (int i = 0; i < natom; i++)
1668         {
1669             clear_rvec(force[atomList->atom[i]]);
1670         }
1671     }
1672 }
1673
1674 void spread_vsite_f(const gmx_vsite_t *vsite,
1675                     const rvec * gmx_restrict x,
1676                     rvec * gmx_restrict f, rvec * gmx_restrict fshift,
1677                     gmx_bool VirCorr, matrix vir,
1678                     t_nrnb *nrnb, const t_idef *idef,
1679                     int ePBC, gmx_bool bMolPBC, const t_graph *g, const matrix box,
1680                     const t_commrec *cr, gmx_wallcycle *wcycle)
1681 {
1682     wallcycle_start(wcycle, ewcVSITESPREAD);
1683     const bool useDomdec = vsite->useDomdec;
1684     GMX_ASSERT(!useDomdec || (cr != nullptr && DOMAINDECOMP(cr)), "When vsites are set up with domain decomposition, we need a valid commrec");
1685
1686     t_pbc pbc, *pbc_null;
1687
1688     /* We only need to do pbc when we have inter-cg vsites */
1689     if ((useDomdec || bMolPBC) && vsite->numInterUpdategroupVsites)
1690     {
1691         /* This is wasting some CPU time as we now do this multiple times
1692          * per MD step.
1693          */
1694         pbc_null = set_pbc_dd(&pbc, ePBC, useDomdec ? cr->dd->nc : nullptr, FALSE, box);
1695     }
1696     else
1697     {
1698         pbc_null = nullptr;
1699     }
1700
1701     if (useDomdec)
1702     {
1703         dd_clear_f_vsites(cr->dd, f);
1704     }
1705
1706     if (vsite->nthreads == 1)
1707     {
1708         matrix dxdf;
1709         if (VirCorr)
1710         {
1711             clear_mat(dxdf);
1712         }
1713         spread_vsite_f_thread(x, f, fshift,
1714                               VirCorr, dxdf,
1715                               idef->iparams, idef->il,
1716                               g, pbc_null);
1717
1718         if (VirCorr)
1719         {
1720             for (int i = 0; i < DIM; i++)
1721             {
1722                 for (int j = 0; j < DIM; j++)
1723                 {
1724                     vir[i][j] += -0.5*dxdf[i][j];
1725                 }
1726             }
1727         }
1728     }
1729     else
1730     {
1731         /* First spread the vsites that might depend on non-local vsites */
1732         if (VirCorr)
1733         {
1734             clear_mat(vsite->tData[vsite->nthreads]->dxdf);
1735         }
1736         spread_vsite_f_thread(x, f, fshift,
1737                               VirCorr, vsite->tData[vsite->nthreads]->dxdf,
1738                               idef->iparams,
1739                               vsite->tData[vsite->nthreads]->ilist,
1740                               g, pbc_null);
1741
1742 #pragma omp parallel num_threads(vsite->nthreads)
1743         {
1744             try
1745             {
1746                 int          thread = gmx_omp_get_thread_num();
1747                 VsiteThread &tData  = *vsite->tData[thread];
1748
1749                 rvec        *fshift_t;
1750                 if (thread == 0 || fshift == nullptr)
1751                 {
1752                     fshift_t = fshift;
1753                 }
1754                 else
1755                 {
1756                     fshift_t = tData.fshift;
1757
1758                     for (int i = 0; i < SHIFTS; i++)
1759                     {
1760                         clear_rvec(fshift_t[i]);
1761                     }
1762                 }
1763                 if (VirCorr)
1764                 {
1765                     clear_mat(tData.dxdf);
1766                 }
1767
1768                 if (tData.useInterdependentTask)
1769                 {
1770                     /* Spread the vsites that spread outside our local range.
1771                      * This is done using a thread-local force buffer force.
1772                      * First we need to copy the input vsite forces to force.
1773                      */
1774                     InterdependentTask *idTask = &tData.idTask;
1775
1776                     /* Clear the buffer elements set by our task during
1777                      * the last call to spread_vsite_f.
1778                      */
1779                     clearTaskForceBufferUsedElements(idTask);
1780
1781                     int nvsite = idTask->vsite.size();
1782                     for (int i = 0; i < nvsite; i++)
1783                     {
1784                         copy_rvec(f[idTask->vsite[i]],
1785                                   idTask->force[idTask->vsite[i]]);
1786                     }
1787                     spread_vsite_f_thread(x, as_rvec_array(idTask->force.data()), fshift_t,
1788                                           VirCorr, tData.dxdf,
1789                                           idef->iparams,
1790                                           tData.idTask.ilist,
1791                                           g, pbc_null);
1792
1793                     /* We need a barrier before reducing forces below
1794                      * that have been produced by a different thread above.
1795                      */
1796 #pragma omp barrier
1797
1798                     /* Loop over all thread task and reduce forces they
1799                      * produced on atoms that fall in our range.
1800                      * Note that atomic reduction would be a simpler solution,
1801                      * but that might not have good support on all platforms.
1802                      */
1803                     int ntask = idTask->reduceTask.size();
1804                     for (int ti = 0; ti < ntask; ti++)
1805                     {
1806                         const InterdependentTask *idt_foreign = &vsite->tData[idTask->reduceTask[ti]]->idTask;
1807                         const AtomIndex          *atomList    = &idt_foreign->atomIndex[thread];
1808                         const RVec               *f_foreign   = idt_foreign->force.data();
1809
1810                         int natom = atomList->atom.size();
1811                         for (int i = 0; i < natom; i++)
1812                         {
1813                             int ind = atomList->atom[i];
1814                             rvec_inc(f[ind], f_foreign[ind]);
1815                             /* Clearing of f_foreign is done at the next step */
1816                         }
1817                     }
1818                     /* Clear the vsite forces, both in f and force */
1819                     for (int i = 0; i < nvsite; i++)
1820                     {
1821                         int ind = tData.idTask.vsite[i];
1822                         clear_rvec(f[ind]);
1823                         clear_rvec(tData.idTask.force[ind]);
1824                     }
1825                 }
1826
1827                 /* Spread the vsites that spread locally only */
1828                 spread_vsite_f_thread(x, f, fshift_t,
1829                                       VirCorr, tData.dxdf,
1830                                       idef->iparams,
1831                                       tData.ilist,
1832                                       g, pbc_null);
1833             }
1834             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
1835         }
1836
1837         if (fshift != nullptr)
1838         {
1839             for (int th = 1; th < vsite->nthreads; th++)
1840             {
1841                 for (int i = 0; i < SHIFTS; i++)
1842                 {
1843                     rvec_inc(fshift[i], vsite->tData[th]->fshift[i]);
1844                 }
1845             }
1846         }
1847
1848         if (VirCorr)
1849         {
1850             for (int th = 0; th < vsite->nthreads + 1; th++)
1851             {
1852                 /* MSVC doesn't like matrix references, so we use a pointer */
1853                 const matrix *dxdf = &vsite->tData[th]->dxdf;
1854
1855                 for (int i = 0; i < DIM; i++)
1856                 {
1857                     for (int j = 0; j < DIM; j++)
1858                     {
1859                         vir[i][j] += -0.5*(*dxdf)[i][j];
1860                     }
1861                 }
1862             }
1863         }
1864     }
1865
1866     if (useDomdec)
1867     {
1868         dd_move_f_vsites(cr->dd, f, fshift);
1869     }
1870
1871     inc_nrnb(nrnb, eNR_VSITE2,   vsite_count(idef->il, F_VSITE2));
1872     inc_nrnb(nrnb, eNR_VSITE2FD, vsite_count(idef->il, F_VSITE2FD));
1873     inc_nrnb(nrnb, eNR_VSITE3,   vsite_count(idef->il, F_VSITE3));
1874     inc_nrnb(nrnb, eNR_VSITE3FD, vsite_count(idef->il, F_VSITE3FD));
1875     inc_nrnb(nrnb, eNR_VSITE3FAD, vsite_count(idef->il, F_VSITE3FAD));
1876     inc_nrnb(nrnb, eNR_VSITE3OUT, vsite_count(idef->il, F_VSITE3OUT));
1877     inc_nrnb(nrnb, eNR_VSITE4FD, vsite_count(idef->il, F_VSITE4FD));
1878     inc_nrnb(nrnb, eNR_VSITE4FDN, vsite_count(idef->il, F_VSITE4FDN));
1879     inc_nrnb(nrnb, eNR_VSITEN,   vsite_count(idef->il, F_VSITEN));
1880
1881     wallcycle_stop(wcycle, ewcVSITESPREAD);
1882 }
1883
1884 /*! \brief Returns the an array with group indices for each atom
1885  *
1886  * \param[in] grouping  The paritioning of the atom range into atom groups
1887  */
1888 static std::vector<int> makeAtomToGroupMapping(const gmx::RangePartitioning &grouping)
1889 {
1890     std::vector<int> atomToGroup(grouping.fullRange().end(), 0);
1891
1892     for (int group = 0; group < grouping.numBlocks(); group++)
1893     {
1894         auto block = grouping.block(group);
1895         std::fill(atomToGroup.begin() + block.begin(),
1896                   atomToGroup.begin() + block.end(),
1897                   group);
1898     }
1899
1900     return atomToGroup;
1901 }
1902
1903 int countNonlinearVsites(const gmx_mtop_t &mtop)
1904 {
1905     int numNonlinearVsites = 0;
1906     for (const gmx_molblock_t &molb : mtop.molblock)
1907     {
1908         const gmx_moltype_t &molt = mtop.moltype[molb.type];
1909
1910         for (const auto &ilist : extractILists(molt.ilist, IF_VSITE))
1911         {
1912             if (ilist.functionType != F_VSITE2 &&
1913                 ilist.functionType != F_VSITE3 &&
1914                 ilist.functionType != F_VSITEN)
1915             {
1916                 numNonlinearVsites += molb.nmol*ilist.iatoms.size()/(1 + NRAL(ilist.functionType));
1917             }
1918         }
1919     }
1920
1921     return numNonlinearVsites;
1922 }
1923
1924 int countInterUpdategroupVsites(const gmx_mtop_t                            &mtop,
1925                                 gmx::ArrayRef<const gmx::RangePartitioning>  updateGroupingPerMoleculetype)
1926 {
1927     int n_intercg_vsite = 0;
1928     for (const gmx_molblock_t &molb : mtop.molblock)
1929     {
1930         const gmx_moltype_t &molt = mtop.moltype[molb.type];
1931
1932         std::vector<int>     atomToGroup;
1933         if (!updateGroupingPerMoleculetype.empty())
1934         {
1935             atomToGroup = makeAtomToGroupMapping(updateGroupingPerMoleculetype[molb.type]);
1936         }
1937         for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
1938         {
1939             const int              nral = NRAL(ftype);
1940             const InteractionList &il   = molt.ilist[ftype];
1941             for (int i = 0; i < il.size(); i += 1 + nral)
1942             {
1943                 bool isInterGroup = atomToGroup.empty();
1944                 if (!isInterGroup)
1945                 {
1946                     const int group = atomToGroup[il.iatoms[1 + i]];
1947                     for (int a = 1; a < nral; a++)
1948                     {
1949                         if (atomToGroup[il.iatoms[1 + a]] != group)
1950                         {
1951                             isInterGroup = true;
1952                             break;
1953                         }
1954                     }
1955                 }
1956                 if (isInterGroup)
1957                 {
1958                     n_intercg_vsite += molb.nmol;
1959                 }
1960             }
1961         }
1962     }
1963
1964     return n_intercg_vsite;
1965 }
1966
1967 std::unique_ptr<gmx_vsite_t>
1968 initVsite(const gmx_mtop_t &mtop,
1969           const t_commrec  *cr)
1970 {
1971     GMX_RELEASE_ASSERT(cr != nullptr, "We need a valid commrec");
1972
1973     std::unique_ptr<gmx_vsite_t> vsite;
1974
1975     /* check if there are vsites */
1976     int nvsite = 0;
1977     for (int ftype = 0; ftype < F_NRE; ftype++)
1978     {
1979         if (interaction_function[ftype].flags & IF_VSITE)
1980         {
1981             GMX_ASSERT(ftype >= c_ftypeVsiteStart && ftype < c_ftypeVsiteEnd, "c_ftypeVsiteStart and/or c_ftypeVsiteEnd do not have correct values");
1982
1983             nvsite += gmx_mtop_ftype_count(&mtop, ftype);
1984         }
1985         else
1986         {
1987             GMX_ASSERT(ftype < c_ftypeVsiteStart || ftype >= c_ftypeVsiteEnd, "c_ftypeVsiteStart and/or c_ftypeVsiteEnd do not have correct values");
1988         }
1989     }
1990
1991     if (nvsite == 0)
1992     {
1993         return vsite;
1994     }
1995
1996     vsite = std::make_unique<gmx_vsite_t>();
1997
1998     gmx::ArrayRef<const gmx::RangePartitioning> updateGroupingPerMoleculetype;
1999     if (DOMAINDECOMP(cr))
2000     {
2001         updateGroupingPerMoleculetype = getUpdateGroupingPerMoleculetype(*cr->dd);
2002     }
2003     vsite->numInterUpdategroupVsites = countInterUpdategroupVsites(mtop, updateGroupingPerMoleculetype);
2004
2005     vsite->useDomdec                 = (DOMAINDECOMP(cr) && cr->dd->nnodes > 1);
2006
2007     vsite->nthreads                  = gmx_omp_nthreads_get(emntVSITE);
2008
2009     if (vsite->nthreads > 1)
2010     {
2011         /* We need one extra thread data structure for the overlap vsites */
2012         vsite->tData.resize(vsite->nthreads + 1);
2013 #pragma omp parallel for num_threads(vsite->nthreads) schedule(static)
2014         for (int thread = 0; thread < vsite->nthreads; thread++)
2015         {
2016             try
2017             {
2018                 vsite->tData[thread] = std::make_unique<VsiteThread>();
2019
2020                 InterdependentTask &idTask = vsite->tData[thread]->idTask;
2021                 idTask.nuse                = 0;
2022                 idTask.atomIndex.resize(vsite->nthreads);
2023             }
2024             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
2025         }
2026         if (vsite->nthreads > 1)
2027         {
2028             vsite->tData[vsite->nthreads] = std::make_unique<VsiteThread>();
2029         }
2030     }
2031
2032     return vsite;
2033 }
2034
2035 gmx_vsite_t::gmx_vsite_t()
2036 {
2037 }
2038
2039 gmx_vsite_t::~gmx_vsite_t()
2040 {
2041 }
2042
2043 static inline void flagAtom(InterdependentTask *idTask, int atom,
2044                             int thread, int nthread, int natperthread)
2045 {
2046     if (!idTask->use[atom])
2047     {
2048         idTask->use[atom] = true;
2049         thread            = atom/natperthread;
2050         /* Assign all non-local atom force writes to thread 0 */
2051         if (thread >= nthread)
2052         {
2053             thread        = 0;
2054         }
2055         idTask->atomIndex[thread].atom.push_back(atom);
2056     }
2057 }
2058
2059 /*\brief Here we try to assign all vsites that are in our local range.
2060  *
2061  * Our task local atom range is tData->rangeStart - tData->rangeEnd.
2062  * Vsites that depend only on local atoms, as indicated by taskIndex[]==thread,
2063  * are assigned to task tData->ilist. Vsites that depend on non-local atoms
2064  * but not on other vsites are assigned to task tData->id_task.ilist.
2065  * taskIndex[] is set for all vsites in our range, either to our local tasks
2066  * or to the single last task as taskIndex[]=2*nthreads.
2067  */
2068 static void assignVsitesToThread(VsiteThread           *tData,
2069                                  int                    thread,
2070                                  int                    nthread,
2071                                  int                    natperthread,
2072                                  gmx::ArrayRef<int>     taskIndex,
2073                                  const t_ilist         *ilist,
2074                                  const t_iparams       *ip,
2075                                  const unsigned short  *ptype)
2076 {
2077     for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
2078     {
2079         tData->ilist[ftype].nr        = 0;
2080         tData->idTask.ilist[ftype].nr = 0;
2081
2082         int      nral1 = 1 + NRAL(ftype);
2083         int      inc   = nral1;
2084         t_iatom *iat   = ilist[ftype].iatoms;
2085         for (int i = 0; i < ilist[ftype].nr; )
2086         {
2087             if (ftype == F_VSITEN)
2088             {
2089                 /* The 3 below is from 1+NRAL(ftype)=3 */
2090                 inc = ip[iat[i]].vsiten.n*3;
2091             }
2092
2093             if (iat[1 + i] <  tData->rangeStart ||
2094                 iat[1 + i] >= tData->rangeEnd)
2095             {
2096                 /* This vsite belongs to a different thread */
2097                 i += inc;
2098                 continue;
2099             }
2100
2101             /* We would like to assign this vsite to task thread,
2102              * but it might depend on atoms outside the atom range of thread
2103              * or on another vsite not assigned to task thread.
2104              */
2105             int task = thread;
2106             if (ftype != F_VSITEN)
2107             {
2108                 for (int j = i + 2; j < i + nral1; j++)
2109                 {
2110                     /* Do a range check to avoid a harmless race on taskIndex */
2111                     if (iat[j] <  tData->rangeStart ||
2112                         iat[j] >= tData->rangeEnd ||
2113                         taskIndex[iat[j]] != thread)
2114                     {
2115                         if (!tData->useInterdependentTask ||
2116                             ptype[iat[j]] == eptVSite)
2117                         {
2118                             /* At least one constructing atom is a vsite
2119                              * that is not assigned to the same thread.
2120                              * Put this vsite into a separate task.
2121                              */
2122                             task = 2*nthread;
2123                             break;
2124                         }
2125
2126                         /* There are constructing atoms outside our range,
2127                          * put this vsite into a second task to be executed
2128                          * on the same thread. During construction no barrier
2129                          * is needed between the two tasks on the same thread.
2130                          * During spreading we need to run this task with
2131                          * an additional thread-local intermediate force buffer
2132                          * (or atomic reduction) and a barrier between the two
2133                          * tasks.
2134                          */
2135                         task = nthread + thread;
2136                     }
2137                 }
2138             }
2139             else
2140             {
2141                 for (int j = i + 2; j < i + inc; j += 3)
2142                 {
2143                     /* Do a range check to avoid a harmless race on taskIndex */
2144                     if (iat[j] <  tData->rangeStart ||
2145                         iat[j] >= tData->rangeEnd ||
2146                         taskIndex[iat[j]] != thread)
2147                     {
2148                         GMX_ASSERT(ptype[iat[j]] != eptVSite, "A vsite to be assigned in assignVsitesToThread has a vsite as a constructing atom that does not belong to our task, such vsites should be assigned to the single 'master' task");
2149
2150                         task = nthread + thread;
2151                     }
2152                 }
2153             }
2154
2155             /* Update this vsite's thread index entry */
2156             taskIndex[iat[1+i]] = task;
2157
2158             if (task == thread || task == nthread + thread)
2159             {
2160                 /* Copy this vsite to the thread data struct of thread */
2161                 t_ilist *il_task;
2162                 if (task == thread)
2163                 {
2164                     il_task = &tData->ilist[ftype];
2165                 }
2166                 else
2167                 {
2168                     il_task = &tData->idTask.ilist[ftype];
2169                 }
2170                 /* Ensure we have sufficient memory allocated */
2171                 if (il_task->nr + inc > il_task->nalloc)
2172                 {
2173                     il_task->nalloc = over_alloc_large(il_task->nr + inc);
2174                     srenew(il_task->iatoms, il_task->nalloc);
2175                 }
2176                 /* Copy the vsite data to the thread-task local array */
2177                 for (int j = i; j < i + inc; j++)
2178                 {
2179                     il_task->iatoms[il_task->nr++] = iat[j];
2180                 }
2181                 if (task == nthread + thread)
2182                 {
2183                     /* This vsite write outside our own task force block.
2184                      * Put it into the interdependent task list and flag
2185                      * the atoms involved for reduction.
2186                      */
2187                     tData->idTask.vsite.push_back(iat[i + 1]);
2188                     if (ftype != F_VSITEN)
2189                     {
2190                         for (int j = i + 2; j < i + nral1; j++)
2191                         {
2192                             flagAtom(&tData->idTask, iat[j],
2193                                      thread, nthread, natperthread);
2194                         }
2195                     }
2196                     else
2197                     {
2198                         for (int j = i + 2; j < i + inc; j += 3)
2199                         {
2200                             flagAtom(&tData->idTask, iat[j],
2201                                      thread, nthread, natperthread);
2202                         }
2203                     }
2204                 }
2205             }
2206
2207             i += inc;
2208         }
2209     }
2210 }
2211
2212 /*! \brief Assign all vsites with taskIndex[]==task to task tData */
2213 static void assignVsitesToSingleTask(VsiteThread              *tData,
2214                                      int                       task,
2215                                      gmx::ArrayRef<const int>  taskIndex,
2216                                      const t_ilist            *ilist,
2217                                      const t_iparams          *ip)
2218 {
2219     for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
2220     {
2221         tData->ilist[ftype].nr        = 0;
2222         tData->idTask.ilist[ftype].nr = 0;
2223
2224         int      nral1   = 1 + NRAL(ftype);
2225         int      inc     = nral1;
2226         t_iatom *iat     = ilist[ftype].iatoms;
2227         t_ilist *il_task = &tData->ilist[ftype];
2228
2229         for (int i = 0; i < ilist[ftype].nr; )
2230         {
2231             if (ftype == F_VSITEN)
2232             {
2233                 /* The 3 below is from 1+NRAL(ftype)=3 */
2234                 inc = ip[iat[i]].vsiten.n*3;
2235             }
2236             /* Check if the vsite is assigned to our task */
2237             if (taskIndex[iat[1 + i]] == task)
2238             {
2239                 /* Ensure we have sufficient memory allocated */
2240                 if (il_task->nr + inc > il_task->nalloc)
2241                 {
2242                     il_task->nalloc = over_alloc_large(il_task->nr + inc);
2243                     srenew(il_task->iatoms, il_task->nalloc);
2244                 }
2245                 /* Copy the vsite data to the thread-task local array */
2246                 for (int j = i; j < i + inc; j++)
2247                 {
2248                     il_task->iatoms[il_task->nr++] = iat[j];
2249                 }
2250             }
2251
2252             i += inc;
2253         }
2254     }
2255 }
2256
2257 void split_vsites_over_threads(const t_ilist   *ilist,
2258                                const t_iparams *ip,
2259                                const t_mdatoms *mdatoms,
2260                                gmx_vsite_t     *vsite)
2261 {
2262     int      vsite_atom_range, natperthread;
2263
2264     if (vsite->nthreads == 1)
2265     {
2266         /* Nothing to do */
2267         return;
2268     }
2269
2270     /* The current way of distributing the vsites over threads in primitive.
2271      * We divide the atom range 0 - natoms_in_vsite uniformly over threads,
2272      * without taking into account how the vsites are distributed.
2273      * Without domain decomposition we at least tighten the upper bound
2274      * of the range (useful for common systems such as a vsite-protein
2275      * in 3-site water).
2276      * With domain decomposition, as long as the vsites are distributed
2277      * uniformly in each domain along the major dimension, usually x,
2278      * it will also perform well.
2279      */
2280     if (!vsite->useDomdec)
2281     {
2282         vsite_atom_range = -1;
2283         for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
2284         {
2285             {   // TODO remove me
2286                 if (ftype != F_VSITEN)
2287                 {
2288                     int            nral1 = 1 + NRAL(ftype);
2289                     const t_iatom *iat   = ilist[ftype].iatoms;
2290                     for (int i = 0; i < ilist[ftype].nr; i += nral1)
2291                     {
2292                         for (int j = i + 1; j < i + nral1; j++)
2293                         {
2294                             vsite_atom_range = std::max(vsite_atom_range, iat[j]);
2295                         }
2296                     }
2297                 }
2298                 else
2299                 {
2300                     int            vs_ind_end;
2301
2302                     const t_iatom *iat = ilist[ftype].iatoms;
2303
2304                     int            i = 0;
2305                     while (i < ilist[ftype].nr)
2306                     {
2307                         /* The 3 below is from 1+NRAL(ftype)=3 */
2308                         vs_ind_end = i + ip[iat[i]].vsiten.n*3;
2309
2310                         vsite_atom_range = std::max(vsite_atom_range, iat[i+1]);
2311                         while (i < vs_ind_end)
2312                         {
2313                             vsite_atom_range = std::max(vsite_atom_range, iat[i+2]);
2314                             i               += 3;
2315                         }
2316                     }
2317                 }
2318             }
2319         }
2320         vsite_atom_range++;
2321         natperthread     = (vsite_atom_range + vsite->nthreads - 1)/vsite->nthreads;
2322     }
2323     else
2324     {
2325         /* Any local or not local atom could be involved in virtual sites.
2326          * But since we usually have very few non-local virtual sites
2327          * (only non-local vsites that depend on local vsites),
2328          * we distribute the local atom range equally over the threads.
2329          * When assigning vsites to threads, we should take care that the last
2330          * threads also covers the non-local range.
2331          */
2332         vsite_atom_range = mdatoms->nr;
2333         natperthread     = (mdatoms->homenr + vsite->nthreads - 1)/vsite->nthreads;
2334     }
2335
2336     if (debug)
2337     {
2338         fprintf(debug, "virtual site thread dist: natoms %d, range %d, natperthread %d\n", mdatoms->nr, vsite_atom_range, natperthread);
2339     }
2340
2341     /* To simplify the vsite assignment, we make an index which tells us
2342      * to which task particles, both non-vsites and vsites, are assigned.
2343      */
2344     vsite->taskIndex.resize(mdatoms->nr);
2345
2346     /* Initialize the task index array. Here we assign the non-vsite
2347      * particles to task=thread, so we easily figure out if vsites
2348      * depend on local and/or non-local particles in assignVsitesToThread.
2349      */
2350     gmx::ArrayRef<int> taskIndex = vsite->taskIndex;
2351     {
2352         int  thread = 0;
2353         for (int i = 0; i < mdatoms->nr; i++)
2354         {
2355             if (mdatoms->ptype[i] == eptVSite)
2356             {
2357                 /* vsites are not assigned to a task yet */
2358                 taskIndex[i] = -1;
2359             }
2360             else
2361             {
2362                 /* assign non-vsite particles to task thread */
2363                 taskIndex[i] = thread;
2364             }
2365             if (i == (thread + 1)*natperthread && thread < vsite->nthreads)
2366             {
2367                 thread++;
2368             }
2369         }
2370     }
2371
2372 #pragma omp parallel num_threads(vsite->nthreads)
2373     {
2374         try
2375         {
2376             int          thread = gmx_omp_get_thread_num();
2377             VsiteThread &tData  = *vsite->tData[thread];
2378
2379             /* Clear the buffer use flags that were set before */
2380             if (tData.useInterdependentTask)
2381             {
2382                 InterdependentTask &idTask = tData.idTask;
2383
2384                 /* To avoid an extra OpenMP barrier in spread_vsite_f,
2385                  * we clear the force buffer at the next step,
2386                  * so we need to do it here as well.
2387                  */
2388                 clearTaskForceBufferUsedElements(&idTask);
2389
2390                 idTask.vsite.resize(0);
2391                 for (int t = 0; t < vsite->nthreads; t++)
2392                 {
2393                     AtomIndex &atomIndex = idTask.atomIndex[t];
2394                     int        natom     = atomIndex.atom.size();
2395                     for (int i = 0; i < natom; i++)
2396                     {
2397                         idTask.use[atomIndex.atom[i]] = false;
2398                     }
2399                     atomIndex.atom.resize(0);
2400                 }
2401                 idTask.nuse = 0;
2402             }
2403
2404             /* To avoid large f_buf allocations of #threads*vsite_atom_range
2405              * we don't use task2 with more than 200000 atoms. This doesn't
2406              * affect performance, since with such a large range relatively few
2407              * vsites will end up in the separate task.
2408              * Note that useTask2 should be the same for all threads.
2409              */
2410             tData.useInterdependentTask = (vsite_atom_range <= 200000);
2411             if (tData.useInterdependentTask)
2412             {
2413                 size_t              natoms_use_in_vsites = vsite_atom_range;
2414                 InterdependentTask &idTask               = tData.idTask;
2415                 /* To avoid resizing and re-clearing every nstlist steps,
2416                  * we never down size the force buffer.
2417                  */
2418                 if (natoms_use_in_vsites > idTask.force.size() ||
2419                     natoms_use_in_vsites > idTask.use.size())
2420                 {
2421                     idTask.force.resize(natoms_use_in_vsites, { 0, 0, 0 });
2422                     idTask.use.resize(natoms_use_in_vsites, false);
2423                 }
2424             }
2425
2426             /* Assign all vsites that can execute independently on threads */
2427             tData.rangeStart     =  thread     *natperthread;
2428             if (thread < vsite->nthreads - 1)
2429             {
2430                 tData.rangeEnd   = (thread + 1)*natperthread;
2431             }
2432             else
2433             {
2434                 /* The last thread should cover up to the end of the range */
2435                 tData.rangeEnd   = mdatoms->nr;
2436             }
2437             assignVsitesToThread(&tData,
2438                                  thread, vsite->nthreads,
2439                                  natperthread,
2440                                  taskIndex,
2441                                  ilist, ip, mdatoms->ptype);
2442
2443             if (tData.useInterdependentTask)
2444             {
2445                 /* In the worst case, all tasks write to force ranges of
2446                  * all other tasks, leading to #tasks^2 scaling (this is only
2447                  * the overhead, the actual flops remain constant).
2448                  * But in most cases there is far less coupling. To improve
2449                  * scaling at high thread counts we therefore construct
2450                  * an index to only loop over the actually affected tasks.
2451                  */
2452                 InterdependentTask &idTask = tData.idTask;
2453
2454                 /* Ensure assignVsitesToThread finished on other threads */
2455 #pragma omp barrier
2456
2457                 idTask.spreadTask.resize(0);
2458                 idTask.reduceTask.resize(0);
2459                 for (int t = 0; t < vsite->nthreads; t++)
2460                 {
2461                     /* Do we write to the force buffer of task t? */
2462                     if (!idTask.atomIndex[t].atom.empty())
2463                     {
2464                         idTask.spreadTask.push_back(t);
2465                     }
2466                     /* Does task t write to our force buffer? */
2467                     if (!vsite->tData[t]->idTask.atomIndex[thread].atom.empty())
2468                     {
2469                         idTask.reduceTask.push_back(t);
2470                     }
2471                 }
2472             }
2473         }
2474         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
2475     }
2476     /* Assign all remaining vsites, that will have taskIndex[]=2*vsite->nthreads,
2477      * to a single task that will not run in parallel with other tasks.
2478      */
2479     assignVsitesToSingleTask(vsite->tData[vsite->nthreads].get(),
2480                              2*vsite->nthreads,
2481                              taskIndex,
2482                              ilist, ip);
2483
2484     if (debug && vsite->nthreads > 1)
2485     {
2486         fprintf(debug, "virtual site useInterdependentTask %d, nuse:\n",
2487                 static_cast<int>(vsite->tData[0]->useInterdependentTask));
2488         for (int th = 0; th < vsite->nthreads + 1; th++)
2489         {
2490             fprintf(debug, " %4d", vsite->tData[th]->idTask.nuse);
2491         }
2492         fprintf(debug, "\n");
2493
2494         for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
2495         {
2496             if (ilist[ftype].nr > 0)
2497             {
2498                 fprintf(debug, "%-20s thread dist:",
2499                         interaction_function[ftype].longname);
2500                 for (int th = 0; th < vsite->nthreads + 1; th++)
2501                 {
2502                     fprintf(debug, " %4d %4d ",
2503                             vsite->tData[th]->ilist[ftype].nr,
2504                             vsite->tData[th]->idTask.ilist[ftype].nr);
2505                 }
2506                 fprintf(debug, "\n");
2507             }
2508         }
2509     }
2510
2511 #ifndef NDEBUG
2512     int nrOrig     = vsiteIlistNrCount(ilist);
2513     int nrThreaded = 0;
2514     for (int th = 0; th < vsite->nthreads + 1; th++)
2515     {
2516         nrThreaded +=
2517             vsiteIlistNrCount(vsite->tData[th]->ilist) +
2518             vsiteIlistNrCount(vsite->tData[th]->idTask.ilist);
2519     }
2520     GMX_ASSERT(nrThreaded == nrOrig, "The number of virtual sites assigned to all thread task has to match the total number of virtual sites");
2521 #endif
2522 }
2523
2524 void set_vsite_top(gmx_vsite_t          *vsite,
2525                    const gmx_localtop_t *top,
2526                    const t_mdatoms      *md)
2527 {
2528     if (vsite->nthreads > 1)
2529     {
2530         split_vsites_over_threads(top->idef.il, top->idef.iparams,
2531                                   md, vsite);
2532     }
2533 }