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.
38 #include "calc_verletbuf.h"
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;
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
;
149 nbnxnKernelType
= Nbnxm::KernelType::Cpu4xN_Simd_4xN
;
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
)
173 /* Ignore massless particles */
178 while (i
< att
->size() && !atom_nonbonded_kinetic_prop_equal(prop
, (*att
)[i
].prop
))
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
)
198 return atoms
.atom
[atomIndex
].m
;
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
,
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
);
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
)
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
));
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
));
260 GMX_RELEASE_ASSERT(false, "VsiteN should not end up in this code path");
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
++;
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
;
294 if (moltype
.atoms
.atom
[aj
].ptype
== eptVSite
)
300 m_aj
= moltype
.atoms
.atom
[aj
].m
;
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
);
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
;
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
];
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
);
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
);
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 */
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
)
502 /* Complicated constraint calculation in a separate function */
503 constrained_atom_sigma2(kT_fac
, prop
, sigma2_2d
, sigma2_3d
);
507 /* Unconstrained atom: trivial */
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.
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
,
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;
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.
570 /* For constraints: adapt r and scaling for the Gaussian */
575 approx_2dof(s2i_2d
, r_buffer
* s2i_2d
/ s2
, &sh
, &sc
);
583 approx_2dof(s2j_2d
, r_buffer
* s2j_2d
/ s2
, &sh
, &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
,
617 const pot_derivatives_t
* ljDisp
,
618 const pot_derivatives_t
* ljRep
,
619 const pot_derivatives_t
* elec
,
625 double drift_tot
= 0;
629 /* No atom displacements: no drift, avoid division by 0 */
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
;
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
;
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
,
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
;
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 */
679 pot
*= static_cast<double>(att
[i
].n
) * (att
[i
].n
- 1) / 2;
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
);
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
)
704 if (rlist
< 0.5 * particle_distance
)
706 /* We have non overlapping spheres */
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
)
725 /* One particle: trivial */
729 /* Two particles: two spheres at fractional distance 2*a */
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.
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
)));
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
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
)
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
;
789 /* This is directly sigma^2 of the displacement */
790 kT_fac
/= ir
.bd_fric
;
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
]);
802 /* This kT_fac needs to be divided by the mass to get sigma^2 */
807 kT_fac
= BOLTZ
* temperature
* gmx::square(timePeriod
);
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
,
832 const int listLifetime
,
833 real referenceTemperature
,
834 const VerletbufListSetup
& listSetup
)
839 real particle_distance
;
840 real nb_clust_frac_pairs_not_in_list_at_cutoff
;
847 if (!EI_DYNAMICS(ir
.eI
))
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 */
872 env
= getenv("GMX_VERLET_BUFFER_RES");
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");
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
)
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 */
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
);
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
;
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
);
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
971 "Energy drift calculation is only implemented for plain cut-off Lennard-Jones "
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
))
984 if (ir
.coulombtype
== eelCUT
)
991 eps_rf
= ir
.epsilon_rf
/ ir
.epsilon_r
;
994 k_rf
= (eps_rf
- ir
.epsilon_r
) / (gmx::power3(ir
.rcoulomb
) * (2 * eps_rf
+ ir
.epsilon_r
));
998 /* epsilon_rf = infinity */
999 k_rf
= 0.5 / gmx::power3(ir
.rcoulomb
);
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
)
1013 b
= calc_ewaldcoeff_q(ir
.rcoulomb
, ir
.ewald_rtol
);
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
);
1023 "Energy drift calculation is only implemented for Reaction-Field and Ewald "
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
);
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 */
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
;
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
)
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 };
1116 for (const VerletbufAtomtype
& att
: atomtypes
)
1118 const atom_nonbonded_kinetic_prop_t
& propAtom
= att
.prop
;
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
;
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
)
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
);
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
,
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
);
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;
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
;
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
);
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
);
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
,
1289 GMX_RELEASE_ASSERT(updateGrouping
.size() == mtop
.moltype
.size(),
1290 "The update groups should match the topology");
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
);
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 */
1331 /* The chance will be neglible at 10 times the max sigma */
1332 int ib1
= int(10 * maxSigma(kT_fac
, atomtypes
) / resolution
) + 1;
1334 while (ib1
- ib0
> 1)
1336 int ib
= (ib0
+ ib1
) / 2;
1337 cellSize
= ib
* resolution
;
1340 if (updateGrouping
.empty())
1342 chance
= chanceOfAtomCrossingCell(atomtypes
, kT_fac
, cellSize
);
1346 chance
= chanceOfUpdateGroupCrossingCell(mtop
, updateGrouping
, kT_fac
, cellSize
);
1349 /* Note: chance is for every nstlist steps */
1350 if (chance
> chanceRequested
* ir
.nstlist
)