Make PBC type enumeration into PbcType enum class
[gromacs.git] / src / gromacs / pulling / pull.cpp
blob712399a7f19f755f5de12f832bbcbe21df9ec7f0
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2004, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
7 * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
8 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9 * and including many others, as listed in the AUTHORS file in the
10 * top-level source directory and at http://www.gromacs.org.
12 * GROMACS is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 2.1
15 * of the License, or (at your option) any later version.
17 * GROMACS is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with GROMACS; if not, see
24 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 * If you want to redistribute modifications to GROMACS, please
28 * consider that scientific software is very special. Version
29 * control is crucial - bugs must be traceable. We will be happy to
30 * consider code for inclusion in the official distribution, but
31 * derived work must not be called official GROMACS. Details are found
32 * in the README & COPYING files - if they are missing, get the
33 * official version at http://www.gromacs.org.
35 * To help us fund GROMACS development, we humbly ask that you cite
36 * the research papers on the package. Check out http://www.gromacs.org.
38 #include "gmxpre.h"
40 #include "pull.h"
42 #include "config.h"
44 #include <cassert>
45 #include <cinttypes>
46 #include <cmath>
47 #include <cstdlib>
49 #include <algorithm>
50 #include <memory>
52 #include "gromacs/commandline/filenm.h"
53 #include "gromacs/domdec/domdec_struct.h"
54 #include "gromacs/domdec/localatomset.h"
55 #include "gromacs/domdec/localatomsetmanager.h"
56 #include "gromacs/fileio/gmxfio.h"
57 #include "gromacs/gmxlib/network.h"
58 #include "gromacs/math/functions.h"
59 #include "gromacs/math/units.h"
60 #include "gromacs/math/vec.h"
61 #include "gromacs/math/vectypes.h"
62 #include "gromacs/mdlib/gmx_omp_nthreads.h"
63 #include "gromacs/mdtypes/commrec.h"
64 #include "gromacs/mdtypes/forceoutput.h"
65 #include "gromacs/mdtypes/inputrec.h"
66 #include "gromacs/mdtypes/md_enums.h"
67 #include "gromacs/mdtypes/mdatom.h"
68 #include "gromacs/mdtypes/state.h"
69 #include "gromacs/pbcutil/pbc.h"
70 #include "gromacs/topology/mtop_lookup.h"
71 #include "gromacs/topology/topology.h"
72 #include "gromacs/utility/cstringutil.h"
73 #include "gromacs/utility/exceptions.h"
74 #include "gromacs/utility/fatalerror.h"
75 #include "gromacs/utility/futil.h"
76 #include "gromacs/utility/gmxassert.h"
77 #include "gromacs/utility/mutex.h"
78 #include "gromacs/utility/pleasecite.h"
79 #include "gromacs/utility/real.h"
80 #include "gromacs/utility/smalloc.h"
81 #include "gromacs/utility/strconvert.h"
83 #include "pull_internal.h"
85 namespace gmx
87 extern template LocalAtomSet LocalAtomSetManager::add<void, void>(ArrayRef<const int> globalAtomIndex);
88 } // namespace gmx
90 static int groupPbcFromParams(const t_pull_group& params, bool setPbcRefToPrevStepCOM)
92 if (params.nat <= 1)
94 /* no PBC required */
95 return epgrppbcNONE;
97 else if (params.pbcatom >= 0)
99 if (setPbcRefToPrevStepCOM)
101 return epgrppbcPREVSTEPCOM;
103 else
105 return epgrppbcREFAT;
108 else
110 return epgrppbcCOS;
114 /* NOTE: The params initialization currently copies pointers.
115 * So the lifetime of the source, currently always inputrec,
116 * should not end before that of this object.
117 * This will be fixed when the pointers are replacted by std::vector.
119 pull_group_work_t::pull_group_work_t(const t_pull_group& params,
120 gmx::LocalAtomSet atomSet,
121 bool bSetPbcRefToPrevStepCOM) :
122 params(params),
123 epgrppbc(groupPbcFromParams(params, bSetPbcRefToPrevStepCOM)),
124 needToCalcCom(false),
125 atomSet(atomSet),
126 mwscale(0),
127 wscale(0),
128 invtm(0)
130 clear_dvec(x);
131 clear_dvec(xp);
134 bool pull_coordinate_is_angletype(const t_pull_coord* pcrd)
136 return (pcrd->eGeom == epullgANGLE || pcrd->eGeom == epullgDIHEDRAL || pcrd->eGeom == epullgANGLEAXIS);
139 static bool pull_coordinate_is_directional(const t_pull_coord* pcrd)
141 return (pcrd->eGeom == epullgDIR || pcrd->eGeom == epullgDIRPBC
142 || pcrd->eGeom == epullgDIRRELATIVE || pcrd->eGeom == epullgCYL);
145 const char* pull_coordinate_units(const t_pull_coord* pcrd)
147 return pull_coordinate_is_angletype(pcrd) ? "deg" : "nm";
150 double pull_conversion_factor_userinput2internal(const t_pull_coord* pcrd)
152 if (pull_coordinate_is_angletype(pcrd))
154 return DEG2RAD;
156 else
158 return 1.0;
162 double pull_conversion_factor_internal2userinput(const t_pull_coord* pcrd)
164 if (pull_coordinate_is_angletype(pcrd))
166 return RAD2DEG;
168 else
170 return 1.0;
174 /* Apply forces in a mass weighted fashion for part of the pull group */
175 static void apply_forces_grp_part(const pull_group_work_t* pgrp,
176 int ind_start,
177 int ind_end,
178 const t_mdatoms* md,
179 const dvec f_pull,
180 int sign,
181 rvec* f)
183 double inv_wm = pgrp->mwscale;
185 auto localAtomIndices = pgrp->atomSet.localIndex();
186 for (int i = ind_start; i < ind_end; i++)
188 int ii = localAtomIndices[i];
189 double wmass = md->massT[ii];
190 if (!pgrp->localWeights.empty())
192 wmass *= pgrp->localWeights[i];
195 for (int d = 0; d < DIM; d++)
197 f[ii][d] += sign * wmass * f_pull[d] * inv_wm;
202 /* Apply forces in a mass weighted fashion */
203 static void apply_forces_grp(const pull_group_work_t* pgrp,
204 const t_mdatoms* md,
205 const dvec f_pull,
206 int sign,
207 rvec* f,
208 int nthreads)
210 auto localAtomIndices = pgrp->atomSet.localIndex();
212 if (pgrp->params.nat == 1 && pgrp->atomSet.numAtomsLocal() == 1)
214 /* Only one atom and our rank has this atom: we can skip
215 * the mass weighting, which means that this code also works
216 * for mass=0, e.g. with a virtual site.
218 for (int d = 0; d < DIM; d++)
220 f[localAtomIndices[0]][d] += sign * f_pull[d];
223 else
225 if (localAtomIndices.size() <= c_pullMaxNumLocalAtomsSingleThreaded)
227 apply_forces_grp_part(pgrp, 0, localAtomIndices.size(), md, f_pull, sign, f);
229 else
231 #pragma omp parallel for num_threads(nthreads) schedule(static)
232 for (int th = 0; th < nthreads; th++)
234 int ind_start = (localAtomIndices.size() * (th + 0)) / nthreads;
235 int ind_end = (localAtomIndices.size() * (th + 1)) / nthreads;
236 apply_forces_grp_part(pgrp, ind_start, ind_end, md, f_pull, sign, f);
242 /* Apply forces in a mass weighted fashion to a cylinder group */
243 static void apply_forces_cyl_grp(const pull_group_work_t* pgrp,
244 const double dv_corr,
245 const t_mdatoms* md,
246 const dvec f_pull,
247 double f_scal,
248 int sign,
249 rvec* f,
250 int gmx_unused nthreads)
252 double inv_wm = pgrp->mwscale;
254 auto localAtomIndices = pgrp->atomSet.localIndex();
256 /* The cylinder group is always a slab in the system, thus large.
257 * Therefore we always thread-parallelize this group.
259 int numAtomsLocal = localAtomIndices.size();
260 #pragma omp parallel for num_threads(nthreads) schedule(static)
261 for (int i = 0; i < numAtomsLocal; i++)
263 double weight = pgrp->localWeights[i];
264 if (weight == 0)
266 continue;
268 int ii = localAtomIndices[i];
269 double mass = md->massT[ii];
270 /* The stored axial distance from the cylinder center (dv) needs
271 * to be corrected for an offset (dv_corr), which was unknown when
272 * we calculated dv.
274 double dv_com = pgrp->dv[i] + dv_corr;
276 /* Here we not only add the pull force working along vec (f_pull),
277 * but also a radial component, due to the dependence of the weights
278 * on the radial distance.
280 for (int m = 0; m < DIM; m++)
282 f[ii][m] += sign * inv_wm * (mass * weight * f_pull[m] + pgrp->mdw[i][m] * dv_com * f_scal);
287 /* Apply torque forces in a mass weighted fashion to the groups that define
288 * the pull vector direction for pull coordinate pcrd.
290 static void apply_forces_vec_torque(const struct pull_t* pull,
291 const pull_coord_work_t* pcrd,
292 const t_mdatoms* md,
293 rvec* f)
295 const PullCoordSpatialData& spatialData = pcrd->spatialData;
297 /* The component inpr along the pull vector is accounted for in the usual
298 * way. Here we account for the component perpendicular to vec.
300 double inpr = 0;
301 for (int m = 0; m < DIM; m++)
303 inpr += spatialData.dr01[m] * spatialData.vec[m];
305 /* The torque force works along the component of the distance vector
306 * of between the two "usual" pull groups that is perpendicular to
307 * the pull vector. The magnitude of this force is the "usual" scale force
308 * multiplied with the ratio of the distance between the two "usual" pull
309 * groups and the distance between the two groups that define the vector.
311 dvec f_perp;
312 for (int m = 0; m < DIM; m++)
314 f_perp[m] = (spatialData.dr01[m] - inpr * spatialData.vec[m]) / spatialData.vec_len * pcrd->scalarForce;
317 /* Apply the force to the groups defining the vector using opposite signs */
318 apply_forces_grp(&pull->group[pcrd->params.group[2]], md, f_perp, -1, f, pull->nthreads);
319 apply_forces_grp(&pull->group[pcrd->params.group[3]], md, f_perp, 1, f, pull->nthreads);
322 /* Apply forces in a mass weighted fashion */
323 static void apply_forces_coord(struct pull_t* pull,
324 int coord,
325 const PullCoordVectorForces& forces,
326 const t_mdatoms* md,
327 rvec* f)
329 /* Here it would be more efficient to use one large thread-parallel
330 * region instead of potential parallel regions within apply_forces_grp.
331 * But there could be overlap between pull groups and this would lead
332 * to data races.
335 const pull_coord_work_t& pcrd = pull->coord[coord];
337 if (pcrd.params.eGeom == epullgCYL)
339 apply_forces_cyl_grp(&pull->dyna[coord], pcrd.spatialData.cyl_dev, md, forces.force01,
340 pcrd.scalarForce, -1, f, pull->nthreads);
342 /* Sum the force along the vector and the radial force */
343 dvec f_tot;
344 for (int m = 0; m < DIM; m++)
346 f_tot[m] = forces.force01[m] + pcrd.scalarForce * pcrd.spatialData.ffrad[m];
348 apply_forces_grp(&pull->group[pcrd.params.group[1]], md, f_tot, 1, f, pull->nthreads);
350 else
352 if (pcrd.params.eGeom == epullgDIRRELATIVE)
354 /* We need to apply the torque forces to the pull groups
355 * that define the pull vector.
357 apply_forces_vec_torque(pull, &pcrd, md, f);
360 if (pull->group[pcrd.params.group[0]].params.nat > 0)
362 apply_forces_grp(&pull->group[pcrd.params.group[0]], md, forces.force01, -1, f, pull->nthreads);
364 apply_forces_grp(&pull->group[pcrd.params.group[1]], md, forces.force01, 1, f, pull->nthreads);
366 if (pcrd.params.ngroup >= 4)
368 apply_forces_grp(&pull->group[pcrd.params.group[2]], md, forces.force23, -1, f, pull->nthreads);
369 apply_forces_grp(&pull->group[pcrd.params.group[3]], md, forces.force23, 1, f, pull->nthreads);
371 if (pcrd.params.ngroup >= 6)
373 apply_forces_grp(&pull->group[pcrd.params.group[4]], md, forces.force45, -1, f, pull->nthreads);
374 apply_forces_grp(&pull->group[pcrd.params.group[5]], md, forces.force45, 1, f, pull->nthreads);
379 real max_pull_distance2(const pull_coord_work_t* pcrd, const t_pbc* pbc)
381 /* Note that this maximum distance calculation is more complex than
382 * most other cases in GROMACS, since here we have to take care of
383 * distance calculations that don't involve all three dimensions.
384 * For example, we can use distances that are larger than the
385 * box X and Y dimensions for a box that is elongated along Z.
388 real max_d2 = GMX_REAL_MAX;
390 if (pull_coordinate_is_directional(&pcrd->params))
392 /* Directional pulling along along direction pcrd->vec.
393 * Calculating the exact maximum distance is complex and bug-prone.
394 * So we take a safe approach by not allowing distances that
395 * are larger than half the distance between unit cell faces
396 * along dimensions involved in pcrd->vec.
398 for (int m = 0; m < DIM; m++)
400 if (m < pbc->ndim_ePBC && pcrd->spatialData.vec[m] != 0)
402 real imageDistance2 = gmx::square(pbc->box[m][m]);
403 for (int d = m + 1; d < DIM; d++)
405 imageDistance2 -= gmx::square(pbc->box[d][m]);
407 max_d2 = std::min(max_d2, imageDistance2);
411 else
413 /* Distance pulling along dimensions with pcrd->params.dim[d]==1.
414 * We use half the minimum box vector length of the dimensions involved.
415 * This is correct for all cases, except for corner cases with
416 * triclinic boxes where e.g. dim[XX]=1 and dim[YY]=0 with
417 * box[YY][XX]!=0 and box[YY][YY] < box[XX][XX]. But since even
418 * in such corner cases the user could get correct results,
419 * depending on the details of the setup, we avoid further
420 * code complications.
422 for (int m = 0; m < DIM; m++)
424 if (m < pbc->ndim_ePBC && pcrd->params.dim[m] != 0)
426 real imageDistance2 = gmx::square(pbc->box[m][m]);
427 for (int d = 0; d < m; d++)
429 if (pcrd->params.dim[d] != 0)
431 imageDistance2 += gmx::square(pbc->box[m][d]);
434 max_d2 = std::min(max_d2, imageDistance2);
439 return 0.25 * max_d2;
442 /* This function returns the distance based on coordinates xg and xref.
443 * Note that the pull coordinate struct pcrd is not modified.
445 static void low_get_pull_coord_dr(const struct pull_t* pull,
446 const pull_coord_work_t* pcrd,
447 const t_pbc* pbc,
448 dvec xg,
449 dvec xref,
450 double max_dist2,
451 dvec dr)
453 const pull_group_work_t* pgrp0 = &pull->group[pcrd->params.group[0]];
455 /* Only the first group can be an absolute reference, in that case nat=0 */
456 if (pgrp0->params.nat == 0)
458 for (int m = 0; m < DIM; m++)
460 xref[m] = pcrd->params.origin[m];
464 dvec xrefr;
465 copy_dvec(xref, xrefr);
467 dvec dref = { 0, 0, 0 };
468 if (pcrd->params.eGeom == epullgDIRPBC)
470 for (int m = 0; m < DIM; m++)
472 dref[m] = pcrd->value_ref * pcrd->spatialData.vec[m];
474 /* Add the reference position, so we use the correct periodic image */
475 dvec_inc(xrefr, dref);
478 pbc_dx_d(pbc, xg, xrefr, dr);
480 bool directional = pull_coordinate_is_directional(&pcrd->params);
481 double dr2 = 0;
482 for (int m = 0; m < DIM; m++)
484 dr[m] *= pcrd->params.dim[m];
485 if (pcrd->params.dim[m] && !(directional && pcrd->spatialData.vec[m] == 0))
487 dr2 += dr[m] * dr[m];
490 /* Check if we are close to switching to another periodic image */
491 if (max_dist2 > 0 && dr2 > 0.98 * 0.98 * max_dist2)
493 /* Note that technically there is no issue with switching periodic
494 * image, as pbc_dx_d returns the distance to the closest periodic
495 * image. However in all cases where periodic image switches occur,
496 * the pull results will be useless in practice.
498 gmx_fatal(FARGS,
499 "Distance between pull groups %d and %d (%f nm) is larger than 0.49 times the "
500 "box size (%f).\n%s",
501 pcrd->params.group[0], pcrd->params.group[1], sqrt(dr2),
502 sqrt(0.98 * 0.98 * max_dist2), pcrd->params.eGeom == epullgDIR ? "You might want to consider using \"pull-geometry = direction-periodic\" instead.\n" : "");
505 if (pcrd->params.eGeom == epullgDIRPBC)
507 dvec_inc(dr, dref);
511 /* This function returns the distance based on the contents of the pull struct.
512 * pull->coord[coord_ind].dr, and potentially vector, are updated.
514 static void get_pull_coord_dr(struct pull_t* pull, int coord_ind, const t_pbc* pbc)
516 pull_coord_work_t* pcrd = &pull->coord[coord_ind];
517 PullCoordSpatialData& spatialData = pcrd->spatialData;
519 double md2;
520 /* With AWH pulling we allow for periodic pulling with geometry=direction.
521 * TODO: Store a periodicity flag instead of checking for external pull provider.
523 if (pcrd->params.eGeom == epullgDIRPBC
524 || (pcrd->params.eGeom == epullgDIR && pcrd->params.eType == epullEXTERNAL))
526 md2 = -1;
528 else
530 md2 = static_cast<double>(max_pull_distance2(pcrd, pbc));
533 if (pcrd->params.eGeom == epullgDIRRELATIVE)
535 /* We need to determine the pull vector */
536 dvec vec;
537 int m;
539 const pull_group_work_t& pgrp2 = pull->group[pcrd->params.group[2]];
540 const pull_group_work_t& pgrp3 = pull->group[pcrd->params.group[3]];
542 pbc_dx_d(pbc, pgrp3.x, pgrp2.x, vec);
544 for (m = 0; m < DIM; m++)
546 vec[m] *= pcrd->params.dim[m];
548 spatialData.vec_len = dnorm(vec);
549 for (m = 0; m < DIM; m++)
551 spatialData.vec[m] = vec[m] / spatialData.vec_len;
553 if (debug)
555 fprintf(debug, "pull coord %d vector: %6.3f %6.3f %6.3f normalized: %6.3f %6.3f %6.3f\n",
556 coord_ind, vec[XX], vec[YY], vec[ZZ], spatialData.vec[XX], spatialData.vec[YY],
557 spatialData.vec[ZZ]);
561 pull_group_work_t* pgrp0 = &pull->group[pcrd->params.group[0]];
562 pull_group_work_t* pgrp1 = &pull->group[pcrd->params.group[1]];
564 low_get_pull_coord_dr(pull, pcrd, pbc, pgrp1->x,
565 pcrd->params.eGeom == epullgCYL ? pull->dyna[coord_ind].x : pgrp0->x, md2,
566 spatialData.dr01);
568 if (pcrd->params.ngroup >= 4)
570 pull_group_work_t *pgrp2, *pgrp3;
571 pgrp2 = &pull->group[pcrd->params.group[2]];
572 pgrp3 = &pull->group[pcrd->params.group[3]];
574 low_get_pull_coord_dr(pull, pcrd, pbc, pgrp3->x, pgrp2->x, md2, spatialData.dr23);
576 if (pcrd->params.ngroup >= 6)
578 pull_group_work_t *pgrp4, *pgrp5;
579 pgrp4 = &pull->group[pcrd->params.group[4]];
580 pgrp5 = &pull->group[pcrd->params.group[5]];
582 low_get_pull_coord_dr(pull, pcrd, pbc, pgrp5->x, pgrp4->x, md2, spatialData.dr45);
586 /* Modify x so that it is periodic in [-pi, pi)
587 * It is assumed that x is in [-3pi, 3pi) so that x
588 * needs to be shifted by at most one period.
590 static void make_periodic_2pi(double* x)
592 if (*x >= M_PI)
594 *x -= M_2PI;
596 else if (*x < -M_PI)
598 *x += M_2PI;
602 /* This function should always be used to modify pcrd->value_ref */
603 static void low_set_pull_coord_reference_value(pull_coord_work_t* pcrd, int coord_ind, double value_ref)
605 GMX_ASSERT(pcrd->params.eType != epullEXTERNAL,
606 "The pull coord reference value should not be used with type external-potential");
608 if (pcrd->params.eGeom == epullgDIST)
610 if (value_ref < 0)
612 gmx_fatal(FARGS,
613 "Pull reference distance for coordinate %d (%f) needs to be non-negative",
614 coord_ind + 1, value_ref);
617 else if (pcrd->params.eGeom == epullgANGLE || pcrd->params.eGeom == epullgANGLEAXIS)
619 if (value_ref < 0 || value_ref > M_PI)
621 gmx_fatal(FARGS,
622 "Pull reference angle for coordinate %d (%f) needs to be in the allowed "
623 "interval [0,180] deg",
624 coord_ind + 1,
625 value_ref * pull_conversion_factor_internal2userinput(&pcrd->params));
628 else if (pcrd->params.eGeom == epullgDIHEDRAL)
630 /* Allow pulling to be periodic for dihedral angles by remapping the reference value to the interval [-pi, pi). */
631 make_periodic_2pi(&value_ref);
634 pcrd->value_ref = value_ref;
637 static void update_pull_coord_reference_value(pull_coord_work_t* pcrd, int coord_ind, double t)
639 /* With zero rate the reference value is set initially and doesn't change */
640 if (pcrd->params.rate != 0)
642 double value_ref = (pcrd->params.init + pcrd->params.rate * t)
643 * pull_conversion_factor_userinput2internal(&pcrd->params);
644 low_set_pull_coord_reference_value(pcrd, coord_ind, value_ref);
648 /* Returns the dihedral angle. Updates the plane normal vectors m, n. */
649 static double get_dihedral_angle_coord(PullCoordSpatialData* spatialData)
651 double phi, sign;
652 dvec dr32; /* store instead of dr23? */
654 dsvmul(-1, spatialData->dr23, dr32);
655 dcprod(spatialData->dr01, dr32, spatialData->planevec_m); /* Normal of first plane */
656 dcprod(dr32, spatialData->dr45, spatialData->planevec_n); /* Normal of second plane */
657 phi = gmx_angle_between_dvecs(spatialData->planevec_m, spatialData->planevec_n);
659 /* Note 1: the sign below is opposite of that in the bondeds or Bekker 1994
660 * because there r_ij = ri - rj, while here dr01 = r_1 - r_0
661 * Note 2: the angle between the plane normal vectors equals pi only when
662 * both planes coincide. Thus, when phi = pi dr01 will lie in both planes and
663 * we get a positive sign below. Thus, the range of the dihedral angle is (-180, 180].
665 sign = (diprod(spatialData->dr01, spatialData->planevec_n) < 0.0) ? 1.0 : -1.0;
666 return sign * phi;
669 /* Calculates pull->coord[coord_ind].value.
670 * This function also updates pull->coord[coord_ind].dr.
672 static void get_pull_coord_distance(struct pull_t* pull, int coord_ind, const t_pbc* pbc)
674 pull_coord_work_t* pcrd;
675 int m;
677 pcrd = &pull->coord[coord_ind];
679 get_pull_coord_dr(pull, coord_ind, pbc);
681 PullCoordSpatialData& spatialData = pcrd->spatialData;
683 switch (pcrd->params.eGeom)
685 case epullgDIST:
686 /* Pull along the vector between the com's */
687 spatialData.value = dnorm(spatialData.dr01);
688 break;
689 case epullgDIR:
690 case epullgDIRPBC:
691 case epullgDIRRELATIVE:
692 case epullgCYL:
693 /* Pull along vec */
694 spatialData.value = 0;
695 for (m = 0; m < DIM; m++)
697 spatialData.value += spatialData.vec[m] * spatialData.dr01[m];
699 break;
700 case epullgANGLE:
701 spatialData.value = gmx_angle_between_dvecs(spatialData.dr01, spatialData.dr23);
702 break;
703 case epullgDIHEDRAL: spatialData.value = get_dihedral_angle_coord(&spatialData); break;
704 case epullgANGLEAXIS:
705 spatialData.value = gmx_angle_between_dvecs(spatialData.dr01, spatialData.vec);
706 break;
707 default: gmx_incons("Unsupported pull type in get_pull_coord_distance");
711 /* Returns the deviation from the reference value.
712 * Updates pull->coord[coord_ind].dr, .value and .value_ref.
714 static double get_pull_coord_deviation(struct pull_t* pull, int coord_ind, const t_pbc* pbc, double t)
716 pull_coord_work_t* pcrd;
717 double dev = 0;
719 pcrd = &pull->coord[coord_ind];
721 /* Update the reference value before computing the distance,
722 * since it is used in the distance computation with periodic pulling.
724 update_pull_coord_reference_value(pcrd, coord_ind, t);
726 get_pull_coord_distance(pull, coord_ind, pbc);
728 get_pull_coord_distance(pull, coord_ind, pbc);
730 /* Determine the deviation */
731 dev = pcrd->spatialData.value - pcrd->value_ref;
733 /* Check that values are allowed */
734 if (pcrd->params.eGeom == epullgDIST && pcrd->spatialData.value == 0)
736 /* With no vector we can not determine the direction for the force,
737 * so we set the force to zero.
739 dev = 0;
741 else if (pcrd->params.eGeom == epullgDIHEDRAL)
743 /* The reference value is in [-pi, pi). The coordinate value is in (-pi, pi].
744 Thus, the unwrapped deviation is here in (-2pi, 2pi].
745 After making it periodic, the deviation will be in [-pi, pi). */
746 make_periodic_2pi(&dev);
749 return dev;
752 double get_pull_coord_value(struct pull_t* pull, int coord_ind, const t_pbc* pbc)
754 get_pull_coord_distance(pull, coord_ind, pbc);
756 return pull->coord[coord_ind].spatialData.value;
759 void clear_pull_forces(pull_t* pull)
761 /* Zeroing the forces is only required for constraint pulling.
762 * It can happen that multiple constraint steps need to be applied
763 * and therefore the constraint forces need to be accumulated.
765 for (pull_coord_work_t& coord : pull->coord)
767 coord.scalarForce = 0;
771 /* Apply constraint using SHAKE */
772 static void
773 do_constraint(struct pull_t* pull, t_pbc* pbc, rvec* x, rvec* v, gmx_bool bMaster, tensor vir, double dt, double t)
776 dvec* r_ij; /* x[i] com of i in prev. step. Obeys constr. -> r_ij[i] */
777 dvec unc_ij; /* xp[i] com of i this step, before constr. -> unc_ij */
778 dvec* rnew; /* current 'new' positions of the groups */
779 double* dr_tot; /* the total update of the coords */
780 dvec vec;
781 double inpr;
782 double lambda, rm, invdt = 0;
783 gmx_bool bConverged_all, bConverged = FALSE;
784 int niter = 0, ii, j, m, max_iter = 100;
785 double a;
786 dvec tmp, tmp3;
788 snew(r_ij, pull->coord.size());
789 snew(dr_tot, pull->coord.size());
791 snew(rnew, pull->group.size());
793 /* copy the current unconstrained positions for use in iterations. We
794 iterate until rinew[i] and rjnew[j] obey the constraints. Then
795 rinew - pull.x_unc[i] is the correction dr to group i */
796 for (size_t g = 0; g < pull->group.size(); g++)
798 copy_dvec(pull->group[g].xp, rnew[g]);
801 /* Determine the constraint directions from the old positions */
802 for (size_t c = 0; c < pull->coord.size(); c++)
804 pull_coord_work_t* pcrd;
806 pcrd = &pull->coord[c];
808 if (pcrd->params.eType != epullCONSTRAINT)
810 continue;
813 /* Note that get_pull_coord_distance also sets pcrd->dr and pcrd->value.
814 * We don't modify dr and value anymore, so these values are also used
815 * for printing.
817 get_pull_coord_distance(pull, c, pbc);
819 const PullCoordSpatialData& spatialData = pcrd->spatialData;
820 if (debug)
822 fprintf(debug, "Pull coord %zu dr %f %f %f\n", c, spatialData.dr01[XX],
823 spatialData.dr01[YY], spatialData.dr01[ZZ]);
826 if (pcrd->params.eGeom == epullgDIR || pcrd->params.eGeom == epullgDIRPBC)
828 /* Select the component along vec */
829 a = 0;
830 for (m = 0; m < DIM; m++)
832 a += spatialData.vec[m] * spatialData.dr01[m];
834 for (m = 0; m < DIM; m++)
836 r_ij[c][m] = a * spatialData.vec[m];
839 else
841 copy_dvec(spatialData.dr01, r_ij[c]);
844 if (dnorm2(r_ij[c]) == 0)
846 gmx_fatal(FARGS,
847 "Distance for pull coordinate %zu is zero with constraint pulling, which is "
848 "not allowed.",
849 c + 1);
853 bConverged_all = FALSE;
854 while (!bConverged_all && niter < max_iter)
856 bConverged_all = TRUE;
858 /* loop over all constraints */
859 for (size_t c = 0; c < pull->coord.size(); c++)
861 pull_coord_work_t* pcrd;
862 pull_group_work_t *pgrp0, *pgrp1;
863 dvec dr0, dr1;
865 pcrd = &pull->coord[c];
867 if (pcrd->params.eType != epullCONSTRAINT)
869 continue;
872 update_pull_coord_reference_value(pcrd, c, t);
874 pgrp0 = &pull->group[pcrd->params.group[0]];
875 pgrp1 = &pull->group[pcrd->params.group[1]];
877 /* Get the current difference vector */
878 low_get_pull_coord_dr(pull, pcrd, pbc, rnew[pcrd->params.group[1]],
879 rnew[pcrd->params.group[0]], -1, unc_ij);
881 if (debug)
883 fprintf(debug, "Pull coord %zu, iteration %d\n", c, niter);
886 rm = 1.0 / (pgrp0->invtm + pgrp1->invtm);
888 switch (pcrd->params.eGeom)
890 case epullgDIST:
891 if (pcrd->value_ref <= 0)
893 gmx_fatal(
894 FARGS,
895 "The pull constraint reference distance for group %zu is <= 0 (%f)",
896 c, pcrd->value_ref);
900 double q, c_a, c_b, c_c;
902 c_a = diprod(r_ij[c], r_ij[c]);
903 c_b = diprod(unc_ij, r_ij[c]) * 2;
904 c_c = diprod(unc_ij, unc_ij) - gmx::square(pcrd->value_ref);
906 if (c_b < 0)
908 q = -0.5 * (c_b - std::sqrt(c_b * c_b - 4 * c_a * c_c));
909 lambda = -q / c_a;
911 else
913 q = -0.5 * (c_b + std::sqrt(c_b * c_b - 4 * c_a * c_c));
914 lambda = -c_c / q;
917 if (debug)
919 fprintf(debug, "Pull ax^2+bx+c=0: a=%e b=%e c=%e lambda=%e\n", c_a, c_b,
920 c_c, lambda);
924 /* The position corrections dr due to the constraints */
925 dsvmul(-lambda * rm * pgrp1->invtm, r_ij[c], dr1);
926 dsvmul(lambda * rm * pgrp0->invtm, r_ij[c], dr0);
927 dr_tot[c] += -lambda * dnorm(r_ij[c]);
928 break;
929 case epullgDIR:
930 case epullgDIRPBC:
931 case epullgCYL:
932 /* A 1-dimensional constraint along a vector */
933 a = 0;
934 for (m = 0; m < DIM; m++)
936 vec[m] = pcrd->spatialData.vec[m];
937 a += unc_ij[m] * vec[m];
939 /* Select only the component along the vector */
940 dsvmul(a, vec, unc_ij);
941 lambda = a - pcrd->value_ref;
942 if (debug)
944 fprintf(debug, "Pull inpr %e lambda: %e\n", a, lambda);
947 /* The position corrections dr due to the constraints */
948 dsvmul(-lambda * rm * pgrp1->invtm, vec, dr1);
949 dsvmul(lambda * rm * pgrp0->invtm, vec, dr0);
950 dr_tot[c] += -lambda;
951 break;
952 default: gmx_incons("Invalid enumeration value for eGeom");
955 /* DEBUG */
956 if (debug)
958 int g0, g1;
960 g0 = pcrd->params.group[0];
961 g1 = pcrd->params.group[1];
962 low_get_pull_coord_dr(pull, pcrd, pbc, rnew[g1], rnew[g0], -1, tmp);
963 low_get_pull_coord_dr(pull, pcrd, pbc, dr1, dr0, -1, tmp3);
964 fprintf(debug, "Pull cur %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n", rnew[g0][0],
965 rnew[g0][1], rnew[g0][2], rnew[g1][0], rnew[g1][1], rnew[g1][2], dnorm(tmp));
966 fprintf(debug, "Pull ref %8s %8s %8s %8s %8s %8s d: %8.5f\n", "", "", "", "", "",
967 "", pcrd->value_ref);
968 fprintf(debug, "Pull cor %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n", dr0[0],
969 dr0[1], dr0[2], dr1[0], dr1[1], dr1[2], dnorm(tmp3));
970 } /* END DEBUG */
972 /* Update the COMs with dr */
973 dvec_inc(rnew[pcrd->params.group[1]], dr1);
974 dvec_inc(rnew[pcrd->params.group[0]], dr0);
977 /* Check if all constraints are fullfilled now */
978 for (pull_coord_work_t& coord : pull->coord)
980 if (coord.params.eType != epullCONSTRAINT)
982 continue;
985 low_get_pull_coord_dr(pull, &coord, pbc, rnew[coord.params.group[1]],
986 rnew[coord.params.group[0]], -1, unc_ij);
988 switch (coord.params.eGeom)
990 case epullgDIST:
991 bConverged = fabs(dnorm(unc_ij) - coord.value_ref) < pull->params.constr_tol;
992 break;
993 case epullgDIR:
994 case epullgDIRPBC:
995 case epullgCYL:
996 for (m = 0; m < DIM; m++)
998 vec[m] = coord.spatialData.vec[m];
1000 inpr = diprod(unc_ij, vec);
1001 dsvmul(inpr, vec, unc_ij);
1002 bConverged = fabs(diprod(unc_ij, vec) - coord.value_ref) < pull->params.constr_tol;
1003 break;
1006 if (!bConverged)
1008 if (debug)
1010 fprintf(debug,
1011 "Pull constraint not converged: "
1012 "groups %d %d,"
1013 "d_ref = %f, current d = %f\n",
1014 coord.params.group[0], coord.params.group[1], coord.value_ref, dnorm(unc_ij));
1017 bConverged_all = FALSE;
1021 niter++;
1022 /* if after all constraints are dealt with and bConverged is still TRUE
1023 we're finished, if not we do another iteration */
1025 if (niter > max_iter)
1027 gmx_fatal(FARGS, "Too many iterations for constraint run: %d", niter);
1030 /* DONE ITERATING, NOW UPDATE COORDINATES AND CALC. CONSTRAINT FORCES */
1032 if (v)
1034 invdt = 1 / dt;
1037 /* update atoms in the groups */
1038 for (size_t g = 0; g < pull->group.size(); g++)
1040 const pull_group_work_t* pgrp;
1041 dvec dr;
1043 pgrp = &pull->group[g];
1045 /* get the final constraint displacement dr for group g */
1046 dvec_sub(rnew[g], pgrp->xp, dr);
1048 if (dnorm2(dr) == 0)
1050 /* No displacement, no update necessary */
1051 continue;
1054 /* update the atom positions */
1055 auto localAtomIndices = pgrp->atomSet.localIndex();
1056 copy_dvec(dr, tmp);
1057 for (gmx::index j = 0; j < localAtomIndices.ssize(); j++)
1059 ii = localAtomIndices[j];
1060 if (!pgrp->localWeights.empty())
1062 dsvmul(pgrp->wscale * pgrp->localWeights[j], dr, tmp);
1064 for (m = 0; m < DIM; m++)
1066 x[ii][m] += tmp[m];
1068 if (v)
1070 for (m = 0; m < DIM; m++)
1072 v[ii][m] += invdt * tmp[m];
1078 /* calculate the constraint forces, used for output and virial only */
1079 for (size_t c = 0; c < pull->coord.size(); c++)
1081 pull_coord_work_t* pcrd;
1083 pcrd = &pull->coord[c];
1085 if (pcrd->params.eType != epullCONSTRAINT)
1087 continue;
1090 /* Accumulate the forces, in case we have multiple constraint steps */
1091 double force =
1092 dr_tot[c]
1093 / ((pull->group[pcrd->params.group[0]].invtm + pull->group[pcrd->params.group[1]].invtm)
1094 * dt * dt);
1095 pcrd->scalarForce += force;
1097 if (vir != nullptr && pcrd->params.eGeom != epullgDIRPBC && bMaster)
1099 double f_invr;
1101 /* Add the pull contribution to the virial */
1102 /* We have already checked above that r_ij[c] != 0 */
1103 f_invr = pcrd->scalarForce / dnorm(r_ij[c]);
1105 for (j = 0; j < DIM; j++)
1107 for (m = 0; m < DIM; m++)
1109 vir[j][m] -= 0.5 * f_invr * r_ij[c][j] * r_ij[c][m];
1115 /* finished! I hope. Give back some memory */
1116 sfree(r_ij);
1117 sfree(dr_tot);
1118 sfree(rnew);
1121 static void add_virial_coord_dr(tensor vir, const dvec dr, const dvec f)
1123 for (int j = 0; j < DIM; j++)
1125 for (int m = 0; m < DIM; m++)
1127 vir[j][m] -= 0.5 * f[j] * dr[m];
1132 /* Adds the pull contribution to the virial */
1133 static void add_virial_coord(tensor vir, const pull_coord_work_t& pcrd, const PullCoordVectorForces& forces)
1135 if (vir != nullptr && pcrd.params.eGeom != epullgDIRPBC)
1137 /* Add the pull contribution for each distance vector to the virial. */
1138 add_virial_coord_dr(vir, pcrd.spatialData.dr01, forces.force01);
1139 if (pcrd.params.ngroup >= 4)
1141 add_virial_coord_dr(vir, pcrd.spatialData.dr23, forces.force23);
1143 if (pcrd.params.ngroup >= 6)
1145 add_virial_coord_dr(vir, pcrd.spatialData.dr45, forces.force45);
1150 static void calc_pull_coord_scalar_force_and_potential(pull_coord_work_t* pcrd,
1151 double dev,
1152 real lambda,
1153 real* V,
1154 real* dVdl)
1156 real k, dkdl;
1158 k = (1.0 - lambda) * pcrd->params.k + lambda * pcrd->params.kB;
1159 dkdl = pcrd->params.kB - pcrd->params.k;
1161 switch (pcrd->params.eType)
1163 case epullUMBRELLA:
1164 case epullFLATBOTTOM:
1165 case epullFLATBOTTOMHIGH:
1166 /* The only difference between an umbrella and a flat-bottom
1167 * potential is that a flat-bottom is zero above or below
1168 the reference value.
1170 if ((pcrd->params.eType == epullFLATBOTTOM && dev < 0)
1171 || (pcrd->params.eType == epullFLATBOTTOMHIGH && dev > 0))
1173 dev = 0;
1176 pcrd->scalarForce = -k * dev;
1177 *V += 0.5 * k * gmx::square(dev);
1178 *dVdl += 0.5 * dkdl * gmx::square(dev);
1179 break;
1180 case epullCONST_F:
1181 pcrd->scalarForce = -k;
1182 *V += k * pcrd->spatialData.value;
1183 *dVdl += dkdl * pcrd->spatialData.value;
1184 break;
1185 case epullEXTERNAL:
1186 gmx_incons(
1187 "the scalar pull force should not be calculated internally for pull type "
1188 "external");
1189 default: gmx_incons("Unsupported pull type in do_pull_pot");
1193 static PullCoordVectorForces calculateVectorForces(const pull_coord_work_t& pcrd)
1195 const t_pull_coord& params = pcrd.params;
1196 const PullCoordSpatialData& spatialData = pcrd.spatialData;
1198 /* The geometry of the coordinate determines how the scalar force relates to the force on each group */
1199 PullCoordVectorForces forces;
1201 if (params.eGeom == epullgDIST)
1203 double invdr01 = spatialData.value > 0 ? 1. / spatialData.value : 0.;
1204 for (int m = 0; m < DIM; m++)
1206 forces.force01[m] = pcrd.scalarForce * spatialData.dr01[m] * invdr01;
1209 else if (params.eGeom == epullgANGLE)
1212 double cos_theta, cos_theta2;
1214 cos_theta = cos(spatialData.value);
1215 cos_theta2 = gmx::square(cos_theta);
1217 /* The force at theta = 0, pi is undefined so we should not apply any force.
1218 * cos(theta) = 1 for theta = 0, pi so this is what we check for below.
1219 * Note that dr01 = 0 or dr23 = 0 evaluates to theta = 0 so we do not
1220 * have to check for this before dividing by their norm below.
1222 if (cos_theta2 < 1)
1224 /* The forces f01 and f23 can be decomposed into one vector parallel to dr01
1225 * and another vector parallel to dr23:
1226 * f01 = -dV/dtheta*(a*dr23/|dr23| - b*dr01*|dr01|)/|dr01|,
1227 * f23 = -dV/dtheta*(a*dr01/|dr01| - b*dr23*|dr23|)/|dr23|,
1229 double a = -gmx::invsqrt(1 - cos_theta2); /* comes from d/dx acos(x) */
1230 double b = a * cos_theta;
1231 double invdr01 = 1. / dnorm(spatialData.dr01);
1232 double invdr23 = 1. / dnorm(spatialData.dr23);
1233 dvec normalized_dr01, normalized_dr23;
1234 dsvmul(invdr01, spatialData.dr01, normalized_dr01);
1235 dsvmul(invdr23, spatialData.dr23, normalized_dr23);
1237 for (int m = 0; m < DIM; m++)
1239 /* Here, f_scal is -dV/dtheta */
1240 forces.force01[m] =
1241 pcrd.scalarForce * invdr01 * (a * normalized_dr23[m] - b * normalized_dr01[m]);
1242 forces.force23[m] =
1243 pcrd.scalarForce * invdr23 * (a * normalized_dr01[m] - b * normalized_dr23[m]);
1246 else
1248 /* No forces to apply for ill-defined cases*/
1249 clear_dvec(forces.force01);
1250 clear_dvec(forces.force23);
1253 else if (params.eGeom == epullgANGLEAXIS)
1255 double cos_theta, cos_theta2;
1257 /* The angle-axis force is exactly the same as the angle force (above) except that in
1258 this case the second vector (dr23) is replaced by the pull vector. */
1259 cos_theta = cos(spatialData.value);
1260 cos_theta2 = gmx::square(cos_theta);
1262 if (cos_theta2 < 1)
1264 double a, b;
1265 double invdr01;
1266 dvec normalized_dr01;
1268 invdr01 = 1. / dnorm(spatialData.dr01);
1269 dsvmul(invdr01, spatialData.dr01, normalized_dr01);
1270 a = -gmx::invsqrt(1 - cos_theta2); /* comes from d/dx acos(x) */
1271 b = a * cos_theta;
1273 for (int m = 0; m < DIM; m++)
1275 forces.force01[m] =
1276 pcrd.scalarForce * invdr01 * (a * spatialData.vec[m] - b * normalized_dr01[m]);
1279 else
1281 clear_dvec(forces.force01);
1284 else if (params.eGeom == epullgDIHEDRAL)
1286 double m2, n2, tol, sqrdist_32;
1287 dvec dr32;
1288 /* Note: there is a small difference here compared to the
1289 dihedral force calculations in the bondeds (ref: Bekker 1994).
1290 There rij = ri - rj, while here dr01 = r1 - r0.
1291 However, all distance vectors occur in form of cross or inner products
1292 so that two signs cancel and we end up with the same expressions.
1293 Also, we treat the more general case of 6 groups (0..5) instead of 4 (i, j, k, l).
1295 m2 = diprod(spatialData.planevec_m, spatialData.planevec_m);
1296 n2 = diprod(spatialData.planevec_n, spatialData.planevec_n);
1297 dsvmul(-1, spatialData.dr23, dr32);
1298 sqrdist_32 = diprod(dr32, dr32);
1299 tol = sqrdist_32 * GMX_REAL_EPS; /* Avoid tiny angles */
1300 if ((m2 > tol) && (n2 > tol))
1302 double a_01, a_23_01, a_23_45, a_45;
1303 double inv_dist_32, inv_sqrdist_32, dist_32;
1304 dvec u, v;
1305 inv_dist_32 = gmx::invsqrt(sqrdist_32);
1306 inv_sqrdist_32 = inv_dist_32 * inv_dist_32;
1307 dist_32 = sqrdist_32 * inv_dist_32;
1309 /* Forces on groups 0, 1 */
1310 a_01 = pcrd.scalarForce * dist_32 / m2; /* scalarForce is -dV/dphi */
1311 dsvmul(-a_01, spatialData.planevec_m,
1312 forces.force01); /* added sign to get force on group 1, not 0 */
1314 /* Forces on groups 4, 5 */
1315 a_45 = -pcrd.scalarForce * dist_32 / n2;
1316 dsvmul(a_45, spatialData.planevec_n, forces.force45); /* force on group 5 */
1318 /* Force on groups 2, 3 (defining the axis) */
1319 a_23_01 = -diprod(spatialData.dr01, dr32) * inv_sqrdist_32;
1320 a_23_45 = -diprod(spatialData.dr45, dr32) * inv_sqrdist_32;
1321 dsvmul(-a_23_01, forces.force01, u); /* added sign to get force from group 0, not 1 */
1322 dsvmul(a_23_45, forces.force45, v);
1323 dvec_sub(u, v, forces.force23); /* force on group 3 */
1325 else
1327 /* No force to apply for ill-defined cases */
1328 clear_dvec(forces.force01);
1329 clear_dvec(forces.force23);
1330 clear_dvec(forces.force45);
1333 else
1335 for (int m = 0; m < DIM; m++)
1337 forces.force01[m] = pcrd.scalarForce * spatialData.vec[m];
1341 return forces;
1345 /* We use a global mutex for locking access to the pull data structure
1346 * during registration of external pull potential providers.
1347 * We could use a different, local mutex for each pull object, but the overhead
1348 * is extremely small here and registration is only done during initialization.
1350 static gmx::Mutex registrationMutex;
1352 using Lock = gmx::lock_guard<gmx::Mutex>;
1354 void register_external_pull_potential(struct pull_t* pull, int coord_index, const char* provider)
1356 GMX_RELEASE_ASSERT(pull != nullptr, "register_external_pull_potential called before init_pull");
1357 GMX_RELEASE_ASSERT(provider != nullptr,
1358 "register_external_pull_potential called with NULL as provider name");
1360 if (coord_index < 0 || coord_index >= gmx::ssize(pull->coord))
1362 gmx_fatal(FARGS,
1363 "Module '%s' attempted to register an external potential for pull coordinate %d "
1364 "which is out of the pull coordinate range %d - %zu\n",
1365 provider, coord_index + 1, 1, pull->coord.size());
1368 pull_coord_work_t* pcrd = &pull->coord[coord_index];
1370 if (pcrd->params.eType != epullEXTERNAL)
1372 gmx_fatal(
1373 FARGS,
1374 "Module '%s' attempted to register an external potential for pull coordinate %d "
1375 "which of type '%s', whereas external potentials are only supported with type '%s'",
1376 provider, coord_index + 1, epull_names[pcrd->params.eType], epull_names[epullEXTERNAL]);
1379 GMX_RELEASE_ASSERT(pcrd->params.externalPotentialProvider != nullptr,
1380 "The external potential provider string for a pull coordinate is NULL");
1382 if (gmx_strcasecmp(provider, pcrd->params.externalPotentialProvider) != 0)
1384 gmx_fatal(FARGS,
1385 "Module '%s' attempted to register an external potential for pull coordinate %d "
1386 "which expects the external potential to be provided by a module named '%s'",
1387 provider, coord_index + 1, pcrd->params.externalPotentialProvider);
1390 /* Lock to avoid (extremely unlikely) simultaneous reading and writing of
1391 * pcrd->bExternalPotentialProviderHasBeenRegistered and
1392 * pull->numUnregisteredExternalPotentials.
1394 Lock registrationLock(registrationMutex);
1396 if (pcrd->bExternalPotentialProviderHasBeenRegistered)
1398 gmx_fatal(FARGS,
1399 "Module '%s' attempted to register an external potential for pull coordinate %d "
1400 "more than once",
1401 provider, coord_index + 1);
1404 pcrd->bExternalPotentialProviderHasBeenRegistered = true;
1405 pull->numUnregisteredExternalPotentials--;
1407 GMX_RELEASE_ASSERT(pull->numUnregisteredExternalPotentials >= 0,
1408 "Negative unregistered potentials, the pull code is inconsistent");
1412 static void check_external_potential_registration(const struct pull_t* pull)
1414 if (pull->numUnregisteredExternalPotentials > 0)
1416 size_t c;
1417 for (c = 0; c < pull->coord.size(); c++)
1419 if (pull->coord[c].params.eType == epullEXTERNAL
1420 && !pull->coord[c].bExternalPotentialProviderHasBeenRegistered)
1422 break;
1426 GMX_RELEASE_ASSERT(c < pull->coord.size(),
1427 "Internal inconsistency in the pull potential provider counting");
1429 gmx_fatal(FARGS,
1430 "No external provider for external pull potentials have been provided for %d "
1431 "pull coordinates. The first coordinate without provider is number %zu, which "
1432 "expects a module named '%s' to provide the external potential.",
1433 pull->numUnregisteredExternalPotentials, c + 1,
1434 pull->coord[c].params.externalPotentialProvider);
1439 /* Pull takes care of adding the forces of the external potential.
1440 * The external potential module has to make sure that the corresponding
1441 * potential energy is added either to the pull term or to a term
1442 * specific to the external module.
1444 void apply_external_pull_coord_force(struct pull_t* pull,
1445 int coord_index,
1446 double coord_force,
1447 const t_mdatoms* mdatoms,
1448 gmx::ForceWithVirial* forceWithVirial)
1450 pull_coord_work_t* pcrd;
1452 GMX_ASSERT(coord_index >= 0 && coord_index < gmx::ssize(pull->coord),
1453 "apply_external_pull_coord_force called with coord_index out of range");
1455 if (pull->comm.bParticipate)
1457 pcrd = &pull->coord[coord_index];
1459 GMX_RELEASE_ASSERT(
1460 pcrd->params.eType == epullEXTERNAL,
1461 "The pull force can only be set externally on pull coordinates of external type");
1463 GMX_ASSERT(pcrd->bExternalPotentialProviderHasBeenRegistered,
1464 "apply_external_pull_coord_force called for an unregistered pull coordinate");
1466 /* Set the force */
1467 pcrd->scalarForce = coord_force;
1469 /* Calculate the forces on the pull groups */
1470 PullCoordVectorForces pullCoordForces = calculateVectorForces(*pcrd);
1472 /* Add the forces for this coordinate to the total virial and force */
1473 if (forceWithVirial->computeVirial_ && pull->comm.isMasterRank)
1475 matrix virial = { { 0 } };
1476 add_virial_coord(virial, *pcrd, pullCoordForces);
1477 forceWithVirial->addVirialContribution(virial);
1480 apply_forces_coord(pull, coord_index, pullCoordForces, mdatoms,
1481 as_rvec_array(forceWithVirial->force_.data()));
1484 pull->numExternalPotentialsStillToBeAppliedThisStep--;
1487 /* Calculate the pull potential and scalar force for a pull coordinate.
1488 * Returns the vector forces for the pull coordinate.
1490 static PullCoordVectorForces do_pull_pot_coord(struct pull_t* pull,
1491 int coord_ind,
1492 t_pbc* pbc,
1493 double t,
1494 real lambda,
1495 real* V,
1496 tensor vir,
1497 real* dVdl)
1499 pull_coord_work_t& pcrd = pull->coord[coord_ind];
1501 assert(pcrd.params.eType != epullCONSTRAINT);
1503 double dev = get_pull_coord_deviation(pull, coord_ind, pbc, t);
1505 calc_pull_coord_scalar_force_and_potential(&pcrd, dev, lambda, V, dVdl);
1507 PullCoordVectorForces pullCoordForces = calculateVectorForces(pcrd);
1509 add_virial_coord(vir, pcrd, pullCoordForces);
1511 return pullCoordForces;
1514 real pull_potential(struct pull_t* pull,
1515 const t_mdatoms* md,
1516 t_pbc* pbc,
1517 const t_commrec* cr,
1518 double t,
1519 real lambda,
1520 const rvec* x,
1521 gmx::ForceWithVirial* force,
1522 real* dvdlambda)
1524 real V = 0;
1526 assert(pull != nullptr);
1528 /* Ideally we should check external potential registration only during
1529 * the initialization phase, but that requires another function call
1530 * that should be done exactly in the right order. So we check here.
1532 check_external_potential_registration(pull);
1534 if (pull->comm.bParticipate)
1536 real dVdl = 0;
1538 pull_calc_coms(cr, pull, md, pbc, t, x, nullptr);
1540 rvec* f = as_rvec_array(force->force_.data());
1541 matrix virial = { { 0 } };
1542 const bool computeVirial = (force->computeVirial_ && MASTER(cr));
1543 for (size_t c = 0; c < pull->coord.size(); c++)
1545 pull_coord_work_t* pcrd;
1546 pcrd = &pull->coord[c];
1548 /* For external potential the force is assumed to be given by an external module by a
1549 call to apply_pull_coord_external_force */
1550 if (pcrd->params.eType == epullCONSTRAINT || pcrd->params.eType == epullEXTERNAL)
1552 continue;
1555 PullCoordVectorForces pullCoordForces = do_pull_pot_coord(
1556 pull, c, pbc, t, lambda, &V, computeVirial ? virial : nullptr, &dVdl);
1558 /* Distribute the force over the atoms in the pulled groups */
1559 apply_forces_coord(pull, c, pullCoordForces, md, f);
1562 if (MASTER(cr))
1564 force->addVirialContribution(virial);
1565 *dvdlambda += dVdl;
1569 GMX_ASSERT(pull->numExternalPotentialsStillToBeAppliedThisStep == 0,
1570 "Too few or too many external pull potentials have been applied the previous step");
1571 /* All external pull potentials still need to be applied */
1572 pull->numExternalPotentialsStillToBeAppliedThisStep = pull->numCoordinatesWithExternalPotential;
1574 return (MASTER(cr) ? V : 0.0);
1577 void pull_constraint(struct pull_t* pull,
1578 const t_mdatoms* md,
1579 t_pbc* pbc,
1580 const t_commrec* cr,
1581 double dt,
1582 double t,
1583 rvec* x,
1584 rvec* xp,
1585 rvec* v,
1586 tensor vir)
1588 assert(pull != nullptr);
1590 if (pull->comm.bParticipate)
1592 pull_calc_coms(cr, pull, md, pbc, t, x, xp);
1594 do_constraint(pull, pbc, xp, v, MASTER(cr), vir, dt, t);
1598 void dd_make_local_pull_groups(const t_commrec* cr, struct pull_t* pull)
1600 gmx_domdec_t* dd;
1601 pull_comm_t* comm;
1602 gmx_bool bMustParticipate;
1604 dd = cr->dd;
1606 comm = &pull->comm;
1608 /* We always make the master node participate, such that it can do i/o,
1609 * add the virial and to simplify MC type extensions people might have.
1611 bMustParticipate = (comm->bParticipateAll || comm->isMasterRank);
1613 for (pull_group_work_t& group : pull->group)
1615 if (!group.globalWeights.empty())
1617 group.localWeights.resize(group.atomSet.numAtomsLocal());
1618 for (size_t i = 0; i < group.atomSet.numAtomsLocal(); ++i)
1620 group.localWeights[i] = group.globalWeights[group.atomSet.collectiveIndex()[i]];
1624 GMX_ASSERT(bMustParticipate || dd != nullptr,
1625 "Either all ranks (including this rank) participate, or we use DD and need to "
1626 "have access to dd here");
1628 /* We should participate if we have pull or pbc atoms */
1629 if (!bMustParticipate
1630 && (group.atomSet.numAtomsLocal() > 0
1631 || (group.epgrppbc == epgrppbcREFAT && group.pbcAtomSet->numAtomsLocal() > 0)))
1633 bMustParticipate = TRUE;
1637 if (!comm->bParticipateAll)
1639 /* Keep currently not required ranks in the communicator
1640 * if they needed to participate up to 20 decompositions ago.
1641 * This avoids frequent rebuilds due to atoms jumping back and forth.
1643 const int64_t history_count = 20;
1644 gmx_bool bWillParticipate;
1645 int count[2];
1647 /* Increase the decomposition counter for the current call */
1648 comm->setup_count++;
1650 if (bMustParticipate)
1652 comm->must_count = comm->setup_count;
1655 bWillParticipate =
1656 bMustParticipate
1657 || (comm->bParticipate && comm->must_count >= comm->setup_count - history_count);
1659 if (debug && dd != nullptr)
1661 fprintf(debug, "Our DD rank (%3d) pull #atoms>0 or master: %s, will be part %s\n", dd->rank,
1662 gmx::boolToString(bMustParticipate), gmx::boolToString(bWillParticipate));
1665 if (bWillParticipate)
1667 /* Count the number of ranks that we want to have participating */
1668 count[0] = 1;
1669 /* Count the number of ranks that need to be added */
1670 count[1] = comm->bParticipate ? 0 : 1;
1672 else
1674 count[0] = 0;
1675 count[1] = 0;
1678 /* The cost of this global operation will be less that the cost
1679 * of the extra MPI_Comm_split calls that we can avoid.
1681 gmx_sumi(2, count, cr);
1683 /* If we are missing ranks or if we have 20% more ranks than needed
1684 * we make a new sub-communicator.
1686 if (count[1] > 0 || 6 * count[0] < 5 * comm->nparticipate)
1688 if (debug)
1690 fprintf(debug, "Creating new pull subcommunicator of size %d\n", count[0]);
1693 #if GMX_MPI
1694 if (comm->mpi_comm_com != MPI_COMM_NULL)
1696 MPI_Comm_free(&comm->mpi_comm_com);
1699 /* This might be an extremely expensive operation, so we try
1700 * to avoid this splitting as much as possible.
1702 assert(dd != nullptr);
1703 MPI_Comm_split(dd->mpi_comm_all, bWillParticipate ? 0 : 1, dd->rank, &comm->mpi_comm_com);
1704 #endif
1706 comm->bParticipate = bWillParticipate;
1707 comm->nparticipate = count[0];
1709 /* When we use the previous COM for PBC, we need to broadcast
1710 * the previous COM to ranks that have joined the communicator.
1712 for (pull_group_work_t& group : pull->group)
1714 if (group.epgrppbc == epgrppbcPREVSTEPCOM)
1716 GMX_ASSERT(comm->bParticipate || !MASTER(cr),
1717 "The master rank has to participate, as it should pass an up to "
1718 "date prev. COM "
1719 "to bcast here as well as to e.g. checkpointing");
1721 gmx_bcast(sizeof(dvec), group.x_prev_step, cr);
1727 /* Since the PBC of atoms might have changed, we need to update the PBC */
1728 pull->bSetPBCatoms = TRUE;
1731 static void init_pull_group_index(FILE* fplog,
1732 const t_commrec* cr,
1733 int g,
1734 pull_group_work_t* pg,
1735 gmx_bool bConstraint,
1736 const ivec pulldim_con,
1737 const gmx_mtop_t* mtop,
1738 const t_inputrec* ir,
1739 real lambda)
1741 /* With EM and BD there are no masses in the integrator.
1742 * But we still want to have the correct mass-weighted COMs.
1743 * So we store the real masses in the weights.
1745 const bool setWeights = (pg->params.nweight > 0 || EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD);
1747 /* In parallel, store we need to extract localWeights from weights at DD time */
1748 std::vector<real>& weights = ((cr && PAR(cr)) ? pg->globalWeights : pg->localWeights);
1750 const SimulationGroups& groups = mtop->groups;
1752 /* Count frozen dimensions and (weighted) mass */
1753 int nfrozen = 0;
1754 double tmass = 0;
1755 double wmass = 0;
1756 double wwmass = 0;
1757 int molb = 0;
1758 for (int i = 0; i < pg->params.nat; i++)
1760 int ii = pg->params.ind[i];
1761 if (bConstraint && ir->opts.nFreeze)
1763 for (int d = 0; d < DIM; d++)
1765 if (pulldim_con[d] == 1
1766 && ir->opts.nFreeze[getGroupType(groups, SimulationAtomGroupType::Freeze, ii)][d])
1768 nfrozen++;
1772 const t_atom& atom = mtopGetAtomParameters(mtop, ii, &molb);
1773 real m;
1774 if (ir->efep == efepNO)
1776 m = atom.m;
1778 else
1780 m = (1 - lambda) * atom.m + lambda * atom.mB;
1782 real w;
1783 if (pg->params.nweight > 0)
1785 w = pg->params.weight[i];
1787 else
1789 w = 1;
1791 if (EI_ENERGY_MINIMIZATION(ir->eI))
1793 /* Move the mass to the weight */
1794 w *= m;
1795 m = 1;
1797 else if (ir->eI == eiBD)
1799 real mbd;
1800 if (ir->bd_fric != 0.0F)
1802 mbd = ir->bd_fric * ir->delta_t;
1804 else
1806 if (groups.groupNumbers[SimulationAtomGroupType::TemperatureCoupling].empty())
1808 mbd = ir->delta_t / ir->opts.tau_t[0];
1810 else
1812 mbd = ir->delta_t
1813 / ir->opts.tau_t[groups.groupNumbers[SimulationAtomGroupType::TemperatureCoupling][ii]];
1816 w *= m / mbd;
1817 m = mbd;
1819 if (setWeights)
1821 weights.push_back(w);
1823 tmass += m;
1824 wmass += m * w;
1825 wwmass += m * w * w;
1828 if (wmass == 0)
1830 /* We can have single atom groups with zero mass with potential pulling
1831 * without cosine weighting.
1833 if (pg->params.nat == 1 && !bConstraint && pg->epgrppbc != epgrppbcCOS)
1835 /* With one atom the mass doesn't matter */
1836 wwmass = 1;
1838 else
1840 gmx_fatal(FARGS, "The total%s mass of pull group %d is zero",
1841 pg->params.weight ? " weighted" : "", g);
1844 if (fplog)
1846 fprintf(fplog, "Pull group %d: %5d atoms, mass %9.3f", g, pg->params.nat, tmass);
1847 if (pg->params.weight || EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD)
1849 fprintf(fplog, ", weighted mass %9.3f", wmass * wmass / wwmass);
1851 if (pg->epgrppbc == epgrppbcCOS)
1853 fprintf(fplog, ", cosine weighting will be used");
1855 fprintf(fplog, "\n");
1858 if (nfrozen == 0)
1860 /* A value != 0 signals not frozen, it is updated later */
1861 pg->invtm = -1.0;
1863 else
1865 int ndim = 0;
1866 for (int d = 0; d < DIM; d++)
1868 ndim += pulldim_con[d] * pg->params.nat;
1870 if (fplog && nfrozen > 0 && nfrozen < ndim)
1872 fprintf(fplog,
1873 "\nWARNING: In pull group %d some, but not all of the degrees of freedom\n"
1874 " that are subject to pulling are frozen.\n"
1875 " For constraint pulling the whole group will be frozen.\n\n",
1878 pg->invtm = 0.0;
1879 pg->wscale = 1.0;
1883 struct pull_t* init_pull(FILE* fplog,
1884 const pull_params_t* pull_params,
1885 const t_inputrec* ir,
1886 const gmx_mtop_t* mtop,
1887 const t_commrec* cr,
1888 gmx::LocalAtomSetManager* atomSets,
1889 real lambda)
1891 struct pull_t* pull;
1892 pull_comm_t* comm;
1894 pull = new pull_t;
1896 /* Copy the pull parameters */
1897 pull->params = *pull_params;
1898 /* Avoid pointer copies */
1899 pull->params.group = nullptr;
1900 pull->params.coord = nullptr;
1902 for (int i = 0; i < pull_params->ngroup; ++i)
1904 pull->group.emplace_back(pull_params->group[i],
1905 atomSets->add({ pull_params->group[i].ind,
1906 pull_params->group[i].ind + pull_params->group[i].nat }),
1907 pull_params->bSetPbcRefToPrevStepCOM);
1910 if (cr != nullptr && DOMAINDECOMP(cr))
1912 /* Set up the global to local atom mapping for PBC atoms */
1913 for (pull_group_work_t& group : pull->group)
1915 if (group.epgrppbc == epgrppbcREFAT || group.epgrppbc == epgrppbcPREVSTEPCOM)
1917 /* pbcAtomSet consists of a single atom */
1918 group.pbcAtomSet = std::make_unique<gmx::LocalAtomSet>(
1919 atomSets->add({ &group.params.pbcatom, &group.params.pbcatom + 1 }));
1924 pull->bPotential = FALSE;
1925 pull->bConstraint = FALSE;
1926 pull->bCylinder = FALSE;
1927 pull->bAngle = FALSE;
1928 pull->bXOutAverage = pull_params->bXOutAverage;
1929 pull->bFOutAverage = pull_params->bFOutAverage;
1931 GMX_RELEASE_ASSERT(pull->group[0].params.nat == 0,
1932 "pull group 0 is an absolute reference group and should not contain atoms");
1934 pull->numCoordinatesWithExternalPotential = 0;
1936 for (int c = 0; c < pull->params.ncoord; c++)
1938 /* Construct a pull coordinate, copying all coordinate parameters */
1939 pull->coord.emplace_back(pull_params->coord[c]);
1941 pull_coord_work_t* pcrd = &pull->coord.back();
1943 switch (pcrd->params.eGeom)
1945 case epullgDIST:
1946 case epullgDIRRELATIVE: /* Direction vector is determined at each step */
1947 case epullgANGLE:
1948 case epullgDIHEDRAL: break;
1949 case epullgDIR:
1950 case epullgDIRPBC:
1951 case epullgCYL:
1952 case epullgANGLEAXIS:
1953 copy_rvec_to_dvec(pull_params->coord[c].vec, pcrd->spatialData.vec);
1954 break;
1955 default:
1956 /* We allow reading of newer tpx files with new pull geometries,
1957 * but with the same tpx format, with old code. A new geometry
1958 * only adds a new enum value, which will be out of range for
1959 * old code. The first place we need to generate an error is
1960 * here, since the pull code can't handle this.
1961 * The enum can be used before arriving here only for printing
1962 * the string corresponding to the geometry, which will result
1963 * in printing "UNDEFINED".
1965 gmx_fatal(FARGS,
1966 "Pull geometry not supported for pull coordinate %d. The geometry enum "
1967 "%d in the input is larger than that supported by the code (up to %d). "
1968 "You are probably reading a tpr file generated with a newer version of "
1969 "Gromacs with an binary from an older version of Gromacs.",
1970 c + 1, pcrd->params.eGeom, epullgNR - 1);
1973 if (pcrd->params.eType == epullCONSTRAINT)
1975 /* Check restrictions of the constraint pull code */
1976 if (pcrd->params.eGeom == epullgCYL || pcrd->params.eGeom == epullgDIRRELATIVE
1977 || pcrd->params.eGeom == epullgANGLE || pcrd->params.eGeom == epullgDIHEDRAL
1978 || pcrd->params.eGeom == epullgANGLEAXIS)
1980 gmx_fatal(FARGS,
1981 "Pulling of type %s can not be combined with geometry %s. Consider using "
1982 "pull type %s.",
1983 epull_names[pcrd->params.eType], epullg_names[pcrd->params.eGeom],
1984 epull_names[epullUMBRELLA]);
1987 pull->bConstraint = TRUE;
1989 else
1991 pull->bPotential = TRUE;
1994 if (pcrd->params.eGeom == epullgCYL)
1996 pull->bCylinder = TRUE;
1998 else if (pcrd->params.eGeom == epullgANGLE || pcrd->params.eGeom == epullgDIHEDRAL
1999 || pcrd->params.eGeom == epullgANGLEAXIS)
2001 pull->bAngle = TRUE;
2004 /* We only need to calculate the plain COM of a group
2005 * when it is not only used as a cylinder group.
2006 * Also the absolute reference group 0 needs no COM computation.
2008 for (int i = 0; i < pcrd->params.ngroup; i++)
2010 int groupIndex = pcrd->params.group[i];
2011 if (groupIndex > 0 && !(pcrd->params.eGeom == epullgCYL && i == 0))
2013 pull->group[groupIndex].needToCalcCom = true;
2017 /* With non-zero rate the reference value is set at every step */
2018 if (pcrd->params.rate == 0)
2020 /* Initialize the constant reference value */
2021 if (pcrd->params.eType != epullEXTERNAL)
2023 low_set_pull_coord_reference_value(
2024 pcrd, c,
2025 pcrd->params.init * pull_conversion_factor_userinput2internal(&pcrd->params));
2027 else
2029 /* With an external pull potential, the reference value loses
2030 * it's meaning and should not be used. Setting it to zero
2031 * makes any terms dependent on it disappear.
2032 * The only issue this causes is that with cylinder pulling
2033 * no atoms of the cylinder group within the cylinder radius
2034 * should be more than half a box length away from the COM of
2035 * the pull group along the axial direction.
2037 pcrd->value_ref = 0.0;
2041 if (pcrd->params.eType == epullEXTERNAL)
2043 GMX_RELEASE_ASSERT(
2044 pcrd->params.rate == 0,
2045 "With an external potential, a pull coordinate should have rate = 0");
2047 /* This external potential needs to be registered later */
2048 pull->numCoordinatesWithExternalPotential++;
2050 pcrd->bExternalPotentialProviderHasBeenRegistered = false;
2053 pull->numUnregisteredExternalPotentials = pull->numCoordinatesWithExternalPotential;
2054 pull->numExternalPotentialsStillToBeAppliedThisStep = 0;
2056 pull->pbcType = ir->pbcType;
2057 switch (pull->pbcType)
2059 case PbcType::No: pull->npbcdim = 0; break;
2060 case PbcType::XY: pull->npbcdim = 2; break;
2061 default: pull->npbcdim = 3; break;
2064 if (fplog)
2066 gmx_bool bAbs, bCos;
2068 bAbs = FALSE;
2069 for (const pull_coord_work_t& coord : pull->coord)
2071 if (pull->group[coord.params.group[0]].params.nat == 0
2072 || pull->group[coord.params.group[1]].params.nat == 0)
2074 bAbs = TRUE;
2078 fprintf(fplog, "\n");
2079 if (pull->bPotential)
2081 fprintf(fplog, "Will apply potential COM pulling\n");
2083 if (pull->bConstraint)
2085 fprintf(fplog, "Will apply constraint COM pulling\n");
2087 // Don't include the reference group 0 in output, so we report ngroup-1
2088 int numRealGroups = pull->group.size() - 1;
2089 GMX_RELEASE_ASSERT(numRealGroups > 0,
2090 "The reference absolute position pull group should always be present");
2091 fprintf(fplog, "with %zu pull coordinate%s and %d group%s\n", pull->coord.size(),
2092 pull->coord.size() == 1 ? "" : "s", numRealGroups, numRealGroups == 1 ? "" : "s");
2093 if (bAbs)
2095 fprintf(fplog, "with an absolute reference\n");
2097 bCos = FALSE;
2098 // Don't include the reference group 0 in loop
2099 for (size_t g = 1; g < pull->group.size(); g++)
2101 if (pull->group[g].params.nat > 1 && pull->group[g].params.pbcatom < 0)
2103 /* We are using cosine weighting */
2104 fprintf(fplog, "Cosine weighting is used for group %zu\n", g);
2105 bCos = TRUE;
2108 if (bCos)
2110 please_cite(fplog, "Engin2010");
2114 pull->bRefAt = FALSE;
2115 pull->cosdim = -1;
2116 for (size_t g = 0; g < pull->group.size(); g++)
2118 pull_group_work_t* pgrp;
2120 pgrp = &pull->group[g];
2121 if (pgrp->params.nat > 0)
2123 /* There is an issue when a group is used in multiple coordinates
2124 * and constraints are applied in different dimensions with atoms
2125 * frozen in some, but not all dimensions.
2126 * Since there is only one mass array per group, we can't have
2127 * frozen/non-frozen atoms for different coords at the same time.
2128 * But since this is a very exotic case, we don't check for this.
2129 * A warning is printed in init_pull_group_index.
2132 gmx_bool bConstraint;
2133 ivec pulldim, pulldim_con;
2135 /* Loop over all pull coordinates to see along which dimensions
2136 * this group is pulled and if it is involved in constraints.
2138 bConstraint = FALSE;
2139 clear_ivec(pulldim);
2140 clear_ivec(pulldim_con);
2141 for (const pull_coord_work_t& coord : pull->coord)
2143 gmx_bool bGroupUsed = FALSE;
2144 for (int gi = 0; gi < coord.params.ngroup; gi++)
2146 if (coord.params.group[gi] == static_cast<int>(g))
2148 bGroupUsed = TRUE;
2152 if (bGroupUsed)
2154 for (int m = 0; m < DIM; m++)
2156 if (coord.params.dim[m] == 1)
2158 pulldim[m] = 1;
2160 if (coord.params.eType == epullCONSTRAINT)
2162 bConstraint = TRUE;
2163 pulldim_con[m] = 1;
2170 /* Determine if we need to take PBC into account for calculating
2171 * the COM's of the pull groups.
2173 switch (pgrp->epgrppbc)
2175 case epgrppbcREFAT: pull->bRefAt = TRUE; break;
2176 case epgrppbcCOS:
2177 if (pgrp->params.weight != nullptr)
2179 gmx_fatal(FARGS,
2180 "Pull groups can not have relative weights and cosine weighting "
2181 "at same time");
2183 for (int m = 0; m < DIM; m++)
2185 if (m < pull->npbcdim && pulldim[m] == 1)
2187 if (pull->cosdim >= 0 && pull->cosdim != m)
2189 gmx_fatal(FARGS,
2190 "Can only use cosine weighting with pulling in one "
2191 "dimension (use mdp option pull-coord?-dim)");
2193 pull->cosdim = m;
2196 break;
2197 case epgrppbcNONE: break;
2200 /* Set the indices */
2201 init_pull_group_index(fplog, cr, g, pgrp, bConstraint, pulldim_con, mtop, ir, lambda);
2203 else
2205 /* Absolute reference, set the inverse mass to zero.
2206 * This is only relevant (and used) with constraint pulling.
2208 pgrp->invtm = 0;
2209 pgrp->wscale = 1;
2213 /* If we use cylinder coordinates, do some initialising for them */
2214 if (pull->bCylinder)
2216 for (const pull_coord_work_t& coord : pull->coord)
2218 if (coord.params.eGeom == epullgCYL)
2220 if (pull->group[coord.params.group[0]].params.nat == 0)
2222 gmx_fatal(FARGS,
2223 "A cylinder pull group is not supported when using absolute "
2224 "reference!\n");
2227 const auto& referenceGroup = pull->group[coord.params.group[0]];
2228 pull->dyna.emplace_back(referenceGroup.params, referenceGroup.atomSet,
2229 pull->params.bSetPbcRefToPrevStepCOM);
2233 /* The gmx_omp_nthreads module might not be initialized here, so max(1,) */
2234 pull->nthreads = std::max(1, gmx_omp_nthreads_get(emntDefault));
2235 pull->comSums.resize(pull->nthreads);
2237 comm = &pull->comm;
2239 #if GMX_MPI
2240 /* Use a sub-communicator when we have more than 32 ranks, but not
2241 * when we have an external pull potential, since then the external
2242 * potential provider expects each rank to have the coordinate.
2244 comm->bParticipateAll = (cr == nullptr || !DOMAINDECOMP(cr) || cr->dd->nnodes <= 32
2245 || pull->numCoordinatesWithExternalPotential > 0
2246 || getenv("GMX_PULL_PARTICIPATE_ALL") != nullptr);
2247 /* This sub-commicator is not used with comm->bParticipateAll,
2248 * so we can always initialize it to NULL.
2250 comm->mpi_comm_com = MPI_COMM_NULL;
2251 comm->nparticipate = 0;
2252 comm->isMasterRank = (cr == nullptr || MASTER(cr));
2253 #else
2254 /* No MPI: 1 rank: all ranks pull */
2255 comm->bParticipateAll = TRUE;
2256 comm->isMasterRank = true;
2257 #endif
2258 comm->bParticipate = comm->bParticipateAll;
2259 comm->setup_count = 0;
2260 comm->must_count = 0;
2262 if (!comm->bParticipateAll && fplog != nullptr)
2264 fprintf(fplog, "Will use a sub-communicator for pull communication\n");
2267 comm->pbcAtomBuffer.resize(pull->group.size());
2268 comm->comBuffer.resize(pull->group.size() * c_comBufferStride);
2269 if (pull->bCylinder)
2271 comm->cylinderBuffer.resize(pull->coord.size() * c_cylinderBufferStride);
2274 /* We still need to initialize the PBC reference coordinates */
2275 pull->bSetPBCatoms = TRUE;
2277 pull->out_x = nullptr;
2278 pull->out_f = nullptr;
2280 return pull;
2283 static void destroy_pull(struct pull_t* pull)
2285 #if GMX_MPI
2286 if (pull->comm.mpi_comm_com != MPI_COMM_NULL)
2288 MPI_Comm_free(&pull->comm.mpi_comm_com);
2290 #endif
2292 delete pull;
2295 void preparePrevStepPullCom(const t_inputrec* ir,
2296 pull_t* pull_work,
2297 const t_mdatoms* md,
2298 t_state* state,
2299 const t_state* state_global,
2300 const t_commrec* cr,
2301 bool startingFromCheckpoint)
2303 if (!ir->pull || !ir->pull->bSetPbcRefToPrevStepCOM)
2305 return;
2307 allocStatePrevStepPullCom(state, pull_work);
2308 if (startingFromCheckpoint)
2310 if (MASTER(cr))
2312 state->pull_com_prev_step = state_global->pull_com_prev_step;
2314 if (PAR(cr))
2316 /* Only the master rank has the checkpointed COM from the previous step */
2317 gmx_bcast(sizeof(double) * state->pull_com_prev_step.size(), &state->pull_com_prev_step[0], cr);
2319 setPrevStepPullComFromState(pull_work, state);
2321 else
2323 t_pbc pbc;
2324 set_pbc(&pbc, ir->pbcType, state->box);
2325 initPullComFromPrevStep(cr, pull_work, md, &pbc, state->x.rvec_array());
2326 updatePrevStepPullCom(pull_work, state);
2330 void finish_pull(struct pull_t* pull)
2332 check_external_potential_registration(pull);
2334 if (pull->out_x)
2336 gmx_fio_fclose(pull->out_x);
2338 if (pull->out_f)
2340 gmx_fio_fclose(pull->out_f);
2343 destroy_pull(pull);
2346 gmx_bool pull_have_potential(const struct pull_t* pull)
2348 return pull->bPotential;
2351 gmx_bool pull_have_constraint(const struct pull_t* pull)
2353 return pull->bConstraint;
2356 bool pull_have_constraint(const pull_params_t* pullParameters)
2358 if (pullParameters == nullptr)
2360 return false;
2362 for (int c = 0; c < pullParameters->ncoord; c++)
2364 if (pullParameters->coord[c].eType == epullCONSTRAINT)
2366 return true;
2369 return false;