2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2020,2021, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 #include "interaction_const.h"
41 #include "gromacs/ewald/ewald_utils.h"
42 #include "gromacs/math/functions.h"
43 #include "gromacs/math/units.h"
44 #include "gromacs/mdlib/rf_util.h"
45 #include "gromacs/mdtypes/forcerec.h"
46 #include "gromacs/mdtypes/inputrec.h"
47 #include "gromacs/topology/topology.h"
48 #include "gromacs/utility/fatalerror.h"
49 #include "gromacs/utility/pleasecite.h"
51 interaction_const_t::SoftCoreParameters::SoftCoreParameters(const t_lambda& fepvals) :
52 alphaVdw(fepvals.sc_alpha),
53 alphaCoulomb(fepvals.bScCoul ? fepvals.sc_alpha : 0),
54 lambdaPower(fepvals.sc_power),
55 sigma6WithInvalidSigma(gmx::power6(fepvals.sc_sigma)),
56 sigma6Minimum(fepvals.bScCoul ? gmx::power6(fepvals.sc_sigma_min) : 0),
57 softcoreType(fepvals.softcoreFunction),
58 gapsysScaleLinpointVdW(fepvals.scGapsysScaleLinpointLJ),
59 gapsysScaleLinpointCoul(fepvals.scGapsysScaleLinpointQ),
60 sigma6VdWGapsys(gmx::power6(fepvals.scSigmaLJGapsys))
62 // This is checked during tpr reading, so we can assert here
63 GMX_RELEASE_ASSERT(fepvals.sc_r_power == 6.0, "We only support soft-core r-power 6");
66 /*! \brief Print Coulomb Ewald citations and set ewald coefficients */
67 static void initCoulombEwaldParameters(FILE* fp,
69 bool systemHasNetCharge,
70 interaction_const_t* ic)
72 if (!EEL_PME_EWALD(ir.coulombtype))
79 fprintf(fp, "Will do PME sum in reciprocal space for electrostatic interactions.\n");
81 if (ir.coulombtype == CoulombInteractionType::P3mAD)
83 please_cite(fp, "Hockney1988");
84 please_cite(fp, "Ballenegger2012");
88 please_cite(fp, "Essmann95a");
91 if (ir.ewald_geometry == EwaldGeometry::ThreeDC)
96 "Using the Ewald3DC correction for systems with a slab geometry%s.\n",
97 systemHasNetCharge ? " and net charge" : "");
99 please_cite(fp, "In-Chul99a");
100 if (systemHasNetCharge)
102 please_cite(fp, "Ballenegger2009");
107 ic->ewaldcoeff_q = calc_ewaldcoeff_q(ir.rcoulomb, ir.ewald_rtol);
110 fprintf(fp, "Using a Gaussian width (1/beta) of %g nm for Ewald\n", 1 / ic->ewaldcoeff_q);
113 if (ic->coulomb_modifier == InteractionModifiers::PotShift)
115 GMX_RELEASE_ASSERT(ic->rcoulomb != 0, "Cutoff radius cannot be zero");
116 ic->sh_ewald = std::erfc(ic->ewaldcoeff_q * ic->rcoulomb) / ic->rcoulomb;
124 /*! \brief Print Van der Waals Ewald citations and set ewald coefficients */
125 static void initVdwEwaldParameters(FILE* fp, const t_inputrec& ir, interaction_const_t* ic)
127 if (!EVDW_PME(ir.vdwtype))
134 fprintf(fp, "Will do PME sum in reciprocal space for LJ dispersion interactions.\n");
135 please_cite(fp, "Essmann95a");
137 ic->ewaldcoeff_lj = calc_ewaldcoeff_lj(ir.rvdw, ir.ewald_rtol_lj);
140 fprintf(fp, "Using a Gaussian width (1/beta) of %g nm for LJ Ewald\n", 1 / ic->ewaldcoeff_lj);
143 if (ic->vdw_modifier == InteractionModifiers::PotShift)
145 real crc2 = gmx::square(ic->ewaldcoeff_lj * ic->rvdw);
146 ic->sh_lj_ewald = (std::exp(-crc2) * (1 + crc2 + 0.5 * crc2 * crc2) - 1) / gmx::power6(ic->rvdw);
154 static real calcBuckinghamBMax(FILE* fplog, const gmx_mtop_t& mtop)
156 const t_atoms *at1, *at2;
157 int i, j, tpi, tpj, ntypes;
162 fprintf(fplog, "Determining largest Buckingham b parameter for table\n");
164 ntypes = mtop.ffparams.atnr;
168 for (size_t mt1 = 0; mt1 < mtop.moltype.size(); mt1++)
170 at1 = &mtop.moltype[mt1].atoms;
171 for (i = 0; (i < at1->nr); i++)
173 tpi = at1->atom[i].type;
176 gmx_fatal(FARGS, "Atomtype[%d] = %d, maximum = %d", i, tpi, ntypes);
179 for (size_t mt2 = mt1; mt2 < mtop.moltype.size(); mt2++)
181 at2 = &mtop.moltype[mt2].atoms;
182 for (j = 0; (j < at2->nr); j++)
184 tpj = at2->atom[j].type;
187 gmx_fatal(FARGS, "Atomtype[%d] = %d, maximum = %d", j, tpj, ntypes);
189 b = mtop.ffparams.iparams[tpi * ntypes + tpj].bham.b;
194 if ((b < bmin) || (bmin == -1))
204 fprintf(fplog, "Buckingham b parameters, min: %g, max: %g\n", bmin, bham_b_max);
210 static void clear_force_switch_constants(shift_consts_t* sc)
217 static void force_switch_constants(real p, real rsw, real rc, shift_consts_t* sc)
219 /* Here we determine the coefficient for shifting the force to zero
220 * between distance rsw and the cut-off rc.
221 * For a potential of r^-p, we have force p*r^-(p+1).
222 * But to save flops we absorb p in the coefficient.
224 * force/p = r^-(p+1) + c2*r^2 + c3*r^3
225 * potential = r^-p + c2/3*r^3 + c3/4*r^4 + cpot
227 sc->c2 = ((p + 1) * rsw - (p + 4) * rc) / (pow(rc, p + 2) * gmx::square(rc - rsw));
228 sc->c3 = -((p + 1) * rsw - (p + 3) * rc) / (pow(rc, p + 2) * gmx::power3(rc - rsw));
229 sc->cpot = -pow(rc, -p) + p * sc->c2 / 3 * gmx::power3(rc - rsw)
230 + p * sc->c3 / 4 * gmx::power4(rc - rsw);
233 static void potential_switch_constants(real rsw, real rc, switch_consts_t* sc)
235 /* The switch function is 1 at rsw and 0 at rc.
236 * The derivative and second derivate are zero at both ends.
237 * rsw = max(r - r_switch, 0)
238 * sw = 1 + c3*rsw^3 + c4*rsw^4 + c5*rsw^5
239 * dsw = 3*c3*rsw^2 + 4*c4*rsw^3 + 5*c5*rsw^4
240 * force = force*dsw - potential*sw
243 sc->c3 = -10 / gmx::power3(rc - rsw);
244 sc->c4 = 15 / gmx::power4(rc - rsw);
245 sc->c5 = -6 / gmx::power5(rc - rsw);
249 interaction_const_t init_interaction_const(FILE* fp, const t_inputrec& ir, const gmx_mtop_t& mtop, bool systemHasNetCharge)
251 interaction_const_t interactionConst;
253 interactionConst.coulombEwaldTables = std::make_unique<EwaldCorrectionTables>();
254 interactionConst.vdwEwaldTables = std::make_unique<EwaldCorrectionTables>();
257 interactionConst.vdwtype = ir.vdwtype;
258 interactionConst.vdw_modifier = ir.vdw_modifier;
259 interactionConst.reppow = mtop.ffparams.reppow;
260 interactionConst.rvdw = cutoff_inf(ir.rvdw);
261 interactionConst.rvdw_switch = ir.rvdw_switch;
262 interactionConst.ljpme_comb_rule = ir.ljpme_combination_rule;
263 interactionConst.useBuckingham = (mtop.ffparams.functype[0] == F_BHAM);
264 if (interactionConst.useBuckingham)
266 interactionConst.buckinghamBMax = calcBuckinghamBMax(fp, mtop);
269 initVdwEwaldParameters(fp, ir, &interactionConst);
271 clear_force_switch_constants(&interactionConst.dispersion_shift);
272 clear_force_switch_constants(&interactionConst.repulsion_shift);
274 switch (interactionConst.vdw_modifier)
276 case InteractionModifiers::PotShift:
277 /* Only shift the potential, don't touch the force */
278 interactionConst.dispersion_shift.cpot = -1.0 / gmx::power6(interactionConst.rvdw);
279 interactionConst.repulsion_shift.cpot = -1.0 / gmx::power12(interactionConst.rvdw);
281 case InteractionModifiers::ForceSwitch:
282 /* Switch the force, switch and shift the potential */
283 force_switch_constants(
284 6.0, interactionConst.rvdw_switch, interactionConst.rvdw, &interactionConst.dispersion_shift);
285 force_switch_constants(
286 12.0, interactionConst.rvdw_switch, interactionConst.rvdw, &interactionConst.repulsion_shift);
288 case InteractionModifiers::PotSwitch:
289 /* Switch the potential and force */
290 potential_switch_constants(
291 interactionConst.rvdw_switch, interactionConst.rvdw, &interactionConst.vdw_switch);
293 case InteractionModifiers::None:
294 case InteractionModifiers::ExactCutoff:
295 /* Nothing to do here */
297 default: gmx_incons("unimplemented potential modifier");
301 interactionConst.eeltype = ir.coulombtype;
302 interactionConst.coulomb_modifier = ir.coulomb_modifier;
303 interactionConst.rcoulomb = cutoff_inf(ir.rcoulomb);
304 interactionConst.rcoulomb_switch = ir.rcoulomb_switch;
305 interactionConst.epsilon_r = ir.epsilon_r;
307 /* Set the Coulomb energy conversion factor */
308 if (interactionConst.epsilon_r != 0)
310 interactionConst.epsfac = gmx::c_one4PiEps0 / interactionConst.epsilon_r;
314 /* eps = 0 is infinite dieletric: no Coulomb interactions */
315 interactionConst.epsfac = 0;
319 if (EEL_RF(interactionConst.eeltype))
321 GMX_RELEASE_ASSERT(interactionConst.eeltype != CoulombInteractionType::GRFNotused,
322 "GRF is no longer supported");
323 interactionConst.reactionFieldPermitivity = ir.epsilon_rf;
325 interactionConst.epsilon_r,
326 interactionConst.reactionFieldPermitivity,
327 interactionConst.rcoulomb,
328 &interactionConst.reactionFieldCoefficient,
329 &interactionConst.reactionFieldShift);
333 /* For plain cut-off we might use the reaction-field kernels */
334 interactionConst.reactionFieldPermitivity = interactionConst.epsilon_r;
335 interactionConst.reactionFieldCoefficient = 0;
336 if (ir.coulomb_modifier == InteractionModifiers::PotShift)
338 interactionConst.reactionFieldShift = 1 / interactionConst.rcoulomb;
342 interactionConst.reactionFieldShift = 0;
346 initCoulombEwaldParameters(fp, ir, systemHasNetCharge, &interactionConst);
350 real dispersion_shift;
352 dispersion_shift = interactionConst.dispersion_shift.cpot;
353 if (EVDW_PME(interactionConst.vdwtype))
355 dispersion_shift -= interactionConst.sh_lj_ewald;
358 "Potential shift: LJ r^-12: %.3e r^-6: %.3e",
359 interactionConst.repulsion_shift.cpot,
362 if (interactionConst.eeltype == CoulombInteractionType::Cut)
364 fprintf(fp, ", Coulomb %.e", -interactionConst.reactionFieldShift);
366 else if (EEL_PME(interactionConst.eeltype))
368 fprintf(fp, ", Ewald %.3e", -interactionConst.sh_ewald);
373 if (ir.efep != FreeEnergyPerturbationType::No)
375 GMX_RELEASE_ASSERT(ir.fepvals, "ir.fepvals should be set wth free-energy");
376 interactionConst.softCoreParameters =
377 std::make_unique<interaction_const_t::SoftCoreParameters>(*ir.fepvals);
380 return interactionConst;