2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
5 * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
38 * \brief This file defines functions for "pair" interactions
39 * (i.e. listed non-bonded interactions, e.g. 1-4 interactions)
41 * \author Mark Abraham <mark.j.abraham@gmail.com>
43 * \ingroup module_listed_forces
51 #include "gromacs/listed_forces/bonded.h"
52 #include "gromacs/math/functions.h"
53 #include "gromacs/math/vec.h"
54 #include "gromacs/mdtypes/forcerec.h"
55 #include "gromacs/mdtypes/group.h"
56 #include "gromacs/mdtypes/interaction_const.h"
57 #include "gromacs/mdtypes/md_enums.h"
58 #include "gromacs/mdtypes/nblist.h"
59 #include "gromacs/mdtypes/simulation_workload.h"
60 #include "gromacs/pbcutil/ishift.h"
61 #include "gromacs/pbcutil/pbc.h"
62 #include "gromacs/pbcutil/pbc_simd.h"
63 #include "gromacs/simd/simd.h"
64 #include "gromacs/simd/simd_math.h"
65 #include "gromacs/simd/vector_operations.h"
66 #include "gromacs/tables/forcetable.h"
67 #include "gromacs/utility/basedefinitions.h"
68 #include "gromacs/utility/fatalerror.h"
69 #include "gromacs/utility/gmxassert.h"
71 #include "listed_internal.h"
73 using namespace gmx; // TODO: Remove when this file is moved into gmx namespace
75 /*! \brief Issue a warning if a listed interaction is beyond a table limit */
76 static void warning_rlimit(const rvec* x, int ai, int aj, int* global_atom_index, real r, real rlimit)
79 "Listed nonbonded interaction between particles %d and %d\n"
80 "at distance %.3f which is larger than the table limit %.3f nm.\n\n"
81 "This is likely either a 1,4 interaction, or a listed interaction inside\n"
82 "a smaller molecule you are decoupling during a free energy calculation.\n"
83 "Since interactions at distances beyond the table cannot be computed,\n"
84 "they are skipped until they are inside the table limit again. You will\n"
85 "only see this message once, even if it occurs for several interactions.\n\n"
86 "IMPORTANT: This should not happen in a stable simulation, so there is\n"
87 "probably something wrong with your system. Only change the table-extension\n"
88 "distance in the mdp file if you are really sure that is the reason.\n",
89 glatnr(global_atom_index, ai),
90 glatnr(global_atom_index, aj),
97 "%8f %8f %8f\n%8f %8f %8f\n1-4 (%d,%d) interaction not within cut-off! r=%g. "
105 glatnr(global_atom_index, ai),
106 glatnr(global_atom_index, aj),
111 /*! \brief Compute the energy and force for a single pair interaction */
112 static real evaluate_single(real r2,
122 real rinv, r, rtab, eps, eps2, Y, F, Geps, Heps2, Fp, VVe, FFe, VVd, FFd, VVr, FFr, fscal;
125 /* Do the tabulated interactions - first table lookup */
126 rinv = gmx::invsqrt(r2);
129 ntab = static_cast<int>(rtab);
132 ntab = static_cast<int>(tableStride * ntab);
136 Geps = eps * vftab[ntab + 2];
137 Heps2 = eps2 * vftab[ntab + 3];
138 Fp = F + Geps + Heps2;
140 FFe = Fp + Geps + 2.0 * Heps2;
144 Geps = eps * vftab[ntab + 6];
145 Heps2 = eps2 * vftab[ntab + 7];
146 Fp = F + Geps + Heps2;
148 FFd = Fp + Geps + 2.0 * Heps2;
152 Geps = eps * vftab[ntab + 10];
153 Heps2 = eps2 * vftab[ntab + 11];
154 Fp = F + Geps + Heps2;
156 FFr = Fp + Geps + 2.0 * Heps2;
159 *vvdw = c6 * VVd + c12 * VVr;
161 fscal = -(qq * FFe + c6 * FFd + c12 * FFr) * tabscale * rinv;
166 static inline real sixthRoot(const real r)
168 return gmx::invsqrt(std::cbrt(r));
171 /*! \brief Compute the energy and force for a single pair interaction under FEP */
172 template<KernelSoftcoreType softcoreType>
173 static real free_energy_evaluate_single(real r2,
175 const interaction_const_t::SoftCoreParameters& scParams,
189 const real lfac_coul[2],
190 const real lfac_vdw[2],
191 const real dlfac_coul[2],
192 const real dlfac_vdw[2],
197 real rtab, eps, eps2, Y, F, Geps, Heps2, Fp, VV, FF, fscal;
198 real qq[2], c6[2], c12[2], sigma6[2], sigma_pow[2];
199 real alpha_coul_eff, alpha_vdw_eff, dvdl_coul_sum, dvdl_vdw_sum;
200 real rpinv, r_coul, r_vdw, velecsum, vvdwsum;
201 real fscal_vdw[2], fscal_elec[2];
202 real velec[2], vvdw[2];
203 real dvdl_elec[2], dvdl_vdw[2];
204 real gapsysScaleLinpointCoul, gapsysScaleLinpointVdW, gapsysSigma6VdW[2];
208 const real half = 0.5_real;
209 const real one = 1.0_real;
210 const real two = 2.0_real;
219 const real rpm2 = r2 * r2; /* r4 */
220 const real rp = rpm2 * r2; /* r6 */
221 const real r = sqrt(r2); /* r1 */
223 /* Loop over state A(0) and B(1) */
224 for (i = 0; i < 2; i++)
226 if constexpr (softcoreType == KernelSoftcoreType::Beutler)
228 if ((c6[i] > 0) && (c12[i] > 0))
230 /* The c6 & c12 coefficients now contain the constants 6.0 and 12.0, respectively.
231 * Correct for this by multiplying with (1/12.0)/(1/6.0)=6.0/12.0=0.5.
233 sigma6[i] = half * c12[i] / c6[i];
234 if (sigma6[i] < scParams.sigma6Minimum) /* for disappearing coul and vdw with soft core at the same time */
236 sigma6[i] = scParams.sigma6Minimum;
241 sigma6[i] = scParams.sigma6WithInvalidSigma;
243 sigma_pow[i] = sigma6[i];
245 // NOLINTNEXTLINE(readability-misleading-indentation) remove when clang-tidy-13 is required
246 if constexpr (softcoreType == KernelSoftcoreType::Gapsys)
248 if ((c6[i] > 0) && (c12[i] > 0))
250 /* The c6 & c12 coefficients now contain the constants 6.0 and 12.0, respectively.
251 * Correct for this by multiplying with (1/12.0)/(1/6.0)=6.0/12.0=0.5.
253 gapsysSigma6VdW[i] = half * c12[i] / c6[i];
257 gapsysSigma6VdW[i] = scParams.gapsysSigma6VdW;
262 // NOLINTNEXTLINE(readability-misleading-indentation) remove when clang-tidy-13 is required
263 if constexpr (softcoreType == KernelSoftcoreType::Beutler)
265 /* only use softcore if one of the states has a zero endstate - softcore is for avoiding infinities!*/
266 if ((c12[0] > 0) && (c12[1] > 0))
273 alpha_vdw_eff = scParams.alphaVdw;
274 alpha_coul_eff = scParams.alphaCoulomb;
277 // NOLINTNEXTLINE(readability-misleading-indentation) remove when clang-tidy-13 is required
278 if constexpr (softcoreType == KernelSoftcoreType::Gapsys)
280 /* only use softcore if one of the states has a zero endstate - softcore is for avoiding infinities!*/
281 if ((c12[0] > 0) && (c12[1] > 0))
283 gapsysScaleLinpointVdW = 0;
284 gapsysScaleLinpointCoul = 0;
288 gapsysScaleLinpointVdW = scParams.gapsysScaleLinpointVdW;
289 gapsysScaleLinpointCoul = scParams.gapsysScaleLinpointCoul;
293 /* Loop over A and B states again */
294 // NOLINTNEXTLINE(readability-misleading-indentation) remove when clang-tidy-13 is required
295 for (i = 0; i < 2; i++)
306 /* Only spend time on A or B state if it is non-zero */
307 if ((qq[i] != 0) || (c6[i] != 0) || (c12[i] != 0))
310 if constexpr (softcoreType == KernelSoftcoreType::Beutler)
312 rpinv = one / (alpha_coul_eff * lfac_coul[i] * sigma_pow[i] + rp);
313 r_coul = sixthRoot(rpinv);
321 // NOLINTNEXTLINE(readability-misleading-indentation) remove when clang-tidy-13 is required
322 if constexpr (softcoreType == KernelSoftcoreType::Gapsys)
324 if ((facel != 0) && (LFC[i] < 1))
326 rQ = gmx::sixthroot(one - LFC[i]) * (one + std::fabs(qq[i] / facel));
327 rQ *= gapsysScaleLinpointCoul;
334 if (rQ > rCoulCutoff)
341 // NOLINTNEXTLINE(readability-misleading-indentation) remove when clang-tidy-13 is required
342 if ((softcoreType == KernelSoftcoreType::Gapsys) && (r < rQ))
344 real rInvQ = one / rQ;
345 real constFac = qq[i] * rInvQ;
346 real linFac = constFac * r * rInvQ;
347 real quadrFac = linFac * r * rInvQ;
349 /* Computing Coulomb force and potential energy */
350 fscal_elec[i] = -2 * quadrFac + 3 * linFac;
351 fscal_elec[i] *= rpinv;
353 velec[i] = quadrFac - 3 * (linFac - constFac);
355 dvdl_elec[i] += scaleDvdlRCoul * DLF[i] * half * (LFC[i] / (1 - LFC[i]))
356 * (quadrFac - 2 * linFac + constFac);
358 else // Beutler, resp. hardcore
360 /* Electrostatics table lookup data */
361 rtab = r_coul * tabscale;
362 ntab = static_cast<int>(rtab);
365 ntab = static_cast<int>(tableStride * ntab);
369 Geps = eps * vftab[ntab + 2];
370 Heps2 = eps2 * vftab[ntab + 3];
371 Fp = F + Geps + Heps2;
373 FF = Fp + Geps + two * Heps2;
374 velec[i] = qq[i] * VV;
375 fscal_elec[i] = -qq[i] * FF * r_coul * rpinv * tabscale;
379 if constexpr (softcoreType == KernelSoftcoreType::Beutler)
381 rpinv = one / (alpha_vdw_eff * lfac_vdw[i] * sigma_pow[i] + rp);
382 r_vdw = sixthRoot(rpinv);
390 if constexpr (softcoreType == KernelSoftcoreType::Gapsys)
392 constexpr real c_twentySixSeventh = 26.0_real / 7.0_real;
397 rLJ = gmx::sixthroot(c_twentySixSeventh * gapsysSigma6VdW[i] * (one - LFV[i]));
398 rLJ *= gapsysScaleLinpointVdW;
406 // NOLINTNEXTLINE(readability-misleading-indentation) remove when clang-tidy-13 is required
407 if ((softcoreType == KernelSoftcoreType::Gapsys) && (r < rLJ))
409 // scaled values for c6 and c12
411 c6s = c6[i] / 6.0_real;
412 c12s = c12[i] / 12.0_real;
414 /* Temporary variables for inverted values */
415 real rInvLJ = one / rLJ;
416 real rInv14, rInv13, rInv12;
417 real rInv8, rInv7, rInv6;
418 rInv6 = rInvLJ * rInvLJ * rInvLJ;
420 rInv7 = rInv6 * rInvLJ;
421 rInv8 = rInv7 * rInvLJ;
422 rInv14 = c12s * rInv7 * rInv7 * r2;
423 rInv13 = c12s * rInv7 * rInv6 * r;
424 rInv12 = c12s * rInv6 * rInv6;
429 /* Temporary variables for A and B */
430 real quadrFac, linearFac, constFac;
431 quadrFac = 156 * rInv14 - 42 * rInv8;
432 linearFac = 168 * rInv13 - 48 * rInv7;
433 constFac = 91 * rInv12 - 28 * rInv6;
435 /* Computing LJ force and potential energy*/
436 fscal_vdw[i] = -quadrFac + linearFac;
437 fscal_vdw[i] *= rpinv;
439 vvdw[i] = 0.5_real * quadrFac - linearFac + constFac;
441 dvdl_vdw[i] += DLF[i] * 28 * (LFV[i] / (one - LFV[i]))
442 * ((6.5_real * rInv14 - rInv8) - (13 * rInv13 - 2 * rInv7)
443 + (6.5_real * rInv12 - rInv6));
445 else // Beutler, resp. hardcore
447 /* Vdw table lookup data */
448 rtab = r_vdw * tabscale;
449 ntab = static_cast<int>(rtab);
456 Geps = eps * vftab[ntab + 6];
457 Heps2 = eps2 * vftab[ntab + 7];
458 Fp = F + Geps + Heps2;
460 FF = Fp + Geps + two * Heps2;
461 vvdw[i] = c6[i] * VV;
462 fscal_vdw[i] = -c6[i] * FF;
467 Geps = eps * vftab[ntab + 10];
468 Heps2 = eps2 * vftab[ntab + 11];
469 Fp = F + Geps + Heps2;
471 FF = Fp + Geps + two * Heps2;
472 vvdw[i] += c12[i] * VV;
473 fscal_vdw[i] -= c12[i] * FF;
474 fscal_vdw[i] *= r_vdw * rpinv * tabscale;
478 /* Now we have velec[i], vvdw[i], and fscal[i] for both states */
479 /* Assemble A and B states */
485 for (i = 0; i < 2; i++)
487 velecsum += LFC[i] * velec[i];
488 vvdwsum += LFV[i] * vvdw[i];
490 fscal += (LFC[i] * fscal_elec[i] + LFV[i] * fscal_vdw[i]) * rpm2;
492 if constexpr (softcoreType == KernelSoftcoreType::Gapsys)
494 dvdl_coul_sum += dvdl_elec[i];
495 dvdl_vdw_sum += dvdl_vdw[i];
497 // NOLINTNEXTLINE(readability-misleading-indentation) remove when clang-tidy-13 is required
498 dvdl_coul_sum += velec[i] * DLF[i];
499 dvdl_vdw_sum += vvdw[i] * DLF[i];
500 if constexpr (softcoreType == KernelSoftcoreType::Beutler)
502 dvdl_coul_sum += LFC[i] * alpha_coul_eff * dlfac_coul[i] * fscal_elec[i] * sigma_pow[i];
503 dvdl_vdw_sum += LFV[i] * alpha_vdw_eff * dlfac_vdw[i] * fscal_vdw[i] * sigma_pow[i];
507 dvdl[static_cast<int>(FreeEnergyPerturbationCouplingType::Coul)] += dvdl_coul_sum;
508 dvdl[static_cast<int>(FreeEnergyPerturbationCouplingType::Vdw)] += dvdl_vdw_sum;
510 *velectot = velecsum;
516 /*! \brief Calculate pair interactions, supports all types and conditions. */
517 template<BondedKernelFlavor flavor>
518 static real do_pairs_general(int ftype,
520 const t_iatom iatoms[],
521 const t_iparams iparams[],
525 const struct t_pbc* pbc,
528 gmx::ArrayRef<real> chargeA,
529 gmx::ArrayRef<real> chargeB,
530 gmx::ArrayRef<bool> atomIsPerturbed,
531 gmx::ArrayRef<unsigned short> cENER,
533 const t_forcerec* fr,
534 gmx_grppairener_t* grppener,
535 int* global_atom_index)
539 int i, itype, ai, aj, gid;
542 real fscal, velec, vvdw;
543 real* energygrp_elec;
545 static gmx_bool warned_rlimit = FALSE;
546 /* Free energy stuff */
547 gmx_bool bFreeEnergy;
548 real LFC[2], LFV[2], DLF[2], lfac_coul[2], lfac_vdw[2], dlfac_coul[2], dlfac_vdw[2];
550 const real oneSixth = 1.0_real / 6.0_real;
556 energygrp_elec = grppener->energyGroupPairTerms[NonBondedEnergyTerms::Coulomb14].data();
557 energygrp_vdw = grppener->energyGroupPairTerms[NonBondedEnergyTerms::LJ14].data();
560 energygrp_elec = grppener->energyGroupPairTerms[NonBondedEnergyTerms::CoulombSR].data();
561 energygrp_vdw = grppener->energyGroupPairTerms[NonBondedEnergyTerms::LJSR].data();
564 energygrp_elec = nullptr; /* Keep compiler happy */
565 energygrp_vdw = nullptr; /* Keep compiler happy */
566 gmx_fatal(FARGS, "Unknown function type %d in do_nonbonded14", ftype);
569 if (fr->efep != FreeEnergyPerturbationType::No)
571 /* Lambda factor for state A=1-lambda and B=lambda */
572 LFC[0] = 1.0 - lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Coul)];
573 LFV[0] = 1.0 - lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Vdw)];
574 LFC[1] = lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Coul)];
575 LFV[1] = lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Vdw)];
577 /*derivative of the lambda factor for state A and B */
581 GMX_ASSERT(fr->ic->softCoreParameters, "We need soft-core parameters");
582 const auto& scParams = *fr->ic->softCoreParameters;
584 for (i = 0; i < 2; i++)
586 lfac_coul[i] = (scParams.lambdaPower == 2 ? (1 - LFC[i]) * (1 - LFC[i]) : (1 - LFC[i]));
587 dlfac_coul[i] = DLF[i] * scParams.lambdaPower * oneSixth
588 * (scParams.lambdaPower == 2 ? (1 - LFC[i]) : 1);
589 lfac_vdw[i] = (scParams.lambdaPower == 2 ? (1 - LFV[i]) * (1 - LFV[i]) : (1 - LFV[i]));
590 dlfac_vdw[i] = DLF[i] * scParams.lambdaPower * oneSixth
591 * (scParams.lambdaPower == 2 ? (1 - LFV[i]) : 1);
595 /* TODO This code depends on the logic in tables.c that constructs
596 the table layout, which should be made explicit in future
600 "Pair-interaction code that uses GROMACS interaction tables supports exactly 3 tables");
602 fr->pairsTable->interaction == TableInteraction::ElectrostaticVdwRepulsionVdwDispersion,
603 "Pair interaction kernels need a table with Coulomb, repulsion and dispersion entries");
605 const real epsfac = fr->ic->epsfac;
608 for (i = 0; (i < nbonds);)
613 gid = GID(cENER[ai], cENER[aj], numEnergyGroups);
620 (fr->efep != FreeEnergyPerturbationType::No
621 && ((!atomIsPerturbed.empty() && (atomIsPerturbed[ai] || atomIsPerturbed[aj]))
622 || iparams[itype].lj14.c6A != iparams[itype].lj14.c6B
623 || iparams[itype].lj14.c12A != iparams[itype].lj14.c12B));
624 qq = chargeA[ai] * chargeA[aj] * epsfac * fr->fudgeQQ;
625 c6 = iparams[itype].lj14.c6A;
626 c12 = iparams[itype].lj14.c12A;
629 qq = iparams[itype].ljc14.qi * iparams[itype].ljc14.qj * epsfac
630 * iparams[itype].ljc14.fqq;
631 c6 = iparams[itype].ljc14.c6;
632 c12 = iparams[itype].ljc14.c12;
635 qq = iparams[itype].ljcnb.qi * iparams[itype].ljcnb.qj * epsfac;
636 c6 = iparams[itype].ljcnb.c6;
637 c12 = iparams[itype].ljcnb.c12;
640 /* Cannot happen since we called gmx_fatal() above in this case */
641 qq = c6 = c12 = 0; /* Keep compiler happy */
645 /* To save flops in the optimized kernels, c6/c12 have 6.0/12.0 derivative prefactors
646 * included in the general nfbp array now. This means the tables are scaled down by the
647 * same factor, so when we use the original c6/c12 parameters from iparams[] they must
653 /* Do we need to apply full periodic boundary conditions? */
656 fshift_index = pbc_dx_aiuc(pbc, x[ai], x[aj], dx);
660 fshift_index = c_centralShiftIndex;
661 rvec_sub(x[ai], x[aj], dx);
665 if (r2 >= fr->pairsTable->interactionRange * fr->pairsTable->interactionRange)
667 /* This check isn't race free. But it doesn't matter because if a race occurs the only
668 * disadvantage is that the warning is printed twice */
671 warning_rlimit(x, ai, aj, global_atom_index, sqrt(r2), fr->pairsTable->interactionRange);
672 warned_rlimit = TRUE;
679 /* Currently free energy is only supported for F_LJ14, so no need to check for that if we got here */
680 qqB = chargeB[ai] * chargeB[aj] * epsfac * fr->fudgeQQ;
681 c6B = iparams[itype].lj14.c6B * 6.0;
682 c12B = iparams[itype].lj14.c12B * 12.0;
684 const auto& scParams = *fr->ic->softCoreParameters;
685 if (scParams.softcoreType == SoftcoreType::Beutler)
687 if (scParams.alphaCoulomb == 0 && scParams.alphaVdw == 0)
689 fscal = free_energy_evaluate_single<KernelSoftcoreType::None>(
692 *fr->ic->softCoreParameters,
693 fr->pairsTable->scale,
694 fr->pairsTable->data.data(),
695 fr->pairsTable->stride,
716 fscal = free_energy_evaluate_single<KernelSoftcoreType::Beutler>(
719 *fr->ic->softCoreParameters,
720 fr->pairsTable->scale,
721 fr->pairsTable->data.data(),
722 fr->pairsTable->stride,
744 if (scParams.gapsysScaleLinpointCoul == 0 && scParams.gapsysScaleLinpointVdW == 0)
746 fscal = free_energy_evaluate_single<KernelSoftcoreType::None>(
749 *fr->ic->softCoreParameters,
750 fr->pairsTable->scale,
751 fr->pairsTable->data.data(),
752 fr->pairsTable->stride,
773 fscal = free_energy_evaluate_single<KernelSoftcoreType::Gapsys>(
776 *fr->ic->softCoreParameters,
777 fr->pairsTable->scale,
778 fr->pairsTable->data.data(),
779 fr->pairsTable->stride,
802 /* Evaluate tabulated interaction without free energy */
803 fscal = evaluate_single(r2,
804 fr->pairsTable->scale,
805 fr->pairsTable->data.data(),
806 fr->pairsTable->stride,
814 energygrp_elec[gid] += velec;
815 energygrp_vdw[gid] += vvdw;
816 svmul(fscal, dx, dx);
822 if (computeVirial(flavor))
824 if (fshift_index != c_centralShiftIndex)
826 rvec_inc(fshift[fshift_index], dx);
827 rvec_dec(fshift[c_centralShiftIndex], dx);
834 /*! \brief Calculate pairs, only for plain-LJ + plain Coulomb normal type.
836 * This function is templated for real/SimdReal and for optimization.
838 template<typename T, int pack_size, typename pbc_type>
839 static void do_pairs_simple(int nbonds,
840 const t_iatom iatoms[],
841 const t_iparams iparams[],
845 gmx::ArrayRef<real> charge,
846 const real scale_factor)
848 const int nfa1 = 1 + 2;
854 #if GMX_SIMD_HAVE_REAL
855 alignas(GMX_SIMD_ALIGNMENT) std::int32_t ai[pack_size];
856 alignas(GMX_SIMD_ALIGNMENT) std::int32_t aj[pack_size];
857 alignas(GMX_SIMD_ALIGNMENT) real coeff[3 * pack_size];
859 std::int32_t ai[pack_size];
860 std::int32_t aj[pack_size];
861 real coeff[3 * pack_size];
864 /* nbonds is #pairs*nfa1, here we step pack_size pairs */
865 for (int i = 0; i < nbonds; i += pack_size * nfa1)
867 /* Collect atoms for pack_size pairs.
868 * iu indexes into iatoms, we should not let iu go beyond nbonds.
871 for (int s = 0; s < pack_size; s++)
873 int itype = iatoms[iu];
874 ai[s] = iatoms[iu + 1];
875 aj[s] = iatoms[iu + 2];
877 if (i + s * nfa1 < nbonds)
879 coeff[0 * pack_size + s] = iparams[itype].lj14.c6A;
880 coeff[1 * pack_size + s] = iparams[itype].lj14.c12A;
881 coeff[2 * pack_size + s] = charge[ai[s]] * charge[aj[s]];
883 /* Avoid indexing the iatoms array out of bounds.
884 * We pad the coordinate indices with the last atom pair.
886 if (iu + nfa1 < nbonds)
893 /* Pad the coefficient arrays with zeros to get zero forces */
894 coeff[0 * pack_size + s] = 0;
895 coeff[1 * pack_size + s] = 0;
896 coeff[2 * pack_size + s] = 0;
900 /* Load the coordinates */
902 gatherLoadUTranspose<3>(reinterpret_cast<const real*>(x), ai, &xi[XX], &xi[YY], &xi[ZZ]);
903 gatherLoadUTranspose<3>(reinterpret_cast<const real*>(x), aj, &xj[XX], &xj[YY], &xj[ZZ]);
905 T c6 = load<T>(coeff + 0 * pack_size);
906 T c12 = load<T>(coeff + 1 * pack_size);
907 T qq = load<T>(coeff + 2 * pack_size);
909 /* We could save these operations by storing 6*C6,12*C12 */
914 pbc_dx_aiuc(pbc, xi, xj, dr);
916 T rsq = dr[XX] * dr[XX] + dr[YY] * dr[YY] + dr[ZZ] * dr[ZZ];
917 T rinv = gmx::invsqrt(rsq);
918 T rinv2 = rinv * rinv;
919 T rinv6 = rinv2 * rinv2 * rinv2;
921 /* Calculate the Coulomb force * r */
922 T cfr = ef * qq * rinv;
924 /* Calculate the LJ force * r and add it to the Coulomb part */
925 T fr = gmx::fma(fms(c12, rinv6, c6), rinv6, cfr);
927 T finvr = fr * rinv2;
928 T fx = finvr * dr[XX];
929 T fy = finvr * dr[YY];
930 T fz = finvr * dr[ZZ];
932 /* Add the pair forces to the force array.
933 * Note that here we might add multiple force components for some atoms
934 * due to the SIMD padding. But the extra force components are zero.
936 transposeScatterIncrU<4>(reinterpret_cast<real*>(f), ai, fx, fy, fz);
937 transposeScatterDecrU<4>(reinterpret_cast<real*>(f), aj, fx, fy, fz);
941 /*! \brief Calculate all listed pair interactions */
942 void do_pairs(int ftype,
944 const t_iatom iatoms[],
945 const t_iparams iparams[],
949 const struct t_pbc* pbc,
952 gmx::ArrayRef<real> chargeA,
953 gmx::ArrayRef<real> chargeB,
954 gmx::ArrayRef<bool> atomIsPerturbed,
955 gmx::ArrayRef<unsigned short> cENER,
956 const int numEnergyGroups,
957 const t_forcerec* fr,
958 const bool havePerturbedInteractions,
959 const gmx::StepWorkload& stepWork,
960 gmx_grppairener_t* grppener,
961 int* global_atom_index)
963 if (ftype == F_LJ14 && fr->ic->vdwtype != VanDerWaalsType::User && !EEL_USER(fr->ic->eeltype)
964 && !havePerturbedInteractions && (!stepWork.computeVirial && !stepWork.computeEnergy))
966 /* We use a fast code-path for plain LJ 1-4 without FEP.
968 * TODO: Add support for energies (straightforward) and virial
969 * in the SIMD template. For the virial it's inconvenient to store
970 * the force sums for the shifts and we should directly calculate
971 * and sum the virial for the shifts. But we should do this
972 * at once for the angles and dihedrals as well.
974 #if GMX_SIMD_HAVE_REAL
975 if (fr->use_simd_kernels)
977 alignas(GMX_SIMD_ALIGNMENT) real pbc_simd[9 * GMX_SIMD_REAL_WIDTH];
978 set_pbc_simd(pbc, pbc_simd);
980 do_pairs_simple<SimdReal, GMX_SIMD_REAL_WIDTH, const real*>(
981 nbonds, iatoms, iparams, x, f, pbc_simd, chargeA, fr->ic->epsfac * fr->fudgeQQ);
986 /* This construct is needed because pbc_dx_aiuc doesn't accept pbc=NULL */
988 const t_pbc* pbc_nonnull;
996 set_pbc(&pbc_no, PbcType::No, nullptr);
997 pbc_nonnull = &pbc_no;
1000 do_pairs_simple<real, 1, const t_pbc*>(
1001 nbonds, iatoms, iparams, x, f, pbc_nonnull, chargeA, fr->ic->epsfac * fr->fudgeQQ);
1004 else if (stepWork.computeVirial)
1006 do_pairs_general<BondedKernelFlavor::ForcesAndVirialAndEnergy>(ftype,
1027 do_pairs_general<BondedKernelFlavor::ForcesAndEnergy>(ftype,