Simplified uniform GPU selection in CMake
[gromacs.git] / src / gromacs / mdlib / calc_verletbuf.cpp
blob90e9acf9275df0eb14a4ba6a3f2791c304240a50
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012-2018, The GROMACS development team.
5 * Copyright (c) 2019,2020, 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 #include "gmxpre.h"
38 #include "calc_verletbuf.h"
40 #include <cmath>
41 #include <cstdlib>
43 #include <algorithm>
45 #include "gromacs/ewald/ewald_utils.h"
46 #include "gromacs/math/functions.h"
47 #include "gromacs/math/units.h"
48 #include "gromacs/math/vec.h"
49 #include "gromacs/mdtypes/inputrec.h"
50 #include "gromacs/mdtypes/md_enums.h"
51 #include "gromacs/nbnxm/nbnxm.h"
52 #include "gromacs/nbnxm/nbnxm_geometry.h"
53 #include "gromacs/nbnxm/nbnxm_simd.h"
54 #include "gromacs/topology/block.h"
55 #include "gromacs/topology/ifunc.h"
56 #include "gromacs/topology/topology.h"
57 #include "gromacs/utility/fatalerror.h"
58 #include "gromacs/utility/real.h"
59 #include "gromacs/utility/strconvert.h"
61 /* The code in this file estimates a pairlist buffer length
62 * given a target energy drift per atom per picosecond.
63 * This is done by estimating the drift given a buffer length.
64 * Ideally we would like to have a tight overestimate of the drift,
65 * but that can be difficult to achieve.
67 * Significant approximations used:
69 * Uniform particle density. UNDERESTIMATES the drift by rho_global/rho_local.
71 * Interactions don't affect particle motion. OVERESTIMATES the drift on longer
72 * time scales. This approximation probably introduces the largest errors.
74 * Only take one constraint per particle into account: OVERESTIMATES the drift.
76 * For rotating constraints assume the same functional shape for time scales
77 * where the constraints rotate significantly as the exact expression for
78 * short time scales. OVERESTIMATES the drift on long time scales.
80 * For non-linear virtual sites use the mass of the lightest constructing atom
81 * to determine the displacement. OVER/UNDERESTIMATES the drift, depending on
82 * the geometry and masses of constructing atoms.
84 * Note that the formulas for normal atoms and linear virtual sites are exact,
85 * apart from the first two approximations.
87 * Note that apart from the effect of the above approximations, the actual
88 * drift of the total energy of a system can be orders of magnitude smaller
89 * due to cancellation of positive and negative drift for different pairs.
93 /* Struct for unique atom type for calculating the energy drift.
94 * The atom displacement depends on mass and constraints.
95 * The energy jump for given distance depend on LJ type and q.
97 struct VerletbufAtomtype
99 atom_nonbonded_kinetic_prop_t prop; /* non-bonded and kinetic atom prop. */
100 int n; /* #atoms of this type in the system */
103 // Struct for derivatives of a non-bonded interaction potential
104 struct pot_derivatives_t
106 real md1; // -V' at the cutoff
107 real d2; // V'' at the cutoff
108 real md3; // -V''' at the cutoff
111 VerletbufListSetup verletbufGetListSetup(Nbnxm::KernelType nbnxnKernelType)
113 /* Note that the current buffer estimation code only handles clusters
114 * of size 1, 2 or 4, so for 4x8 or 8x8 we use the estimate for 4x4.
116 VerletbufListSetup listSetup;
118 listSetup.cluster_size_i = Nbnxm::IClusterSizePerKernelType[nbnxnKernelType];
119 listSetup.cluster_size_j = Nbnxm::JClusterSizePerKernelType[nbnxnKernelType];
121 if (!Nbnxm::kernelTypeUsesSimplePairlist(nbnxnKernelType))
123 /* The GPU kernels (except for OpenCL) split the j-clusters in two halves */
124 listSetup.cluster_size_j /= 2;
127 return listSetup;
130 VerletbufListSetup verletbufGetSafeListSetup(ListSetupType listType)
132 /* When calling this function we often don't know which kernel type we
133 * are going to use. We choose the kernel type with the smallest possible
134 * i- and j-cluster sizes, so we potentially overestimate, but never
135 * underestimate, the buffer drift.
137 Nbnxm::KernelType nbnxnKernelType;
139 if (listType == ListSetupType::Gpu)
141 nbnxnKernelType = Nbnxm::KernelType::Gpu8x8x8;
143 else if (GMX_SIMD && listType == ListSetupType::CpuSimdWhenSupported)
145 #ifdef GMX_NBNXN_SIMD_2XNN
146 /* We use the smallest cluster size to be on the safe side */
147 nbnxnKernelType = Nbnxm::KernelType::Cpu4xN_Simd_2xNN;
148 #else
149 nbnxnKernelType = Nbnxm::KernelType::Cpu4xN_Simd_4xN;
150 #endif
152 else
154 nbnxnKernelType = Nbnxm::KernelType::Cpu4x4_PlainC;
157 return verletbufGetListSetup(nbnxnKernelType);
160 // Returns whether prop1 and prop2 are identical
161 static bool atom_nonbonded_kinetic_prop_equal(const atom_nonbonded_kinetic_prop_t& prop1,
162 const atom_nonbonded_kinetic_prop_t& prop2)
164 return (prop1.mass == prop2.mass && prop1.type == prop2.type && prop1.q == prop2.q
165 && prop1.bConstr == prop2.bConstr && prop1.con_mass == prop2.con_mass
166 && prop1.con_len == prop2.con_len);
169 static void addAtomtype(std::vector<VerletbufAtomtype>* att, const atom_nonbonded_kinetic_prop_t& prop, int nmol)
171 if (prop.mass == 0)
173 /* Ignore massless particles */
174 return;
177 size_t i = 0;
178 while (i < att->size() && !atom_nonbonded_kinetic_prop_equal(prop, (*att)[i].prop))
180 i++;
183 if (i < att->size())
185 (*att)[i].n += nmol;
187 else
189 att->push_back({ prop, nmol });
193 /* Returns the mass of atom atomIndex or 1 when setMassesToOne=true */
194 static real getMass(const t_atoms& atoms, int atomIndex, bool setMassesToOne)
196 if (!setMassesToOne)
198 return atoms.atom[atomIndex].m;
200 else
202 return 1;
206 // Set the masses of a vsites in vsite_m and the non-linear vsite count in n_nonlin_vsite
207 static void get_vsite_masses(const gmx_moltype_t& moltype,
208 const gmx_ffparams_t& ffparams,
209 bool setMassesToOne,
210 gmx::ArrayRef<real> vsite_m)
212 int numNonlinearVsites = 0;
214 /* Check for virtual sites, determine mass from constructing atoms */
215 for (const auto& ilist : extractILists(moltype.ilist, IF_VSITE))
217 for (size_t i = 0; i < ilist.iatoms.size(); i += ilistStride(ilist))
219 const t_iparams& ip = ffparams.iparams[ilist.iatoms[i]];
220 const int a1 = ilist.iatoms[i + 1];
222 if (ilist.functionType != F_VSITEN)
224 /* Only vsiten can have more than four
225 constructing atoms, so NRAL(ft) <= 5 */
226 const int maxj = NRAL(ilist.functionType);
227 std::vector<real> cam(maxj, 0);
228 GMX_ASSERT(maxj <= 5, "This code expect at most 5 atoms in a vsite");
229 for (int j = 1; j < maxj; j++)
231 const int aj = ilist.iatoms[i + 1 + j];
232 cam[j] = getMass(moltype.atoms, aj, setMassesToOne);
233 if (cam[j] == 0)
235 cam[j] = vsite_m[aj];
237 /* A vsite should be constructed from normal atoms or
238 * vsites of lower complexity, which we have processed
239 * in a previous iteration.
241 GMX_ASSERT(cam[j] != 0, "We should have a non-zero mass");
244 switch (ilist.functionType)
246 case F_VSITE2:
247 /* Exact */
248 vsite_m[a1] = (cam[1] * cam[2])
249 / (cam[2] * gmx::square(1 - ip.vsite.a)
250 + cam[1] * gmx::square(ip.vsite.a));
251 break;
252 case F_VSITE3:
253 /* Exact */
254 vsite_m[a1] = (cam[1] * cam[2] * cam[3])
255 / (cam[2] * cam[3] * gmx::square(1 - ip.vsite.a - ip.vsite.b)
256 + cam[1] * cam[3] * gmx::square(ip.vsite.a)
257 + cam[1] * cam[2] * gmx::square(ip.vsite.b));
258 break;
259 case F_VSITEN:
260 GMX_RELEASE_ASSERT(false, "VsiteN should not end up in this code path");
261 break;
262 default:
263 /* Use the mass of the lightest constructing atom.
264 * This is an approximation.
265 * If the distance of the virtual site to the
266 * constructing atom is less than all distances
267 * between constructing atoms, this is a safe
268 * over-estimate of the displacement of the vsite.
269 * This condition holds for all H mass replacement
270 * vsite constructions, except for SP2/3 groups.
271 * In SP3 groups one H will have a F_VSITE3
272 * construction, so even there the total drift
273 * estimate shouldn't be far off.
275 vsite_m[a1] = cam[1];
276 for (int j = 2; j < maxj; j++)
278 vsite_m[a1] = std::min(vsite_m[a1], cam[j]);
280 numNonlinearVsites++;
281 break;
284 else
286 /* Exact */
287 real inv_mass = 0;
288 int numConstructingAtoms = ffparams.iparams[ilist.iatoms[i]].vsiten.n;
289 for (int j = 0; j < 3 * numConstructingAtoms; j += 3)
291 int aj = ilist.iatoms[i + j + 2];
292 real coeff = ffparams.iparams[ilist.iatoms[i + j]].vsiten.a;
293 real m_aj;
294 if (moltype.atoms.atom[aj].ptype == eptVSite)
296 m_aj = vsite_m[aj];
298 else
300 m_aj = moltype.atoms.atom[aj].m;
302 if (m_aj <= 0)
304 gmx_incons("The mass of a vsiten constructing atom is <= 0");
306 inv_mass += coeff * coeff / m_aj;
308 vsite_m[a1] = 1 / inv_mass;
309 /* Correct the loop increment of i for processes more than 1 entry */
310 i += (numConstructingAtoms - 1) * ilistStride(ilist);
312 if (gmx_debug_at)
314 fprintf(debug, "atom %4d %-20s mass %6.3f\n", a1,
315 interaction_function[ilist.functionType].longname, vsite_m[a1]);
320 if (debug && numNonlinearVsites > 0)
322 fprintf(debug, "The molecule type has %d non-linear virtual constructions\n", numNonlinearVsites);
326 static std::vector<VerletbufAtomtype> getVerletBufferAtomtypes(const gmx_mtop_t& mtop, const bool setMassesToOne)
328 std::vector<VerletbufAtomtype> att;
329 int ft, i, a1, a2, a3, a;
330 const t_iparams* ip;
332 for (const gmx_molblock_t& molblock : mtop.molblock)
334 int nmol = molblock.nmol;
335 const gmx_moltype_t& moltype = mtop.moltype[molblock.type];
336 const t_atoms* atoms = &moltype.atoms;
338 /* Check for constraints, as they affect the kinetic energy.
339 * For virtual sites we need the masses and geometry of
340 * the constructing atoms to determine their velocity distribution.
341 * Thus we need a list of properties for all atoms which
342 * we partially fill when looping over constraints.
344 std::vector<atom_nonbonded_kinetic_prop_t> prop(atoms->nr);
346 for (ft = F_CONSTR; ft <= F_CONSTRNC; ft++)
348 const InteractionList& il = moltype.ilist[ft];
350 for (i = 0; i < il.size(); i += 1 + NRAL(ft))
352 ip = &mtop.ffparams.iparams[il.iatoms[i]];
353 a1 = il.iatoms[i + 1];
354 a2 = il.iatoms[i + 2];
355 real mass1 = getMass(*atoms, a1, setMassesToOne);
356 real mass2 = getMass(*atoms, a2, setMassesToOne);
357 if (mass2 > prop[a1].con_mass)
359 prop[a1].con_mass = mass2;
360 prop[a1].con_len = ip->constr.dA;
362 if (mass1 > prop[a2].con_mass)
364 prop[a2].con_mass = mass1;
365 prop[a2].con_len = ip->constr.dA;
370 const InteractionList& il = moltype.ilist[F_SETTLE];
372 for (i = 0; i < il.size(); i += 1 + NRAL(F_SETTLE))
374 ip = &mtop.ffparams.iparams[il.iatoms[i]];
375 a1 = il.iatoms[i + 1];
376 a2 = il.iatoms[i + 2];
377 a3 = il.iatoms[i + 3];
378 /* Usually the mass of a1 (usually oxygen) is larger than a2/a3.
379 * If this is not the case, we overestimate the displacement,
380 * which leads to a larger buffer (ok since this is an exotic case).
382 prop[a1].con_mass = getMass(*atoms, a2, setMassesToOne);
383 prop[a1].con_len = ip->settle.doh;
385 prop[a2].con_mass = getMass(*atoms, a1, setMassesToOne);
386 prop[a2].con_len = ip->settle.doh;
388 prop[a3].con_mass = getMass(*atoms, a1, setMassesToOne);
389 prop[a3].con_len = ip->settle.doh;
392 std::vector<real> vsite_m(atoms->nr);
393 get_vsite_masses(moltype, mtop.ffparams, setMassesToOne, vsite_m);
395 for (a = 0; a < atoms->nr; a++)
397 if (atoms->atom[a].ptype == eptVSite)
399 prop[a].mass = vsite_m[a];
401 else
403 prop[a].mass = getMass(*atoms, a, setMassesToOne);
405 prop[a].type = atoms->atom[a].type;
406 prop[a].q = atoms->atom[a].q;
407 /* We consider an atom constrained, #DOF=2, when it is
408 * connected with constraints to (at least one) atom with
409 * a mass of more than 0.4x its own mass. This is not a critical
410 * parameter, since with roughly equal masses the unconstrained
411 * and constrained displacement will not differ much (and both
412 * overestimate the displacement).
414 prop[a].bConstr = (prop[a].con_mass > 0.4 * prop[a].mass);
416 addAtomtype(&att, prop[a], nmol);
420 if (gmx_debug_at)
422 for (size_t a = 0; a < att.size(); a++)
424 fprintf(debug, "type %zu: m %5.2f t %d q %6.3f con %s con_m %5.3f con_l %5.3f n %d\n",
425 a, att[a].prop.mass, att[a].prop.type, att[a].prop.q,
426 gmx::boolToString(att[a].prop.bConstr), att[a].prop.con_mass,
427 att[a].prop.con_len, att[a].n);
431 return att;
434 /* This function computes two components of the estimate of the variance
435 * in the displacement of one atom in a system of two constrained atoms.
436 * Returns in sigma2_2d the variance due to rotation of the constrained
437 * atom around the atom to which it constrained.
438 * Returns in sigma2_3d the variance due to displacement of the COM
439 * of the whole system of the two constrained atoms.
441 * Note that we only take a single constraint (the one to the heaviest atom)
442 * into account. If an atom has multiple constraints, this will result in
443 * an overestimate of the displacement, which gives a larger drift and buffer.
445 void constrained_atom_sigma2(real kT_fac, const atom_nonbonded_kinetic_prop_t* prop, real* sigma2_2d, real* sigma2_3d)
447 /* Here we decompose the motion of a constrained atom into two
448 * components: rotation around the COM and translation of the COM.
451 /* Determine the variance of the arc length for the two rotational DOFs */
452 real massFraction = prop->con_mass / (prop->mass + prop->con_mass);
453 real sigma2_rot = kT_fac * massFraction / prop->mass;
455 /* The distance from the atom to the COM, i.e. the rotational arm */
456 real comDistance = prop->con_len * massFraction;
458 /* The variance relative to the arm */
459 real sigma2_rel = sigma2_rot / gmx::square(comDistance);
461 /* For sigma2_rel << 1 we don't notice the rotational effect and
462 * we have a normal, Gaussian displacement distribution.
463 * For larger sigma2_rel the displacement is much less, in fact it can
464 * not exceed 2*comDistance. We can calculate MSD/arm^2 as:
465 * integral_x=0-inf distance2(x) x/sigma2_rel exp(-x^2/(2 sigma2_rel)) dx
466 * where x is angular displacement and distance2(x) is the distance^2
467 * between points at angle 0 and x:
468 * distance2(x) = (sin(x) - sin(0))^2 + (cos(x) - cos(0))^2
469 * The limiting value of this MSD is 2, which is also the value for
470 * a uniform rotation distribution that would be reached at long time.
471 * The maximum is 2.5695 at sigma2_rel = 4.5119.
472 * We approximate this integral with a rational polynomial with
473 * coefficients from a Taylor expansion. This approximation is an
474 * overestimate for all values of sigma2_rel. Its maximum value
475 * of 2.6491 is reached at sigma2_rel = sqrt(45/2) = 4.7434.
476 * We keep the approximation constant after that.
477 * We use this approximate MSD as the variance for a Gaussian distribution.
479 * NOTE: For any sensible buffer tolerance this will result in a (large)
480 * overestimate of the buffer size, since the Gaussian has a long tail,
481 * whereas the actual distribution can not reach values larger than 2.
483 /* Coeffients obtained from a Taylor expansion */
484 const real a = 1.0 / 3.0;
485 const real b = 2.0 / 45.0;
487 /* Our approximation is constant after sigma2_rel = 1/sqrt(b) */
488 sigma2_rel = std::min(sigma2_rel, 1 / std::sqrt(b));
490 /* Compute the approximate sigma^2 for 2D motion due to the rotation */
491 *sigma2_2d =
492 gmx::square(comDistance) * sigma2_rel / (1 + a * sigma2_rel + b * gmx::square(sigma2_rel));
494 /* The constrained atom also moves (in 3D) with the COM of both atoms */
495 *sigma2_3d = kT_fac / (prop->mass + prop->con_mass);
498 static void get_atom_sigma2(real kT_fac, const atom_nonbonded_kinetic_prop_t* prop, real* sigma2_2d, real* sigma2_3d)
500 if (prop->bConstr)
502 /* Complicated constraint calculation in a separate function */
503 constrained_atom_sigma2(kT_fac, prop, sigma2_2d, sigma2_3d);
505 else
507 /* Unconstrained atom: trivial */
508 *sigma2_2d = 0;
509 *sigma2_3d = kT_fac / prop->mass;
513 static void approx_2dof(real s2, real x, real* shift, real* scale)
515 /* A particle with 1 DOF constrained has 2 DOFs instead of 3.
516 * This code is also used for particles with multiple constraints,
517 * in which case we overestimate the displacement.
518 * The 2DOF distribution is sqrt(pi/2)*erfc(r/(sqrt(2)*s))/(2*s).
519 * We approximate this with scale*Gaussian(s,r+shift),
520 * by matching the distribution value and derivative at x.
521 * This is a tight overestimate for all r>=0 at any s and x.
523 real ex, er;
525 ex = std::exp(-x * x / (2 * s2));
526 er = std::erfc(x / std::sqrt(2 * s2));
528 *shift = -x + std::sqrt(2 * s2 / M_PI) * ex / er;
529 *scale = 0.5 * M_PI * std::exp(ex * ex / (M_PI * er * er)) * er;
532 // Returns an (over)estimate of the energy drift for a single atom pair,
533 // given the kinetic properties, displacement variances and list buffer.
534 static real energyDriftAtomPair(bool isConstrained_i,
535 bool isConstrained_j,
536 real s2,
537 real s2i_2d,
538 real s2j_2d,
539 real r_buffer,
540 const pot_derivatives_t* der)
542 // For relatively small arguments erfc() is so small that if will be 0.0
543 // when stored in a float. We set an argument limit of 8 (Erfc(8)=1e-29),
544 // such that we can divide by erfc and have some space left for arithmetic.
545 const real erfc_arg_max = 8.0;
547 real rsh = r_buffer;
548 real sc_fac = 1.0;
550 real c_exp, c_erfc;
552 if (rsh * rsh > 2 * s2 * erfc_arg_max * erfc_arg_max)
554 // Below we calculate c_erfc = 0.5*erfc(rsh/sqrt(2*s2))
555 // When rsh/sqrt(2*s2) increases, this erfc will be the first
556 // result that underflows and becomes 0.0. To avoid this,
557 // we set c_exp=0 and c_erfc=0 for large arguments.
558 // This also avoids NaN in approx_2dof().
559 // In any relevant case this has no effect on the results,
560 // since c_exp < 6e-29, so the displacement is completely
561 // negligible for such atom pairs (and an overestimate).
562 // In nearly all use cases, there will be other atom pairs
563 // that contribute much more to the total, so zeroing
564 // this particular contribution has no effect at all.
565 c_exp = 0;
566 c_erfc = 0;
568 else
570 /* For constraints: adapt r and scaling for the Gaussian */
571 if (isConstrained_i)
573 real sh, sc;
575 approx_2dof(s2i_2d, r_buffer * s2i_2d / s2, &sh, &sc);
576 rsh += sh;
577 sc_fac *= sc;
579 if (isConstrained_j)
581 real sh, sc;
583 approx_2dof(s2j_2d, r_buffer * s2j_2d / s2, &sh, &sc);
584 rsh += sh;
585 sc_fac *= sc;
588 /* Exact contribution of an atom pair with Gaussian displacement
589 * with sigma s to the energy drift for a potential with
590 * derivative -md and second derivative dd at the cut-off.
591 * The only catch is that for potentials that change sign
592 * near the cut-off there could be an unlucky compensation
593 * of positive and negative energy drift.
594 * Such potentials are extremely rare though.
596 * Note that pot has unit energy*length, as the linear
597 * atom density still needs to be put in.
599 c_exp = std::exp(-rsh * rsh / (2 * s2)) / std::sqrt(2 * M_PI);
600 c_erfc = 0.5 * std::erfc(rsh / (std::sqrt(2 * s2)));
602 real s = std::sqrt(s2);
603 real rsh2 = rsh * rsh;
605 real pot1 = sc_fac * der->md1 / 2 * ((rsh2 + s2) * c_erfc - rsh * s * c_exp);
606 real pot2 = sc_fac * der->d2 / 6 * (s * (rsh2 + 2 * s2) * c_exp - rsh * (rsh2 + 3 * s2) * c_erfc);
607 real pot3 = sc_fac * der->md3 / 24
608 * ((rsh2 * rsh2 + 6 * rsh2 * s2 + 3 * s2 * s2) * c_erfc - rsh * s * (rsh2 + 5 * s2) * c_exp);
610 return pot1 + pot2 + pot3;
613 // Computes and returns an estimate of the energy drift for the whole system
614 static real energyDrift(gmx::ArrayRef<const VerletbufAtomtype> att,
615 const gmx_ffparams_t* ffp,
616 real kT_fac,
617 const pot_derivatives_t* ljDisp,
618 const pot_derivatives_t* ljRep,
619 const pot_derivatives_t* elec,
620 real rlj,
621 real rcoulomb,
622 real rlist,
623 real boxvol)
625 double drift_tot = 0;
627 if (kT_fac == 0)
629 /* No atom displacements: no drift, avoid division by 0 */
630 return drift_tot;
633 // Here add up the contribution of all atom pairs in the system to
634 // (estimated) energy drift by looping over all atom type pairs.
635 for (gmx::index i = 0; i < att.ssize(); i++)
637 // Get the thermal displacement variance for the i-atom type
638 const atom_nonbonded_kinetic_prop_t* prop_i = &att[i].prop;
639 real s2i_2d, s2i_3d;
640 get_atom_sigma2(kT_fac, prop_i, &s2i_2d, &s2i_3d);
642 for (gmx::index j = i; j < att.ssize(); j++)
644 // Get the thermal displacement variance for the j-atom type
645 const atom_nonbonded_kinetic_prop_t* prop_j = &att[j].prop;
646 real s2j_2d, s2j_3d;
647 get_atom_sigma2(kT_fac, prop_j, &s2j_2d, &s2j_3d);
649 /* Add up the up to four independent variances */
650 real s2 = s2i_2d + s2i_3d + s2j_2d + s2j_3d;
652 // Set -V', V'' and -V''' at the cut-off for LJ */
653 real c6 = ffp->iparams[prop_i->type * ffp->atnr + prop_j->type].lj.c6;
654 real c12 = ffp->iparams[prop_i->type * ffp->atnr + prop_j->type].lj.c12;
655 pot_derivatives_t lj;
656 lj.md1 = c6 * ljDisp->md1 + c12 * ljRep->md1;
657 lj.d2 = c6 * ljDisp->d2 + c12 * ljRep->d2;
658 lj.md3 = c6 * ljDisp->md3 + c12 * ljRep->md3;
660 real pot_lj = energyDriftAtomPair(prop_i->bConstr, prop_j->bConstr, s2, s2i_2d, s2j_2d,
661 rlist - rlj, &lj);
663 // Set -V' and V'' at the cut-off for Coulomb
664 pot_derivatives_t elec_qq;
665 elec_qq.md1 = elec->md1 * prop_i->q * prop_j->q;
666 elec_qq.d2 = elec->d2 * prop_i->q * prop_j->q;
667 elec_qq.md3 = 0;
669 real pot_q = energyDriftAtomPair(prop_i->bConstr, prop_j->bConstr, s2, s2i_2d, s2j_2d,
670 rlist - rcoulomb, &elec_qq);
672 // Note that attractive and repulsive potentials for individual
673 // pairs can partially cancel.
674 real pot = pot_lj + pot_q;
676 /* Multiply by the number of atom pairs */
677 if (j == i)
679 pot *= static_cast<double>(att[i].n) * (att[i].n - 1) / 2;
681 else
683 pot *= static_cast<double>(att[i].n) * att[j].n;
685 /* We need the line density to get the energy drift of the system.
686 * The effective average r^2 is close to (rlist+sigma)^2.
688 pot *= 4 * M_PI * gmx::square(rlist + std::sqrt(s2)) / boxvol;
690 /* Add the unsigned drift to avoid cancellation of errors */
691 drift_tot += std::abs(pot);
695 return drift_tot;
698 // Returns the chance that a particle in a cluster is at distance rlist
699 // when the cluster is at distance rlist
700 static real surface_frac(int cluster_size, real particle_distance, real rlist)
702 real d, area_rel;
704 if (rlist < 0.5 * particle_distance)
706 /* We have non overlapping spheres */
707 return 1.0;
710 /* Half the inter-particle distance relative to rlist */
711 d = 0.5 * particle_distance / rlist;
713 /* Determine the area of the surface at distance rlist to the closest
714 * particle, relative to surface of a sphere of radius rlist.
715 * The formulas below assume close to cubic cells for the pair search grid,
716 * which the pair search code tries to achieve.
717 * Note that in practice particle distances will not be delta distributed,
718 * but have some spread, often involving shorter distances,
719 * as e.g. O-H bonds in a water molecule. Thus the estimates below will
720 * usually be slightly too high and thus conservative.
722 switch (cluster_size)
724 case 1:
725 /* One particle: trivial */
726 area_rel = 1.0;
727 break;
728 case 2:
729 /* Two particles: two spheres at fractional distance 2*a */
730 area_rel = 1.0 + d;
731 break;
732 case 4:
733 /* We assume a perfect, symmetric tetrahedron geometry.
734 * The surface around a tetrahedron is too complex for a full
735 * analytical solution, so we use a Taylor expansion.
737 area_rel = (1.0
738 + 1 / M_PI
739 * (6 * std::acos(1 / std::sqrt(3)) * d
740 + std::sqrt(3) * d * d
741 * (1.0 + 5.0 / 18.0 * d * d + 7.0 / 45.0 * d * d * d * d
742 + 83.0 / 756.0 * d * d * d * d * d * d)));
743 break;
744 default: gmx_incons("surface_frac called with unsupported cluster_size");
747 return area_rel / cluster_size;
750 /* Returns the negative of the third derivative of a potential r^-p
751 * with a force-switch function, evaluated at the cut-off rc.
753 static real md3_force_switch(real p, real rswitch, real rc)
755 /* The switched force function is:
756 * p*r^-(p+1) + a*(r - rswitch)^2 + b*(r - rswitch)^3
758 real a, b;
759 real md3_pot, md3_sw;
761 a = -((p + 4) * rc - (p + 1) * rswitch) / (pow(rc, p + 2) * gmx::square(rc - rswitch));
762 b = ((p + 3) * rc - (p + 1) * rswitch) / (pow(rc, p + 2) * gmx::power3(rc - rswitch));
764 md3_pot = (p + 2) * (p + 1) * p * pow(rc, p + 3);
765 md3_sw = 2 * a + 6 * b * (rc - rswitch);
767 return md3_pot + md3_sw;
770 /* Returns the variance of the atomic displacement over timePeriod.
772 * Note: When not using BD with a non-mass dependendent friction coefficient,
773 * the return value still needs to be divided by the particle mass.
775 static real displacementVariance(const t_inputrec& ir, real temperature, real timePeriod)
777 real kT_fac;
779 if (ir.eI == eiBD)
781 /* Get the displacement distribution from the random component only.
782 * With accurate integration the systematic (force) displacement
783 * should be negligible (unless nstlist is extremely large, which
784 * you wouldn't do anyhow).
786 kT_fac = 2 * BOLTZ * temperature * timePeriod;
787 if (ir.bd_fric > 0)
789 /* This is directly sigma^2 of the displacement */
790 kT_fac /= ir.bd_fric;
792 else
794 /* Per group tau_t is not implemented yet, use the maximum */
795 real tau_t = ir.opts.tau_t[0];
796 for (int i = 1; i < ir.opts.ngtc; i++)
798 tau_t = std::max(tau_t, ir.opts.tau_t[i]);
801 kT_fac *= tau_t;
802 /* This kT_fac needs to be divided by the mass to get sigma^2 */
805 else
807 kT_fac = BOLTZ * temperature * gmx::square(timePeriod);
810 return kT_fac;
813 /* Returns the largest sigma of the Gaussian displacement over all particle
814 * types. This ignores constraints, so is an overestimate.
816 static real maxSigma(real kT_fac, gmx::ArrayRef<const VerletbufAtomtype> att)
818 GMX_ASSERT(!att.empty(), "We should have at least one type");
819 real smallestMass = att[0].prop.mass;
820 for (const auto& atomType : att)
822 smallestMass = std::min(smallestMass, atomType.prop.mass);
825 return 2 * std::sqrt(kT_fac / smallestMass);
828 real calcVerletBufferSize(const gmx_mtop_t& mtop,
829 const real boxVolume,
830 const t_inputrec& ir,
831 const int nstlist,
832 const int listLifetime,
833 real referenceTemperature,
834 const VerletbufListSetup& listSetup)
836 double resolution;
837 char* env;
839 real particle_distance;
840 real nb_clust_frac_pairs_not_in_list_at_cutoff;
842 real elfac;
843 int ib0, ib1, ib;
844 real rb, rl;
845 real drift;
847 if (!EI_DYNAMICS(ir.eI))
849 gmx_incons(
850 "Can only determine the Verlet buffer size for integrators that perform dynamics");
852 if (ir.verletbuf_tol <= 0)
854 gmx_incons("The Verlet buffer tolerance needs to be larger than zero");
857 if (referenceTemperature < 0)
859 /* We use the maximum temperature with multiple T-coupl groups.
860 * We could use a per particle temperature, but since particles
861 * interact, this might underestimate the buffer size.
863 referenceTemperature = maxReferenceTemperature(ir);
865 GMX_RELEASE_ASSERT(referenceTemperature >= 0,
866 "Without T-coupling we should not end up here");
869 /* Resolution of the buffer size */
870 resolution = 0.001;
872 env = getenv("GMX_VERLET_BUFFER_RES");
873 if (env != nullptr)
875 sscanf(env, "%lf", &resolution);
878 /* In an atom wise pair-list there would be no pairs in the list
879 * beyond the pair-list cut-off.
880 * However, we use a pair-list of groups vs groups of atoms.
881 * For groups of 4 atoms, the parallelism of SSE instructions, only
882 * 10% of the atoms pairs are not in the list just beyond the cut-off.
883 * As this percentage increases slowly compared to the decrease of the
884 * Gaussian displacement distribution over this range, we can simply
885 * reduce the drift by this fraction.
886 * For larger groups, e.g. of 8 atoms, this fraction will be lower,
887 * so then buffer size will be on the conservative (large) side.
889 * Note that the formulas used here do not take into account
890 * cancellation of errors which could occur by missing both
891 * attractive and repulsive interactions.
893 * The only major assumption is homogeneous particle distribution.
894 * For an inhomogeneous system, such as a liquid-vapor system,
895 * the buffer will be underestimated. The actual energy drift
896 * will be higher by the factor: local/homogeneous particle density.
898 * The results of this estimate have been checked againt simulations.
899 * In most cases the real drift differs by less than a factor 2.
902 /* Worst case assumption: HCP packing of particles gives largest distance */
903 particle_distance = std::cbrt(boxVolume * std::sqrt(2) / mtop.natoms);
905 /* TODO: Obtain masses through (future) integrator functionality
906 * to avoid scattering the code with (or forgetting) checks.
908 const bool setMassesToOne = (ir.eI == eiBD && ir.bd_fric > 0);
909 const auto att = getVerletBufferAtomtypes(mtop, setMassesToOne);
910 GMX_ASSERT(!att.empty(), "We expect at least one type");
912 if (debug)
914 fprintf(debug, "particle distance assuming HCP packing: %f nm\n", particle_distance);
915 fprintf(debug, "energy drift atom types: %zu\n", att.size());
918 pot_derivatives_t ljDisp = { 0, 0, 0 };
919 pot_derivatives_t ljRep = { 0, 0, 0 };
920 real repPow = mtop.ffparams.reppow;
922 if (ir.vdwtype == evdwCUT)
924 real sw_range, md3_pswf;
926 switch (ir.vdw_modifier)
928 case eintmodNONE:
929 case eintmodPOTSHIFT:
930 /* -dV/dr of -r^-6 and r^-reppow */
931 ljDisp.md1 = -6 * std::pow(ir.rvdw, -7.0);
932 ljRep.md1 = repPow * std::pow(ir.rvdw, -(repPow + 1));
933 /* The contribution of the higher derivatives is negligible */
934 break;
935 case eintmodFORCESWITCH:
936 /* At the cut-off: V=V'=V''=0, so we use only V''' */
937 ljDisp.md3 = -md3_force_switch(6.0, ir.rvdw_switch, ir.rvdw);
938 ljRep.md3 = md3_force_switch(repPow, ir.rvdw_switch, ir.rvdw);
939 break;
940 case eintmodPOTSWITCH:
941 /* At the cut-off: V=V'=V''=0.
942 * V''' is given by the original potential times
943 * the third derivative of the switch function.
945 sw_range = ir.rvdw - ir.rvdw_switch;
946 md3_pswf = 60.0 / gmx::power3(sw_range);
948 ljDisp.md3 = -std::pow(ir.rvdw, -6.0) * md3_pswf;
949 ljRep.md3 = std::pow(ir.rvdw, -repPow) * md3_pswf;
950 break;
951 default: gmx_incons("Unimplemented VdW modifier");
954 else if (EVDW_PME(ir.vdwtype))
956 real b = calc_ewaldcoeff_lj(ir.rvdw, ir.ewald_rtol_lj);
957 real r = ir.rvdw;
958 real br = b * r;
959 real br2 = br * br;
960 real br4 = br2 * br2;
961 real br6 = br4 * br2;
962 // -dV/dr of g(br)*r^-6 [where g(x) = exp(-x^2)(1+x^2+x^4/2),
963 // see LJ-PME equations in manual] and r^-reppow
964 ljDisp.md1 = -std::exp(-br2) * (br6 + 3.0 * br4 + 6.0 * br2 + 6.0) * std::pow(r, -7.0);
965 ljRep.md1 = repPow * pow(r, -(repPow + 1));
966 // The contribution of the higher derivatives is negligible
968 else
970 gmx_fatal(FARGS,
971 "Energy drift calculation is only implemented for plain cut-off Lennard-Jones "
972 "interactions");
975 elfac = ONE_4PI_EPS0 / ir.epsilon_r;
977 // Determine the 1st and 2nd derivative for the electostatics
978 pot_derivatives_t elec = { 0, 0, 0 };
980 if (ir.coulombtype == eelCUT || EEL_RF(ir.coulombtype))
982 real eps_rf, k_rf;
984 if (ir.coulombtype == eelCUT)
986 eps_rf = 1;
987 k_rf = 0;
989 else
991 eps_rf = ir.epsilon_rf / ir.epsilon_r;
992 if (eps_rf != 0)
994 k_rf = (eps_rf - ir.epsilon_r) / (gmx::power3(ir.rcoulomb) * (2 * eps_rf + ir.epsilon_r));
996 else
998 /* epsilon_rf = infinity */
999 k_rf = 0.5 / gmx::power3(ir.rcoulomb);
1003 if (eps_rf > 0)
1005 elec.md1 = elfac * (1.0 / gmx::square(ir.rcoulomb) - 2 * k_rf * ir.rcoulomb);
1007 elec.d2 = elfac * (2.0 / gmx::power3(ir.rcoulomb) + 2 * k_rf);
1009 else if (EEL_PME(ir.coulombtype) || ir.coulombtype == eelEWALD)
1011 real b, rc, br;
1013 b = calc_ewaldcoeff_q(ir.rcoulomb, ir.ewald_rtol);
1014 rc = ir.rcoulomb;
1015 br = b * rc;
1016 elec.md1 = elfac * (b * std::exp(-br * br) * M_2_SQRTPI / rc + std::erfc(br) / (rc * rc));
1017 elec.d2 = elfac / (rc * rc)
1018 * (2 * b * (1 + br * br) * std::exp(-br * br) * M_2_SQRTPI + 2 * std::erfc(br) / rc);
1020 else
1022 gmx_fatal(FARGS,
1023 "Energy drift calculation is only implemented for Reaction-Field and Ewald "
1024 "electrostatics");
1027 /* Determine the variance of the atomic displacement
1028 * over list_lifetime steps: kT_fac
1029 * For inertial dynamics (not Brownian dynamics) the mass factor
1030 * is not included in kT_fac, it is added later.
1032 const real kT_fac = displacementVariance(ir, referenceTemperature, listLifetime * ir.delta_t);
1034 if (debug)
1036 fprintf(debug, "Derivatives of non-bonded potentials at the cut-off:\n");
1037 fprintf(debug, "LJ disp. -V' %9.2e V'' %9.2e -V''' %9.2e\n", ljDisp.md1, ljDisp.d2, ljDisp.md3);
1038 fprintf(debug, "LJ rep. -V' %9.2e V'' %9.2e -V''' %9.2e\n", ljRep.md1, ljRep.d2, ljRep.md3);
1039 fprintf(debug, "Electro. -V' %9.2e V'' %9.2e\n", elec.md1, elec.d2);
1040 fprintf(debug, "sqrt(kT_fac) %f\n", std::sqrt(kT_fac));
1043 /* Search using bisection */
1044 ib0 = -1;
1045 /* The drift will be neglible at 5 times the max sigma */
1046 ib1 = static_cast<int>(5 * maxSigma(kT_fac, att) / resolution) + 1;
1047 while (ib1 - ib0 > 1)
1049 ib = (ib0 + ib1) / 2;
1050 rb = ib * resolution;
1051 rl = std::max(ir.rvdw, ir.rcoulomb) + rb;
1053 /* Calculate the average energy drift at the last step
1054 * of the nstlist steps at which the pair-list is used.
1056 drift = energyDrift(att, &mtop.ffparams, kT_fac, &ljDisp, &ljRep, &elec, ir.rvdw,
1057 ir.rcoulomb, rl, boxVolume);
1059 /* Correct for the fact that we are using a Ni x Nj particle pair list
1060 * and not a 1 x 1 particle pair list. This reduces the drift.
1062 /* We don't have a formula for 8 (yet), use 4 which is conservative */
1063 nb_clust_frac_pairs_not_in_list_at_cutoff =
1064 surface_frac(std::min(listSetup.cluster_size_i, 4), particle_distance, rl)
1065 * surface_frac(std::min(listSetup.cluster_size_j, 4), particle_distance, rl);
1066 drift *= nb_clust_frac_pairs_not_in_list_at_cutoff;
1068 /* Convert the drift to drift per unit time per atom */
1069 drift /= nstlist * ir.delta_t * mtop.natoms;
1071 if (debug)
1073 fprintf(debug, "ib %3d %3d %3d rb %.3f %dx%d fac %.3f drift %.1e\n", ib0, ib, ib1, rb,
1074 listSetup.cluster_size_i, listSetup.cluster_size_j,
1075 nb_clust_frac_pairs_not_in_list_at_cutoff, drift);
1078 if (std::abs(drift) > ir.verletbuf_tol)
1080 ib0 = ib;
1082 else
1084 ib1 = ib;
1088 return std::max(ir.rvdw, ir.rcoulomb) + ib1 * resolution;
1091 /* Returns the pairlist buffer size for use as a minimum buffer size
1093 * Note that this is a rather crude estimate. It is ok for a buffer
1094 * set for good energy conservation or RF electrostatics. But it is
1095 * too small with PME and the buffer set with the default tolerance.
1097 static real minCellSizeFromPairlistBuffer(const t_inputrec& ir)
1099 return ir.rlist - std::max(ir.rvdw, ir.rcoulomb);
1102 static real chanceOfAtomCrossingCell(gmx::ArrayRef<const VerletbufAtomtype> atomtypes, real kT_fac, real cellSize)
1104 /* We assume atoms are distributed uniformly over the cell width.
1105 * Once an atom has moved by more than the cellSize (as passed
1106 * as the buffer argument to energyDriftAtomPair() below),
1107 * the chance of crossing the boundary of the neighbor cell
1108 * thus increases as 1/cellSize with the additional displacement
1109 * on top of cellSize. We thus create a linear interaction with
1110 * derivative = -1/cellSize. Using this in the energyDriftAtomPair
1111 * function will return the chance of crossing the next boundary.
1113 const pot_derivatives_t boundaryInteraction = { 1 / cellSize, 0, 0 };
1115 real chance = 0;
1116 for (const VerletbufAtomtype& att : atomtypes)
1118 const atom_nonbonded_kinetic_prop_t& propAtom = att.prop;
1119 real s2_2d;
1120 real s2_3d;
1121 get_atom_sigma2(kT_fac, &propAtom, &s2_2d, &s2_3d);
1123 real chancePerAtom = energyDriftAtomPair(propAtom.bConstr, false, s2_2d + s2_3d, s2_2d, 0,
1124 cellSize, &boundaryInteraction);
1126 if (propAtom.bConstr)
1128 /* energyDriftAtomPair() uses an unlimited Gaussian displacement
1129 * distribution for constrained atoms, whereas they can
1130 * actually not move more than the COM of the two constrained
1131 * atoms plus twice the distance from the COM.
1132 * Use this maximum, limited displacement when this results in
1133 * a smaller chance (note that this is still an overestimate).
1135 real massFraction = propAtom.con_mass / (propAtom.mass + propAtom.con_mass);
1136 real comDistance = propAtom.con_len * massFraction;
1138 real chanceWithMaxDistance = energyDriftAtomPair(
1139 false, false, s2_3d, 0, 0, cellSize - 2 * comDistance, &boundaryInteraction);
1140 chancePerAtom = std::min(chancePerAtom, chanceWithMaxDistance);
1143 /* Take into account the line density of the boundary */
1144 chancePerAtom /= cellSize;
1146 chance += att.n * chancePerAtom;
1149 return chance;
1152 /* Struct for storing constraint properties of atoms */
1153 struct AtomConstraintProps
1155 void addConstraint(real length)
1157 numConstraints += 1;
1158 sumLengths += length;
1161 int numConstraints = 0; /* The number of constraints of an atom */
1162 real sumLengths = 0; /* The sum of constraint length over the constraints */
1165 /* Constructs and returns a list of constraint properties per atom */
1166 static std::vector<AtomConstraintProps> getAtomConstraintProps(const gmx_moltype_t& moltype,
1167 const gmx_ffparams_t& ffparams)
1169 const t_atoms& atoms = moltype.atoms;
1170 std::vector<AtomConstraintProps> props(atoms.nr);
1172 for (const auto& ilist : extractILists(moltype.ilist, IF_CONSTRAINT))
1174 // Settles are handled separately
1175 if (ilist.functionType == F_SETTLE)
1177 continue;
1180 for (size_t i = 0; i < ilist.iatoms.size(); i += ilistStride(ilist))
1182 int type = ilist.iatoms[i];
1183 int a1 = ilist.iatoms[i + 1];
1184 int a2 = ilist.iatoms[i + 2];
1185 real length = ffparams.iparams[type].constr.dA;
1186 props[a1].addConstraint(length);
1187 props[a2].addConstraint(length);
1191 return props;
1194 /* Return the chance of at least one update group in a molecule crossing a cell of size cellSize */
1195 static real chanceOfUpdateGroupCrossingCell(const gmx_moltype_t& moltype,
1196 const gmx_ffparams_t& ffparams,
1197 const gmx::RangePartitioning& updateGrouping,
1198 real kT_fac,
1199 real cellSize)
1201 const t_atoms& atoms = moltype.atoms;
1202 GMX_ASSERT(updateGrouping.fullRange().end() == atoms.nr,
1203 "The update groups should match the molecule type");
1205 const pot_derivatives_t boundaryInteraction = { 1 / cellSize, 0, 0 };
1207 const auto atomConstraintProps = getAtomConstraintProps(moltype, ffparams);
1209 real chance = 0;
1210 for (int group = 0; group < updateGrouping.numBlocks(); group++)
1212 const auto& block = updateGrouping.block(group);
1213 /* Determine the number of atoms with constraints and the mass of the COG */
1214 int numAtomsWithConstraints = 0;
1215 real massSum = 0;
1216 for (const int atom : block)
1218 if (atomConstraintProps[atom].numConstraints > 0)
1220 numAtomsWithConstraints++;
1222 massSum += moltype.atoms.atom[atom].m;
1224 /* Determine the maximum possible distance between the center of mass
1225 * and the center of geometry of the update group
1227 real maxComCogDistance = 0;
1228 if (numAtomsWithConstraints == 2)
1230 for (const int atom : block)
1232 if (atomConstraintProps[atom].numConstraints > 0)
1234 GMX_ASSERT(atomConstraintProps[atom].numConstraints == 1,
1235 "Two atoms should be connected by one constraint");
1236 maxComCogDistance = std::abs(atoms.atom[atom].m / massSum - 0.5)
1237 * atomConstraintProps[atom].sumLengths;
1238 break;
1242 else if (numAtomsWithConstraints > 2)
1244 for (const int atom : block)
1246 if (atomConstraintProps[atom].numConstraints == numAtomsWithConstraints - 1)
1248 real comCogDistance = atomConstraintProps[atom].sumLengths / numAtomsWithConstraints;
1249 maxComCogDistance = std::max(maxComCogDistance, comCogDistance);
1253 else if (block.size() > 1)
1255 // All normal atoms must be connected by SETTLE
1256 for (const int atom : block)
1258 const auto& ilist = moltype.ilist[F_SETTLE];
1259 GMX_RELEASE_ASSERT(!ilist.empty(),
1260 "There should be at least one settle in this moltype");
1261 for (int i = 0; i < ilist.size(); i += 1 + NRAL(F_SETTLE))
1263 if (atom == ilist.iatoms[i + 1])
1265 const t_iparams& iparams = ffparams.iparams[ilist.iatoms[i]];
1266 real dOH = iparams.settle.doh;
1267 real dHH = iparams.settle.dhh;
1268 real dOMidH = std::sqrt(dOH * dOH - 0.25_real * dHH * dHH);
1269 maxComCogDistance =
1270 std::abs(atoms.atom[atom].m / massSum - 1.0_real / 3.0_real) * dOMidH;
1275 real s2_3d = kT_fac / massSum;
1276 chance += energyDriftAtomPair(false, false, s2_3d, 0, 0, cellSize - 2 * maxComCogDistance,
1277 &boundaryInteraction);
1280 return chance;
1283 /* Return the chance of at least one update group in the system crossing a cell of size cellSize */
1284 static real chanceOfUpdateGroupCrossingCell(const gmx_mtop_t& mtop,
1285 PartitioningPerMoltype updateGrouping,
1286 real kT_fac,
1287 real cellSize)
1289 GMX_RELEASE_ASSERT(updateGrouping.size() == mtop.moltype.size(),
1290 "The update groups should match the topology");
1292 real chance = 0;
1293 for (const gmx_molblock_t& molblock : mtop.molblock)
1295 const gmx_moltype_t& moltype = mtop.moltype[molblock.type];
1296 chance += molblock.nmol
1297 * chanceOfUpdateGroupCrossingCell(moltype, mtop.ffparams,
1298 updateGrouping[molblock.type], kT_fac, cellSize);
1301 return chance;
1304 real minCellSizeForAtomDisplacement(const gmx_mtop_t& mtop,
1305 const t_inputrec& ir,
1306 PartitioningPerMoltype updateGrouping,
1307 real chanceRequested)
1309 if (!EI_DYNAMICS(ir.eI) || (EI_MD(ir.eI) && ir.etc == etcNO))
1311 return minCellSizeFromPairlistBuffer(ir);
1314 /* We use the maximum temperature with multiple T-coupl groups.
1315 * We could use a per particle temperature, but since particles
1316 * interact, this might underestimate the displacements.
1318 const real temperature = maxReferenceTemperature(ir);
1320 const bool setMassesToOne = (ir.eI == eiBD && ir.bd_fric > 0);
1322 const auto atomtypes = getVerletBufferAtomtypes(mtop, setMassesToOne);
1324 const real kT_fac = displacementVariance(ir, temperature, ir.nstlist * ir.delta_t);
1326 /* Resolution of the cell size */
1327 real resolution = 0.001;
1329 /* Search using bisection, avoid 0 and start at 1 */
1330 int ib0 = 0;
1331 /* The chance will be neglible at 10 times the max sigma */
1332 int ib1 = int(10 * maxSigma(kT_fac, atomtypes) / resolution) + 1;
1333 real cellSize = 0;
1334 while (ib1 - ib0 > 1)
1336 int ib = (ib0 + ib1) / 2;
1337 cellSize = ib * resolution;
1339 real chance;
1340 if (updateGrouping.empty())
1342 chance = chanceOfAtomCrossingCell(atomtypes, kT_fac, cellSize);
1344 else
1346 chance = chanceOfUpdateGroupCrossingCell(mtop, updateGrouping, kT_fac, cellSize);
1349 /* Note: chance is for every nstlist steps */
1350 if (chance > chanceRequested * ir.nstlist)
1352 ib0 = ib;
1354 else
1356 ib1 = ib;
1360 return cellSize;