2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2004, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2016,2017,2018, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
46 #include "gromacs/domdec/domdec.h"
47 #include "gromacs/domdec/domdec_struct.h"
48 #include "gromacs/gmxlib/network.h"
49 #include "gromacs/gmxlib/nrnb.h"
50 #include "gromacs/math/functions.h"
51 #include "gromacs/math/vec.h"
52 #include "gromacs/mdlib/gmx_omp_nthreads.h"
53 #include "gromacs/mdtypes/commrec.h"
54 #include "gromacs/mdtypes/mdatom.h"
55 #include "gromacs/pbcutil/ishift.h"
56 #include "gromacs/pbcutil/mshift.h"
57 #include "gromacs/pbcutil/pbc.h"
58 #include "gromacs/timing/wallcycle.h"
59 #include "gromacs/topology/ifunc.h"
60 #include "gromacs/topology/mtop_util.h"
61 #include "gromacs/topology/topology.h"
62 #include "gromacs/utility/exceptions.h"
63 #include "gromacs/utility/fatalerror.h"
64 #include "gromacs/utility/gmxassert.h"
65 #include "gromacs/utility/gmxomp.h"
66 #include "gromacs/utility/smalloc.h"
68 /* The strategy used here for assigning virtual sites to (thread-)tasks
71 * We divide the atom range that vsites operate on (natoms_local with DD,
72 * 0 - last atom involved in vsites without DD) equally over all threads.
74 * Vsites in the local range constructed from atoms in the local range
75 * and/or other vsites that are fully local are assigned to a simple,
78 * Vsites that are not assigned after using the above criterion get assigned
79 * to a so called "interdependent" thread task when none of the constructing
80 * atoms is a vsite. These tasks are called interdependent, because one task
81 * accesses atoms assigned to a different task/thread.
82 * Note that this option is turned off with large (local) atom counts
83 * to avoid high memory usage.
85 * Any remaining vsites are assigned to a separate master thread task.
90 static void init_ilist(t_ilist *ilist)
92 for (int i = 0; i < F_NRE; i++)
96 ilist[i].iatoms = nullptr;
100 /*! \brief List of atom indices belonging to a task */
102 //! List of atom indices
103 std::vector<int> atom;
106 /*! \brief Data structure for thread tasks that use constructing atoms outside their own atom range */
107 struct InterdependentTask
109 //! The interaction lists, only vsite entries are used
110 t_ilist ilist[F_NRE];
111 //! Thread/task-local force buffer
112 std::vector<RVec> force;
113 //! The atom indices of the vsites of our task
114 std::vector<int> vsite;
115 //! Flags if elements in force are spread to or not
116 std::vector<bool> use;
117 //! The number of entries set to true in use
119 //! Array of atoms indices, size nthreads, covering all nuse set elements in use
120 std::vector<AtomIndex> atomIndex;
121 //! List of tasks (force blocks) this task spread forces to
122 std::vector<int> spreadTask;
123 //! List of tasks that write to this tasks force block range
124 std::vector<int> reduceTask;
133 /*! \brief Vsite thread task data structure */
135 //! Start of atom range of this task
137 //! End of atom range of this task
139 //! The interaction lists, only vsite entries are used
140 t_ilist ilist[F_NRE];
141 //! Local fshift accumulation buffer
143 //! Local virial dx*df accumulation buffer
145 //! 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
146 bool useInterdependentTask;
147 //! Data for vsites that involve constructing atoms in the atom range of other threads/tasks
148 InterdependentTask idTask;
150 /*! \brief Constructor */
156 clear_rvecs(SHIFTS, fshift);
158 useInterdependentTask = false;
163 /* The start and end values of for the vsite indices in the ftype enum.
164 * The validity of these values is checked in init_vsite.
165 * This is used to avoid loops over all ftypes just to get the vsite entries.
166 * (We should replace the fixed ilist array by only the used entries.)
168 static const int c_ftypeVsiteStart = F_VSITE2;
169 static const int c_ftypeVsiteEnd = F_VSITEN + 1;
172 /*! \brief Returns the sum of the vsite ilist sizes over all vsite types
174 * \param[in] ilist The interaction list
176 static int vsiteIlistNrCount(const t_ilist *ilist)
179 for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
181 nr += ilist[ftype].nr;
187 static int pbc_rvec_sub(const t_pbc *pbc, const rvec xi, const rvec xj, rvec dx)
191 return pbc_dx_aiuc(pbc, xi, xj, dx);
195 rvec_sub(xi, xj, dx);
200 /* Vsite construction routines */
202 static void constr_vsite2(const rvec xi, const rvec xj, rvec x, real a, const t_pbc *pbc)
210 pbc_dx_aiuc(pbc, xj, xi, dx);
211 x[XX] = xi[XX] + a*dx[XX];
212 x[YY] = xi[YY] + a*dx[YY];
213 x[ZZ] = xi[ZZ] + a*dx[ZZ];
217 x[XX] = b*xi[XX] + a*xj[XX];
218 x[YY] = b*xi[YY] + a*xj[YY];
219 x[ZZ] = b*xi[ZZ] + a*xj[ZZ];
223 /* TOTAL: 10 flops */
226 static void constr_vsite3(const rvec xi, const rvec xj, const rvec xk, rvec x, real a, real b,
236 pbc_dx_aiuc(pbc, xj, xi, dxj);
237 pbc_dx_aiuc(pbc, xk, xi, dxk);
238 x[XX] = xi[XX] + a*dxj[XX] + b*dxk[XX];
239 x[YY] = xi[YY] + a*dxj[YY] + b*dxk[YY];
240 x[ZZ] = xi[ZZ] + a*dxj[ZZ] + b*dxk[ZZ];
244 x[XX] = c*xi[XX] + a*xj[XX] + b*xk[XX];
245 x[YY] = c*xi[YY] + a*xj[YY] + b*xk[YY];
246 x[ZZ] = c*xi[ZZ] + a*xj[ZZ] + b*xk[ZZ];
250 /* TOTAL: 17 flops */
253 static void constr_vsite3FD(const rvec xi, const rvec xj, const rvec xk, rvec x, real a, real b,
259 pbc_rvec_sub(pbc, xj, xi, xij);
260 pbc_rvec_sub(pbc, xk, xj, xjk);
263 /* temp goes from i to a point on the line jk */
264 temp[XX] = xij[XX] + a*xjk[XX];
265 temp[YY] = xij[YY] + a*xjk[YY];
266 temp[ZZ] = xij[ZZ] + a*xjk[ZZ];
269 c = b*gmx::invsqrt(iprod(temp, temp));
272 x[XX] = xi[XX] + c*temp[XX];
273 x[YY] = xi[YY] + c*temp[YY];
274 x[ZZ] = xi[ZZ] + c*temp[ZZ];
277 /* TOTAL: 34 flops */
280 static void constr_vsite3FAD(const rvec xi, const rvec xj, const rvec xk, rvec x, real a, real b, const t_pbc *pbc)
283 real a1, b1, c1, invdij;
285 pbc_rvec_sub(pbc, xj, xi, xij);
286 pbc_rvec_sub(pbc, xk, xj, xjk);
289 invdij = gmx::invsqrt(iprod(xij, xij));
290 c1 = invdij * invdij * iprod(xij, xjk);
291 xp[XX] = xjk[XX] - c1*xij[XX];
292 xp[YY] = xjk[YY] - c1*xij[YY];
293 xp[ZZ] = xjk[ZZ] - c1*xij[ZZ];
295 b1 = b*gmx::invsqrt(iprod(xp, xp));
298 x[XX] = xi[XX] + a1*xij[XX] + b1*xp[XX];
299 x[YY] = xi[YY] + a1*xij[YY] + b1*xp[YY];
300 x[ZZ] = xi[ZZ] + a1*xij[ZZ] + b1*xp[ZZ];
303 /* TOTAL: 63 flops */
306 static void constr_vsite3OUT(const rvec xi, const rvec xj, const rvec xk, rvec x,
307 real a, real b, real c, const t_pbc *pbc)
311 pbc_rvec_sub(pbc, xj, xi, xij);
312 pbc_rvec_sub(pbc, xk, xi, xik);
313 cprod(xij, xik, temp);
316 x[XX] = xi[XX] + a*xij[XX] + b*xik[XX] + c*temp[XX];
317 x[YY] = xi[YY] + a*xij[YY] + b*xik[YY] + c*temp[YY];
318 x[ZZ] = xi[ZZ] + a*xij[ZZ] + b*xik[ZZ] + c*temp[ZZ];
321 /* TOTAL: 33 flops */
324 static void constr_vsite4FD(const rvec xi, const rvec xj, const rvec xk, const rvec xl, rvec x,
325 real a, real b, real c, const t_pbc *pbc)
327 rvec xij, xjk, xjl, temp;
330 pbc_rvec_sub(pbc, xj, xi, xij);
331 pbc_rvec_sub(pbc, xk, xj, xjk);
332 pbc_rvec_sub(pbc, xl, xj, xjl);
335 /* temp goes from i to a point on the plane jkl */
336 temp[XX] = xij[XX] + a*xjk[XX] + b*xjl[XX];
337 temp[YY] = xij[YY] + a*xjk[YY] + b*xjl[YY];
338 temp[ZZ] = xij[ZZ] + a*xjk[ZZ] + b*xjl[ZZ];
341 d = c*gmx::invsqrt(iprod(temp, temp));
344 x[XX] = xi[XX] + d*temp[XX];
345 x[YY] = xi[YY] + d*temp[YY];
346 x[ZZ] = xi[ZZ] + d*temp[ZZ];
349 /* TOTAL: 43 flops */
352 static void constr_vsite4FDN(const rvec xi, const rvec xj, const rvec xk, const rvec xl, rvec x,
353 real a, real b, real c, const t_pbc *pbc)
355 rvec xij, xik, xil, ra, rb, rja, rjb, rm;
358 pbc_rvec_sub(pbc, xj, xi, xij);
359 pbc_rvec_sub(pbc, xk, xi, xik);
360 pbc_rvec_sub(pbc, xl, xi, xil);
373 rvec_sub(ra, xij, rja);
374 rvec_sub(rb, xij, rjb);
380 d = c*gmx::invsqrt(norm2(rm));
383 x[XX] = xi[XX] + d*rm[XX];
384 x[YY] = xi[YY] + d*rm[YY];
385 x[ZZ] = xi[ZZ] + d*rm[ZZ];
388 /* TOTAL: 47 flops */
392 static int constr_vsiten(const t_iatom *ia, const t_iparams ip[],
393 rvec *x, const t_pbc *pbc)
400 n3 = 3*ip[ia[0]].vsiten.n;
403 copy_rvec(x[ai], x1);
405 for (int i = 3; i < n3; i += 3)
408 a = ip[ia[i]].vsiten.a;
411 pbc_dx_aiuc(pbc, x[ai], x1, dx);
415 rvec_sub(x[ai], x1, dx);
417 dsum[XX] += a*dx[XX];
418 dsum[YY] += a*dx[YY];
419 dsum[ZZ] += a*dx[ZZ];
423 x[av][XX] = x1[XX] + dsum[XX];
424 x[av][YY] = x1[YY] + dsum[YY];
425 x[av][ZZ] = x1[ZZ] + dsum[ZZ];
430 /*! \brief PBC modes for vsite construction and spreading */
433 all, // Apply normal, simple PBC for all vsites
434 chargeGroup, // Keep vsite in the same periodic image as the rest of it's charge group
435 none // No PBC treatment needed
438 /*! \brief Returns the PBC mode based on the system PBC and vsite properties
440 * \param[in] pbcPtr A pointer to a PBC struct or nullptr when no PBC treatment is required
441 * \param[in] vsite A pointer to the vsite struct, can be nullptr
443 static PbcMode getPbcMode(const t_pbc *pbcPtr,
444 const gmx_vsite_t *vsite)
446 if (pbcPtr == nullptr)
448 return PbcMode::none;
450 else if (vsite != nullptr && vsite->bHaveChargeGroups)
452 return PbcMode::chargeGroup;
460 static void construct_vsites_thread(const gmx_vsite_t *vsite,
463 const t_iparams ip[], const t_ilist ilist[],
464 const t_pbc *pbc_null)
476 const PbcMode pbcMode = getPbcMode(pbc_null, vsite);
477 /* We need another pbc pointer, as with charge groups we switch per vsite */
478 const t_pbc *pbc_null2 = pbc_null;
479 const int *vsite_pbc = nullptr;
481 for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
483 if (ilist[ftype].nr == 0)
489 int nra = interaction_function[ftype].nratoms;
491 int nr = ilist[ftype].nr;
493 const t_iatom *ia = ilist[ftype].iatoms;
495 if (pbcMode == PbcMode::chargeGroup)
497 vsite_pbc = vsite->vsite_pbc_loc[ftype - c_ftypeVsiteStart];
500 for (int i = 0; i < nr; )
503 /* The vsite and constructing atoms */
506 /* Constants for constructing vsites */
507 real a1 = ip[tp].vsite.a;
508 /* Check what kind of pbc we need to use */
511 if (pbcMode == PbcMode::all)
513 /* No charge groups, vsite follows its own pbc */
515 copy_rvec(x[avsite], xpbc);
517 else if (pbcMode == PbcMode::chargeGroup)
519 pbc_atom = vsite_pbc[i/(1 + nra)];
524 /* We need to copy the coordinates here,
525 * single for single atom cg's pbc_atom
526 * is the vsite itself.
528 copy_rvec(x[pbc_atom], xpbc);
530 pbc_null2 = pbc_null;
541 /* Copy the old position */
543 copy_rvec(x[avsite], xv);
545 /* Construct the vsite depending on type */
552 constr_vsite2(x[ai], x[aj], x[avsite], a1, pbc_null2);
558 constr_vsite3(x[ai], x[aj], x[ak], x[avsite], a1, b1, pbc_null2);
564 constr_vsite3FD(x[ai], x[aj], x[ak], x[avsite], a1, b1, pbc_null2);
570 constr_vsite3FAD(x[ai], x[aj], x[ak], x[avsite], a1, b1, pbc_null2);
577 constr_vsite3OUT(x[ai], x[aj], x[ak], x[avsite], a1, b1, c1, pbc_null2);
585 constr_vsite4FD(x[ai], x[aj], x[ak], x[al], x[avsite], a1, b1, c1,
594 constr_vsite4FDN(x[ai], x[aj], x[ak], x[al], x[avsite], a1, b1, c1,
598 inc = constr_vsiten(ia, ip, x, pbc_null2);
601 gmx_fatal(FARGS, "No such vsite type %d in %s, line %d",
602 ftype, __FILE__, __LINE__);
607 /* Match the pbc of this vsite to the rest of its charge group */
609 int ishift = pbc_dx_aiuc(pbc_null, x[avsite], xpbc, dx);
610 if (ishift != CENTRAL)
612 rvec_add(xpbc, dx, x[avsite]);
617 /* Calculate velocity of vsite... */
619 rvec_sub(x[avsite], xv, vv);
620 svmul(inv_dt, vv, v[avsite]);
623 /* Increment loop variables */
631 void construct_vsites(const gmx_vsite_t *vsite,
634 const t_iparams ip[], const t_ilist ilist[],
635 int ePBC, gmx_bool bMolPBC,
639 const bool useDomdec = (vsite != nullptr && vsite->useDomdec);
640 GMX_ASSERT(!useDomdec || (cr != nullptr && DOMAINDECOMP(cr)), "When vsites are set up with domain decomposition, we need a valid commrec");
641 // TODO: Remove this assertion when we remove charge groups
642 GMX_ASSERT(vsite != nullptr || ePBC == epbcNONE, "Without a vsite struct we can not do PBC (in case we have charge groups)");
644 t_pbc pbc, *pbc_null;
646 /* We only need to do pbc when we have inter-cg vsites.
647 * Note that with domain decomposition we do not need to apply PBC here
648 * when we have at least 3 domains along each dimension. Currently we
649 * do not optimize this case.
651 if (ePBC != epbcNONE && (useDomdec || bMolPBC) &&
652 !(vsite != nullptr && vsite->n_intercg_vsite == 0))
654 /* This is wasting some CPU time as we now do this multiple times
658 clear_ivec(null_ivec);
659 pbc_null = set_pbc_dd(&pbc, ePBC,
660 useDomdec ? cr->dd->nc : null_ivec,
670 dd_move_x_vsites(cr->dd, box, x);
673 // cppcheck-suppress nullPointerRedundantCheck
674 if (vsite == nullptr || vsite->nthreads == 1)
676 construct_vsites_thread(vsite,
683 #pragma omp parallel num_threads(vsite->nthreads)
687 const int th = gmx_omp_get_thread_num();
688 const VsiteThread &tData = *vsite->tData[th];
689 GMX_ASSERT(tData.rangeStart >= 0, "The thread data should be initialized before calling construct_vsites");
691 construct_vsites_thread(vsite,
695 if (tData.useInterdependentTask)
697 /* Here we don't need a barrier (unlike the spreading),
698 * since both tasks only construct vsites from particles,
699 * or local vsites, not from non-local vsites.
701 construct_vsites_thread(vsite,
703 ip, tData.idTask.ilist,
707 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
709 /* Now we can construct the vsites that might depend on other vsites */
710 construct_vsites_thread(vsite,
712 ip, vsite->tData[vsite->nthreads]->ilist,
717 static void spread_vsite2(const t_iatom ia[], real a,
719 rvec f[], rvec fshift[],
720 const t_pbc *pbc, const t_graph *g)
731 svmul(1 - a, f[av], fi);
732 svmul( a, f[av], fj);
741 ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, av), di);
743 ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), di);
748 siv = pbc_dx_aiuc(pbc, x[ai], x[av], dx);
749 sij = pbc_dx_aiuc(pbc, x[ai], x[aj], dx);
757 if (fshift && (siv != CENTRAL || sij != CENTRAL))
759 rvec_inc(fshift[siv], f[av]);
760 rvec_dec(fshift[CENTRAL], fi);
761 rvec_dec(fshift[sij], fj);
764 /* TOTAL: 13 flops */
767 void constructVsitesGlobal(const gmx_mtop_t &mtop,
768 gmx::ArrayRef<gmx::RVec> x)
770 GMX_ASSERT(x.size() >= static_cast<size_t>(mtop.natoms), "x should contain the whole system");
771 GMX_ASSERT(!mtop.moleculeBlockIndices.empty(), "molblock indices are needed in constructVsitesGlobal");
773 for (size_t mb = 0; mb < mtop.molblock.size(); mb++)
775 const gmx_molblock_t &molb = mtop.molblock[mb];
776 const gmx_moltype_t &molt = mtop.moltype[molb.type];
777 if (vsiteIlistNrCount(molt.ilist) > 0)
779 int atomOffset = mtop.moleculeBlockIndices[mb].globalAtomStart;
780 for (int mol = 0; mol < molb.nmol; mol++)
782 construct_vsites(nullptr, as_rvec_array(x.data()) + atomOffset,
784 mtop.ffparams.iparams, molt.ilist,
785 epbcNONE, TRUE, nullptr, nullptr);
786 atomOffset += molt.atoms.nr;
792 static void spread_vsite3(const t_iatom ia[], real a, real b,
794 rvec f[], rvec fshift[],
795 const t_pbc *pbc, const t_graph *g)
807 svmul(1 - a - b, f[av], fi);
808 svmul( a, f[av], fj);
809 svmul( b, f[av], fk);
819 ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, ia[1]), di);
821 ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), di);
823 ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, ak), di);
828 siv = pbc_dx_aiuc(pbc, x[ai], x[av], dx);
829 sij = pbc_dx_aiuc(pbc, x[ai], x[aj], dx);
830 sik = pbc_dx_aiuc(pbc, x[ai], x[ak], dx);
839 if (fshift && (siv != CENTRAL || sij != CENTRAL || sik != CENTRAL))
841 rvec_inc(fshift[siv], f[av]);
842 rvec_dec(fshift[CENTRAL], fi);
843 rvec_dec(fshift[sij], fj);
844 rvec_dec(fshift[sik], fk);
847 /* TOTAL: 20 flops */
850 static void spread_vsite3FD(const t_iatom ia[], real a, real b,
852 rvec f[], rvec fshift[],
853 gmx_bool VirCorr, matrix dxdf,
854 const t_pbc *pbc, const t_graph *g)
856 real c, invl, fproj, a1;
857 rvec xvi, xij, xjk, xix, fv, temp;
858 t_iatom av, ai, aj, ak;
866 copy_rvec(f[av], fv);
868 sji = pbc_rvec_sub(pbc, x[aj], x[ai], xij);
869 skj = pbc_rvec_sub(pbc, x[ak], x[aj], xjk);
872 /* xix goes from i to point x on the line jk */
873 xix[XX] = xij[XX]+a*xjk[XX];
874 xix[YY] = xij[YY]+a*xjk[YY];
875 xix[ZZ] = xij[ZZ]+a*xjk[ZZ];
878 invl = gmx::invsqrt(iprod(xix, xix));
882 fproj = iprod(xix, fv)*invl*invl; /* = (xix . f)/(xix . xix) */
884 temp[XX] = c*(fv[XX]-fproj*xix[XX]);
885 temp[YY] = c*(fv[YY]-fproj*xix[YY]);
886 temp[ZZ] = c*(fv[ZZ]-fproj*xix[ZZ]);
889 /* c is already calculated in constr_vsite3FD
890 storing c somewhere will save 26 flops! */
893 f[ai][XX] += fv[XX] - temp[XX];
894 f[ai][YY] += fv[YY] - temp[YY];
895 f[ai][ZZ] += fv[ZZ] - temp[ZZ];
896 f[aj][XX] += a1*temp[XX];
897 f[aj][YY] += a1*temp[YY];
898 f[aj][ZZ] += a1*temp[ZZ];
899 f[ak][XX] += a*temp[XX];
900 f[ak][YY] += a*temp[YY];
901 f[ak][ZZ] += a*temp[ZZ];
906 ivec_sub(SHIFT_IVEC(g, ia[1]), SHIFT_IVEC(g, ai), di);
908 ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
910 ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, aj), di);
915 svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
922 if (fshift && (svi != CENTRAL || sji != CENTRAL || skj != CENTRAL))
924 rvec_dec(fshift[svi], fv);
925 fshift[CENTRAL][XX] += fv[XX] - (1 + a)*temp[XX];
926 fshift[CENTRAL][YY] += fv[YY] - (1 + a)*temp[YY];
927 fshift[CENTRAL][ZZ] += fv[ZZ] - (1 + a)*temp[ZZ];
928 fshift[ sji][XX] += temp[XX];
929 fshift[ sji][YY] += temp[YY];
930 fshift[ sji][ZZ] += temp[ZZ];
931 fshift[ skj][XX] += a*temp[XX];
932 fshift[ skj][YY] += a*temp[YY];
933 fshift[ skj][ZZ] += a*temp[ZZ];
938 /* When VirCorr=TRUE, the virial for the current forces is not
939 * calculated from the redistributed forces. This means that
940 * the effect of non-linear virtual site constructions on the virial
941 * needs to be added separately. This contribution can be calculated
942 * in many ways, but the simplest and cheapest way is to use
943 * the first constructing atom ai as a reference position in space:
944 * subtract (xv-xi)*fv and add (xj-xi)*fj + (xk-xi)*fk.
948 pbc_rvec_sub(pbc, x[av], x[ai], xiv);
950 for (int i = 0; i < DIM; i++)
952 for (int j = 0; j < DIM; j++)
954 /* As xix is a linear combination of j and k, use that here */
955 dxdf[i][j] += -xiv[i]*fv[j] + xix[i]*temp[j];
960 /* TOTAL: 61 flops */
963 static void spread_vsite3FAD(const t_iatom ia[], real a, real b,
965 rvec f[], rvec fshift[],
966 gmx_bool VirCorr, matrix dxdf,
967 const t_pbc *pbc, const t_graph *g)
969 rvec xvi, xij, xjk, xperp, Fpij, Fppp, fv, f1, f2, f3;
970 real a1, b1, c1, c2, invdij, invdij2, invdp, fproj;
971 t_iatom av, ai, aj, ak;
972 int svi, sji, skj, d;
979 copy_rvec(f[ia[1]], fv);
981 sji = pbc_rvec_sub(pbc, x[aj], x[ai], xij);
982 skj = pbc_rvec_sub(pbc, x[ak], x[aj], xjk);
985 invdij = gmx::invsqrt(iprod(xij, xij));
986 invdij2 = invdij * invdij;
987 c1 = iprod(xij, xjk) * invdij2;
988 xperp[XX] = xjk[XX] - c1*xij[XX];
989 xperp[YY] = xjk[YY] - c1*xij[YY];
990 xperp[ZZ] = xjk[ZZ] - c1*xij[ZZ];
991 /* xperp in plane ijk, perp. to ij */
992 invdp = gmx::invsqrt(iprod(xperp, xperp));
997 /* a1, b1 and c1 are already calculated in constr_vsite3FAD
998 storing them somewhere will save 45 flops! */
1000 fproj = iprod(xij, fv)*invdij2;
1001 svmul(fproj, xij, Fpij); /* proj. f on xij */
1002 svmul(iprod(xperp, fv)*invdp*invdp, xperp, Fppp); /* proj. f on xperp */
1003 svmul(b1*fproj, xperp, f3);
1006 rvec_sub(fv, Fpij, f1); /* f1 = f - Fpij */
1007 rvec_sub(f1, Fppp, f2); /* f2 = f - Fpij - Fppp */
1008 for (d = 0; (d < DIM); d++)
1016 f[ai][XX] += fv[XX] - f1[XX] + c1*f2[XX] + f3[XX];
1017 f[ai][YY] += fv[YY] - f1[YY] + c1*f2[YY] + f3[YY];
1018 f[ai][ZZ] += fv[ZZ] - f1[ZZ] + c1*f2[ZZ] + f3[ZZ];
1019 f[aj][XX] += f1[XX] - c2*f2[XX] - f3[XX];
1020 f[aj][YY] += f1[YY] - c2*f2[YY] - f3[YY];
1021 f[aj][ZZ] += f1[ZZ] - c2*f2[ZZ] - f3[ZZ];
1022 f[ak][XX] += f2[XX];
1023 f[ak][YY] += f2[YY];
1024 f[ak][ZZ] += f2[ZZ];
1029 ivec_sub(SHIFT_IVEC(g, ia[1]), SHIFT_IVEC(g, ai), di);
1031 ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
1033 ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, aj), di);
1038 svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
1045 if (fshift && (svi != CENTRAL || sji != CENTRAL || skj != CENTRAL))
1047 rvec_dec(fshift[svi], fv);
1048 fshift[CENTRAL][XX] += fv[XX] - f1[XX] - (1-c1)*f2[XX] + f3[XX];
1049 fshift[CENTRAL][YY] += fv[YY] - f1[YY] - (1-c1)*f2[YY] + f3[YY];
1050 fshift[CENTRAL][ZZ] += fv[ZZ] - f1[ZZ] - (1-c1)*f2[ZZ] + f3[ZZ];
1051 fshift[ sji][XX] += f1[XX] - c1 *f2[XX] - f3[XX];
1052 fshift[ sji][YY] += f1[YY] - c1 *f2[YY] - f3[YY];
1053 fshift[ sji][ZZ] += f1[ZZ] - c1 *f2[ZZ] - f3[ZZ];
1054 fshift[ skj][XX] += f2[XX];
1055 fshift[ skj][YY] += f2[YY];
1056 fshift[ skj][ZZ] += f2[ZZ];
1064 pbc_rvec_sub(pbc, x[av], x[ai], xiv);
1066 for (i = 0; i < DIM; i++)
1068 for (j = 0; j < DIM; j++)
1070 /* Note that xik=xij+xjk, so we have to add xij*f2 */
1073 + xij[i]*(f1[j] + (1 - c2)*f2[j] - f3[j])
1079 /* TOTAL: 113 flops */
1082 static void spread_vsite3OUT(const t_iatom ia[], real a, real b, real c,
1084 rvec f[], rvec fshift[],
1085 gmx_bool VirCorr, matrix dxdf,
1086 const t_pbc *pbc, const t_graph *g)
1088 rvec xvi, xij, xik, fv, fj, fk;
1099 sji = pbc_rvec_sub(pbc, x[aj], x[ai], xij);
1100 ski = pbc_rvec_sub(pbc, x[ak], x[ai], xik);
1103 copy_rvec(f[av], fv);
1110 fj[XX] = a*fv[XX] - xik[ZZ]*cfy + xik[YY]*cfz;
1111 fj[YY] = xik[ZZ]*cfx + a*fv[YY] - xik[XX]*cfz;
1112 fj[ZZ] = -xik[YY]*cfx + xik[XX]*cfy + a*fv[ZZ];
1114 fk[XX] = b*fv[XX] + xij[ZZ]*cfy - xij[YY]*cfz;
1115 fk[YY] = -xij[ZZ]*cfx + b*fv[YY] + xij[XX]*cfz;
1116 fk[ZZ] = xij[YY]*cfx - xij[XX]*cfy + b*fv[ZZ];
1119 f[ai][XX] += fv[XX] - fj[XX] - fk[XX];
1120 f[ai][YY] += fv[YY] - fj[YY] - fk[YY];
1121 f[ai][ZZ] += fv[ZZ] - fj[ZZ] - fk[ZZ];
1122 rvec_inc(f[aj], fj);
1123 rvec_inc(f[ak], fk);
1128 ivec_sub(SHIFT_IVEC(g, ia[1]), SHIFT_IVEC(g, ai), di);
1130 ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
1132 ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, ai), di);
1137 svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
1144 if (fshift && (svi != CENTRAL || sji != CENTRAL || ski != CENTRAL))
1146 rvec_dec(fshift[svi], fv);
1147 fshift[CENTRAL][XX] += fv[XX] - fj[XX] - fk[XX];
1148 fshift[CENTRAL][YY] += fv[YY] - fj[YY] - fk[YY];
1149 fshift[CENTRAL][ZZ] += fv[ZZ] - fj[ZZ] - fk[ZZ];
1150 rvec_inc(fshift[sji], fj);
1151 rvec_inc(fshift[ski], fk);
1158 pbc_rvec_sub(pbc, x[av], x[ai], xiv);
1160 for (int i = 0; i < DIM; i++)
1162 for (int j = 0; j < DIM; j++)
1164 dxdf[i][j] += -xiv[i]*fv[j] + xij[i]*fj[j] + xik[i]*fk[j];
1169 /* TOTAL: 54 flops */
1172 static void spread_vsite4FD(const t_iatom ia[], real a, real b, real c,
1174 rvec f[], rvec fshift[],
1175 gmx_bool VirCorr, matrix dxdf,
1176 const t_pbc *pbc, const t_graph *g)
1178 real d, invl, fproj, a1;
1179 rvec xvi, xij, xjk, xjl, xix, fv, temp;
1180 int av, ai, aj, ak, al;
1182 int svi, sji, skj, slj, m;
1190 sji = pbc_rvec_sub(pbc, x[aj], x[ai], xij);
1191 skj = pbc_rvec_sub(pbc, x[ak], x[aj], xjk);
1192 slj = pbc_rvec_sub(pbc, x[al], x[aj], xjl);
1195 /* xix goes from i to point x on the plane jkl */
1196 for (m = 0; m < DIM; m++)
1198 xix[m] = xij[m] + a*xjk[m] + b*xjl[m];
1202 invl = gmx::invsqrt(iprod(xix, xix));
1204 /* 4 + ?10? flops */
1206 copy_rvec(f[av], fv);
1208 fproj = iprod(xix, fv)*invl*invl; /* = (xix . f)/(xix . xix) */
1210 for (m = 0; m < DIM; m++)
1212 temp[m] = d*(fv[m] - fproj*xix[m]);
1216 /* c is already calculated in constr_vsite3FD
1217 storing c somewhere will save 35 flops! */
1220 for (m = 0; m < DIM; m++)
1222 f[ai][m] += fv[m] - temp[m];
1223 f[aj][m] += a1*temp[m];
1224 f[ak][m] += a*temp[m];
1225 f[al][m] += b*temp[m];
1231 ivec_sub(SHIFT_IVEC(g, ia[1]), SHIFT_IVEC(g, ai), di);
1233 ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
1235 ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, aj), di);
1237 ivec_sub(SHIFT_IVEC(g, al), SHIFT_IVEC(g, aj), di);
1242 svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
1250 (svi != CENTRAL || sji != CENTRAL || skj != CENTRAL || slj != CENTRAL))
1252 rvec_dec(fshift[svi], fv);
1253 for (m = 0; m < DIM; m++)
1255 fshift[CENTRAL][m] += fv[m] - (1 + a + b)*temp[m];
1256 fshift[ sji][m] += temp[m];
1257 fshift[ skj][m] += a*temp[m];
1258 fshift[ slj][m] += b*temp[m];
1267 pbc_rvec_sub(pbc, x[av], x[ai], xiv);
1269 for (i = 0; i < DIM; i++)
1271 for (j = 0; j < DIM; j++)
1273 dxdf[i][j] += -xiv[i]*fv[j] + xix[i]*temp[j];
1278 /* TOTAL: 77 flops */
1282 static void spread_vsite4FDN(const t_iatom ia[], real a, real b, real c,
1284 rvec f[], rvec fshift[],
1285 gmx_bool VirCorr, matrix dxdf,
1286 const t_pbc *pbc, const t_graph *g)
1288 rvec xvi, xij, xik, xil, ra, rb, rja, rjb, rab, rm, rt;
1289 rvec fv, fj, fk, fl;
1293 int av, ai, aj, ak, al;
1294 int svi, sij, sik, sil;
1296 /* DEBUG: check atom indices */
1303 copy_rvec(f[av], fv);
1305 sij = pbc_rvec_sub(pbc, x[aj], x[ai], xij);
1306 sik = pbc_rvec_sub(pbc, x[ak], x[ai], xik);
1307 sil = pbc_rvec_sub(pbc, x[al], x[ai], xil);
1320 rvec_sub(ra, xij, rja);
1321 rvec_sub(rb, xij, rjb);
1322 rvec_sub(rb, ra, rab);
1325 cprod(rja, rjb, rm);
1328 invrm = gmx::invsqrt(norm2(rm));
1329 denom = invrm*invrm;
1332 cfx = c*invrm*fv[XX];
1333 cfy = c*invrm*fv[YY];
1334 cfz = c*invrm*fv[ZZ];
1345 fj[XX] = ( -rm[XX]*rt[XX]) * cfx + ( rab[ZZ]-rm[YY]*rt[XX]) * cfy + (-rab[YY]-rm[ZZ]*rt[XX]) * cfz;
1346 fj[YY] = (-rab[ZZ]-rm[XX]*rt[YY]) * cfx + ( -rm[YY]*rt[YY]) * cfy + ( rab[XX]-rm[ZZ]*rt[YY]) * cfz;
1347 fj[ZZ] = ( rab[YY]-rm[XX]*rt[ZZ]) * cfx + (-rab[XX]-rm[YY]*rt[ZZ]) * cfy + ( -rm[ZZ]*rt[ZZ]) * cfz;
1358 fk[XX] = ( -rm[XX]*rt[XX]) * cfx + (-a*rjb[ZZ]-rm[YY]*rt[XX]) * cfy + ( a*rjb[YY]-rm[ZZ]*rt[XX]) * cfz;
1359 fk[YY] = ( a*rjb[ZZ]-rm[XX]*rt[YY]) * cfx + ( -rm[YY]*rt[YY]) * cfy + (-a*rjb[XX]-rm[ZZ]*rt[YY]) * cfz;
1360 fk[ZZ] = (-a*rjb[YY]-rm[XX]*rt[ZZ]) * cfx + ( a*rjb[XX]-rm[YY]*rt[ZZ]) * cfy + ( -rm[ZZ]*rt[ZZ]) * cfz;
1371 fl[XX] = ( -rm[XX]*rt[XX]) * cfx + ( b*rja[ZZ]-rm[YY]*rt[XX]) * cfy + (-b*rja[YY]-rm[ZZ]*rt[XX]) * cfz;
1372 fl[YY] = (-b*rja[ZZ]-rm[XX]*rt[YY]) * cfx + ( -rm[YY]*rt[YY]) * cfy + ( b*rja[XX]-rm[ZZ]*rt[YY]) * cfz;
1373 fl[ZZ] = ( b*rja[YY]-rm[XX]*rt[ZZ]) * cfx + (-b*rja[XX]-rm[YY]*rt[ZZ]) * cfy + ( -rm[ZZ]*rt[ZZ]) * cfz;
1376 f[ai][XX] += fv[XX] - fj[XX] - fk[XX] - fl[XX];
1377 f[ai][YY] += fv[YY] - fj[YY] - fk[YY] - fl[YY];
1378 f[ai][ZZ] += fv[ZZ] - fj[ZZ] - fk[ZZ] - fl[ZZ];
1379 rvec_inc(f[aj], fj);
1380 rvec_inc(f[ak], fk);
1381 rvec_inc(f[al], fl);
1386 ivec_sub(SHIFT_IVEC(g, av), SHIFT_IVEC(g, ai), di);
1388 ivec_sub(SHIFT_IVEC(g, aj), SHIFT_IVEC(g, ai), di);
1390 ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, ai), di);
1392 ivec_sub(SHIFT_IVEC(g, al), SHIFT_IVEC(g, ai), di);
1397 svi = pbc_rvec_sub(pbc, x[av], x[ai], xvi);
1404 if (fshift && (svi != CENTRAL || sij != CENTRAL || sik != CENTRAL || sil != CENTRAL))
1406 rvec_dec(fshift[svi], fv);
1407 fshift[CENTRAL][XX] += fv[XX] - fj[XX] - fk[XX] - fl[XX];
1408 fshift[CENTRAL][YY] += fv[YY] - fj[YY] - fk[YY] - fl[YY];
1409 fshift[CENTRAL][ZZ] += fv[ZZ] - fj[ZZ] - fk[ZZ] - fl[ZZ];
1410 rvec_inc(fshift[sij], fj);
1411 rvec_inc(fshift[sik], fk);
1412 rvec_inc(fshift[sil], fl);
1420 pbc_rvec_sub(pbc, x[av], x[ai], xiv);
1422 for (i = 0; i < DIM; i++)
1424 for (j = 0; j < DIM; j++)
1426 dxdf[i][j] += -xiv[i]*fv[j] + xij[i]*fj[j] + xik[i]*fk[j] + xil[i]*fl[j];
1431 /* Total: 207 flops (Yuck!) */
1435 static int spread_vsiten(const t_iatom ia[], const t_iparams ip[],
1437 rvec f[], rvec fshift[],
1438 const t_pbc *pbc, const t_graph *g)
1446 n3 = 3*ip[ia[0]].vsiten.n;
1448 copy_rvec(x[av], xv);
1450 for (i = 0; i < n3; i += 3)
1455 ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, av), di);
1460 siv = pbc_dx_aiuc(pbc, x[ai], xv, dx);
1466 a = ip[ia[i]].vsiten.a;
1467 svmul(a, f[av], fi);
1468 rvec_inc(f[ai], fi);
1469 if (fshift && siv != CENTRAL)
1471 rvec_inc(fshift[siv], fi);
1472 rvec_dec(fshift[CENTRAL], fi);
1481 static int vsite_count(const t_ilist *ilist, int ftype)
1483 if (ftype == F_VSITEN)
1485 return ilist[ftype].nr/3;
1489 return ilist[ftype].nr/(1 + interaction_function[ftype].nratoms);
1493 static void spread_vsite_f_thread(const gmx_vsite_t *vsite,
1495 rvec f[], rvec *fshift,
1496 gmx_bool VirCorr, matrix dxdf,
1497 t_iparams ip[], const t_ilist ilist[],
1498 const t_graph *g, const t_pbc *pbc_null)
1500 const PbcMode pbcMode = getPbcMode(pbc_null, vsite);
1501 /* We need another pbc pointer, as with charge groups we switch per vsite */
1502 const t_pbc *pbc_null2 = pbc_null;
1503 const int *vsite_pbc = nullptr;
1505 /* this loop goes backwards to be able to build *
1506 * higher type vsites from lower types */
1507 for (int ftype = c_ftypeVsiteEnd - 1; ftype >= c_ftypeVsiteStart; ftype--)
1509 if (ilist[ftype].nr == 0)
1515 int nra = interaction_function[ftype].nratoms;
1517 int nr = ilist[ftype].nr;
1519 const t_iatom *ia = ilist[ftype].iatoms;
1521 if (pbcMode == PbcMode::all)
1523 pbc_null2 = pbc_null;
1525 else if (pbcMode == PbcMode::chargeGroup)
1527 vsite_pbc = vsite->vsite_pbc_loc[ftype - c_ftypeVsiteStart];
1530 for (int i = 0; i < nr; )
1532 if (vsite_pbc != nullptr)
1534 if (vsite_pbc[i/(1 + nra)] > -2)
1536 pbc_null2 = pbc_null;
1540 pbc_null2 = nullptr;
1546 /* Constants for constructing */
1548 a1 = ip[tp].vsite.a;
1549 /* Construct the vsite depending on type */
1553 spread_vsite2(ia, a1, x, f, fshift, pbc_null2, g);
1556 b1 = ip[tp].vsite.b;
1557 spread_vsite3(ia, a1, b1, x, f, fshift, pbc_null2, g);
1560 b1 = ip[tp].vsite.b;
1561 spread_vsite3FD(ia, a1, b1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
1564 b1 = ip[tp].vsite.b;
1565 spread_vsite3FAD(ia, a1, b1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
1568 b1 = ip[tp].vsite.b;
1569 c1 = ip[tp].vsite.c;
1570 spread_vsite3OUT(ia, a1, b1, c1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
1573 b1 = ip[tp].vsite.b;
1574 c1 = ip[tp].vsite.c;
1575 spread_vsite4FD(ia, a1, b1, c1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
1578 b1 = ip[tp].vsite.b;
1579 c1 = ip[tp].vsite.c;
1580 spread_vsite4FDN(ia, a1, b1, c1, x, f, fshift, VirCorr, dxdf, pbc_null2, g);
1583 inc = spread_vsiten(ia, ip, x, f, fshift, pbc_null2, g);
1586 gmx_fatal(FARGS, "No such vsite type %d in %s, line %d",
1587 ftype, __FILE__, __LINE__);
1589 clear_rvec(f[ia[1]]);
1591 /* Increment loop variables */
1599 /*! \brief Clears the task force buffer elements that are written by task idTask */
1600 static void clearTaskForceBufferUsedElements(InterdependentTask *idTask)
1602 int ntask = idTask->spreadTask.size();
1603 for (int ti = 0; ti < ntask; ti++)
1605 const AtomIndex *atomList = &idTask->atomIndex[idTask->spreadTask[ti]];
1606 int natom = atomList->atom.size();
1607 RVec *force = idTask->force.data();
1608 for (int i = 0; i < natom; i++)
1610 clear_rvec(force[atomList->atom[i]]);
1615 void spread_vsite_f(const gmx_vsite_t *vsite,
1616 const rvec * gmx_restrict x,
1617 rvec * gmx_restrict f, rvec * gmx_restrict fshift,
1618 gmx_bool VirCorr, matrix vir,
1619 t_nrnb *nrnb, const t_idef *idef,
1620 int ePBC, gmx_bool bMolPBC, const t_graph *g, const matrix box,
1621 const t_commrec *cr, gmx_wallcycle *wcycle)
1623 wallcycle_start(wcycle, ewcVSITESPREAD);
1624 const bool useDomdec = vsite->useDomdec;
1625 GMX_ASSERT(!useDomdec || (cr != nullptr && DOMAINDECOMP(cr)), "When vsites are set up with domain decomposition, we need a valid commrec");
1627 t_pbc pbc, *pbc_null;
1629 /* We only need to do pbc when we have inter-cg vsites */
1630 if ((useDomdec || bMolPBC) && vsite->n_intercg_vsite)
1632 /* This is wasting some CPU time as we now do this multiple times
1635 pbc_null = set_pbc_dd(&pbc, ePBC, useDomdec ? cr->dd->nc : nullptr, FALSE, box);
1644 dd_clear_f_vsites(cr->dd, f);
1647 if (vsite->nthreads == 1)
1654 spread_vsite_f_thread(vsite,
1657 idef->iparams, idef->il,
1662 for (int i = 0; i < DIM; i++)
1664 for (int j = 0; j < DIM; j++)
1666 vir[i][j] += -0.5*dxdf[i][j];
1673 /* First spread the vsites that might depend on non-local vsites */
1676 clear_mat(vsite->tData[vsite->nthreads]->dxdf);
1678 spread_vsite_f_thread(vsite,
1680 VirCorr, vsite->tData[vsite->nthreads]->dxdf,
1682 vsite->tData[vsite->nthreads]->ilist,
1685 #pragma omp parallel num_threads(vsite->nthreads)
1689 int thread = gmx_omp_get_thread_num();
1690 VsiteThread *tData = vsite->tData[thread];
1693 if (thread == 0 || fshift == nullptr)
1699 fshift_t = tData->fshift;
1701 for (int i = 0; i < SHIFTS; i++)
1703 clear_rvec(fshift_t[i]);
1708 clear_mat(tData->dxdf);
1711 if (tData->useInterdependentTask)
1713 /* Spread the vsites that spread outside our local range.
1714 * This is done using a thread-local force buffer force.
1715 * First we need to copy the input vsite forces to force.
1717 InterdependentTask *idTask = &tData->idTask;
1719 /* Clear the buffer elements set by our task during
1720 * the last call to spread_vsite_f.
1722 clearTaskForceBufferUsedElements(idTask);
1724 int nvsite = idTask->vsite.size();
1725 for (int i = 0; i < nvsite; i++)
1727 copy_rvec(f[idTask->vsite[i]],
1728 idTask->force[idTask->vsite[i]]);
1730 spread_vsite_f_thread(vsite,
1731 x, as_rvec_array(idTask->force.data()), fshift_t,
1732 VirCorr, tData->dxdf,
1734 tData->idTask.ilist,
1737 /* We need a barrier before reducing forces below
1738 * that have been produced by a different thread above.
1742 /* Loop over all thread task and reduce forces they
1743 * produced on atoms that fall in our range.
1744 * Note that atomic reduction would be a simpler solution,
1745 * but that might not have good support on all platforms.
1747 int ntask = idTask->reduceTask.size();
1748 for (int ti = 0; ti < ntask; ti++)
1750 const InterdependentTask *idt_foreign = &vsite->tData[idTask->reduceTask[ti]]->idTask;
1751 const AtomIndex *atomList = &idt_foreign->atomIndex[thread];
1752 const RVec *f_foreign = idt_foreign->force.data();
1754 int natom = atomList->atom.size();
1755 for (int i = 0; i < natom; i++)
1757 int ind = atomList->atom[i];
1758 rvec_inc(f[ind], f_foreign[ind]);
1759 /* Clearing of f_foreign is done at the next step */
1762 /* Clear the vsite forces, both in f and force */
1763 for (int i = 0; i < nvsite; i++)
1765 int ind = tData->idTask.vsite[i];
1767 clear_rvec(tData->idTask.force[ind]);
1771 /* Spread the vsites that spread locally only */
1772 spread_vsite_f_thread(vsite,
1774 VirCorr, tData->dxdf,
1779 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
1782 if (fshift != nullptr)
1784 for (int th = 1; th < vsite->nthreads; th++)
1786 for (int i = 0; i < SHIFTS; i++)
1788 rvec_inc(fshift[i], vsite->tData[th]->fshift[i]);
1795 for (int th = 0; th < vsite->nthreads + 1; th++)
1797 /* MSVC doesn't like matrix references, so we use a pointer */
1798 const matrix *dxdf = &vsite->tData[th]->dxdf;
1800 for (int i = 0; i < DIM; i++)
1802 for (int j = 0; j < DIM; j++)
1804 vir[i][j] += -0.5*(*dxdf)[i][j];
1813 dd_move_f_vsites(cr->dd, f, fshift);
1816 inc_nrnb(nrnb, eNR_VSITE2, vsite_count(idef->il, F_VSITE2));
1817 inc_nrnb(nrnb, eNR_VSITE3, vsite_count(idef->il, F_VSITE3));
1818 inc_nrnb(nrnb, eNR_VSITE3FD, vsite_count(idef->il, F_VSITE3FD));
1819 inc_nrnb(nrnb, eNR_VSITE3FAD, vsite_count(idef->il, F_VSITE3FAD));
1820 inc_nrnb(nrnb, eNR_VSITE3OUT, vsite_count(idef->il, F_VSITE3OUT));
1821 inc_nrnb(nrnb, eNR_VSITE4FD, vsite_count(idef->il, F_VSITE4FD));
1822 inc_nrnb(nrnb, eNR_VSITE4FDN, vsite_count(idef->il, F_VSITE4FDN));
1823 inc_nrnb(nrnb, eNR_VSITEN, vsite_count(idef->il, F_VSITEN));
1825 wallcycle_stop(wcycle, ewcVSITESPREAD);
1828 /*! \brief Returns the an array with charge-group indices for each atom
1830 * \param[in] chargeGroups The charge group block struct
1832 static std::vector<int> atom2cg(const t_block &chargeGroups)
1834 std::vector<int> a2cg(chargeGroups.index[chargeGroups.nr], 0);
1836 for (int chargeGroup = 0; chargeGroup < chargeGroups.nr; chargeGroup++)
1838 std::fill(a2cg.begin() + chargeGroups.index[chargeGroup],
1839 a2cg.begin() + chargeGroups.index[chargeGroup + 1],
1846 int count_intercg_vsites(const gmx_mtop_t *mtop)
1848 int n_intercg_vsite = 0;
1849 for (const gmx_molblock_t &molb : mtop->molblock)
1851 const gmx_moltype_t &molt = mtop->moltype[molb.type];
1853 std::vector<int> a2cg = atom2cg(molt.cgs);
1854 for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
1856 int nral = NRAL(ftype);
1857 const t_ilist &il = molt.ilist[ftype];
1858 const t_iatom *ia = il.iatoms;
1859 for (int i = 0; i < il.nr; i += 1 + nral)
1861 int cg = a2cg[ia[1+i]];
1862 for (int a = 1; a < nral; a++)
1864 if (a2cg[ia[1+a]] != cg)
1866 n_intercg_vsite += molb.nmol;
1874 return n_intercg_vsite;
1877 static int **get_vsite_pbc(const t_iparams *iparams, const t_ilist *ilist,
1878 const t_atom *atom, const t_mdatoms *md,
1881 /* Make an atom to charge group index */
1882 std::vector<int> a2cg = atom2cg(cgs);
1884 /* Make an array that tells if the pbc of an atom is set */
1885 std::vector<bool> pbc_set(cgs.index[cgs.nr], false);
1886 /* PBC is set for all non vsites */
1887 for (int a = 0; a < cgs.index[cgs.nr]; a++)
1889 if ((atom && atom[a].ptype != eptVSite) ||
1890 (md && md->ptype[a] != eptVSite))
1897 snew(vsite_pbc, F_VSITEN-F_VSITE2+1);
1899 for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
1902 int nral = NRAL(ftype);
1903 const t_ilist *il = &ilist[ftype];
1904 const t_iatom *ia = il->iatoms;
1907 snew(vsite_pbc[ftype-F_VSITE2], il->nr/(1 + nral));
1908 vsite_pbc_f = vsite_pbc[ftype-F_VSITE2];
1913 int vsi = i/(1 + nral);
1914 t_iatom vsite = ia[i+1];
1915 int cg_v = a2cg[vsite];
1916 /* A value of -2 signals that this vsite and its contructing
1917 * atoms are all within the same cg, so no pbc is required.
1919 vsite_pbc_f[vsi] = -2;
1920 /* Check if constructing atoms are outside the vsite's cg */
1922 if (ftype == F_VSITEN)
1924 nc3 = 3*iparams[ia[i]].vsiten.n;
1925 for (int j = 0; j < nc3; j += 3)
1927 if (a2cg[ia[i+j+2]] != cg_v)
1929 vsite_pbc_f[vsi] = -1;
1935 for (int a = 1; a < nral; a++)
1937 if (a2cg[ia[i+1+a]] != cg_v)
1939 vsite_pbc_f[vsi] = -1;
1943 if (vsite_pbc_f[vsi] == -1)
1945 /* Check if this is the first processed atom of a vsite only cg */
1946 gmx_bool bViteOnlyCG_and_FirstAtom = TRUE;
1947 for (int a = cgs.index[cg_v]; a < cgs.index[cg_v + 1]; a++)
1949 /* Non-vsites already have pbc set, so simply check for pbc_set */
1952 bViteOnlyCG_and_FirstAtom = FALSE;
1956 if (bViteOnlyCG_and_FirstAtom)
1958 /* First processed atom of a vsite only charge group.
1959 * The pbc of the input coordinates to construct_vsites
1960 * should be preserved.
1962 vsite_pbc_f[vsi] = vsite;
1964 else if (cg_v != a2cg[ia[1+i+1]])
1966 /* This vsite has a different charge group index
1967 * than it's first constructing atom
1968 * and the charge group has more than one atom,
1969 * search for the first normal particle
1970 * or vsite that already had its pbc defined.
1971 * If nothing is found, use full pbc for this vsite.
1973 for (int a = cgs.index[cg_v]; a < cgs.index[cg_v + 1]; a++)
1975 if (a != vsite && pbc_set[a])
1977 vsite_pbc_f[vsi] = a;
1980 fprintf(debug, "vsite %d match pbc with atom %d\n",
1988 fprintf(debug, "vsite atom %d cg %d - %d pbc atom %d\n",
1989 vsite+1, cgs.index[cg_v] + 1, cgs.index[cg_v + 1],
1990 vsite_pbc_f[vsi] + 1);
1994 if (ftype == F_VSITEN)
1996 /* The other entries in vsite_pbc_f are not used for center vsites */
2004 /* This vsite now has its pbc defined */
2005 pbc_set[vsite] = true;
2014 gmx_vsite_t *initVsite(const gmx_mtop_t &mtop,
2015 const t_commrec *cr)
2017 GMX_RELEASE_ASSERT(cr != nullptr, "We need a valid commrec");
2019 /* check if there are vsites */
2021 for (int ftype = 0; ftype < F_NRE; ftype++)
2023 if (interaction_function[ftype].flags & IF_VSITE)
2025 GMX_ASSERT(ftype >= c_ftypeVsiteStart && ftype < c_ftypeVsiteEnd, "c_ftypeVsiteStart and/or c_ftypeVsiteEnd do not have correct values");
2027 nvsite += gmx_mtop_ftype_count(&mtop, ftype);
2031 GMX_ASSERT(ftype < c_ftypeVsiteStart || ftype >= c_ftypeVsiteEnd, "c_ftypeVsiteStart and/or c_ftypeVsiteEnd do not have correct values");
2040 gmx_vsite_t *vsite = new(gmx_vsite_t);
2042 vsite->n_intercg_vsite = count_intercg_vsites(&mtop);
2044 vsite->bHaveChargeGroups = (ncg_mtop(&mtop) < mtop.natoms);
2046 vsite->useDomdec = (DOMAINDECOMP(cr) && cr->dd->nnodes > 1);
2048 /* If we don't have charge groups, the vsite follows its own pbc.
2050 * With charge groups, each vsite needs to follow the pbc of the charge
2051 * group. Thus we need to keep track of PBC. Here we assume that without
2052 * domain decomposition all molecules are whole (which will not be
2053 * the case with periodic molecules).
2055 if (vsite->bHaveChargeGroups &&
2056 vsite->n_intercg_vsite > 0 &&
2059 vsite->nvsite_pbc_molt = mtop.moltype.size();
2060 snew(vsite->vsite_pbc_molt, vsite->nvsite_pbc_molt);
2061 for (size_t mt = 0; mt < mtop.moltype.size(); mt++)
2063 const gmx_moltype_t &molt = mtop.moltype[mt];
2064 vsite->vsite_pbc_molt[mt] = get_vsite_pbc(mtop.ffparams.iparams,
2066 molt.atoms.atom, nullptr,
2070 snew(vsite->vsite_pbc_loc_nalloc, c_ftypeVsiteEnd - c_ftypeVsiteStart);
2071 snew(vsite->vsite_pbc_loc, c_ftypeVsiteEnd - c_ftypeVsiteStart);
2075 vsite->vsite_pbc_molt = nullptr;
2076 vsite->vsite_pbc_loc = nullptr;
2079 vsite->nthreads = gmx_omp_nthreads_get(emntVSITE);
2081 if (vsite->nthreads > 1)
2083 /* We need one extra thread data structure for the overlap vsites */
2084 snew(vsite->tData, vsite->nthreads + 1);
2085 #pragma omp parallel for num_threads(vsite->nthreads) schedule(static)
2086 for (int thread = 0; thread < vsite->nthreads; thread++)
2090 vsite->tData[thread] = new VsiteThread;
2092 InterdependentTask *idTask = &vsite->tData[thread]->idTask;
2094 idTask->atomIndex.resize(vsite->nthreads);
2096 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
2098 if (vsite->nthreads > 1)
2100 vsite->tData[vsite->nthreads] = new VsiteThread;
2104 vsite->taskIndex = nullptr;
2105 vsite->taskIndexNalloc = 0;
2110 static inline void flagAtom(InterdependentTask *idTask, int atom,
2111 int thread, int nthread, int natperthread)
2113 if (!idTask->use[atom])
2115 idTask->use[atom] = true;
2116 thread = atom/natperthread;
2117 /* Assign all non-local atom force writes to thread 0 */
2118 if (thread >= nthread)
2122 idTask->atomIndex[thread].atom.push_back(atom);
2126 /*\brief Here we try to assign all vsites that are in our local range.
2128 * Our task local atom range is tData->rangeStart - tData->rangeEnd.
2129 * Vsites that depend only on local atoms, as indicated by taskIndex[]==thread,
2130 * are assigned to task tData->ilist. Vsites that depend on non-local atoms
2131 * but not on other vsites are assigned to task tData->id_task.ilist.
2132 * taskIndex[] is set for all vsites in our range, either to our local tasks
2133 * or to the single last task as taskIndex[]=2*nthreads.
2135 static void assignVsitesToThread(VsiteThread *tData,
2140 const t_ilist *ilist,
2141 const t_iparams *ip,
2142 const unsigned short *ptype)
2144 for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
2146 tData->ilist[ftype].nr = 0;
2147 tData->idTask.ilist[ftype].nr = 0;
2149 int nral1 = 1 + NRAL(ftype);
2151 t_iatom *iat = ilist[ftype].iatoms;
2152 for (int i = 0; i < ilist[ftype].nr; )
2154 if (ftype == F_VSITEN)
2156 /* The 3 below is from 1+NRAL(ftype)=3 */
2157 inc = ip[iat[i]].vsiten.n*3;
2160 if (iat[1 + i] < tData->rangeStart ||
2161 iat[1 + i] >= tData->rangeEnd)
2163 /* This vsite belongs to a different thread */
2168 /* We would like to assign this vsite to task thread,
2169 * but it might depend on atoms outside the atom range of thread
2170 * or on another vsite not assigned to task thread.
2173 if (ftype != F_VSITEN)
2175 for (int j = i + 2; j < i + nral1; j++)
2177 /* Do a range check to avoid a harmless race on taskIndex */
2178 if (iat[j] < tData->rangeStart ||
2179 iat[j] >= tData->rangeEnd ||
2180 taskIndex[iat[j]] != thread)
2182 if (!tData->useInterdependentTask ||
2183 ptype[iat[j]] == eptVSite)
2185 /* At least one constructing atom is a vsite
2186 * that is not assigned to the same thread.
2187 * Put this vsite into a separate task.
2193 /* There are constructing atoms outside our range,
2194 * put this vsite into a second task to be executed
2195 * on the same thread. During construction no barrier
2196 * is needed between the two tasks on the same thread.
2197 * During spreading we need to run this task with
2198 * an additional thread-local intermediate force buffer
2199 * (or atomic reduction) and a barrier between the two
2202 task = nthread + thread;
2208 for (int j = i + 2; j < i + inc; j += 3)
2210 /* Do a range check to avoid a harmless race on taskIndex */
2211 if (iat[j] < tData->rangeStart ||
2212 iat[j] >= tData->rangeEnd ||
2213 taskIndex[iat[j]] != thread)
2215 GMX_ASSERT(ptype[iat[j]] != eptVSite, "A vsite to be assigned in assignVsitesToThread has a vsite as a constructing atom that does not belong to our task, such vsites should be assigned to the single 'master' task");
2217 task = nthread + thread;
2222 /* Update this vsite's thread index entry */
2223 taskIndex[iat[1+i]] = task;
2225 if (task == thread || task == nthread + thread)
2227 /* Copy this vsite to the thread data struct of thread */
2231 il_task = &tData->ilist[ftype];
2235 il_task = &tData->idTask.ilist[ftype];
2237 /* Ensure we have sufficient memory allocated */
2238 if (il_task->nr + inc > il_task->nalloc)
2240 il_task->nalloc = over_alloc_large(il_task->nr + inc);
2241 srenew(il_task->iatoms, il_task->nalloc);
2243 /* Copy the vsite data to the thread-task local array */
2244 for (int j = i; j < i + inc; j++)
2246 il_task->iatoms[il_task->nr++] = iat[j];
2248 if (task == nthread + thread)
2250 /* This vsite write outside our own task force block.
2251 * Put it into the interdependent task list and flag
2252 * the atoms involved for reduction.
2254 tData->idTask.vsite.push_back(iat[i + 1]);
2255 if (ftype != F_VSITEN)
2257 for (int j = i + 2; j < i + nral1; j++)
2259 flagAtom(&tData->idTask, iat[j],
2260 thread, nthread, natperthread);
2265 for (int j = i + 2; j < i + inc; j += 3)
2267 flagAtom(&tData->idTask, iat[j],
2268 thread, nthread, natperthread);
2279 /*! \brief Assign all vsites with taskIndex[]==task to task tData */
2280 static void assignVsitesToSingleTask(VsiteThread *tData,
2282 const int *taskIndex,
2283 const t_ilist *ilist,
2284 const t_iparams *ip)
2286 for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
2288 tData->ilist[ftype].nr = 0;
2289 tData->idTask.ilist[ftype].nr = 0;
2291 int nral1 = 1 + NRAL(ftype);
2293 t_iatom *iat = ilist[ftype].iatoms;
2294 t_ilist *il_task = &tData->ilist[ftype];
2296 for (int i = 0; i < ilist[ftype].nr; )
2298 if (ftype == F_VSITEN)
2300 /* The 3 below is from 1+NRAL(ftype)=3 */
2301 inc = ip[iat[i]].vsiten.n*3;
2303 /* Check if the vsite is assigned to our task */
2304 if (taskIndex[iat[1 + i]] == task)
2306 /* Ensure we have sufficient memory allocated */
2307 if (il_task->nr + inc > il_task->nalloc)
2309 il_task->nalloc = over_alloc_large(il_task->nr + inc);
2310 srenew(il_task->iatoms, il_task->nalloc);
2312 /* Copy the vsite data to the thread-task local array */
2313 for (int j = i; j < i + inc; j++)
2315 il_task->iatoms[il_task->nr++] = iat[j];
2324 void split_vsites_over_threads(const t_ilist *ilist,
2325 const t_iparams *ip,
2326 const t_mdatoms *mdatoms,
2329 int vsite_atom_range, natperthread;
2331 if (vsite->nthreads == 1)
2337 /* The current way of distributing the vsites over threads in primitive.
2338 * We divide the atom range 0 - natoms_in_vsite uniformly over threads,
2339 * without taking into account how the vsites are distributed.
2340 * Without domain decomposition we at least tighten the upper bound
2341 * of the range (useful for common systems such as a vsite-protein
2343 * With domain decomposition, as long as the vsites are distributed
2344 * uniformly in each domain along the major dimension, usually x,
2345 * it will also perform well.
2347 if (!vsite->useDomdec)
2349 vsite_atom_range = -1;
2350 for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
2353 if (ftype != F_VSITEN)
2355 int nral1 = 1 + NRAL(ftype);
2356 const t_iatom *iat = ilist[ftype].iatoms;
2357 for (int i = 0; i < ilist[ftype].nr; i += nral1)
2359 for (int j = i + 1; j < i + nral1; j++)
2361 vsite_atom_range = std::max(vsite_atom_range, iat[j]);
2369 const t_iatom *iat = ilist[ftype].iatoms;
2372 while (i < ilist[ftype].nr)
2374 /* The 3 below is from 1+NRAL(ftype)=3 */
2375 vs_ind_end = i + ip[iat[i]].vsiten.n*3;
2377 vsite_atom_range = std::max(vsite_atom_range, iat[i+1]);
2378 while (i < vs_ind_end)
2380 vsite_atom_range = std::max(vsite_atom_range, iat[i+2]);
2388 natperthread = (vsite_atom_range + vsite->nthreads - 1)/vsite->nthreads;
2392 /* Any local or not local atom could be involved in virtual sites.
2393 * But since we usually have very few non-local virtual sites
2394 * (only non-local vsites that depend on local vsites),
2395 * we distribute the local atom range equally over the threads.
2396 * When assigning vsites to threads, we should take care that the last
2397 * threads also covers the non-local range.
2399 vsite_atom_range = mdatoms->nr;
2400 natperthread = (mdatoms->homenr + vsite->nthreads - 1)/vsite->nthreads;
2405 fprintf(debug, "virtual site thread dist: natoms %d, range %d, natperthread %d\n", mdatoms->nr, vsite_atom_range, natperthread);
2408 /* To simplify the vsite assignment, we make an index which tells us
2409 * to which task particles, both non-vsites and vsites, are assigned.
2411 if (mdatoms->nr > vsite->taskIndexNalloc)
2413 vsite->taskIndexNalloc = over_alloc_large(mdatoms->nr);
2414 srenew(vsite->taskIndex, vsite->taskIndexNalloc);
2417 /* Initialize the task index array. Here we assign the non-vsite
2418 * particles to task=thread, so we easily figure out if vsites
2419 * depend on local and/or non-local particles in assignVsitesToThread.
2421 int *taskIndex = vsite->taskIndex;
2424 for (int i = 0; i < mdatoms->nr; i++)
2426 if (mdatoms->ptype[i] == eptVSite)
2428 /* vsites are not assigned to a task yet */
2433 /* assign non-vsite particles to task thread */
2434 taskIndex[i] = thread;
2436 if (i == (thread + 1)*natperthread && thread < vsite->nthreads)
2443 #pragma omp parallel num_threads(vsite->nthreads)
2447 int thread = gmx_omp_get_thread_num();
2448 VsiteThread *tData = vsite->tData[thread];
2450 /* Clear the buffer use flags that were set before */
2451 if (tData->useInterdependentTask)
2453 InterdependentTask *idTask = &tData->idTask;
2455 /* To avoid an extra OpenMP barrier in spread_vsite_f,
2456 * we clear the force buffer at the next step,
2457 * so we need to do it here as well.
2459 clearTaskForceBufferUsedElements(idTask);
2461 idTask->vsite.resize(0);
2462 for (int t = 0; t < vsite->nthreads; t++)
2464 AtomIndex *atomIndex = &idTask->atomIndex[t];
2465 int natom = atomIndex->atom.size();
2466 for (int i = 0; i < natom; i++)
2468 idTask->use[atomIndex->atom[i]] = false;
2470 atomIndex->atom.resize(0);
2475 /* To avoid large f_buf allocations of #threads*vsite_atom_range
2476 * we don't use task2 with more than 200000 atoms. This doesn't
2477 * affect performance, since with such a large range relatively few
2478 * vsites will end up in the separate task.
2479 * Note that useTask2 should be the same for all threads.
2481 tData->useInterdependentTask = (vsite_atom_range <= 200000);
2482 if (tData->useInterdependentTask)
2484 size_t natoms_use_in_vsites = vsite_atom_range;
2485 InterdependentTask *idTask = &tData->idTask;
2486 /* To avoid resizing and re-clearing every nstlist steps,
2487 * we never down size the force buffer.
2489 if (natoms_use_in_vsites > idTask->force.size() ||
2490 natoms_use_in_vsites > idTask->use.size())
2492 idTask->force.resize(natoms_use_in_vsites, { 0, 0, 0 });
2493 idTask->use.resize(natoms_use_in_vsites, false);
2497 /* Assign all vsites that can execute independently on threads */
2498 tData->rangeStart = thread *natperthread;
2499 if (thread < vsite->nthreads - 1)
2501 tData->rangeEnd = (thread + 1)*natperthread;
2505 /* The last thread should cover up to the end of the range */
2506 tData->rangeEnd = mdatoms->nr;
2508 assignVsitesToThread(tData,
2509 thread, vsite->nthreads,
2512 ilist, ip, mdatoms->ptype);
2514 if (tData->useInterdependentTask)
2516 /* In the worst case, all tasks write to force ranges of
2517 * all other tasks, leading to #tasks^2 scaling (this is only
2518 * the overhead, the actual flops remain constant).
2519 * But in most cases there is far less coupling. To improve
2520 * scaling at high thread counts we therefore construct
2521 * an index to only loop over the actually affected tasks.
2523 InterdependentTask *idTask = &tData->idTask;
2525 /* Ensure assignVsitesToThread finished on other threads */
2528 idTask->spreadTask.resize(0);
2529 idTask->reduceTask.resize(0);
2530 for (int t = 0; t < vsite->nthreads; t++)
2532 /* Do we write to the force buffer of task t? */
2533 if (idTask->atomIndex[t].atom.size() > 0)
2535 idTask->spreadTask.push_back(t);
2537 /* Does task t write to our force buffer? */
2538 if (vsite->tData[t]->idTask.atomIndex[thread].atom.size() > 0)
2540 idTask->reduceTask.push_back(t);
2545 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
2547 /* Assign all remaining vsites, that will have taskIndex[]=2*vsite->nthreads,
2548 * to a single task that will not run in parallel with other tasks.
2550 assignVsitesToSingleTask(vsite->tData[vsite->nthreads],
2555 if (debug && vsite->nthreads > 1)
2557 fprintf(debug, "virtual site useInterdependentTask %d, nuse:\n",
2558 vsite->tData[0]->useInterdependentTask);
2559 for (int th = 0; th < vsite->nthreads + 1; th++)
2561 fprintf(debug, " %4d", vsite->tData[th]->idTask.nuse);
2563 fprintf(debug, "\n");
2565 for (int ftype = c_ftypeVsiteStart; ftype < c_ftypeVsiteEnd; ftype++)
2567 if (ilist[ftype].nr > 0)
2569 fprintf(debug, "%-20s thread dist:",
2570 interaction_function[ftype].longname);
2571 for (int th = 0; th < vsite->nthreads + 1; th++)
2573 fprintf(debug, " %4d %4d ",
2574 vsite->tData[th]->ilist[ftype].nr,
2575 vsite->tData[th]->idTask.ilist[ftype].nr);
2577 fprintf(debug, "\n");
2583 int nrOrig = vsiteIlistNrCount(ilist);
2585 for (int th = 0; th < vsite->nthreads + 1; th++)
2588 vsiteIlistNrCount(vsite->tData[th]->ilist) +
2589 vsiteIlistNrCount(vsite->tData[th]->idTask.ilist);
2591 GMX_ASSERT(nrThreaded == nrOrig, "The number of virtual sites assigned to all thread task has to match the total number of virtual sites");
2595 void set_vsite_top(gmx_vsite_t *vsite,
2596 const gmx_localtop_t *top,
2597 const t_mdatoms *md)
2599 if (vsite->n_intercg_vsite > 0 && vsite->bHaveChargeGroups)
2601 vsite->vsite_pbc_loc = get_vsite_pbc(top->idef.iparams,
2602 top->idef.il, nullptr, md,
2606 if (vsite->nthreads > 1)
2608 if (vsite->bHaveChargeGroups)
2610 gmx_fatal(FARGS, "The combination of threading, virtual sites and charge groups is not implemented");
2613 split_vsites_over_threads(top->idef.il, top->idef.iparams,