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,2018,2019, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
50 #include "gromacs/commandline/filenm.h"
51 #include "gromacs/compat/make_unique.h"
52 #include "gromacs/domdec/domdec_struct.h"
53 #include "gromacs/domdec/localatomset.h"
54 #include "gromacs/domdec/localatomsetmanager.h"
55 #include "gromacs/fileio/gmxfio.h"
56 #include "gromacs/gmxlib/network.h"
57 #include "gromacs/math/functions.h"
58 #include "gromacs/math/units.h"
59 #include "gromacs/math/vec.h"
60 #include "gromacs/math/vectypes.h"
61 #include "gromacs/mdlib/gmx_omp_nthreads.h"
62 #include "gromacs/mdlib/mdrun.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 static int groupPbcFromParams(const t_pull_group ¶ms, bool setPbcRefToPrevStepCOM)
92 else if (params.pbcatom >= 0)
94 if (setPbcRefToPrevStepCOM)
96 return epgrppbcPREVSTEPCOM;
100 return epgrppbcREFAT;
109 /* NOTE: The params initialization currently copies pointers.
110 * So the lifetime of the source, currently always inputrec,
111 * should not end before that of this object.
112 * This will be fixed when the pointers are replacted by std::vector.
114 pull_group_work_t::pull_group_work_t(const t_pull_group ¶ms,
115 gmx::LocalAtomSet atomSet,
116 bool bSetPbcRefToPrevStepCOM) :
118 epgrppbc(groupPbcFromParams(params, bSetPbcRefToPrevStepCOM)),
119 needToCalcCom(false),
129 bool pull_coordinate_is_angletype(const t_pull_coord *pcrd)
131 return (pcrd->eGeom == epullgANGLE ||
132 pcrd->eGeom == epullgDIHEDRAL ||
133 pcrd->eGeom == epullgANGLEAXIS);
136 static bool pull_coordinate_is_directional(const t_pull_coord *pcrd)
138 return (pcrd->eGeom == epullgDIR ||
139 pcrd->eGeom == epullgDIRPBC ||
140 pcrd->eGeom == epullgDIRRELATIVE ||
141 pcrd->eGeom == epullgCYL);
144 const char *pull_coordinate_units(const t_pull_coord *pcrd)
146 return pull_coordinate_is_angletype(pcrd) ? "deg" : "nm";
149 double pull_conversion_factor_userinput2internal(const t_pull_coord *pcrd)
151 if (pull_coordinate_is_angletype(pcrd))
161 double pull_conversion_factor_internal2userinput(const t_pull_coord *pcrd)
163 if (pull_coordinate_is_angletype(pcrd))
173 /* Apply forces in a mass weighted fashion for part of the pull group */
174 static void apply_forces_grp_part(const pull_group_work_t *pgrp,
175 int ind_start, int ind_end,
177 const dvec f_pull, int sign, rvec *f)
179 double inv_wm = pgrp->mwscale;
181 auto localAtomIndices = pgrp->atomSet.localIndex();
182 for (int i = ind_start; i < ind_end; i++)
184 int ii = localAtomIndices[i];
185 double wmass = md->massT[ii];
186 if (!pgrp->localWeights.empty())
188 wmass *= pgrp->localWeights[i];
191 for (int d = 0; d < DIM; d++)
193 f[ii][d] += sign * wmass * f_pull[d] * inv_wm;
198 /* Apply forces in a mass weighted fashion */
199 static void apply_forces_grp(const pull_group_work_t *pgrp,
201 const dvec f_pull, int sign, rvec *f,
204 auto localAtomIndices = pgrp->atomSet.localIndex();
206 if (pgrp->params.nat == 1 && pgrp->atomSet.numAtomsLocal() == 1)
208 /* Only one atom and our rank has this atom: we can skip
209 * the mass weighting, which means that this code also works
210 * for mass=0, e.g. with a virtual site.
212 for (int d = 0; d < DIM; d++)
214 f[localAtomIndices[0]][d] += sign*f_pull[d];
219 if (localAtomIndices.size() <= c_pullMaxNumLocalAtomsSingleThreaded)
221 apply_forces_grp_part(pgrp, 0, localAtomIndices.size(), md, f_pull, sign, f);
225 #pragma omp parallel for num_threads(nthreads) schedule(static)
226 for (int th = 0; th < nthreads; th++)
228 int ind_start = (localAtomIndices.size()*(th + 0))/nthreads;
229 int ind_end = (localAtomIndices.size()*(th + 1))/nthreads;
230 apply_forces_grp_part(pgrp, ind_start, ind_end,
231 md, f_pull, sign, f);
237 /* Apply forces in a mass weighted fashion to a cylinder group */
238 static void apply_forces_cyl_grp(const pull_group_work_t *pgrp,
239 const double dv_corr,
241 const dvec f_pull, double f_scal,
243 int gmx_unused nthreads)
245 double inv_wm = pgrp->mwscale;
247 auto localAtomIndices = pgrp->atomSet.localIndex();
249 /* The cylinder group is always a slab in the system, thus large.
250 * Therefore we always thread-parallelize this group.
252 int numAtomsLocal = localAtomIndices.size();
253 #pragma omp parallel for num_threads(nthreads) schedule(static)
254 for (int i = 0; i < numAtomsLocal; i++)
256 double weight = pgrp->localWeights[i];
261 int ii = localAtomIndices[i];
262 double mass = md->massT[ii];
263 /* The stored axial distance from the cylinder center (dv) needs
264 * to be corrected for an offset (dv_corr), which was unknown when
267 double dv_com = pgrp->dv[i] + dv_corr;
269 /* Here we not only add the pull force working along vec (f_pull),
270 * but also a radial component, due to the dependence of the weights
271 * on the radial distance.
273 for (int m = 0; m < DIM; m++)
275 f[ii][m] += sign*inv_wm*(mass*weight*f_pull[m] +
276 pgrp->mdw[i][m]*dv_com*f_scal);
281 /* Apply torque forces in a mass weighted fashion to the groups that define
282 * the pull vector direction for pull coordinate pcrd.
284 static void apply_forces_vec_torque(const struct pull_t *pull,
285 const pull_coord_work_t *pcrd,
289 const PullCoordSpatialData &spatialData = pcrd->spatialData;
291 /* The component inpr along the pull vector is accounted for in the usual
292 * way. Here we account for the component perpendicular to vec.
295 for (int m = 0; m < DIM; m++)
297 inpr += spatialData.dr01[m]*spatialData.vec[m];
299 /* The torque force works along the component of the distance vector
300 * of between the two "usual" pull groups that is perpendicular to
301 * the pull vector. The magnitude of this force is the "usual" scale force
302 * multiplied with the ratio of the distance between the two "usual" pull
303 * groups and the distance between the two groups that define the vector.
306 for (int m = 0; m < DIM; m++)
308 f_perp[m] = (spatialData.dr01[m] - inpr*spatialData.vec[m])/spatialData.vec_len*pcrd->scalarForce;
311 /* Apply the force to the groups defining the vector using opposite signs */
312 apply_forces_grp(&pull->group[pcrd->params.group[2]], md,
313 f_perp, -1, f, pull->nthreads);
314 apply_forces_grp(&pull->group[pcrd->params.group[3]], md,
315 f_perp, 1, f, pull->nthreads);
318 /* Apply forces in a mass weighted fashion */
319 static void apply_forces_coord(struct pull_t * pull, int coord,
320 const PullCoordVectorForces &forces,
321 const t_mdatoms * md,
324 /* Here it would be more efficient to use one large thread-parallel
325 * region instead of potential parallel regions within apply_forces_grp.
326 * But there could be overlap between pull groups and this would lead
330 const pull_coord_work_t &pcrd = pull->coord[coord];
332 if (pcrd.params.eGeom == epullgCYL)
334 apply_forces_cyl_grp(&pull->dyna[coord], pcrd.spatialData.cyl_dev, md,
335 forces.force01, pcrd.scalarForce, -1, f,
338 /* Sum the force along the vector and the radial force */
340 for (int m = 0; m < DIM; m++)
342 f_tot[m] = forces.force01[m] + pcrd.scalarForce*pcrd.spatialData.ffrad[m];
344 apply_forces_grp(&pull->group[pcrd.params.group[1]], md,
345 f_tot, 1, f, pull->nthreads);
349 if (pcrd.params.eGeom == epullgDIRRELATIVE)
351 /* We need to apply the torque forces to the pull groups
352 * that define the pull vector.
354 apply_forces_vec_torque(pull, &pcrd, md, f);
357 if (pull->group[pcrd.params.group[0]].params.nat > 0)
359 apply_forces_grp(&pull->group[pcrd.params.group[0]], md,
360 forces.force01, -1, f, pull->nthreads);
362 apply_forces_grp(&pull->group[pcrd.params.group[1]], md,
363 forces.force01, 1, f, pull->nthreads);
365 if (pcrd.params.ngroup >= 4)
367 apply_forces_grp(&pull->group[pcrd.params.group[2]], md,
368 forces.force23, -1, f, pull->nthreads);
369 apply_forces_grp(&pull->group[pcrd.params.group[3]], md,
370 forces.force23, 1, f, pull->nthreads);
372 if (pcrd.params.ngroup >= 6)
374 apply_forces_grp(&pull->group[pcrd.params.group[4]], md,
375 forces.force45, -1, f, pull->nthreads);
376 apply_forces_grp(&pull->group[pcrd.params.group[5]], md,
377 forces.force45, 1, f, pull->nthreads);
382 real max_pull_distance2(const pull_coord_work_t *pcrd,
385 /* Note that this maximum distance calculation is more complex than
386 * most other cases in GROMACS, since here we have to take care of
387 * distance calculations that don't involve all three dimensions.
388 * For example, we can use distances that are larger than the
389 * box X and Y dimensions for a box that is elongated along Z.
392 real max_d2 = GMX_REAL_MAX;
394 if (pull_coordinate_is_directional(&pcrd->params))
396 /* Directional pulling along along direction pcrd->vec.
397 * Calculating the exact maximum distance is complex and bug-prone.
398 * So we take a safe approach by not allowing distances that
399 * are larger than half the distance between unit cell faces
400 * along dimensions involved in pcrd->vec.
402 for (int m = 0; m < DIM; m++)
404 if (m < pbc->ndim_ePBC && pcrd->spatialData.vec[m] != 0)
406 real imageDistance2 = gmx::square(pbc->box[m][m]);
407 for (int d = m + 1; d < DIM; d++)
409 imageDistance2 -= gmx::square(pbc->box[d][m]);
411 max_d2 = std::min(max_d2, imageDistance2);
417 /* Distance pulling along dimensions with pcrd->params.dim[d]==1.
418 * We use half the minimum box vector length of the dimensions involved.
419 * This is correct for all cases, except for corner cases with
420 * triclinic boxes where e.g. dim[XX]=1 and dim[YY]=0 with
421 * box[YY][XX]!=0 and box[YY][YY] < box[XX][XX]. But since even
422 * in such corner cases the user could get correct results,
423 * depending on the details of the setup, we avoid further
424 * code complications.
426 for (int m = 0; m < DIM; m++)
428 if (m < pbc->ndim_ePBC && pcrd->params.dim[m] != 0)
430 real imageDistance2 = gmx::square(pbc->box[m][m]);
431 for (int d = 0; d < m; d++)
433 if (pcrd->params.dim[d] != 0)
435 imageDistance2 += gmx::square(pbc->box[m][d]);
438 max_d2 = std::min(max_d2, imageDistance2);
446 /* This function returns the distance based on coordinates xg and xref.
447 * Note that the pull coordinate struct pcrd is not modified.
449 static void low_get_pull_coord_dr(const struct pull_t *pull,
450 const pull_coord_work_t *pcrd,
452 dvec xg, dvec xref, double max_dist2,
455 const pull_group_work_t *pgrp0 = &pull->group[pcrd->params.group[0]];
457 /* Only the first group can be an absolute reference, in that case nat=0 */
458 if (pgrp0->params.nat == 0)
460 for (int m = 0; m < DIM; m++)
462 xref[m] = pcrd->params.origin[m];
467 copy_dvec(xref, xrefr);
469 dvec dref = {0, 0, 0};
470 if (pcrd->params.eGeom == epullgDIRPBC)
472 for (int m = 0; m < DIM; m++)
474 dref[m] = pcrd->value_ref*pcrd->spatialData.vec[m];
476 /* Add the reference position, so we use the correct periodic image */
477 dvec_inc(xrefr, dref);
480 pbc_dx_d(pbc, xg, xrefr, dr);
482 bool directional = pull_coordinate_is_directional(&pcrd->params);
484 for (int m = 0; m < DIM; m++)
486 dr[m] *= pcrd->params.dim[m];
487 if (pcrd->params.dim[m] && !(directional && pcrd->spatialData.vec[m] == 0))
492 /* Check if we are close to switching to another periodic image */
493 if (max_dist2 > 0 && dr2 > 0.98*0.98*max_dist2)
495 /* Note that technically there is no issue with switching periodic
496 * image, as pbc_dx_d returns the distance to the closest periodic
497 * image. However in all cases where periodic image switches occur,
498 * the pull results will be useless in practice.
500 gmx_fatal(FARGS, "Distance between pull groups %d and %d (%f nm) is larger than 0.49 times the box size (%f).\n%s",
501 pcrd->params.group[0], pcrd->params.group[1],
502 sqrt(dr2), sqrt(0.98*0.98*max_dist2),
503 pcrd->params.eGeom == epullgDIR ? "You might want to consider using \"pull-geometry = direction-periodic\" instead.\n" : "");
506 if (pcrd->params.eGeom == epullgDIRPBC)
512 /* This function returns the distance based on the contents of the pull struct.
513 * pull->coord[coord_ind].dr, and potentially vector, are updated.
515 static void get_pull_coord_dr(struct pull_t *pull,
519 pull_coord_work_t *pcrd = &pull->coord[coord_ind];
520 PullCoordSpatialData &spatialData = pcrd->spatialData;
523 /* With AWH pulling we allow for periodic pulling with geometry=direction.
524 * TODO: Store a periodicity flag instead of checking for external pull provider.
526 if (pcrd->params.eGeom == epullgDIRPBC || (pcrd->params.eGeom == epullgDIR &&
527 pcrd->params.eType == epullEXTERNAL))
533 md2 = static_cast<double>(max_pull_distance2(pcrd, pbc));
536 if (pcrd->params.eGeom == epullgDIRRELATIVE)
538 /* We need to determine the pull vector */
542 const pull_group_work_t &pgrp2 = pull->group[pcrd->params.group[2]];
543 const pull_group_work_t &pgrp3 = pull->group[pcrd->params.group[3]];
545 pbc_dx_d(pbc, pgrp3.x, pgrp2.x, vec);
547 for (m = 0; m < DIM; m++)
549 vec[m] *= pcrd->params.dim[m];
551 spatialData.vec_len = dnorm(vec);
552 for (m = 0; m < DIM; m++)
554 spatialData.vec[m] = vec[m]/spatialData.vec_len;
558 fprintf(debug, "pull coord %d vector: %6.3f %6.3f %6.3f normalized: %6.3f %6.3f %6.3f\n",
560 vec[XX], vec[YY], vec[ZZ],
561 spatialData.vec[XX], spatialData.vec[YY], spatialData.vec[ZZ]);
565 pull_group_work_t *pgrp0 = &pull->group[pcrd->params.group[0]];
566 pull_group_work_t *pgrp1 = &pull->group[pcrd->params.group[1]];
568 low_get_pull_coord_dr(pull, pcrd, pbc,
570 pcrd->params.eGeom == epullgCYL ? pull->dyna[coord_ind].x : pgrp0->x,
574 if (pcrd->params.ngroup >= 4)
576 pull_group_work_t *pgrp2, *pgrp3;
577 pgrp2 = &pull->group[pcrd->params.group[2]];
578 pgrp3 = &pull->group[pcrd->params.group[3]];
580 low_get_pull_coord_dr(pull, pcrd, pbc,
586 if (pcrd->params.ngroup >= 6)
588 pull_group_work_t *pgrp4, *pgrp5;
589 pgrp4 = &pull->group[pcrd->params.group[4]];
590 pgrp5 = &pull->group[pcrd->params.group[5]];
592 low_get_pull_coord_dr(pull, pcrd, pbc,
600 /* Modify x so that it is periodic in [-pi, pi)
601 * It is assumed that x is in [-3pi, 3pi) so that x
602 * needs to be shifted by at most one period.
604 static void make_periodic_2pi(double *x)
616 /* This function should always be used to modify pcrd->value_ref */
617 static void low_set_pull_coord_reference_value(pull_coord_work_t *pcrd,
621 GMX_ASSERT(pcrd->params.eType != epullEXTERNAL, "The pull coord reference value should not be used with type external-potential");
623 if (pcrd->params.eGeom == epullgDIST)
627 gmx_fatal(FARGS, "Pull reference distance for coordinate %d (%f) needs to be non-negative", coord_ind + 1, value_ref);
630 else if (pcrd->params.eGeom == epullgANGLE || pcrd->params.eGeom == epullgANGLEAXIS)
632 if (value_ref < 0 || value_ref > M_PI)
634 gmx_fatal(FARGS, "Pull reference angle for coordinate %d (%f) needs to be in the allowed interval [0,180] deg", coord_ind + 1, value_ref*pull_conversion_factor_internal2userinput(&pcrd->params));
637 else if (pcrd->params.eGeom == epullgDIHEDRAL)
639 /* Allow pulling to be periodic for dihedral angles by remapping the reference value to the interval [-pi, pi). */
640 make_periodic_2pi(&value_ref);
643 pcrd->value_ref = value_ref;
646 static void update_pull_coord_reference_value(pull_coord_work_t *pcrd, int coord_ind, double t)
648 /* With zero rate the reference value is set initially and doesn't change */
649 if (pcrd->params.rate != 0)
651 double value_ref = (pcrd->params.init + pcrd->params.rate*t)*pull_conversion_factor_userinput2internal(&pcrd->params);
652 low_set_pull_coord_reference_value(pcrd, coord_ind, value_ref);
656 /* Returns the dihedral angle. Updates the plane normal vectors m, n. */
657 static double get_dihedral_angle_coord(PullCoordSpatialData *spatialData)
660 dvec dr32; /* store instead of dr23? */
662 dsvmul(-1, spatialData->dr23, dr32);
663 dcprod(spatialData->dr01, dr32, spatialData->planevec_m); /* Normal of first plane */
664 dcprod(dr32, spatialData->dr45, spatialData->planevec_n); /* Normal of second plane */
665 phi = gmx_angle_between_dvecs(spatialData->planevec_m, spatialData->planevec_n);
667 /* Note 1: the sign below is opposite of that in the bondeds or Bekker 1994
668 * because there r_ij = ri - rj, while here dr01 = r_1 - r_0
669 * Note 2: the angle between the plane normal vectors equals pi only when
670 * both planes coincide. Thus, when phi = pi dr01 will lie in both planes and
671 * we get a positive sign below. Thus, the range of the dihedral angle is (-180, 180].
673 sign = (diprod(spatialData->dr01, spatialData->planevec_n) < 0.0) ? 1.0 : -1.0;
677 /* Calculates pull->coord[coord_ind].value.
678 * This function also updates pull->coord[coord_ind].dr.
680 static void get_pull_coord_distance(struct pull_t *pull,
684 pull_coord_work_t *pcrd;
687 pcrd = &pull->coord[coord_ind];
689 get_pull_coord_dr(pull, coord_ind, pbc);
691 PullCoordSpatialData &spatialData = pcrd->spatialData;
693 switch (pcrd->params.eGeom)
696 /* Pull along the vector between the com's */
697 spatialData.value = dnorm(spatialData.dr01);
701 case epullgDIRRELATIVE:
704 spatialData.value = 0;
705 for (m = 0; m < DIM; m++)
707 spatialData.value += spatialData.vec[m]*spatialData.dr01[m];
711 spatialData.value = gmx_angle_between_dvecs(spatialData.dr01, spatialData.dr23);
714 spatialData.value = get_dihedral_angle_coord(&spatialData);
716 case epullgANGLEAXIS:
717 spatialData.value = gmx_angle_between_dvecs(spatialData.dr01, spatialData.vec);
720 gmx_incons("Unsupported pull type in get_pull_coord_distance");
724 /* Returns the deviation from the reference value.
725 * Updates pull->coord[coord_ind].dr, .value and .value_ref.
727 static double get_pull_coord_deviation(struct pull_t *pull,
732 pull_coord_work_t *pcrd;
735 pcrd = &pull->coord[coord_ind];
737 /* Update the reference value before computing the distance,
738 * since it is used in the distance computation with periodic pulling.
740 update_pull_coord_reference_value(pcrd, coord_ind, t);
742 get_pull_coord_distance(pull, coord_ind, pbc);
744 get_pull_coord_distance(pull, coord_ind, pbc);
746 /* Determine the deviation */
747 dev = pcrd->spatialData.value - pcrd->value_ref;
749 /* Check that values are allowed */
750 if (pcrd->params.eGeom == epullgDIST && pcrd->spatialData.value == 0)
752 /* With no vector we can not determine the direction for the force,
753 * so we set the force to zero.
757 else if (pcrd->params.eGeom == epullgDIHEDRAL)
759 /* The reference value is in [-pi, pi). The coordinate value is in (-pi, pi].
760 Thus, the unwrapped deviation is here in (-2pi, 2pi].
761 After making it periodic, the deviation will be in [-pi, pi). */
762 make_periodic_2pi(&dev);
768 double get_pull_coord_value(struct pull_t *pull,
772 get_pull_coord_distance(pull, coord_ind, pbc);
774 return pull->coord[coord_ind].spatialData.value;
777 void clear_pull_forces(pull_t *pull)
779 /* Zeroing the forces is only required for constraint pulling.
780 * It can happen that multiple constraint steps need to be applied
781 * and therefore the constraint forces need to be accumulated.
783 for (pull_coord_work_t &coord : pull->coord)
785 coord.scalarForce = 0;
789 /* Apply constraint using SHAKE */
790 static void do_constraint(struct pull_t *pull, t_pbc *pbc,
792 gmx_bool bMaster, tensor vir,
796 dvec *r_ij; /* x[i] com of i in prev. step. Obeys constr. -> r_ij[i] */
797 dvec unc_ij; /* xp[i] com of i this step, before constr. -> unc_ij */
798 dvec *rnew; /* current 'new' positions of the groups */
799 double *dr_tot; /* the total update of the coords */
802 double lambda, rm, invdt = 0;
803 gmx_bool bConverged_all, bConverged = FALSE;
804 int niter = 0, ii, j, m, max_iter = 100;
808 snew(r_ij, pull->coord.size());
809 snew(dr_tot, pull->coord.size());
811 snew(rnew, pull->group.size());
813 /* copy the current unconstrained positions for use in iterations. We
814 iterate until rinew[i] and rjnew[j] obey the constraints. Then
815 rinew - pull.x_unc[i] is the correction dr to group i */
816 for (size_t g = 0; g < pull->group.size(); g++)
818 copy_dvec(pull->group[g].xp, rnew[g]);
821 /* Determine the constraint directions from the old positions */
822 for (size_t c = 0; c < pull->coord.size(); c++)
824 pull_coord_work_t *pcrd;
826 pcrd = &pull->coord[c];
828 if (pcrd->params.eType != epullCONSTRAINT)
833 /* Note that get_pull_coord_distance also sets pcrd->dr and pcrd->value.
834 * We don't modify dr and value anymore, so these values are also used
837 get_pull_coord_distance(pull, c, pbc);
839 const PullCoordSpatialData &spatialData = pcrd->spatialData;
842 fprintf(debug, "Pull coord %zu dr %f %f %f\n",
843 c, spatialData.dr01[XX], spatialData.dr01[YY], spatialData.dr01[ZZ]);
846 if (pcrd->params.eGeom == epullgDIR ||
847 pcrd->params.eGeom == epullgDIRPBC)
849 /* Select the component along vec */
851 for (m = 0; m < DIM; m++)
853 a += spatialData.vec[m]*spatialData.dr01[m];
855 for (m = 0; m < DIM; m++)
857 r_ij[c][m] = a*spatialData.vec[m];
862 copy_dvec(spatialData.dr01, r_ij[c]);
865 if (dnorm2(r_ij[c]) == 0)
867 gmx_fatal(FARGS, "Distance for pull coordinate %zu is zero with constraint pulling, which is not allowed.", c + 1);
871 bConverged_all = FALSE;
872 while (!bConverged_all && niter < max_iter)
874 bConverged_all = TRUE;
876 /* loop over all constraints */
877 for (size_t c = 0; c < pull->coord.size(); c++)
879 pull_coord_work_t *pcrd;
880 pull_group_work_t *pgrp0, *pgrp1;
883 pcrd = &pull->coord[c];
885 if (pcrd->params.eType != epullCONSTRAINT)
890 update_pull_coord_reference_value(pcrd, c, t);
892 pgrp0 = &pull->group[pcrd->params.group[0]];
893 pgrp1 = &pull->group[pcrd->params.group[1]];
895 /* Get the current difference vector */
896 low_get_pull_coord_dr(pull, pcrd, pbc,
897 rnew[pcrd->params.group[1]],
898 rnew[pcrd->params.group[0]],
903 fprintf(debug, "Pull coord %zu, iteration %d\n", c, niter);
906 rm = 1.0/(pgrp0->invtm + pgrp1->invtm);
908 switch (pcrd->params.eGeom)
911 if (pcrd->value_ref <= 0)
913 gmx_fatal(FARGS, "The pull constraint reference distance for group %zu is <= 0 (%f)", c, pcrd->value_ref);
917 double q, c_a, c_b, c_c;
919 c_a = diprod(r_ij[c], r_ij[c]);
920 c_b = diprod(unc_ij, r_ij[c])*2;
921 c_c = diprod(unc_ij, unc_ij) - gmx::square(pcrd->value_ref);
925 q = -0.5*(c_b - std::sqrt(c_b*c_b - 4*c_a*c_c));
930 q = -0.5*(c_b + std::sqrt(c_b*c_b - 4*c_a*c_c));
937 "Pull ax^2+bx+c=0: a=%e b=%e c=%e lambda=%e\n",
938 c_a, c_b, c_c, lambda);
942 /* The position corrections dr due to the constraints */
943 dsvmul(-lambda*rm*pgrp1->invtm, r_ij[c], dr1);
944 dsvmul( lambda*rm*pgrp0->invtm, r_ij[c], dr0);
945 dr_tot[c] += -lambda*dnorm(r_ij[c]);
950 /* A 1-dimensional constraint along a vector */
952 for (m = 0; m < DIM; m++)
954 vec[m] = pcrd->spatialData.vec[m];
955 a += unc_ij[m]*vec[m];
957 /* Select only the component along the vector */
958 dsvmul(a, vec, unc_ij);
959 lambda = a - pcrd->value_ref;
962 fprintf(debug, "Pull inpr %e lambda: %e\n", a, lambda);
965 /* The position corrections dr due to the constraints */
966 dsvmul(-lambda*rm*pgrp1->invtm, vec, dr1);
967 dsvmul( lambda*rm*pgrp0->invtm, vec, dr0);
968 dr_tot[c] += -lambda;
971 gmx_incons("Invalid enumeration value for eGeom");
979 g0 = pcrd->params.group[0];
980 g1 = pcrd->params.group[1];
981 low_get_pull_coord_dr(pull, pcrd, pbc, rnew[g1], rnew[g0], -1, tmp);
982 low_get_pull_coord_dr(pull, pcrd, pbc, dr1, dr0, -1, tmp3);
984 "Pull cur %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
985 rnew[g0][0], rnew[g0][1], rnew[g0][2],
986 rnew[g1][0], rnew[g1][1], rnew[g1][2], dnorm(tmp));
988 "Pull ref %8s %8s %8s %8s %8s %8s d: %8.5f\n",
989 "", "", "", "", "", "", pcrd->value_ref);
991 "Pull cor %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
992 dr0[0], dr0[1], dr0[2],
993 dr1[0], dr1[1], dr1[2],
997 /* Update the COMs with dr */
998 dvec_inc(rnew[pcrd->params.group[1]], dr1);
999 dvec_inc(rnew[pcrd->params.group[0]], dr0);
1002 /* Check if all constraints are fullfilled now */
1003 for (pull_coord_work_t &coord : pull->coord)
1005 if (coord.params.eType != epullCONSTRAINT)
1010 low_get_pull_coord_dr(pull, &coord, pbc,
1011 rnew[coord.params.group[1]],
1012 rnew[coord.params.group[0]],
1015 switch (coord.params.eGeom)
1019 fabs(dnorm(unc_ij) - coord.value_ref) < pull->params.constr_tol;
1024 for (m = 0; m < DIM; m++)
1026 vec[m] = coord.spatialData.vec[m];
1028 inpr = diprod(unc_ij, vec);
1029 dsvmul(inpr, vec, unc_ij);
1031 fabs(diprod(unc_ij, vec) - coord.value_ref) < pull->params.constr_tol;
1039 fprintf(debug, "Pull constraint not converged: "
1041 "d_ref = %f, current d = %f\n",
1042 coord.params.group[0], coord.params.group[1],
1043 coord.value_ref, dnorm(unc_ij));
1046 bConverged_all = FALSE;
1051 /* if after all constraints are dealt with and bConverged is still TRUE
1052 we're finished, if not we do another iteration */
1054 if (niter > max_iter)
1056 gmx_fatal(FARGS, "Too many iterations for constraint run: %d", niter);
1059 /* DONE ITERATING, NOW UPDATE COORDINATES AND CALC. CONSTRAINT FORCES */
1066 /* update atoms in the groups */
1067 for (size_t g = 0; g < pull->group.size(); g++)
1069 const pull_group_work_t *pgrp;
1072 pgrp = &pull->group[g];
1074 /* get the final constraint displacement dr for group g */
1075 dvec_sub(rnew[g], pgrp->xp, dr);
1077 if (dnorm2(dr) == 0)
1079 /* No displacement, no update necessary */
1083 /* update the atom positions */
1084 auto localAtomIndices = pgrp->atomSet.localIndex();
1086 for (gmx::index j = 0; j < localAtomIndices.size(); j++)
1088 ii = localAtomIndices[j];
1089 if (!pgrp->localWeights.empty())
1091 dsvmul(pgrp->wscale*pgrp->localWeights[j], dr, tmp);
1093 for (m = 0; m < DIM; m++)
1099 for (m = 0; m < DIM; m++)
1101 v[ii][m] += invdt*tmp[m];
1107 /* calculate the constraint forces, used for output and virial only */
1108 for (size_t c = 0; c < pull->coord.size(); c++)
1110 pull_coord_work_t *pcrd;
1112 pcrd = &pull->coord[c];
1114 if (pcrd->params.eType != epullCONSTRAINT)
1119 /* Accumulate the forces, in case we have multiple constraint steps */
1120 double force = dr_tot[c]/((pull->group[pcrd->params.group[0]].invtm + pull->group[pcrd->params.group[1]].invtm)*dt*dt);
1121 pcrd->scalarForce += force;
1123 if (vir != nullptr && pcrd->params.eGeom != epullgDIRPBC && bMaster)
1127 /* Add the pull contribution to the virial */
1128 /* We have already checked above that r_ij[c] != 0 */
1129 f_invr = pcrd->scalarForce/dnorm(r_ij[c]);
1131 for (j = 0; j < DIM; j++)
1133 for (m = 0; m < DIM; m++)
1135 vir[j][m] -= 0.5*f_invr*r_ij[c][j]*r_ij[c][m];
1141 /* finished! I hope. Give back some memory */
1147 static void add_virial_coord_dr(tensor vir, const dvec dr, const dvec f)
1149 for (int j = 0; j < DIM; j++)
1151 for (int m = 0; m < DIM; m++)
1153 vir[j][m] -= 0.5*f[j]*dr[m];
1158 /* Adds the pull contribution to the virial */
1159 static void add_virial_coord(tensor vir,
1160 const pull_coord_work_t &pcrd,
1161 const PullCoordVectorForces &forces)
1163 if (vir != nullptr && pcrd.params.eGeom != epullgDIRPBC)
1165 /* Add the pull contribution for each distance vector to the virial. */
1166 add_virial_coord_dr(vir, pcrd.spatialData.dr01, forces.force01);
1167 if (pcrd.params.ngroup >= 4)
1169 add_virial_coord_dr(vir, pcrd.spatialData.dr23, forces.force23);
1171 if (pcrd.params.ngroup >= 6)
1173 add_virial_coord_dr(vir, pcrd.spatialData.dr45, forces.force45);
1178 static void calc_pull_coord_scalar_force_and_potential(pull_coord_work_t *pcrd,
1179 double dev, real lambda,
1180 real *V, real *dVdl)
1184 k = (1.0 - lambda)*pcrd->params.k + lambda*pcrd->params.kB;
1185 dkdl = pcrd->params.kB - pcrd->params.k;
1187 switch (pcrd->params.eType)
1190 case epullFLATBOTTOM:
1191 case epullFLATBOTTOMHIGH:
1192 /* The only difference between an umbrella and a flat-bottom
1193 * potential is that a flat-bottom is zero above or below
1194 the reference value.
1196 if ((pcrd->params.eType == epullFLATBOTTOM && dev < 0) ||
1197 (pcrd->params.eType == epullFLATBOTTOMHIGH && dev > 0))
1202 pcrd->scalarForce = -k*dev;
1203 *V += 0.5* k*gmx::square(dev);
1204 *dVdl += 0.5*dkdl*gmx::square(dev);
1207 pcrd->scalarForce = -k;
1208 *V += k*pcrd->spatialData.value;
1209 *dVdl += dkdl*pcrd->spatialData.value;
1212 gmx_incons("the scalar pull force should not be calculated internally for pull type external");
1214 gmx_incons("Unsupported pull type in do_pull_pot");
1218 static PullCoordVectorForces
1219 calculateVectorForces(const pull_coord_work_t &pcrd)
1221 const t_pull_coord ¶ms = pcrd.params;
1222 const PullCoordSpatialData &spatialData = pcrd.spatialData;
1224 /* The geometry of the coordinate determines how the scalar force relates to the force on each group */
1225 PullCoordVectorForces forces;
1227 if (params.eGeom == epullgDIST)
1229 double invdr01 = spatialData.value > 0 ? 1./spatialData.value : 0.;
1230 for (int m = 0; m < DIM; m++)
1232 forces.force01[m] = pcrd.scalarForce*spatialData.dr01[m]*invdr01;
1235 else if (params.eGeom == epullgANGLE)
1238 double cos_theta, cos_theta2;
1240 cos_theta = cos(spatialData.value);
1241 cos_theta2 = gmx::square(cos_theta);
1243 /* The force at theta = 0, pi is undefined so we should not apply any force.
1244 * cos(theta) = 1 for theta = 0, pi so this is what we check for below.
1245 * Note that dr01 = 0 or dr23 = 0 evaluates to theta = 0 so we do not
1246 * have to check for this before dividing by their norm below.
1250 /* The forces f01 and f23 can be decomposed into one vector parallel to dr01
1251 * and another vector parallel to dr23:
1252 * f01 = -dV/dtheta*(a*dr23/|dr23| - b*dr01*|dr01|)/|dr01|,
1253 * f23 = -dV/dtheta*(a*dr01/|dr01| - b*dr23*|dr23|)/|dr23|,
1255 double a = -gmx::invsqrt(1 - cos_theta2); /* comes from d/dx acos(x) */
1256 double b = a*cos_theta;
1257 double invdr01 = 1./dnorm(spatialData.dr01);
1258 double invdr23 = 1./dnorm(spatialData.dr23);
1259 dvec normalized_dr01, normalized_dr23;
1260 dsvmul(invdr01, spatialData.dr01, normalized_dr01);
1261 dsvmul(invdr23, spatialData.dr23, normalized_dr23);
1263 for (int m = 0; m < DIM; m++)
1265 /* Here, f_scal is -dV/dtheta */
1266 forces.force01[m] = pcrd.scalarForce*invdr01*(a*normalized_dr23[m] - b*normalized_dr01[m]);
1267 forces.force23[m] = pcrd.scalarForce*invdr23*(a*normalized_dr01[m] - b*normalized_dr23[m]);
1272 /* No forces to apply for ill-defined cases*/
1273 clear_dvec(forces.force01);
1274 clear_dvec(forces.force23);
1277 else if (params.eGeom == epullgANGLEAXIS)
1279 double cos_theta, cos_theta2;
1281 /* The angle-axis force is exactly the same as the angle force (above) except that in
1282 this case the second vector (dr23) is replaced by the pull vector. */
1283 cos_theta = cos(spatialData.value);
1284 cos_theta2 = gmx::square(cos_theta);
1290 dvec normalized_dr01;
1292 invdr01 = 1./dnorm(spatialData.dr01);
1293 dsvmul(invdr01, spatialData.dr01, normalized_dr01);
1294 a = -gmx::invsqrt(1 - cos_theta2); /* comes from d/dx acos(x) */
1297 for (int m = 0; m < DIM; m++)
1299 forces.force01[m] = pcrd.scalarForce*invdr01*(a*spatialData.vec[m] - b*normalized_dr01[m]);
1304 clear_dvec(forces.force01);
1307 else if (params.eGeom == epullgDIHEDRAL)
1309 double m2, n2, tol, sqrdist_32;
1311 /* Note: there is a small difference here compared to the
1312 dihedral force calculations in the bondeds (ref: Bekker 1994).
1313 There rij = ri - rj, while here dr01 = r1 - r0.
1314 However, all distance vectors occur in form of cross or inner products
1315 so that two signs cancel and we end up with the same expressions.
1316 Also, we treat the more general case of 6 groups (0..5) instead of 4 (i, j, k, l).
1318 m2 = diprod(spatialData.planevec_m, spatialData.planevec_m);
1319 n2 = diprod(spatialData.planevec_n, spatialData.planevec_n);
1320 dsvmul(-1, spatialData.dr23, dr32);
1321 sqrdist_32 = diprod(dr32, dr32);
1322 tol = sqrdist_32*GMX_REAL_EPS; /* Avoid tiny angles */
1323 if ((m2 > tol) && (n2 > tol))
1325 double a_01, a_23_01, a_23_45, a_45;
1326 double inv_dist_32, inv_sqrdist_32, dist_32;
1328 inv_dist_32 = gmx::invsqrt(sqrdist_32);
1329 inv_sqrdist_32 = inv_dist_32*inv_dist_32;
1330 dist_32 = sqrdist_32*inv_dist_32;
1332 /* Forces on groups 0, 1 */
1333 a_01 = pcrd.scalarForce*dist_32/m2; /* scalarForce is -dV/dphi */
1334 dsvmul(-a_01, spatialData.planevec_m, forces.force01); /* added sign to get force on group 1, not 0 */
1336 /* Forces on groups 4, 5 */
1337 a_45 = -pcrd.scalarForce*dist_32/n2;
1338 dsvmul(a_45, spatialData.planevec_n, forces.force45); /* force on group 5 */
1340 /* Force on groups 2, 3 (defining the axis) */
1341 a_23_01 = -diprod(spatialData.dr01, dr32)*inv_sqrdist_32;
1342 a_23_45 = -diprod(spatialData.dr45, dr32)*inv_sqrdist_32;
1343 dsvmul(-a_23_01, forces.force01, u); /* added sign to get force from group 0, not 1 */
1344 dsvmul(a_23_45, forces.force45, v);
1345 dvec_sub(u, v, forces.force23); /* force on group 3 */
1349 /* No force to apply for ill-defined cases */
1350 clear_dvec(forces.force01);
1351 clear_dvec(forces.force23);
1352 clear_dvec(forces.force45);
1357 for (int m = 0; m < DIM; m++)
1359 forces.force01[m] = pcrd.scalarForce*spatialData.vec[m];
1367 /* We use a global mutex for locking access to the pull data structure
1368 * during registration of external pull potential providers.
1369 * We could use a different, local mutex for each pull object, but the overhead
1370 * is extremely small here and registration is only done during initialization.
1372 static gmx::Mutex registrationMutex;
1374 using Lock = gmx::lock_guard<gmx::Mutex>;
1376 void register_external_pull_potential(struct pull_t *pull,
1378 const char *provider)
1380 GMX_RELEASE_ASSERT(pull != nullptr, "register_external_pull_potential called before init_pull");
1381 GMX_RELEASE_ASSERT(provider != nullptr, "register_external_pull_potential called with NULL as provider name");
1383 if (coord_index < 0 || coord_index >= static_cast<int>(pull->coord.size()))
1385 gmx_fatal(FARGS, "Module '%s' attempted to register an external potential for pull coordinate %d which is out of the pull coordinate range %d - %zu\n",
1386 provider, coord_index + 1, 1, pull->coord.size());
1389 pull_coord_work_t *pcrd = &pull->coord[coord_index];
1391 if (pcrd->params.eType != epullEXTERNAL)
1393 gmx_fatal(FARGS, "Module '%s' attempted to register an external potential for pull coordinate %d which of type '%s', whereas external potentials are only supported with type '%s'",
1394 provider, coord_index + 1, epull_names[pcrd->params.eType], epull_names[epullEXTERNAL]);
1397 GMX_RELEASE_ASSERT(pcrd->params.externalPotentialProvider != nullptr, "The external potential provider string for a pull coordinate is NULL");
1399 if (gmx_strcasecmp(provider, pcrd->params.externalPotentialProvider) != 0)
1401 gmx_fatal(FARGS, "Module '%s' attempted to register an external potential for pull coordinate %d which expects the external potential to be provided by a module named '%s'",
1402 provider, coord_index + 1, pcrd->params.externalPotentialProvider);
1405 /* Lock to avoid (extremely unlikely) simultaneous reading and writing of
1406 * pcrd->bExternalPotentialProviderHasBeenRegistered and
1407 * pull->numUnregisteredExternalPotentials.
1409 Lock registrationLock(registrationMutex);
1411 if (pcrd->bExternalPotentialProviderHasBeenRegistered)
1413 gmx_fatal(FARGS, "Module '%s' attempted to register an external potential for pull coordinate %d more than once",
1414 provider, coord_index + 1);
1417 pcrd->bExternalPotentialProviderHasBeenRegistered = true;
1418 pull->numUnregisteredExternalPotentials--;
1420 GMX_RELEASE_ASSERT(pull->numUnregisteredExternalPotentials >= 0, "Negative unregistered potentials, the pull code is inconsistent");
1424 static void check_external_potential_registration(const struct pull_t *pull)
1426 if (pull->numUnregisteredExternalPotentials > 0)
1429 for (c = 0; c < pull->coord.size(); c++)
1431 if (pull->coord[c].params.eType == epullEXTERNAL &&
1432 !pull->coord[c].bExternalPotentialProviderHasBeenRegistered)
1438 GMX_RELEASE_ASSERT(c < pull->coord.size(), "Internal inconsistency in the pull potential provider counting");
1440 gmx_fatal(FARGS, "No external provider for external pull potentials have been provided for %d pull coordinates. The first coordinate without provider is number %zu, which expects a module named '%s' to provide the external potential.",
1441 pull->numUnregisteredExternalPotentials,
1442 c + 1, pull->coord[c].params.externalPotentialProvider);
1447 /* Pull takes care of adding the forces of the external potential.
1448 * The external potential module has to make sure that the corresponding
1449 * potential energy is added either to the pull term or to a term
1450 * specific to the external module.
1452 void apply_external_pull_coord_force(struct pull_t *pull,
1455 const t_mdatoms *mdatoms,
1456 gmx::ForceWithVirial *forceWithVirial)
1458 pull_coord_work_t *pcrd;
1460 GMX_ASSERT(coord_index >= 0 && coord_index < static_cast<int>(pull->coord.size()), "apply_external_pull_coord_force called with coord_index out of range");
1462 if (pull->comm.bParticipate)
1464 pcrd = &pull->coord[coord_index];
1466 GMX_RELEASE_ASSERT(pcrd->params.eType == epullEXTERNAL, "The pull force can only be set externally on pull coordinates of external type");
1468 GMX_ASSERT(pcrd->bExternalPotentialProviderHasBeenRegistered, "apply_external_pull_coord_force called for an unregistered pull coordinate");
1471 pcrd->scalarForce = coord_force;
1473 /* Calculate the forces on the pull groups */
1474 PullCoordVectorForces pullCoordForces = calculateVectorForces(*pcrd);
1476 /* Add the forces for this coordinate to the total virial and force */
1477 if (forceWithVirial->computeVirial_ && pull->comm.isMasterRank)
1479 matrix virial = { { 0 } };
1480 add_virial_coord(virial, *pcrd, pullCoordForces);
1481 forceWithVirial->addVirialContribution(virial);
1484 apply_forces_coord(pull, coord_index, pullCoordForces, mdatoms,
1485 as_rvec_array(forceWithVirial->force_.data()));
1488 pull->numExternalPotentialsStillToBeAppliedThisStep--;
1491 /* Calculate the pull potential and scalar force for a pull coordinate.
1492 * Returns the vector forces for the pull coordinate.
1494 static PullCoordVectorForces
1495 do_pull_pot_coord(struct pull_t *pull, int coord_ind, t_pbc *pbc,
1496 double t, real lambda,
1497 real *V, tensor vir, 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, const t_mdatoms *md, t_pbc *pbc,
1515 const t_commrec *cr, double t, real lambda,
1516 const rvec *x, gmx::ForceWithVirial *force, real *dvdlambda)
1520 assert(pull != nullptr);
1522 /* Ideally we should check external potential registration only during
1523 * the initialization phase, but that requires another function call
1524 * that should be done exactly in the right order. So we check here.
1526 check_external_potential_registration(pull);
1528 if (pull->comm.bParticipate)
1532 pull_calc_coms(cr, pull, md, pbc, t, x, nullptr);
1534 rvec *f = as_rvec_array(force->force_.data());
1535 matrix virial = { { 0 } };
1536 const bool computeVirial = (force->computeVirial_ && MASTER(cr));
1537 for (size_t c = 0; c < pull->coord.size(); c++)
1539 pull_coord_work_t *pcrd;
1540 pcrd = &pull->coord[c];
1542 /* For external potential the force is assumed to be given by an external module by a call to
1543 apply_pull_coord_external_force */
1544 if (pcrd->params.eType == epullCONSTRAINT || pcrd->params.eType == epullEXTERNAL)
1549 PullCoordVectorForces pullCoordForces =
1550 do_pull_pot_coord(pull, c, pbc, t, lambda,
1552 computeVirial ? virial : nullptr,
1555 /* Distribute the force over the atoms in the pulled groups */
1556 apply_forces_coord(pull, c, pullCoordForces, md, f);
1561 force->addVirialContribution(virial);
1566 GMX_ASSERT(pull->numExternalPotentialsStillToBeAppliedThisStep == 0, "Too few or too many external pull potentials have been applied the previous step");
1567 /* All external pull potentials still need to be applied */
1568 pull->numExternalPotentialsStillToBeAppliedThisStep =
1569 pull->numCoordinatesWithExternalPotential;
1571 return (MASTER(cr) ? V : 0.0);
1574 void pull_constraint(struct pull_t *pull, const t_mdatoms *md, t_pbc *pbc,
1575 const t_commrec *cr, double dt, double t,
1576 rvec *x, rvec *xp, rvec *v, tensor vir)
1578 assert(pull != nullptr);
1580 if (pull->comm.bParticipate)
1582 pull_calc_coms(cr, pull, md, pbc, t, x, xp);
1584 do_constraint(pull, pbc, xp, v, MASTER(cr), vir, dt, t);
1588 void dd_make_local_pull_groups(const t_commrec *cr, struct pull_t *pull)
1592 gmx_bool bMustParticipate;
1598 /* We always make the master node participate, such that it can do i/o,
1599 * add the virial and to simplify MC type extensions people might have.
1601 bMustParticipate = (comm->bParticipateAll || comm->isMasterRank);
1603 for (pull_group_work_t &group : pull->group)
1605 if (!group.globalWeights.empty())
1607 group.localWeights.resize(group.atomSet.numAtomsLocal());
1608 for (size_t i = 0; i < group.atomSet.numAtomsLocal(); ++i)
1610 group.localWeights[i] = group.globalWeights[group.atomSet.collectiveIndex()[i]];
1614 GMX_ASSERT(bMustParticipate || dd != nullptr, "Either all ranks (including this rank) participate, or we use DD and need to have access to dd here");
1616 /* We should participate if we have pull or pbc atoms */
1617 if (!bMustParticipate &&
1618 (group.atomSet.numAtomsLocal() > 0 || (group.epgrppbc == epgrppbcREFAT &&
1619 group.pbcAtomSet->numAtomsLocal() > 0)))
1621 bMustParticipate = TRUE;
1625 if (!comm->bParticipateAll)
1627 /* Keep currently not required ranks in the communicator
1628 * if they needed to participate up to 20 decompositions ago.
1629 * This avoids frequent rebuilds due to atoms jumping back and forth.
1631 const int64_t history_count = 20;
1632 gmx_bool bWillParticipate;
1635 /* Increase the decomposition counter for the current call */
1636 comm->setup_count++;
1638 if (bMustParticipate)
1640 comm->must_count = comm->setup_count;
1645 (comm->bParticipate &&
1646 comm->must_count >= comm->setup_count - history_count);
1648 if (debug && dd != nullptr)
1650 fprintf(debug, "Our DD rank (%3d) pull #atoms>0 or master: %s, will be part %s\n",
1651 dd->rank, gmx::boolToString(bMustParticipate), gmx::boolToString(bWillParticipate));
1654 if (bWillParticipate)
1656 /* Count the number of ranks that we want to have participating */
1658 /* Count the number of ranks that need to be added */
1659 count[1] = comm->bParticipate ? 0 : 1;
1667 /* The cost of this global operation will be less that the cost
1668 * of the extra MPI_Comm_split calls that we can avoid.
1670 gmx_sumi(2, count, cr);
1672 /* If we are missing ranks or if we have 20% more ranks than needed
1673 * we make a new sub-communicator.
1675 if (count[1] > 0 || 6*count[0] < 5*comm->nparticipate)
1679 fprintf(debug, "Creating new pull subcommunicator of size %d\n",
1684 if (comm->mpi_comm_com != MPI_COMM_NULL)
1686 MPI_Comm_free(&comm->mpi_comm_com);
1689 /* This might be an extremely expensive operation, so we try
1690 * to avoid this splitting as much as possible.
1692 assert(dd != nullptr);
1693 MPI_Comm_split(dd->mpi_comm_all, bWillParticipate ? 0 : 1, dd->rank,
1694 &comm->mpi_comm_com);
1697 comm->bParticipate = bWillParticipate;
1698 comm->nparticipate = count[0];
1700 /* When we use the previous COM for PBC, we need to broadcast
1701 * the previous COM to ranks that have joined the communicator.
1703 for (pull_group_work_t &group : pull->group)
1705 if (group.epgrppbc == epgrppbcPREVSTEPCOM)
1707 GMX_ASSERT(comm->bParticipate || !MASTER(cr),
1708 "The master rank has to participate, as it should pass an up to date prev. COM "
1709 "to bcast here as well as to e.g. checkpointing");
1711 gmx_bcast(sizeof(dvec), group.x_prev_step, cr);
1717 /* Since the PBC of atoms might have changed, we need to update the PBC */
1718 pull->bSetPBCatoms = TRUE;
1721 static void init_pull_group_index(FILE *fplog, const t_commrec *cr,
1722 int g, pull_group_work_t *pg,
1723 gmx_bool bConstraint, const ivec pulldim_con,
1724 const gmx_mtop_t *mtop,
1725 const t_inputrec *ir, real lambda)
1727 /* With EM and BD there are no masses in the integrator.
1728 * But we still want to have the correct mass-weighted COMs.
1729 * So we store the real masses in the weights.
1731 const bool setWeights = (pg->params.nweight > 0 ||
1732 EI_ENERGY_MINIMIZATION(ir->eI) ||
1735 /* In parallel, store we need to extract localWeights from weights at DD time */
1736 std::vector<real> &weights = ((cr && PAR(cr)) ? pg->globalWeights : pg->localWeights);
1738 const gmx_groups_t *groups = &mtop->groups;
1740 /* Count frozen dimensions and (weighted) mass */
1746 for (int i = 0; i < pg->params.nat; i++)
1748 int ii = pg->params.ind[i];
1749 if (bConstraint && ir->opts.nFreeze)
1751 for (int d = 0; d < DIM; d++)
1753 if (pulldim_con[d] == 1 &&
1754 ir->opts.nFreeze[getGroupType(groups, egcFREEZE, ii)][d])
1760 const t_atom &atom = mtopGetAtomParameters(mtop, ii, &molb);
1762 if (ir->efep == efepNO)
1768 m = (1 - lambda)*atom.m + lambda*atom.mB;
1771 if (pg->params.nweight > 0)
1773 w = pg->params.weight[i];
1779 if (EI_ENERGY_MINIMIZATION(ir->eI))
1781 /* Move the mass to the weight */
1785 else if (ir->eI == eiBD)
1788 if (ir->bd_fric != 0.0f)
1790 mbd = ir->bd_fric*ir->delta_t;
1794 if (groups->grpnr[egcTC] == nullptr)
1796 mbd = ir->delta_t/ir->opts.tau_t[0];
1800 mbd = ir->delta_t/ir->opts.tau_t[groups->grpnr[egcTC][ii]];
1808 weights.push_back(w);
1817 /* We can have single atom groups with zero mass with potential pulling
1818 * without cosine weighting.
1820 if (pg->params.nat == 1 && !bConstraint && pg->epgrppbc != epgrppbcCOS)
1822 /* With one atom the mass doesn't matter */
1827 gmx_fatal(FARGS, "The total%s mass of pull group %d is zero",
1828 pg->params.weight ? " weighted" : "", g);
1834 "Pull group %d: %5d atoms, mass %9.3f",
1835 g, pg->params.nat, tmass);
1836 if (pg->params.weight ||
1837 EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD)
1839 fprintf(fplog, ", weighted mass %9.3f", wmass*wmass/wwmass);
1841 if (pg->epgrppbc == epgrppbcCOS)
1843 fprintf(fplog, ", cosine weighting will be used");
1845 fprintf(fplog, "\n");
1850 /* A value != 0 signals not frozen, it is updated later */
1856 for (int d = 0; d < DIM; d++)
1858 ndim += pulldim_con[d]*pg->params.nat;
1860 if (fplog && nfrozen > 0 && nfrozen < ndim)
1863 "\nWARNING: In pull group %d some, but not all of the degrees of freedom\n"
1864 " that are subject to pulling are frozen.\n"
1865 " For constraint pulling the whole group will be frozen.\n\n",
1874 init_pull(FILE *fplog, const pull_params_t *pull_params, const t_inputrec *ir,
1875 const gmx_mtop_t *mtop, const t_commrec *cr, gmx::LocalAtomSetManager *atomSets,
1878 struct pull_t *pull;
1883 /* Copy the pull parameters */
1884 pull->params = *pull_params;
1885 /* Avoid pointer copies */
1886 pull->params.group = nullptr;
1887 pull->params.coord = nullptr;
1889 for (int i = 0; i < pull_params->ngroup; ++i)
1891 pull->group.emplace_back(pull_params->group[i], atomSets->add({pull_params->group[i].ind, pull_params->group[i].ind+pull_params->group[i].nat}),
1892 pull_params->bSetPbcRefToPrevStepCOM);
1895 if (cr != nullptr && DOMAINDECOMP(cr))
1897 /* Set up the global to local atom mapping for PBC atoms */
1898 for (pull_group_work_t &group : pull->group)
1900 if (group.epgrppbc == epgrppbcREFAT || group.epgrppbc == epgrppbcPREVSTEPCOM)
1902 /* pbcAtomSet consists of a single atom */
1903 group.pbcAtomSet = gmx::compat::make_unique<gmx::LocalAtomSet>(atomSets->add({&group.params.pbcatom, &group.params.pbcatom + 1}));
1908 pull->bPotential = FALSE;
1909 pull->bConstraint = FALSE;
1910 pull->bCylinder = FALSE;
1911 pull->bAngle = FALSE;
1912 pull->bXOutAverage = pull_params->bXOutAverage;
1913 pull->bFOutAverage = pull_params->bFOutAverage;
1915 GMX_RELEASE_ASSERT(pull->group[0].params.nat == 0, "pull group 0 is an absolute reference group and should not contain atoms");
1917 pull->numCoordinatesWithExternalPotential = 0;
1919 for (int c = 0; c < pull->params.ncoord; c++)
1921 /* Construct a pull coordinate, copying all coordinate parameters */
1922 pull->coord.emplace_back(pull_params->coord[c]);
1924 pull_coord_work_t *pcrd = &pull->coord.back();
1926 switch (pcrd->params.eGeom)
1929 case epullgDIRRELATIVE: /* Direction vector is determined at each step */
1931 case epullgDIHEDRAL:
1936 case epullgANGLEAXIS:
1937 copy_rvec_to_dvec(pull_params->coord[c].vec, pcrd->spatialData.vec);
1940 /* We allow reading of newer tpx files with new pull geometries,
1941 * but with the same tpx format, with old code. A new geometry
1942 * only adds a new enum value, which will be out of range for
1943 * old code. The first place we need to generate an error is
1944 * here, since the pull code can't handle this.
1945 * The enum can be used before arriving here only for printing
1946 * the string corresponding to the geometry, which will result
1947 * in printing "UNDEFINED".
1949 gmx_fatal(FARGS, "Pull geometry not supported for pull coordinate %d. The geometry enum %d in the input is larger than that supported by the code (up to %d). You are probably reading a tpr file generated with a newer version of Gromacs with an binary from an older version of Gromacs.",
1950 c + 1, pcrd->params.eGeom, epullgNR - 1);
1953 if (pcrd->params.eType == epullCONSTRAINT)
1955 /* Check restrictions of the constraint pull code */
1956 if (pcrd->params.eGeom == epullgCYL ||
1957 pcrd->params.eGeom == epullgDIRRELATIVE ||
1958 pcrd->params.eGeom == epullgANGLE ||
1959 pcrd->params.eGeom == epullgDIHEDRAL ||
1960 pcrd->params.eGeom == epullgANGLEAXIS)
1962 gmx_fatal(FARGS, "Pulling of type %s can not be combined with geometry %s. Consider using pull type %s.",
1963 epull_names[pcrd->params.eType],
1964 epullg_names[pcrd->params.eGeom],
1965 epull_names[epullUMBRELLA]);
1968 pull->bConstraint = TRUE;
1972 pull->bPotential = TRUE;
1975 if (pcrd->params.eGeom == epullgCYL)
1977 pull->bCylinder = TRUE;
1979 else if (pcrd->params.eGeom == epullgANGLE || pcrd->params.eGeom == epullgDIHEDRAL || pcrd->params.eGeom == epullgANGLEAXIS)
1981 pull->bAngle = TRUE;
1984 /* We only need to calculate the plain COM of a group
1985 * when it is not only used as a cylinder group.
1986 * Also the absolute reference group 0 needs no COM computation.
1988 for (int i = 0; i < pcrd->params.ngroup; i++)
1990 int groupIndex = pcrd->params.group[i];
1991 if (groupIndex > 0 &&
1992 !(pcrd->params.eGeom == epullgCYL && i == 0))
1994 pull->group[groupIndex].needToCalcCom = true;
1998 /* With non-zero rate the reference value is set at every step */
1999 if (pcrd->params.rate == 0)
2001 /* Initialize the constant reference value */
2002 if (pcrd->params.eType != epullEXTERNAL)
2004 low_set_pull_coord_reference_value(pcrd, c, pcrd->params.init*pull_conversion_factor_userinput2internal(&pcrd->params));
2008 /* With an external pull potential, the reference value loses
2009 * it's meaning and should not be used. Setting it to zero
2010 * makes any terms dependent on it disappear.
2011 * The only issue this causes is that with cylinder pulling
2012 * no atoms of the cylinder group within the cylinder radius
2013 * should be more than half a box length away from the COM of
2014 * the pull group along the axial direction.
2016 pcrd->value_ref = 0.0;
2020 if (pcrd->params.eType == epullEXTERNAL)
2022 GMX_RELEASE_ASSERT(pcrd->params.rate == 0, "With an external potential, a pull coordinate should have rate = 0");
2024 /* This external potential needs to be registered later */
2025 pull->numCoordinatesWithExternalPotential++;
2027 pcrd->bExternalPotentialProviderHasBeenRegistered = false;
2030 pull->numUnregisteredExternalPotentials =
2031 pull->numCoordinatesWithExternalPotential;
2032 pull->numExternalPotentialsStillToBeAppliedThisStep = 0;
2034 pull->ePBC = ir->ePBC;
2037 case epbcNONE: pull->npbcdim = 0; break;
2038 case epbcXY: pull->npbcdim = 2; break;
2039 default: pull->npbcdim = 3; break;
2044 gmx_bool bAbs, bCos;
2047 for (const pull_coord_work_t &coord : pull->coord)
2049 if (pull->group[coord.params.group[0]].params.nat == 0 ||
2050 pull->group[coord.params.group[1]].params.nat == 0)
2056 fprintf(fplog, "\n");
2057 if (pull->bPotential)
2059 fprintf(fplog, "Will apply potential COM pulling\n");
2061 if (pull->bConstraint)
2063 fprintf(fplog, "Will apply constraint COM pulling\n");
2065 // Don't include the reference group 0 in output, so we report ngroup-1
2066 int numRealGroups = pull->group.size() - 1;
2067 GMX_RELEASE_ASSERT(numRealGroups > 0, "The reference absolute position pull group should always be present");
2068 fprintf(fplog, "with %zu pull coordinate%s and %d group%s\n",
2069 pull->coord.size(), pull->coord.size() == 1 ? "" : "s",
2070 numRealGroups, numRealGroups == 1 ? "" : "s");
2073 fprintf(fplog, "with an absolute reference\n");
2076 // Don't include the reference group 0 in loop
2077 for (size_t g = 1; g < pull->group.size(); g++)
2079 if (pull->group[g].params.nat > 1 &&
2080 pull->group[g].params.pbcatom < 0)
2082 /* We are using cosine weighting */
2083 fprintf(fplog, "Cosine weighting is used for group %zu\n", g);
2089 please_cite(fplog, "Engin2010");
2093 pull->bRefAt = FALSE;
2095 for (size_t g = 0; g < pull->group.size(); g++)
2097 pull_group_work_t *pgrp;
2099 pgrp = &pull->group[g];
2100 if (pgrp->params.nat > 0)
2102 /* There is an issue when a group is used in multiple coordinates
2103 * and constraints are applied in different dimensions with atoms
2104 * frozen in some, but not all dimensions.
2105 * Since there is only one mass array per group, we can't have
2106 * frozen/non-frozen atoms for different coords at the same time.
2107 * But since this is a very exotic case, we don't check for this.
2108 * A warning is printed in init_pull_group_index.
2111 gmx_bool bConstraint;
2112 ivec pulldim, pulldim_con;
2114 /* Loop over all pull coordinates to see along which dimensions
2115 * this group is pulled and if it is involved in constraints.
2117 bConstraint = FALSE;
2118 clear_ivec(pulldim);
2119 clear_ivec(pulldim_con);
2120 for (const pull_coord_work_t &coord : pull->coord)
2122 gmx_bool bGroupUsed = FALSE;
2123 for (int gi = 0; gi < coord.params.ngroup; gi++)
2125 if (coord.params.group[gi] == static_cast<int>(g))
2133 for (int m = 0; m < DIM; m++)
2135 if (coord.params.dim[m] == 1)
2139 if (coord.params.eType == epullCONSTRAINT)
2149 /* Determine if we need to take PBC into account for calculating
2150 * the COM's of the pull groups.
2152 switch (pgrp->epgrppbc)
2155 pull->bRefAt = TRUE;
2158 if (pgrp->params.weight != nullptr)
2160 gmx_fatal(FARGS, "Pull groups can not have relative weights and cosine weighting at same time");
2162 for (int m = 0; m < DIM; m++)
2164 if (m < pull->npbcdim && pulldim[m] == 1)
2166 if (pull->cosdim >= 0 && pull->cosdim != m)
2168 gmx_fatal(FARGS, "Can only use cosine weighting with pulling in one dimension (use mdp option pull-coord?-dim)");
2178 /* Set the indices */
2179 init_pull_group_index(fplog, cr, g, pgrp,
2180 bConstraint, pulldim_con,
2185 /* Absolute reference, set the inverse mass to zero.
2186 * This is only relevant (and used) with constraint pulling.
2193 /* If we use cylinder coordinates, do some initialising for them */
2194 if (pull->bCylinder)
2196 for (const pull_coord_work_t &coord : pull->coord)
2198 if (coord.params.eGeom == epullgCYL)
2200 if (pull->group[coord.params.group[0]].params.nat == 0)
2202 gmx_fatal(FARGS, "A cylinder pull group is not supported when using absolute reference!\n");
2205 const auto &referenceGroup = pull->group[coord.params.group[0]];
2206 pull->dyna.emplace_back(referenceGroup.params, referenceGroup.atomSet, pull->params.bSetPbcRefToPrevStepCOM);
2210 /* The gmx_omp_nthreads module might not be initialized here, so max(1,) */
2211 pull->nthreads = std::max(1, gmx_omp_nthreads_get(emntDefault));
2212 pull->comSums.resize(pull->nthreads);
2217 /* Use a sub-communicator when we have more than 32 ranks, but not
2218 * when we have an external pull potential, since then the external
2219 * potential provider expects each rank to have the coordinate.
2221 comm->bParticipateAll = (cr == nullptr || !DOMAINDECOMP(cr) ||
2222 cr->dd->nnodes <= 32 ||
2223 pull->numCoordinatesWithExternalPotential > 0 ||
2224 getenv("GMX_PULL_PARTICIPATE_ALL") != nullptr);
2225 /* This sub-commicator is not used with comm->bParticipateAll,
2226 * so we can always initialize it to NULL.
2228 comm->mpi_comm_com = MPI_COMM_NULL;
2229 comm->nparticipate = 0;
2230 comm->isMasterRank = (cr == nullptr || MASTER(cr));
2232 /* No MPI: 1 rank: all ranks pull */
2233 comm->bParticipateAll = TRUE;
2234 comm->isMasterRank = true;
2236 comm->bParticipate = comm->bParticipateAll;
2237 comm->setup_count = 0;
2238 comm->must_count = 0;
2240 if (!comm->bParticipateAll && fplog != nullptr)
2242 fprintf(fplog, "Will use a sub-communicator for pull communication\n");
2245 comm->pbcAtomBuffer.resize(pull->group.size());
2246 comm->comBuffer.resize(pull->group.size()*c_comBufferStride);
2247 if (pull->bCylinder)
2249 comm->cylinderBuffer.resize(pull->coord.size()*c_cylinderBufferStride);
2252 /* We still need to initialize the PBC reference coordinates */
2253 pull->bSetPBCatoms = TRUE;
2255 pull->out_x = nullptr;
2256 pull->out_f = nullptr;
2261 static void destroy_pull(struct pull_t *pull)
2264 if (pull->comm.mpi_comm_com != MPI_COMM_NULL)
2266 MPI_Comm_free(&pull->comm.mpi_comm_com);
2273 void preparePrevStepPullCom(const t_inputrec *ir, const t_mdatoms *md, t_state *state, const t_state *state_global, const t_commrec *cr, bool startingFromCheckpoint)
2275 if (!ir->pull || !ir->pull->bSetPbcRefToPrevStepCOM)
2279 allocStatePrevStepPullCom(state, ir->pull_work);
2280 if (startingFromCheckpoint)
2284 state->pull_com_prev_step = state_global->pull_com_prev_step;
2288 /* Only the master rank has the checkpointed COM from the previous step */
2289 gmx_bcast(sizeof(double) * state->pull_com_prev_step.size(), &state->pull_com_prev_step[0], cr);
2291 setPrevStepPullComFromState(ir->pull_work, state);
2296 set_pbc(&pbc, ir->ePBC, state->box);
2297 initPullComFromPrevStep(cr, ir->pull_work, md, &pbc, state->x.rvec_array());
2298 updatePrevStepPullCom(ir->pull_work, state);
2302 void finish_pull(struct pull_t *pull)
2304 check_external_potential_registration(pull);
2308 gmx_fio_fclose(pull->out_x);
2312 gmx_fio_fclose(pull->out_f);
2318 gmx_bool pull_have_potential(const struct pull_t *pull)
2320 return pull->bPotential;
2323 gmx_bool pull_have_constraint(const struct pull_t *pull)
2325 return pull->bConstraint;