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