Add con/destructor for t_forcetable
[gromacs.git] / src / gromacs / mdlib / dispersioncorrection.cpp
blob4c7b2d90490ee05c27e06bdc7ca83c6d107c4d32
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2019, 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.
35 #include "gmxpre.h"
37 #include "dispersioncorrection.h"
39 #include <cstdio>
41 #include "gromacs/math/units.h"
42 #include "gromacs/math/utilities.h"
43 #include "gromacs/math/vec.h"
44 #include "gromacs/mdtypes/forcerec.h"
45 #include "gromacs/mdtypes/inputrec.h"
46 #include "gromacs/mdtypes/md_enums.h"
47 #include "gromacs/mdtypes/nblist.h"
48 #include "gromacs/tables/forcetable.h"
49 #include "gromacs/topology/mtop_util.h"
50 #include "gromacs/topology/topology.h"
51 #include "gromacs/utility/arrayref.h"
52 #include "gromacs/utility/fatalerror.h"
53 #include "gromacs/utility/logger.h"
55 /* Implementation here to avoid other files needing to include the file that defines t_nblists */
56 DispersionCorrection::InteractionParams::~InteractionParams() = default;
58 /* Returns a matrix, as flat list, of combination rule combined LJ parameters */
59 static std::vector<real> mk_nbfp_combination_rule(const gmx_ffparams_t &ffparams,
60 const int comb_rule)
62 const int atnr = ffparams.atnr;
64 std::vector<real> nbfp(atnr*atnr*2);
66 for (int i = 0; i < atnr; ++i)
68 for (int j = 0; j < atnr; ++j)
70 const real c6i = ffparams.iparams[i*(atnr + 1)].lj.c6;
71 const real c12i = ffparams.iparams[i*(atnr + 1)].lj.c12;
72 const real c6j = ffparams.iparams[j*(atnr + 1)].lj.c6;
73 const real c12j = ffparams.iparams[j*(atnr + 1)].lj.c12;
74 real c6 = std::sqrt(c6i * c6j);
75 real c12 = std::sqrt(c12i * c12j);
76 if (comb_rule == eCOMB_ARITHMETIC
77 && !gmx_numzero(c6) && !gmx_numzero(c12))
79 const real sigmai = gmx::sixthroot(c12i / c6i);
80 const real sigmaj = gmx::sixthroot(c12j / c6j);
81 const real epsi = c6i * c6i / c12i;
82 const real epsj = c6j * c6j / c12j;
83 const real sigma = 0.5*(sigmai + sigmaj);
84 const real eps = std::sqrt(epsi * epsj);
85 c6 = eps * gmx::power6(sigma);
86 c12 = eps * gmx::power12(sigma);
88 C6(nbfp, atnr, i, j) = c6*6.0;
89 C12(nbfp, atnr, i, j) = c12*12.0;
93 return nbfp;
96 /* Returns the A-topology atom type when aOrB=0, the B-topology atom type when aOrB=1 */
97 static int atomtypeAOrB(const t_atom &atom,
98 int aOrB)
100 if (aOrB == 0)
102 return atom.type;
104 else
106 return atom.typeB;
110 DispersionCorrection::TopologyParams::TopologyParams(const gmx_mtop_t &mtop,
111 const t_inputrec &inputrec,
112 bool useBuckingham,
113 int numAtomTypes,
114 gmx::ArrayRef<const real> nonbondedForceParameters)
116 const int ntp = numAtomTypes;
117 const gmx_bool bBHAM = useBuckingham;
119 gmx::ArrayRef<const real> nbfp = nonbondedForceParameters;
120 std::vector<real> nbfp_comb;
121 /* For LJ-PME, we want to correct for the difference between the
122 * actual C6 values and the C6 values used by the LJ-PME based on
123 * combination rules. */
124 if (EVDW_PME(inputrec.vdwtype))
126 nbfp_comb = mk_nbfp_combination_rule(mtop.ffparams,
127 (inputrec.ljpme_combination_rule == eljpmeLB) ? eCOMB_ARITHMETIC : eCOMB_GEOMETRIC);
128 for (int tpi = 0; tpi < ntp; ++tpi)
130 for (int tpj = 0; tpj < ntp; ++tpj)
132 C6(nbfp_comb, ntp, tpi, tpj) =
133 C6(nbfp, ntp, tpi, tpj) - C6(nbfp_comb, ntp, tpi, tpj);
134 C12(nbfp_comb, ntp, tpi, tpj) = C12(nbfp, ntp, tpi, tpj);
137 nbfp = nbfp_comb;
140 for (int q = 0; q < (inputrec.efep == efepNO ? 1 : 2); q++)
142 double csix = 0;
143 double ctwelve = 0;
144 int npair = 0;
145 int nexcl = 0;
146 if (!EI_TPI(inputrec.eI))
148 numAtomsForDensity_ = mtop.natoms;
149 numCorrections_ = 0.5*mtop.natoms;
151 /* Count the types so we avoid natoms^2 operations */
152 std::vector<int> typecount(ntp);
153 gmx_mtop_count_atomtypes(&mtop, q, typecount.data());
155 for (int tpi = 0; tpi < ntp; tpi++)
157 for (int tpj = tpi; tpj < ntp; tpj++)
159 const int iCount = typecount[tpi];
160 const int jCount = typecount[tpj];
161 int npair_ij;
162 if (tpi != tpj)
164 npair_ij = iCount*jCount;
166 else
168 npair_ij = iCount*(iCount - 1)/2;
170 if (bBHAM)
172 /* nbfp now includes the 6.0 derivative prefactor */
173 csix += npair_ij*BHAMC(nbfp, ntp, tpi, tpj)/6.0;
175 else
177 /* nbfp now includes the 6.0/12.0 derivative prefactors */
178 csix += npair_ij* C6(nbfp, ntp, tpi, tpj)/6.0;
179 ctwelve += npair_ij* C12(nbfp, ntp, tpi, tpj)/12.0;
181 npair += npair_ij;
184 /* Subtract the excluded pairs.
185 * The main reason for substracting exclusions is that in some cases
186 * some combinations might never occur and the parameters could have
187 * any value. These unused values should not influence the dispersion
188 * correction.
190 for (const gmx_molblock_t &molb : mtop.molblock)
192 const int nmol = molb.nmol;
193 const t_atoms *atoms = &mtop.moltype[molb.type].atoms;
194 const t_blocka *excl = &mtop.moltype[molb.type].excls;
195 for (int i = 0; (i < atoms->nr); i++)
197 const int tpi = atomtypeAOrB( atoms->atom[i], q);
198 const int j1 = excl->index[i];
199 const int j2 = excl->index[i+1];
200 for (int j = j1; j < j2; j++)
202 const int k = excl->a[j];
203 if (k > i)
205 const int tpj = atomtypeAOrB(atoms->atom[k], q);
206 if (bBHAM)
208 /* nbfp now includes the 6.0 derivative prefactor */
209 csix -= nmol*BHAMC(nbfp, ntp, tpi, tpj)/6.0;
211 else
213 /* nbfp now includes the 6.0/12.0 derivative prefactors */
214 csix -= nmol*C6 (nbfp, ntp, tpi, tpj)/6.0;
215 ctwelve -= nmol*C12(nbfp, ntp, tpi, tpj)/12.0;
217 nexcl += molb.nmol;
223 else
225 const t_atoms &atoms_tpi =
226 mtop.moltype[mtop.molblock.back().type].atoms;
228 /* Only correct for the interaction of the test particle
229 * with the rest of the system.
231 numAtomsForDensity_ = mtop.natoms - atoms_tpi.nr;
232 numCorrections_ = atoms_tpi.nr;
234 npair = 0;
235 for (size_t mb = 0; mb < mtop.molblock.size(); mb++)
237 const gmx_molblock_t &molb = mtop.molblock[mb];
238 const t_atoms &atoms = mtop.moltype[molb.type].atoms;
239 for (int j = 0; j < atoms.nr; j++)
241 int nmolc = molb.nmol;
242 /* Remove the interaction of the test charge group
243 * with itself.
245 if (mb == mtop.molblock.size() - 1)
247 nmolc--;
249 if (mb == 0 && molb.nmol == 1)
251 gmx_fatal(FARGS, "Old format tpr with TPI, please generate a new tpr file");
254 const int tpj = atomtypeAOrB(atoms.atom[j], q);
255 for (int i = 0; i < atoms_tpi.nr; i++)
257 const int tpi = atomtypeAOrB(atoms_tpi.atom[i], q);
258 if (bBHAM)
260 /* nbfp now includes the 6.0 derivative prefactor */
261 csix += nmolc*BHAMC(nbfp, ntp, tpi, tpj)/6.0;
263 else
265 /* nbfp now includes the 6.0/12.0 derivative prefactors */
266 csix += nmolc*C6 (nbfp, ntp, tpi, tpj)/6.0;
267 ctwelve += nmolc*C12(nbfp, ntp, tpi, tpj)/12.0;
269 npair += nmolc;
274 if (npair - nexcl <= 0)
276 csix = 0;
277 ctwelve = 0;
279 else
281 csix /= npair - nexcl;
282 ctwelve /= npair - nexcl;
284 if (debug)
286 fprintf(debug, "Counted %d exclusions\n", nexcl);
287 fprintf(debug, "Average C6 parameter is: %10g\n", csix);
288 fprintf(debug, "Average C12 parameter is: %10g\n", ctwelve);
290 avcsix_[q] = csix;
291 avctwelve_[q] = ctwelve;
295 static void
296 integrate_table(const real vdwtab[],
297 const real scale,
298 const int offstart,
299 const int rstart,
300 const int rend,
301 double *enerout,
302 double *virout)
304 const double invscale = 1.0/scale;
305 const double invscale2 = invscale*invscale;
306 const double invscale3 = invscale*invscale2;
308 /* Following summation derived from cubic spline definition,
309 * Numerical Recipies in C, second edition, p. 113-116. Exact for
310 * the cubic spline. We first calculate the negative of the
311 * energy from rvdw to rvdw_switch, assuming that g(r)=1, and then
312 * add the more standard, abrupt cutoff correction to that result,
313 * yielding the long-range correction for a switched function. We
314 * perform both the pressure and energy loops at the same time for
315 * simplicity, as the computational cost is low. */
317 /* Since the dispersion table has been scaled down a factor
318 * 6.0 and the repulsion a factor 12.0 to compensate for the
319 * c6/c12 parameters inside nbfp[] being scaled up (to save
320 * flops in kernels), we need to correct for this.
322 const double tabfactor = (offstart == 0 ? 6.0 : 12.0);
324 double enersum = 0.0;
325 double virsum = 0.0;
326 for (int ri = rstart; ri < rend; ++ri)
328 const double r = ri*invscale;
329 const double ea = invscale3;
330 const double eb = 2.0*invscale2*r;
331 const double ec = invscale*r*r;
333 const double pa = invscale3;
334 const double pb = 3.0*invscale2*r;
335 const double pc = 3.0*invscale*r*r;
336 const double pd = r*r*r;
338 /* this "8" is from the packing in the vdwtab array - perhaps
339 should be defined? */
341 const int offset = 8*ri + offstart;
342 const double y0 = vdwtab[offset];
343 const double f = vdwtab[offset+1];
344 const double g = vdwtab[offset+2];
345 const double h = vdwtab[offset+3];
347 enersum += y0*(ea/3 + eb/2 + ec) + f*(ea/4 + eb/3 + ec/2) + g*(ea/5 + eb/4 + ec/3) + h*(ea/6 + eb/5 + ec/4);
348 virsum += f*(pa/4 + pb/3 + pc/2 + pd) + 2*g*(pa/5 + pb/4 + pc/3 + pd/2) + 3*h*(pa/6 + pb/5 + pc/4 + pd/3);
350 *enerout = 4.0*M_PI*enersum*tabfactor;
351 *virout = 4.0*M_PI*virsum*tabfactor;
354 /* Struct for storing and passing energy or virial corrections */
355 struct InteractionCorrection
357 real dispersion = 0;
358 real repulsion = 0;
361 /* Adds the energy and virial corrections beyond the cut-off */
362 static void addCorrectionBeyondCutoff(InteractionCorrection *energy,
363 InteractionCorrection *virial,
364 const double cutoffDistance)
366 const double rc3 = cutoffDistance*cutoffDistance*cutoffDistance;
367 const double rc9 = rc3*rc3*rc3;
369 energy->dispersion += -4.0*M_PI/(3.0*rc3);
370 energy->repulsion += 4.0*M_PI/(9.0*rc9);
371 virial->dispersion += 8.0*M_PI/rc3;
372 virial->repulsion += -16.0*M_PI/(3.0*rc9);
375 void
376 DispersionCorrection::setInteractionParameters(InteractionParams *iParams,
377 const interaction_const_t &ic,
378 const char *tableFileName)
380 /* We only need to set the tables at first call, i.e. tableFileName!=nullptr
381 * or when we changed the cut-off with LJ-PME tuning.
383 if (tableFileName || EVDW_PME(ic.vdwtype))
385 iParams->dispersionCorrectionTable_ =
386 makeDispersionCorrectionTable(nullptr, &ic, ic.rvdw, tableFileName);
389 InteractionCorrection energy;
390 InteractionCorrection virial;
392 if ((ic.vdw_modifier == eintmodPOTSHIFT) ||
393 (ic.vdw_modifier == eintmodPOTSWITCH) ||
394 (ic.vdw_modifier == eintmodFORCESWITCH) ||
395 (ic.vdwtype == evdwSHIFT) ||
396 (ic.vdwtype == evdwSWITCH))
398 if (((ic.vdw_modifier == eintmodPOTSWITCH) ||
399 (ic.vdw_modifier == eintmodFORCESWITCH) ||
400 (ic.vdwtype == evdwSWITCH)) && ic.rvdw_switch == 0)
402 gmx_fatal(FARGS,
403 "With dispersion correction rvdw-switch can not be zero "
404 "for vdw-type = %s", evdw_names[ic.vdwtype]);
407 GMX_ASSERT(iParams->dispersionCorrectionTable_, "We need an initialized table");
409 /* TODO This code depends on the logic in tables.c that
410 constructs the table layout, which should be made
411 explicit in future cleanup. */
412 GMX_ASSERT(iParams->dispersionCorrectionTable_->interaction == GMX_TABLE_INTERACTION_VDWREP_VDWDISP,
413 "Dispersion-correction code needs a table with both repulsion and dispersion terms");
414 const real scale = iParams->dispersionCorrectionTable_->scale;
415 const real *vdwtab = iParams->dispersionCorrectionTable_->data;
417 /* Round the cut-offs to exact table values for precision */
418 int ri0 = static_cast<int>(std::floor(ic.rvdw_switch*scale));
419 int ri1 = static_cast<int>(std::ceil(ic.rvdw*scale));
421 /* The code below has some support for handling force-switching, i.e.
422 * when the force (instead of potential) is switched over a limited
423 * region. This leads to a constant shift in the potential inside the
424 * switching region, which we can handle by adding a constant energy
425 * term in the force-switch case just like when we do potential-shift.
427 * For now this is not enabled, but to keep the functionality in the
428 * code we check separately for switch and shift. When we do force-switch
429 * the shifting point is rvdw_switch, while it is the cutoff when we
430 * have a classical potential-shift.
432 * For a pure potential-shift the potential has a constant shift
433 * all the way out to the cutoff, and that is it. For other forms
434 * we need to calculate the constant shift up to the point where we
435 * start modifying the potential.
437 ri0 = (ic.vdw_modifier == eintmodPOTSHIFT) ? ri1 : ri0;
439 const double r0 = ri0/scale;
440 const double rc3 = r0*r0*r0;
441 const double rc9 = rc3*rc3*rc3;
443 if ((ic.vdw_modifier == eintmodFORCESWITCH) ||
444 (ic.vdwtype == evdwSHIFT))
446 /* Determine the constant energy shift below rvdw_switch.
447 * Table has a scale factor since we have scaled it down to compensate
448 * for scaling-up c6/c12 with the derivative factors to save flops in analytical kernels.
450 iParams->enershiftsix_ = static_cast<real>(-1.0/(rc3*rc3)) - 6.0*vdwtab[8*ri0];
451 iParams->enershifttwelve_ = static_cast<real>( 1.0/(rc9*rc3)) - 12.0*vdwtab[8*ri0 + 4];
453 else if (ic.vdw_modifier == eintmodPOTSHIFT)
455 iParams->enershiftsix_ = static_cast<real>(-1.0/(rc3*rc3));
456 iParams->enershifttwelve_ = static_cast<real>( 1.0/(rc9*rc3));
459 /* Add the constant part from 0 to rvdw_switch.
460 * This integration from 0 to rvdw_switch overcounts the number
461 * of interactions by 1, as it also counts the self interaction.
462 * We will correct for this later.
464 energy.dispersion += 4.0*M_PI*iParams->enershiftsix_*rc3/3.0;
465 energy.repulsion += 4.0*M_PI*iParams->enershifttwelve_*rc3/3.0;
467 /* Calculate the contribution in the range [r0,r1] where we
468 * modify the potential. For a pure potential-shift modifier we will
469 * have ri0==ri1, and there will not be any contribution here.
471 double enersum = 0;
472 double virsum = 0;
473 integrate_table(vdwtab, scale, 0, ri0, ri1, &enersum, &virsum);
474 energy.dispersion -= enersum;
475 virial.dispersion -= virsum;
476 integrate_table(vdwtab, scale, 4, ri0, ri1, &enersum, &virsum);
477 energy.repulsion -= enersum;
478 virial.repulsion -= virsum;
481 /* Alright: Above we compensated by REMOVING the parts outside r0
482 * corresponding to the ideal VdW 1/r6 and /r12 potentials.
484 * Regardless of whether r0 is the point where we start switching,
485 * or the cutoff where we calculated the constant shift, we include
486 * all the parts we are missing out to infinity from r0 by
487 * calculating the analytical dispersion correction.
489 addCorrectionBeyondCutoff(&energy, &virial, r0);
491 else if (ic.vdwtype == evdwCUT ||
492 EVDW_PME(ic.vdwtype) ||
493 ic.vdwtype == evdwUSER)
495 /* Note that with LJ-PME, the dispersion correction is multiplied
496 * by the difference between the actual C6 and the value of C6
497 * that would produce the combination rule.
498 * This means the normal energy and virial difference formulas
499 * can be used here.
502 const double rc3 = ic.rvdw*ic.rvdw*ic.rvdw;
503 const double rc9 = rc3*rc3*rc3;
504 if (ic.vdw_modifier == eintmodPOTSHIFT)
506 /* Contribution within the cut-off */
507 energy.dispersion += -4.0*M_PI/(3.0*rc3);
508 energy.repulsion += 4.0*M_PI/(3.0*rc9);
510 /* Contribution beyond the cut-off */
511 addCorrectionBeyondCutoff(&energy, &virial, ic.rvdw);
513 else
515 gmx_fatal(FARGS,
516 "Dispersion correction is not implemented for vdw-type = %s",
517 evdw_names[ic.vdwtype]);
520 iParams->enerdiffsix_ = energy.dispersion;
521 iParams->enerdifftwelve_ = energy.repulsion;
522 /* The 0.5 is due to the Gromacs definition of the virial */
523 iParams->virdiffsix_ = 0.5*virial.dispersion;
524 iParams->virdifftwelve_ = 0.5*virial.repulsion;
527 DispersionCorrection::DispersionCorrection(const gmx_mtop_t &mtop,
528 const t_inputrec &inputrec,
529 bool useBuckingham,
530 int numAtomTypes,
531 gmx::ArrayRef<const real> nonbondedForceParameters,
532 const interaction_const_t &ic,
533 const char *tableFileName) :
534 eDispCorr_(inputrec.eDispCorr),
535 vdwType_(inputrec.vdwtype),
536 eFep_(inputrec.efep),
537 topParams_(mtop, inputrec, useBuckingham, numAtomTypes, nonbondedForceParameters)
539 if (eDispCorr_ != edispcNO)
541 GMX_RELEASE_ASSERT(tableFileName, "Need a table file name");
543 setInteractionParameters(&iParams_, ic, tableFileName);
547 bool DispersionCorrection::correctFullInteraction() const
549 return (eDispCorr_ == edispcAllEner || eDispCorr_ == edispcAllEnerPres);
552 void DispersionCorrection::print(const gmx::MDLogger &mdlog) const
554 if (topParams_.avcsix_[0] == 0 && topParams_.avctwelve_[0] == 0)
556 GMX_LOG(mdlog.warning).asParagraph().appendText("WARNING: There are no atom pairs for dispersion correction");
558 else if (vdwType_ == evdwUSER)
560 GMX_LOG(mdlog.warning).asParagraph().appendText("WARNING: using dispersion correction with user tables\n");
563 std::string text = gmx::formatString("Long Range LJ corr.: <C6> %10.4e",
564 topParams_.avcsix_[0]);
565 if (correctFullInteraction())
567 text += gmx::formatString(" <C12> %10.4e",
568 topParams_.avctwelve_[0]);
570 GMX_LOG(mdlog.info).appendText(text);
573 void DispersionCorrection::setParameters(const interaction_const_t &ic)
575 if (eDispCorr_ != edispcNO)
577 setInteractionParameters(&iParams_, ic, nullptr);
581 DispersionCorrection::Correction
582 DispersionCorrection::calculate(const matrix box,
583 const real lambda) const
586 Correction corr;
588 if (eDispCorr_ == edispcNO)
590 return corr;
593 const bool bCorrAll = correctFullInteraction();
594 const bool bCorrPres = (eDispCorr_ == edispcEnerPres ||
595 eDispCorr_ == edispcAllEnerPres);
597 const real invvol = 1/det(box);
598 const real density = topParams_.numAtomsForDensity_*invvol;
599 const real numCorr = topParams_.numCorrections_;
601 real avcsix;
602 real avctwelve;
603 if (eFep_ == efepNO)
605 avcsix = topParams_.avcsix_[0];
606 avctwelve = topParams_.avctwelve_[0];
608 else
610 avcsix = (1 - lambda)*topParams_.avcsix_[0] + lambda*topParams_.avcsix_[1];
611 avctwelve = (1 - lambda)*topParams_.avctwelve_[0] + lambda*topParams_.avctwelve_[1];
614 const real enerdiff = numCorr*(density*iParams_.enerdiffsix_ - iParams_.enershiftsix_);
615 corr.energy += avcsix*enerdiff;
616 real dvdlambda = 0;
617 if (eFep_ != efepNO)
619 dvdlambda += (topParams_.avcsix_[1] - topParams_.avcsix_[0])*enerdiff;
621 if (bCorrAll)
623 const real enerdiff = numCorr*(density*iParams_.enerdifftwelve_ - iParams_.enershifttwelve_);
624 corr.energy += avctwelve*enerdiff;
625 if (eFep_ != efepNO)
627 dvdlambda += (topParams_.avctwelve_[1] - topParams_.avctwelve_[0])*enerdiff;
631 if (bCorrPres)
633 corr.virial = numCorr*density*avcsix*iParams_.virdiffsix_/3.0;
634 if (eDispCorr_ == edispcAllEnerPres)
636 corr.virial += numCorr*density*avctwelve*iParams_.virdifftwelve_/3.0;
638 /* The factor 2 is because of the Gromacs virial definition */
639 corr.pressure = -2.0*invvol*corr.virial*PRESFAC;
642 if (eFep_ != efepNO)
644 corr.dvdl += dvdlambda;
647 return corr;