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.
37 #include "dispersioncorrection.h"
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
,
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;
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
,
110 DispersionCorrection::TopologyParams::TopologyParams(const gmx_mtop_t
&mtop
,
111 const t_inputrec
&inputrec
,
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
);
140 for (int q
= 0; q
< (inputrec
.efep
== efepNO
? 1 : 2); q
++)
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
];
164 npair_ij
= iCount
*jCount
;
168 npair_ij
= iCount
*(iCount
- 1)/2;
172 /* nbfp now includes the 6.0 derivative prefactor */
173 csix
+= npair_ij
*BHAMC(nbfp
, ntp
, tpi
, tpj
)/6.0;
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;
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
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
];
205 const int tpj
= atomtypeAOrB(atoms
->atom
[k
], q
);
208 /* nbfp now includes the 6.0 derivative prefactor */
209 csix
-= nmol
*BHAMC(nbfp
, ntp
, tpi
, tpj
)/6.0;
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;
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
;
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
245 if (mb
== mtop
.molblock
.size() - 1)
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
);
260 /* nbfp now includes the 6.0 derivative prefactor */
261 csix
+= nmolc
*BHAMC(nbfp
, ntp
, tpi
, tpj
)/6.0;
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;
274 if (npair
- nexcl
<= 0)
281 csix
/= npair
- nexcl
;
282 ctwelve
/= npair
- nexcl
;
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
);
291 avctwelve_
[q
] = ctwelve
;
296 integrate_table(const real vdwtab
[],
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;
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
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
);
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)
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.
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
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
);
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
,
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
588 if (eDispCorr_
== edispcNO
)
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_
;
605 avcsix
= topParams_
.avcsix_
[0];
606 avctwelve
= topParams_
.avctwelve_
[0];
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
;
619 dvdlambda
+= (topParams_
.avcsix_
[1] - topParams_
.avcsix_
[0])*enerdiff
;
623 const real enerdiff
= numCorr
*(density
*iParams_
.enerdifftwelve_
- iParams_
.enershifttwelve_
);
624 corr
.energy
+= avctwelve
*enerdiff
;
627 dvdlambda
+= (topParams_
.avctwelve_
[1] - topParams_
.avctwelve_
[0])*enerdiff
;
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
;
644 corr
.dvdl
+= dvdlambda
;