2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2016,2017 by the GROMACS development team.
5 * Copyright (c) 2018,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.
36 #ifndef GMX_GMXLIB_NONBONDED_SOFTCORE_H
37 #define GMX_GMXLIB_NONBONDED_SOFTCORE_H
41 #include "gromacs/math/functions.h"
42 #include "gromacs/simd/simd.h"
43 #include "gromacs/simd/simd_math.h"
45 /* linearized electrostatics */
46 template<bool computeForces, class RealType, class BoolType>
47 static inline void quadraticApproximationCoulomb(const RealType qq,
51 const real dLambdaFac,
52 RealType gmx_unused* force,
57 RealType constFac = qq * rInvQ;
58 RealType linFac = constFac * r * rInvQ;
59 RealType quadrFac = linFac * r * rInvQ;
61 /* Computing Coulomb force and potential energy */
62 if constexpr (computeForces)
64 *force = -2 * quadrFac + 3 * linFac;
67 *potential = quadrFac - 3 * (linFac - constFac);
69 RealType lambdaFacRevInv = gmx::maskzInv(1 - lambdaFac, dvdlMask);
70 *dvdl = dLambdaFac * 0.5_real * (lambdaFac * lambdaFacRevInv) * (quadrFac - 2 * linFac + constFac);
73 /* reaction-field linearized electrostatics */
74 template<bool computeForces, class RealType, class BoolType>
75 static inline void reactionFieldQuadraticPotential(const RealType qq,
80 const real dLambdaFac,
81 const RealType alphaEff,
83 const real potentialShift,
84 RealType gmx_unused* force,
89 /* check if we have to use the hardcore values */
90 BoolType computeValues = mask && (lambdaFac < 1 && 0 < alphaEff && facel != 0);
91 if (gmx::anyTrue(computeValues))
93 RealType lambdaFacRev = gmx::selectByMask(1 - lambdaFac, computeValues);
95 RealType rQ = gmx::cbrt(lambdaFacRev);
96 rQ = gmx::sqrt(rQ) * (1 + gmx::abs(qq / facel));
99 // ensure that the linearization point doesn't go beyond rCutoff
100 BoolType beyondCutoff = rCutoff < rQ;
101 BoolType withinCutoff = rQ <= rCutoff;
102 if (gmx::anyTrue(beyondCutoff))
104 rQ = gmx::blend(rQ, rCutoff, beyondCutoff);
107 computeValues = computeValues && (r < rQ);
108 if (gmx::anyTrue(computeValues))
110 RealType rInvQ = gmx::maskzInv(rQ, computeValues);
112 // Compute quadratic force, potential and dvdl
113 RealType forceQuad(0);
114 RealType potentialQuad(0);
115 RealType dvdlQuad(0);
116 quadraticApproximationCoulomb<computeForces>(
117 qq, rInvQ, r, lambdaFac, dLambdaFac, &forceQuad, &potentialQuad, &dvdlQuad, computeValues);
120 forceQuad = forceQuad - qq * 2 * krf * r * r;
121 potentialQuad = potentialQuad + qq * (krf * r * r - potentialShift);
124 if constexpr (computeForces)
126 *force = gmx::blend(*force, forceQuad, computeValues);
128 *potential = gmx::blend(*potential, potentialQuad, computeValues);
129 *dvdl = *dvdl + gmx::selectByMask(dvdlQuad, computeValues && withinCutoff);
134 /* ewald linearized electrostatics */
135 template<bool computeForces, class RealType, class BoolType>
136 static inline void ewaldQuadraticPotential(const RealType qq,
140 const real lambdaFac,
141 const real dLambdaFac,
142 const RealType alphaEff,
143 const real potentialShift,
144 RealType gmx_unused* force,
149 /* check if we have to use the hardcore values */
150 BoolType computeValues = mask && (lambdaFac < 1 && 0 < alphaEff && facel != 0);
151 if (gmx::anyTrue(computeValues))
153 RealType lambdaFacRev = gmx::selectByMask(1 - lambdaFac, computeValues);
155 RealType rQ = gmx::cbrt(lambdaFacRev);
156 rQ = gmx::sqrt(rQ) * (1 + gmx::abs(qq / facel));
159 // ensure that the linearization point doesn't go beyond rCutoff
160 BoolType beyondCutoff = rCutoff < rQ;
161 BoolType withinCutoff = rQ <= rCutoff;
162 if (gmx::anyTrue(beyondCutoff))
164 rQ = gmx::blend(rQ, rCutoff, beyondCutoff);
167 computeValues = computeValues && (r < rQ);
168 if (gmx::anyTrue(computeValues))
170 RealType rInvQ = gmx::maskzInv(rQ, computeValues);
172 // Compute quadratic force, potential and dvdl
173 RealType forceQuad(0);
174 RealType potentialQuad(0);
175 RealType dvdlQuad(0);
176 quadraticApproximationCoulomb<computeForces>(
177 qq, rInvQ, r, lambdaFac, dLambdaFac, &forceQuad, &potentialQuad, &dvdlQuad, computeValues);
179 // ewald modification
180 potentialQuad = potentialQuad - qq * potentialShift;
183 if constexpr (computeForces)
185 *force = gmx::blend(*force, forceQuad, computeValues);
187 *potential = gmx::blend(*potential, potentialQuad, computeValues);
188 *dvdl = *dvdl + gmx::selectByMask(dvdlQuad, computeValues && withinCutoff);
193 /* cutoff LJ with quadratic appximation of lj-potential */
194 template<bool computeForces, class RealType, class BoolType>
195 static inline void lennardJonesQuadraticPotential(const RealType c6,
199 const real lambdaFac,
200 const real dLambdaFac,
201 const RealType sigma6,
202 const RealType alphaEff,
203 const real repulsionShift,
204 const real dispersionShift,
205 RealType gmx_unused* force,
210 constexpr real c_twentySixSeventh = 26.0_real / 7.0_real;
211 constexpr real c_oneSixth = 1.0_real / 6.0_real;
212 constexpr real c_oneTwelth = 1.0_real / 12.0_real;
213 constexpr real c_half = 1.0_real / 2.0_real;
215 /* check if we have to use the hardcore values */
216 BoolType computeValues = mask && (lambdaFac < 1 && 0 < alphaEff);
217 if (gmx::anyTrue(computeValues))
219 RealType lambdaFacRev = gmx::selectByMask(1 - lambdaFac, computeValues);
220 RealType lambdaFacRevInv = gmx::maskzInv(1 - lambdaFac, computeValues);
222 RealType rQ = gmx::cbrt(c_twentySixSeventh * sigma6 * lambdaFacRev);
226 computeValues = (computeValues && r < rQ);
227 if (gmx::anyTrue(computeValues))
229 /* scaled values for c6 and c12 */
231 c6s = c_oneSixth * c6;
232 c12s = c_oneTwelth * c12;
233 /* Temporary variables for inverted values */
234 RealType rInvQ = gmx::maskzInv(rQ, computeValues);
235 RealType rInv14C, rInv13C, rInv12C;
236 RealType rInv8C, rInv7C, rInv6C;
237 rInv6C = rInvQ * rInvQ * rInvQ;
238 rInv6C = rInv6C * rInv6C;
239 rInv7C = rInv6C * rInvQ;
240 rInv8C = rInv7C * rInvQ;
241 rInv14C = c12s * rInv7C * rInv7C * rsq;
242 rInv13C = c12s * rInv7C * rInv6C * r;
243 rInv12C = c12s * rInv6C * rInv6C;
244 rInv8C = rInv8C * c6s * rsq;
245 rInv7C = rInv7C * c6s * r;
246 rInv6C = rInv6C * c6s;
248 /* Temporary variables for A and B */
249 RealType quadrFac, linearFac, constFac;
250 quadrFac = 156 * rInv14C - 42 * rInv8C;
251 linearFac = 168 * rInv13C - 48 * rInv7C;
252 constFac = 91 * rInv12C - 28 * rInv6C;
254 /* Computing LJ force and potential energy */
255 RealType gmx_unused forceQuad = -quadrFac + linearFac;
256 RealType potentialQuad = c_half * quadrFac - linearFac + constFac;
257 RealType dvdlQuad = dLambdaFac * 28 * (lambdaFac * lambdaFacRevInv)
258 * ((6.5_real * rInv14C - rInv8C) - (13 * rInv13C - 2 * rInv7C)
259 + (6.5_real * rInv12C - rInv6C));
261 potentialQuad = potentialQuad
262 + gmx::selectByMask(((c12s * repulsionShift) - (c6s * dispersionShift)),
264 if constexpr (computeForces)
266 *force = gmx::blend(*force, forceQuad, computeValues);
268 *potential = gmx::blend(*potential, potentialQuad, computeValues);
269 *dvdl = *dvdl + gmx::selectByMask(dvdlQuad, computeValues);