Make pull with COM from previous step work with MPI
[gromacs.git] / src / gromacs / pulling / pull.cpp
blobf9ae38483a80b526a95aa065f69a861044e25bc2
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,2018, 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.
37 #include "gmxpre.h"
39 #include "pull.h"
41 #include "config.h"
43 #include <cassert>
44 #include <cinttypes>
45 #include <cmath>
46 #include <cstdlib>
48 #include <algorithm>
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 &params, bool setPbcRefToPrevStepCOM)
87 if (params.nat <= 1)
89 /* no PBC required */
90 return epgrppbcNONE;
92 else if (params.pbcatom >= 0)
94 if (setPbcRefToPrevStepCOM)
96 return epgrppbcPREVSTEPCOM;
98 else
100 return epgrppbcREFAT;
103 else
105 return epgrppbcCOS;
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 &params,
115 gmx::LocalAtomSet atomSet,
116 bool bSetPbcRefToPrevStepCOM) :
117 params(params),
118 epgrppbc(groupPbcFromParams(params, bSetPbcRefToPrevStepCOM)),
119 needToCalcCom(false),
120 atomSet(atomSet),
121 mwscale(0),
122 wscale(0),
123 invtm(0)
125 clear_dvec(x);
126 clear_dvec(xp);
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))
153 return DEG2RAD;
155 else
157 return 1.0;
161 double pull_conversion_factor_internal2userinput(const t_pull_coord *pcrd)
163 if (pull_coordinate_is_angletype(pcrd))
165 return RAD2DEG;
167 else
169 return 1.0;
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,
176 const t_mdatoms *md,
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,
200 const t_mdatoms *md,
201 const dvec f_pull, int sign, rvec *f,
202 int nthreads)
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];
217 else
219 if (localAtomIndices.size() <= c_pullMaxNumLocalAtomsSingleThreaded)
221 apply_forces_grp_part(pgrp, 0, localAtomIndices.size(), md, f_pull, sign, f);
223 else
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,
240 const t_mdatoms *md,
241 const dvec f_pull, double f_scal,
242 int sign, rvec *f,
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];
257 if (weight == 0)
259 continue;
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
265 * we calculated dv.
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,
286 const t_mdatoms *md,
287 rvec *f)
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.
294 double inpr = 0;
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.
305 dvec f_perp;
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,
322 rvec *f)
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
327 * to data races.
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,
336 pull->nthreads);
338 /* Sum the force along the vector and the radial force */
339 dvec f_tot;
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);
347 else
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,
383 const t_pbc *pbc)
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);
415 else
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);
443 return 0.25*max_d2;
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,
451 const t_pbc *pbc,
452 dvec xg, dvec xref, double max_dist2,
453 dvec dr)
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];
466 dvec xrefr;
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);
483 double dr2 = 0;
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))
489 dr2 += dr[m]*dr[m];
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)
508 dvec_inc(dr, dref);
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,
516 int coord_ind,
517 const t_pbc *pbc)
519 pull_coord_work_t *pcrd = &pull->coord[coord_ind];
520 PullCoordSpatialData &spatialData = pcrd->spatialData;
522 double md2;
523 if (pcrd->params.eGeom == epullgDIRPBC)
525 md2 = -1;
527 else
529 md2 = static_cast<double>(max_pull_distance2(pcrd, pbc));
532 if (pcrd->params.eGeom == epullgDIRRELATIVE)
534 /* We need to determine the pull vector */
535 dvec vec;
536 int m;
538 const pull_group_work_t &pgrp2 = pull->group[pcrd->params.group[2]];
539 const pull_group_work_t &pgrp3 = pull->group[pcrd->params.group[3]];
541 pbc_dx_d(pbc, pgrp3.x, pgrp2.x, vec);
543 for (m = 0; m < DIM; m++)
545 vec[m] *= pcrd->params.dim[m];
547 spatialData.vec_len = dnorm(vec);
548 for (m = 0; m < DIM; m++)
550 spatialData.vec[m] = vec[m]/spatialData.vec_len;
552 if (debug)
554 fprintf(debug, "pull coord %d vector: %6.3f %6.3f %6.3f normalized: %6.3f %6.3f %6.3f\n",
555 coord_ind,
556 vec[XX], vec[YY], vec[ZZ],
557 spatialData.vec[XX], spatialData.vec[YY], 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,
565 pgrp1->x,
566 pcrd->params.eGeom == epullgCYL ? pull->dyna[coord_ind].x : pgrp0->x,
567 md2,
568 spatialData.dr01);
570 if (pcrd->params.ngroup >= 4)
572 pull_group_work_t *pgrp2, *pgrp3;
573 pgrp2 = &pull->group[pcrd->params.group[2]];
574 pgrp3 = &pull->group[pcrd->params.group[3]];
576 low_get_pull_coord_dr(pull, pcrd, pbc,
577 pgrp3->x,
578 pgrp2->x,
579 md2,
580 spatialData.dr23);
582 if (pcrd->params.ngroup >= 6)
584 pull_group_work_t *pgrp4, *pgrp5;
585 pgrp4 = &pull->group[pcrd->params.group[4]];
586 pgrp5 = &pull->group[pcrd->params.group[5]];
588 low_get_pull_coord_dr(pull, pcrd, pbc,
589 pgrp5->x,
590 pgrp4->x,
591 md2,
592 spatialData.dr45);
596 /* Modify x so that it is periodic in [-pi, pi)
597 * It is assumed that x is in [-3pi, 3pi) so that x
598 * needs to be shifted by at most one period.
600 static void make_periodic_2pi(double *x)
602 if (*x >= M_PI)
604 *x -= M_2PI;
606 else if (*x < -M_PI)
608 *x += M_2PI;
612 /* This function should always be used to modify pcrd->value_ref */
613 static void low_set_pull_coord_reference_value(pull_coord_work_t *pcrd,
614 int coord_ind,
615 double value_ref)
617 GMX_ASSERT(pcrd->params.eType != epullEXTERNAL, "The pull coord reference value should not be used with type external-potential");
619 if (pcrd->params.eGeom == epullgDIST)
621 if (value_ref < 0)
623 gmx_fatal(FARGS, "Pull reference distance for coordinate %d (%f) needs to be non-negative", coord_ind + 1, value_ref);
626 else if (pcrd->params.eGeom == epullgANGLE || pcrd->params.eGeom == epullgANGLEAXIS)
628 if (value_ref < 0 || value_ref > M_PI)
630 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));
633 else if (pcrd->params.eGeom == epullgDIHEDRAL)
635 /* Allow pulling to be periodic for dihedral angles by remapping the reference value to the interval [-pi, pi). */
636 make_periodic_2pi(&value_ref);
639 pcrd->value_ref = value_ref;
642 static void update_pull_coord_reference_value(pull_coord_work_t *pcrd, int coord_ind, double t)
644 /* With zero rate the reference value is set initially and doesn't change */
645 if (pcrd->params.rate != 0)
647 double value_ref = (pcrd->params.init + pcrd->params.rate*t)*pull_conversion_factor_userinput2internal(&pcrd->params);
648 low_set_pull_coord_reference_value(pcrd, coord_ind, value_ref);
652 /* Returns the dihedral angle. Updates the plane normal vectors m, n. */
653 static double get_dihedral_angle_coord(PullCoordSpatialData *spatialData)
655 double phi, sign;
656 dvec dr32; /* store instead of dr23? */
658 dsvmul(-1, spatialData->dr23, dr32);
659 dcprod(spatialData->dr01, dr32, spatialData->planevec_m); /* Normal of first plane */
660 dcprod(dr32, spatialData->dr45, spatialData->planevec_n); /* Normal of second plane */
661 phi = gmx_angle_between_dvecs(spatialData->planevec_m, spatialData->planevec_n);
663 /* Note 1: the sign below is opposite of that in the bondeds or Bekker 1994
664 * because there r_ij = ri - rj, while here dr01 = r_1 - r_0
665 * Note 2: the angle between the plane normal vectors equals pi only when
666 * both planes coincide. Thus, when phi = pi dr01 will lie in both planes and
667 * we get a positive sign below. Thus, the range of the dihedral angle is (-180, 180].
669 sign = (diprod(spatialData->dr01, spatialData->planevec_n) < 0.0) ? 1.0 : -1.0;
670 return sign*phi;
673 /* Calculates pull->coord[coord_ind].value.
674 * This function also updates pull->coord[coord_ind].dr.
676 static void get_pull_coord_distance(struct pull_t *pull,
677 int coord_ind,
678 const t_pbc *pbc)
680 pull_coord_work_t *pcrd;
681 int m;
683 pcrd = &pull->coord[coord_ind];
685 get_pull_coord_dr(pull, coord_ind, pbc);
687 PullCoordSpatialData &spatialData = pcrd->spatialData;
689 switch (pcrd->params.eGeom)
691 case epullgDIST:
692 /* Pull along the vector between the com's */
693 spatialData.value = dnorm(spatialData.dr01);
694 break;
695 case epullgDIR:
696 case epullgDIRPBC:
697 case epullgDIRRELATIVE:
698 case epullgCYL:
699 /* Pull along vec */
700 spatialData.value = 0;
701 for (m = 0; m < DIM; m++)
703 spatialData.value += spatialData.vec[m]*spatialData.dr01[m];
705 break;
706 case epullgANGLE:
707 spatialData.value = gmx_angle_between_dvecs(spatialData.dr01, spatialData.dr23);
708 break;
709 case epullgDIHEDRAL:
710 spatialData.value = get_dihedral_angle_coord(&spatialData);
711 break;
712 case epullgANGLEAXIS:
713 spatialData.value = gmx_angle_between_dvecs(spatialData.dr01, spatialData.vec);
714 break;
715 default:
716 gmx_incons("Unsupported pull type in get_pull_coord_distance");
720 /* Returns the deviation from the reference value.
721 * Updates pull->coord[coord_ind].dr, .value and .value_ref.
723 static double get_pull_coord_deviation(struct pull_t *pull,
724 int coord_ind,
725 const t_pbc *pbc,
726 double t)
728 pull_coord_work_t *pcrd;
729 double dev = 0;
731 pcrd = &pull->coord[coord_ind];
733 /* Update the reference value before computing the distance,
734 * since it is used in the distance computation with periodic pulling.
736 update_pull_coord_reference_value(pcrd, coord_ind, t);
738 get_pull_coord_distance(pull, coord_ind, pbc);
740 get_pull_coord_distance(pull, coord_ind, pbc);
742 /* Determine the deviation */
743 dev = pcrd->spatialData.value - pcrd->value_ref;
745 /* Check that values are allowed */
746 if (pcrd->params.eGeom == epullgDIST && pcrd->spatialData.value == 0)
748 /* With no vector we can not determine the direction for the force,
749 * so we set the force to zero.
751 dev = 0;
753 else if (pcrd->params.eGeom == epullgDIHEDRAL)
755 /* The reference value is in [-pi, pi). The coordinate value is in (-pi, pi].
756 Thus, the unwrapped deviation is here in (-2pi, 2pi].
757 After making it periodic, the deviation will be in [-pi, pi). */
758 make_periodic_2pi(&dev);
761 return dev;
764 double get_pull_coord_value(struct pull_t *pull,
765 int coord_ind,
766 const t_pbc *pbc)
768 get_pull_coord_distance(pull, coord_ind, pbc);
770 return pull->coord[coord_ind].spatialData.value;
773 void clear_pull_forces(pull_t *pull)
775 /* Zeroing the forces is only required for constraint pulling.
776 * It can happen that multiple constraint steps need to be applied
777 * and therefore the constraint forces need to be accumulated.
779 for (pull_coord_work_t &coord : pull->coord)
781 coord.scalarForce = 0;
785 /* Apply constraint using SHAKE */
786 static void do_constraint(struct pull_t *pull, t_pbc *pbc,
787 rvec *x, rvec *v,
788 gmx_bool bMaster, tensor vir,
789 double dt, double t)
792 dvec *r_ij; /* x[i] com of i in prev. step. Obeys constr. -> r_ij[i] */
793 dvec unc_ij; /* xp[i] com of i this step, before constr. -> unc_ij */
794 dvec *rnew; /* current 'new' positions of the groups */
795 double *dr_tot; /* the total update of the coords */
796 dvec vec;
797 double inpr;
798 double lambda, rm, invdt = 0;
799 gmx_bool bConverged_all, bConverged = FALSE;
800 int niter = 0, ii, j, m, max_iter = 100;
801 double a;
802 dvec tmp, tmp3;
804 snew(r_ij, pull->coord.size());
805 snew(dr_tot, pull->coord.size());
807 snew(rnew, pull->group.size());
809 /* copy the current unconstrained positions for use in iterations. We
810 iterate until rinew[i] and rjnew[j] obey the constraints. Then
811 rinew - pull.x_unc[i] is the correction dr to group i */
812 for (size_t g = 0; g < pull->group.size(); g++)
814 copy_dvec(pull->group[g].xp, rnew[g]);
817 /* Determine the constraint directions from the old positions */
818 for (size_t c = 0; c < pull->coord.size(); c++)
820 pull_coord_work_t *pcrd;
822 pcrd = &pull->coord[c];
824 if (pcrd->params.eType != epullCONSTRAINT)
826 continue;
829 /* Note that get_pull_coord_distance also sets pcrd->dr and pcrd->value.
830 * We don't modify dr and value anymore, so these values are also used
831 * for printing.
833 get_pull_coord_distance(pull, c, pbc);
835 const PullCoordSpatialData &spatialData = pcrd->spatialData;
836 if (debug)
838 fprintf(debug, "Pull coord %zu dr %f %f %f\n",
839 c, spatialData.dr01[XX], spatialData.dr01[YY], spatialData.dr01[ZZ]);
842 if (pcrd->params.eGeom == epullgDIR ||
843 pcrd->params.eGeom == epullgDIRPBC)
845 /* Select the component along vec */
846 a = 0;
847 for (m = 0; m < DIM; m++)
849 a += spatialData.vec[m]*spatialData.dr01[m];
851 for (m = 0; m < DIM; m++)
853 r_ij[c][m] = a*spatialData.vec[m];
856 else
858 copy_dvec(spatialData.dr01, r_ij[c]);
861 if (dnorm2(r_ij[c]) == 0)
863 gmx_fatal(FARGS, "Distance for pull coordinate %zu is zero with constraint pulling, which is not allowed.", c + 1);
867 bConverged_all = FALSE;
868 while (!bConverged_all && niter < max_iter)
870 bConverged_all = TRUE;
872 /* loop over all constraints */
873 for (size_t c = 0; c < pull->coord.size(); c++)
875 pull_coord_work_t *pcrd;
876 pull_group_work_t *pgrp0, *pgrp1;
877 dvec dr0, dr1;
879 pcrd = &pull->coord[c];
881 if (pcrd->params.eType != epullCONSTRAINT)
883 continue;
886 update_pull_coord_reference_value(pcrd, c, t);
888 pgrp0 = &pull->group[pcrd->params.group[0]];
889 pgrp1 = &pull->group[pcrd->params.group[1]];
891 /* Get the current difference vector */
892 low_get_pull_coord_dr(pull, pcrd, pbc,
893 rnew[pcrd->params.group[1]],
894 rnew[pcrd->params.group[0]],
895 -1, unc_ij);
897 if (debug)
899 fprintf(debug, "Pull coord %zu, iteration %d\n", c, niter);
902 rm = 1.0/(pgrp0->invtm + pgrp1->invtm);
904 switch (pcrd->params.eGeom)
906 case epullgDIST:
907 if (pcrd->value_ref <= 0)
909 gmx_fatal(FARGS, "The pull constraint reference distance for group %zu is <= 0 (%f)", c, pcrd->value_ref);
913 double q, c_a, c_b, c_c;
915 c_a = diprod(r_ij[c], r_ij[c]);
916 c_b = diprod(unc_ij, r_ij[c])*2;
917 c_c = diprod(unc_ij, unc_ij) - gmx::square(pcrd->value_ref);
919 if (c_b < 0)
921 q = -0.5*(c_b - std::sqrt(c_b*c_b - 4*c_a*c_c));
922 lambda = -q/c_a;
924 else
926 q = -0.5*(c_b + std::sqrt(c_b*c_b - 4*c_a*c_c));
927 lambda = -c_c/q;
930 if (debug)
932 fprintf(debug,
933 "Pull ax^2+bx+c=0: a=%e b=%e c=%e lambda=%e\n",
934 c_a, c_b, c_c, lambda);
938 /* The position corrections dr due to the constraints */
939 dsvmul(-lambda*rm*pgrp1->invtm, r_ij[c], dr1);
940 dsvmul( lambda*rm*pgrp0->invtm, r_ij[c], dr0);
941 dr_tot[c] += -lambda*dnorm(r_ij[c]);
942 break;
943 case epullgDIR:
944 case epullgDIRPBC:
945 case epullgCYL:
946 /* A 1-dimensional constraint along a vector */
947 a = 0;
948 for (m = 0; m < DIM; m++)
950 vec[m] = pcrd->spatialData.vec[m];
951 a += unc_ij[m]*vec[m];
953 /* Select only the component along the vector */
954 dsvmul(a, vec, unc_ij);
955 lambda = a - pcrd->value_ref;
956 if (debug)
958 fprintf(debug, "Pull inpr %e lambda: %e\n", a, lambda);
961 /* The position corrections dr due to the constraints */
962 dsvmul(-lambda*rm*pgrp1->invtm, vec, dr1);
963 dsvmul( lambda*rm*pgrp0->invtm, vec, dr0);
964 dr_tot[c] += -lambda;
965 break;
966 default:
967 gmx_incons("Invalid enumeration value for eGeom");
970 /* DEBUG */
971 if (debug)
973 int g0, g1;
975 g0 = pcrd->params.group[0];
976 g1 = pcrd->params.group[1];
977 low_get_pull_coord_dr(pull, pcrd, pbc, rnew[g1], rnew[g0], -1, tmp);
978 low_get_pull_coord_dr(pull, pcrd, pbc, dr1, dr0, -1, tmp3);
979 fprintf(debug,
980 "Pull cur %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
981 rnew[g0][0], rnew[g0][1], rnew[g0][2],
982 rnew[g1][0], rnew[g1][1], rnew[g1][2], dnorm(tmp));
983 fprintf(debug,
984 "Pull ref %8s %8s %8s %8s %8s %8s d: %8.5f\n",
985 "", "", "", "", "", "", pcrd->value_ref);
986 fprintf(debug,
987 "Pull cor %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
988 dr0[0], dr0[1], dr0[2],
989 dr1[0], dr1[1], dr1[2],
990 dnorm(tmp3));
991 } /* END DEBUG */
993 /* Update the COMs with dr */
994 dvec_inc(rnew[pcrd->params.group[1]], dr1);
995 dvec_inc(rnew[pcrd->params.group[0]], dr0);
998 /* Check if all constraints are fullfilled now */
999 for (pull_coord_work_t &coord : pull->coord)
1001 if (coord.params.eType != epullCONSTRAINT)
1003 continue;
1006 low_get_pull_coord_dr(pull, &coord, pbc,
1007 rnew[coord.params.group[1]],
1008 rnew[coord.params.group[0]],
1009 -1, unc_ij);
1011 switch (coord.params.eGeom)
1013 case epullgDIST:
1014 bConverged =
1015 fabs(dnorm(unc_ij) - coord.value_ref) < pull->params.constr_tol;
1016 break;
1017 case epullgDIR:
1018 case epullgDIRPBC:
1019 case epullgCYL:
1020 for (m = 0; m < DIM; m++)
1022 vec[m] = coord.spatialData.vec[m];
1024 inpr = diprod(unc_ij, vec);
1025 dsvmul(inpr, vec, unc_ij);
1026 bConverged =
1027 fabs(diprod(unc_ij, vec) - coord.value_ref) < pull->params.constr_tol;
1028 break;
1031 if (!bConverged)
1033 if (debug)
1035 fprintf(debug, "Pull constraint not converged: "
1036 "groups %d %d,"
1037 "d_ref = %f, current d = %f\n",
1038 coord.params.group[0], coord.params.group[1],
1039 coord.value_ref, dnorm(unc_ij));
1042 bConverged_all = FALSE;
1046 niter++;
1047 /* if after all constraints are dealt with and bConverged is still TRUE
1048 we're finished, if not we do another iteration */
1050 if (niter > max_iter)
1052 gmx_fatal(FARGS, "Too many iterations for constraint run: %d", niter);
1055 /* DONE ITERATING, NOW UPDATE COORDINATES AND CALC. CONSTRAINT FORCES */
1057 if (v)
1059 invdt = 1/dt;
1062 /* update atoms in the groups */
1063 for (size_t g = 0; g < pull->group.size(); g++)
1065 const pull_group_work_t *pgrp;
1066 dvec dr;
1068 pgrp = &pull->group[g];
1070 /* get the final constraint displacement dr for group g */
1071 dvec_sub(rnew[g], pgrp->xp, dr);
1073 if (dnorm2(dr) == 0)
1075 /* No displacement, no update necessary */
1076 continue;
1079 /* update the atom positions */
1080 auto localAtomIndices = pgrp->atomSet.localIndex();
1081 copy_dvec(dr, tmp);
1082 for (gmx::index j = 0; j < localAtomIndices.size(); j++)
1084 ii = localAtomIndices[j];
1085 if (!pgrp->localWeights.empty())
1087 dsvmul(pgrp->wscale*pgrp->localWeights[j], dr, tmp);
1089 for (m = 0; m < DIM; m++)
1091 x[ii][m] += tmp[m];
1093 if (v)
1095 for (m = 0; m < DIM; m++)
1097 v[ii][m] += invdt*tmp[m];
1103 /* calculate the constraint forces, used for output and virial only */
1104 for (size_t c = 0; c < pull->coord.size(); c++)
1106 pull_coord_work_t *pcrd;
1108 pcrd = &pull->coord[c];
1110 if (pcrd->params.eType != epullCONSTRAINT)
1112 continue;
1115 /* Accumulate the forces, in case we have multiple constraint steps */
1116 double force = dr_tot[c]/((pull->group[pcrd->params.group[0]].invtm + pull->group[pcrd->params.group[1]].invtm)*dt*dt);
1117 pcrd->scalarForce += force;
1119 if (vir != nullptr && pcrd->params.eGeom != epullgDIRPBC && bMaster)
1121 double f_invr;
1123 /* Add the pull contribution to the virial */
1124 /* We have already checked above that r_ij[c] != 0 */
1125 f_invr = pcrd->scalarForce/dnorm(r_ij[c]);
1127 for (j = 0; j < DIM; j++)
1129 for (m = 0; m < DIM; m++)
1131 vir[j][m] -= 0.5*f_invr*r_ij[c][j]*r_ij[c][m];
1137 /* finished! I hope. Give back some memory */
1138 sfree(r_ij);
1139 sfree(dr_tot);
1140 sfree(rnew);
1143 static void add_virial_coord_dr(tensor vir, const dvec dr, const dvec f)
1145 for (int j = 0; j < DIM; j++)
1147 for (int m = 0; m < DIM; m++)
1149 vir[j][m] -= 0.5*f[j]*dr[m];
1154 /* Adds the pull contribution to the virial */
1155 static void add_virial_coord(tensor vir,
1156 const pull_coord_work_t &pcrd,
1157 const PullCoordVectorForces &forces)
1159 if (vir != nullptr && pcrd.params.eGeom != epullgDIRPBC)
1161 /* Add the pull contribution for each distance vector to the virial. */
1162 add_virial_coord_dr(vir, pcrd.spatialData.dr01, forces.force01);
1163 if (pcrd.params.ngroup >= 4)
1165 add_virial_coord_dr(vir, pcrd.spatialData.dr23, forces.force23);
1167 if (pcrd.params.ngroup >= 6)
1169 add_virial_coord_dr(vir, pcrd.spatialData.dr45, forces.force45);
1174 static void calc_pull_coord_scalar_force_and_potential(pull_coord_work_t *pcrd,
1175 double dev, real lambda,
1176 real *V, real *dVdl)
1178 real k, dkdl;
1180 k = (1.0 - lambda)*pcrd->params.k + lambda*pcrd->params.kB;
1181 dkdl = pcrd->params.kB - pcrd->params.k;
1183 switch (pcrd->params.eType)
1185 case epullUMBRELLA:
1186 case epullFLATBOTTOM:
1187 case epullFLATBOTTOMHIGH:
1188 /* The only difference between an umbrella and a flat-bottom
1189 * potential is that a flat-bottom is zero above or below
1190 the reference value.
1192 if ((pcrd->params.eType == epullFLATBOTTOM && dev < 0) ||
1193 (pcrd->params.eType == epullFLATBOTTOMHIGH && dev > 0))
1195 dev = 0;
1198 pcrd->scalarForce = -k*dev;
1199 *V += 0.5* k*gmx::square(dev);
1200 *dVdl += 0.5*dkdl*gmx::square(dev);
1201 break;
1202 case epullCONST_F:
1203 pcrd->scalarForce = -k;
1204 *V += k*pcrd->spatialData.value;
1205 *dVdl += dkdl*pcrd->spatialData.value;
1206 break;
1207 case epullEXTERNAL:
1208 gmx_incons("the scalar pull force should not be calculated internally for pull type external");
1209 default:
1210 gmx_incons("Unsupported pull type in do_pull_pot");
1214 static PullCoordVectorForces
1215 calculateVectorForces(const pull_coord_work_t &pcrd)
1217 const t_pull_coord &params = pcrd.params;
1218 const PullCoordSpatialData &spatialData = pcrd.spatialData;
1220 /* The geometry of the coordinate determines how the scalar force relates to the force on each group */
1221 PullCoordVectorForces forces;
1223 if (params.eGeom == epullgDIST)
1225 double invdr01 = spatialData.value > 0 ? 1./spatialData.value : 0.;
1226 for (int m = 0; m < DIM; m++)
1228 forces.force01[m] = pcrd.scalarForce*spatialData.dr01[m]*invdr01;
1231 else if (params.eGeom == epullgANGLE)
1234 double cos_theta, cos_theta2;
1236 cos_theta = cos(spatialData.value);
1237 cos_theta2 = gmx::square(cos_theta);
1239 /* The force at theta = 0, pi is undefined so we should not apply any force.
1240 * cos(theta) = 1 for theta = 0, pi so this is what we check for below.
1241 * Note that dr01 = 0 or dr23 = 0 evaluates to theta = 0 so we do not
1242 * have to check for this before dividing by their norm below.
1244 if (cos_theta2 < 1)
1246 /* The forces f01 and f23 can be decomposed into one vector parallel to dr01
1247 * and another vector parallel to dr23:
1248 * f01 = -dV/dtheta*(a*dr23/|dr23| - b*dr01*|dr01|)/|dr01|,
1249 * f23 = -dV/dtheta*(a*dr01/|dr01| - b*dr23*|dr23|)/|dr23|,
1251 double a = -gmx::invsqrt(1 - cos_theta2); /* comes from d/dx acos(x) */
1252 double b = a*cos_theta;
1253 double invdr01 = 1./dnorm(spatialData.dr01);
1254 double invdr23 = 1./dnorm(spatialData.dr23);
1255 dvec normalized_dr01, normalized_dr23;
1256 dsvmul(invdr01, spatialData.dr01, normalized_dr01);
1257 dsvmul(invdr23, spatialData.dr23, normalized_dr23);
1259 for (int m = 0; m < DIM; m++)
1261 /* Here, f_scal is -dV/dtheta */
1262 forces.force01[m] = pcrd.scalarForce*invdr01*(a*normalized_dr23[m] - b*normalized_dr01[m]);
1263 forces.force23[m] = pcrd.scalarForce*invdr23*(a*normalized_dr01[m] - b*normalized_dr23[m]);
1266 else
1268 /* No forces to apply for ill-defined cases*/
1269 clear_dvec(forces.force01);
1270 clear_dvec(forces.force23);
1273 else if (params.eGeom == epullgANGLEAXIS)
1275 double cos_theta, cos_theta2;
1277 /* The angle-axis force is exactly the same as the angle force (above) except that in
1278 this case the second vector (dr23) is replaced by the pull vector. */
1279 cos_theta = cos(spatialData.value);
1280 cos_theta2 = gmx::square(cos_theta);
1282 if (cos_theta2 < 1)
1284 double a, b;
1285 double invdr01;
1286 dvec normalized_dr01;
1288 invdr01 = 1./dnorm(spatialData.dr01);
1289 dsvmul(invdr01, spatialData.dr01, normalized_dr01);
1290 a = -gmx::invsqrt(1 - cos_theta2); /* comes from d/dx acos(x) */
1291 b = a*cos_theta;
1293 for (int m = 0; m < DIM; m++)
1295 forces.force01[m] = pcrd.scalarForce*invdr01*(a*spatialData.vec[m] - b*normalized_dr01[m]);
1298 else
1300 clear_dvec(forces.force01);
1303 else if (params.eGeom == epullgDIHEDRAL)
1305 double m2, n2, tol, sqrdist_32;
1306 dvec dr32;
1307 /* Note: there is a small difference here compared to the
1308 dihedral force calculations in the bondeds (ref: Bekker 1994).
1309 There rij = ri - rj, while here dr01 = r1 - r0.
1310 However, all distance vectors occur in form of cross or inner products
1311 so that two signs cancel and we end up with the same expressions.
1312 Also, we treat the more general case of 6 groups (0..5) instead of 4 (i, j, k, l).
1314 m2 = diprod(spatialData.planevec_m, spatialData.planevec_m);
1315 n2 = diprod(spatialData.planevec_n, spatialData.planevec_n);
1316 dsvmul(-1, spatialData.dr23, dr32);
1317 sqrdist_32 = diprod(dr32, dr32);
1318 tol = sqrdist_32*GMX_REAL_EPS; /* Avoid tiny angles */
1319 if ((m2 > tol) && (n2 > tol))
1321 double a_01, a_23_01, a_23_45, a_45;
1322 double inv_dist_32, inv_sqrdist_32, dist_32;
1323 dvec u, v;
1324 inv_dist_32 = gmx::invsqrt(sqrdist_32);
1325 inv_sqrdist_32 = inv_dist_32*inv_dist_32;
1326 dist_32 = sqrdist_32*inv_dist_32;
1328 /* Forces on groups 0, 1 */
1329 a_01 = pcrd.scalarForce*dist_32/m2; /* scalarForce is -dV/dphi */
1330 dsvmul(-a_01, spatialData.planevec_m, forces.force01); /* added sign to get force on group 1, not 0 */
1332 /* Forces on groups 4, 5 */
1333 a_45 = -pcrd.scalarForce*dist_32/n2;
1334 dsvmul(a_45, spatialData.planevec_n, forces.force45); /* force on group 5 */
1336 /* Force on groups 2, 3 (defining the axis) */
1337 a_23_01 = -diprod(spatialData.dr01, dr32)*inv_sqrdist_32;
1338 a_23_45 = -diprod(spatialData.dr45, dr32)*inv_sqrdist_32;
1339 dsvmul(-a_23_01, forces.force01, u); /* added sign to get force from group 0, not 1 */
1340 dsvmul(a_23_45, forces.force45, v);
1341 dvec_sub(u, v, forces.force23); /* force on group 3 */
1343 else
1345 /* No force to apply for ill-defined cases */
1346 clear_dvec(forces.force01);
1347 clear_dvec(forces.force23);
1348 clear_dvec(forces.force45);
1351 else
1353 for (int m = 0; m < DIM; m++)
1355 forces.force01[m] = pcrd.scalarForce*spatialData.vec[m];
1359 return forces;
1363 /* We use a global mutex for locking access to the pull data structure
1364 * during registration of external pull potential providers.
1365 * We could use a different, local mutex for each pull object, but the overhead
1366 * is extremely small here and registration is only done during initialization.
1368 static gmx::Mutex registrationMutex;
1370 using Lock = gmx::lock_guard<gmx::Mutex>;
1372 void register_external_pull_potential(struct pull_t *pull,
1373 int coord_index,
1374 const char *provider)
1376 GMX_RELEASE_ASSERT(pull != nullptr, "register_external_pull_potential called before init_pull");
1377 GMX_RELEASE_ASSERT(provider != nullptr, "register_external_pull_potential called with NULL as provider name");
1379 if (coord_index < 0 || coord_index >= static_cast<int>(pull->coord.size()))
1381 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",
1382 provider, coord_index + 1, 1, pull->coord.size());
1385 pull_coord_work_t *pcrd = &pull->coord[coord_index];
1387 if (pcrd->params.eType != epullEXTERNAL)
1389 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'",
1390 provider, coord_index + 1, epull_names[pcrd->params.eType], epull_names[epullEXTERNAL]);
1393 GMX_RELEASE_ASSERT(pcrd->params.externalPotentialProvider != nullptr, "The external potential provider string for a pull coordinate is NULL");
1395 if (gmx_strcasecmp(provider, pcrd->params.externalPotentialProvider) != 0)
1397 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'",
1398 provider, coord_index + 1, pcrd->params.externalPotentialProvider);
1401 /* Lock to avoid (extremely unlikely) simultaneous reading and writing of
1402 * pcrd->bExternalPotentialProviderHasBeenRegistered and
1403 * pull->numUnregisteredExternalPotentials.
1405 Lock registrationLock(registrationMutex);
1407 if (pcrd->bExternalPotentialProviderHasBeenRegistered)
1409 gmx_fatal(FARGS, "Module '%s' attempted to register an external potential for pull coordinate %d more than once",
1410 provider, coord_index + 1);
1413 pcrd->bExternalPotentialProviderHasBeenRegistered = true;
1414 pull->numUnregisteredExternalPotentials--;
1416 GMX_RELEASE_ASSERT(pull->numUnregisteredExternalPotentials >= 0, "Negative unregistered potentials, the pull code is inconsistent");
1420 static void check_external_potential_registration(const struct pull_t *pull)
1422 if (pull->numUnregisteredExternalPotentials > 0)
1424 size_t c;
1425 for (c = 0; c < pull->coord.size(); c++)
1427 if (pull->coord[c].params.eType == epullEXTERNAL &&
1428 !pull->coord[c].bExternalPotentialProviderHasBeenRegistered)
1430 break;
1434 GMX_RELEASE_ASSERT(c < pull->coord.size(), "Internal inconsistency in the pull potential provider counting");
1436 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.",
1437 pull->numUnregisteredExternalPotentials,
1438 c + 1, pull->coord[c].params.externalPotentialProvider);
1443 /* Pull takes care of adding the forces of the external potential.
1444 * The external potential module has to make sure that the corresponding
1445 * potential energy is added either to the pull term or to a term
1446 * specific to the external module.
1448 void apply_external_pull_coord_force(struct pull_t *pull,
1449 int coord_index,
1450 double coord_force,
1451 const t_mdatoms *mdatoms,
1452 gmx::ForceWithVirial *forceWithVirial)
1454 pull_coord_work_t *pcrd;
1456 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");
1458 if (pull->comm.bParticipate)
1460 pcrd = &pull->coord[coord_index];
1462 GMX_RELEASE_ASSERT(pcrd->params.eType == epullEXTERNAL, "The pull force can only be set externally on pull coordinates of external type");
1464 GMX_ASSERT(pcrd->bExternalPotentialProviderHasBeenRegistered, "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
1491 do_pull_pot_coord(struct pull_t *pull, int coord_ind, t_pbc *pbc,
1492 double t, real lambda,
1493 real *V, tensor vir, real *dVdl)
1495 pull_coord_work_t &pcrd = pull->coord[coord_ind];
1497 assert(pcrd.params.eType != epullCONSTRAINT);
1499 double dev = get_pull_coord_deviation(pull, coord_ind, pbc, t);
1501 calc_pull_coord_scalar_force_and_potential(&pcrd, dev, lambda, V, dVdl);
1503 PullCoordVectorForces pullCoordForces = calculateVectorForces(pcrd);
1505 add_virial_coord(vir, pcrd, pullCoordForces);
1507 return pullCoordForces;
1510 real pull_potential(struct pull_t *pull, const t_mdatoms *md, t_pbc *pbc,
1511 const t_commrec *cr, double t, real lambda,
1512 const rvec *x, gmx::ForceWithVirial *force, real *dvdlambda)
1514 real V = 0;
1516 assert(pull != nullptr);
1518 /* Ideally we should check external potential registration only during
1519 * the initialization phase, but that requires another function call
1520 * that should be done exactly in the right order. So we check here.
1522 check_external_potential_registration(pull);
1524 if (pull->comm.bParticipate)
1526 real dVdl = 0;
1528 pull_calc_coms(cr, pull, md, pbc, t, x, nullptr);
1530 rvec *f = as_rvec_array(force->force_.data());
1531 matrix virial = { { 0 } };
1532 const bool computeVirial = (force->computeVirial_ && MASTER(cr));
1533 for (size_t c = 0; c < pull->coord.size(); c++)
1535 pull_coord_work_t *pcrd;
1536 pcrd = &pull->coord[c];
1538 /* For external potential the force is assumed to be given by an external module by a call to
1539 apply_pull_coord_external_force */
1540 if (pcrd->params.eType == epullCONSTRAINT || pcrd->params.eType == epullEXTERNAL)
1542 continue;
1545 PullCoordVectorForces pullCoordForces =
1546 do_pull_pot_coord(pull, c, pbc, t, lambda,
1548 computeVirial ? virial : nullptr,
1549 &dVdl);
1551 /* Distribute the force over the atoms in the pulled groups */
1552 apply_forces_coord(pull, c, pullCoordForces, md, f);
1555 if (MASTER(cr))
1557 force->addVirialContribution(virial);
1558 *dvdlambda += dVdl;
1562 GMX_ASSERT(pull->numExternalPotentialsStillToBeAppliedThisStep == 0, "Too few or too many external pull potentials have been applied the previous step");
1563 /* All external pull potentials still need to be applied */
1564 pull->numExternalPotentialsStillToBeAppliedThisStep =
1565 pull->numCoordinatesWithExternalPotential;
1567 return (MASTER(cr) ? V : 0.0);
1570 void pull_constraint(struct pull_t *pull, const t_mdatoms *md, t_pbc *pbc,
1571 const t_commrec *cr, double dt, double t,
1572 rvec *x, rvec *xp, rvec *v, tensor vir)
1574 assert(pull != nullptr);
1576 if (pull->comm.bParticipate)
1578 pull_calc_coms(cr, pull, md, pbc, t, x, xp);
1580 do_constraint(pull, pbc, xp, v, MASTER(cr), vir, dt, t);
1584 void dd_make_local_pull_groups(const t_commrec *cr, struct pull_t *pull)
1586 gmx_domdec_t *dd;
1587 pull_comm_t *comm;
1588 gmx_bool bMustParticipate;
1590 dd = cr->dd;
1592 comm = &pull->comm;
1594 /* We always make the master node participate, such that it can do i/o,
1595 * add the virial and to simplify MC type extensions people might have.
1597 bMustParticipate = (comm->bParticipateAll || comm->isMasterRank);
1599 for (pull_group_work_t &group : pull->group)
1601 if (group.epgrppbc == epgrppbcCOS || !group.globalWeights.empty())
1603 group.localWeights.resize(group.atomSet.numAtomsLocal());
1604 for (size_t i = 0; i < group.atomSet.numAtomsLocal(); ++i)
1606 group.localWeights[i] = group.globalWeights[group.atomSet.collectiveIndex()[i]];
1610 GMX_ASSERT(bMustParticipate || dd != nullptr, "Either all ranks (including this rank) participate, or we use DD and need to have access to dd here");
1612 /* We should participate if we have pull or pbc atoms */
1613 if (!bMustParticipate &&
1614 (group.atomSet.numAtomsLocal() > 0 || (group.epgrppbc == epgrppbcREFAT &&
1615 group.pbcAtomSet->numAtomsLocal() > 0)))
1617 bMustParticipate = TRUE;
1621 if (!comm->bParticipateAll)
1623 /* Keep currently not required ranks in the communicator
1624 * if they needed to participate up to 20 decompositions ago.
1625 * This avoids frequent rebuilds due to atoms jumping back and forth.
1627 const int64_t history_count = 20;
1628 gmx_bool bWillParticipate;
1629 int count[2];
1631 /* Increase the decomposition counter for the current call */
1632 comm->setup_count++;
1634 if (bMustParticipate)
1636 comm->must_count = comm->setup_count;
1639 bWillParticipate =
1640 bMustParticipate ||
1641 (comm->bParticipate &&
1642 comm->must_count >= comm->setup_count - history_count);
1644 if (debug && dd != nullptr)
1646 fprintf(debug, "Our DD rank (%3d) pull #atoms>0 or master: %s, will be part %s\n",
1647 dd->rank, gmx::boolToString(bMustParticipate), gmx::boolToString(bWillParticipate));
1650 if (bWillParticipate)
1652 /* Count the number of ranks that we want to have participating */
1653 count[0] = 1;
1654 /* Count the number of ranks that need to be added */
1655 count[1] = comm->bParticipate ? 0 : 1;
1657 else
1659 count[0] = 0;
1660 count[1] = 0;
1663 /* The cost of this global operation will be less that the cost
1664 * of the extra MPI_Comm_split calls that we can avoid.
1666 gmx_sumi(2, count, cr);
1668 /* If we are missing ranks or if we have 20% more ranks than needed
1669 * we make a new sub-communicator.
1671 if (count[1] > 0 || 6*count[0] < 5*comm->nparticipate)
1673 if (debug)
1675 fprintf(debug, "Creating new pull subcommunicator of size %d\n",
1676 count[0]);
1679 #if GMX_MPI
1680 if (comm->mpi_comm_com != MPI_COMM_NULL)
1682 MPI_Comm_free(&comm->mpi_comm_com);
1685 /* This might be an extremely expensive operation, so we try
1686 * to avoid this splitting as much as possible.
1688 assert(dd != nullptr);
1689 MPI_Comm_split(dd->mpi_comm_all, bWillParticipate ? 0 : 1, dd->rank,
1690 &comm->mpi_comm_com);
1691 #endif
1693 comm->bParticipate = bWillParticipate;
1694 comm->nparticipate = count[0];
1696 /* When we use the previous COM for PBC, we need to broadcast
1697 * the previous COM to ranks that have joined the communicator.
1699 for (pull_group_work_t &group : pull->group)
1701 if (group.epgrppbc == epgrppbcPREVSTEPCOM)
1703 GMX_ASSERT(comm->bParticipate || !MASTER(cr),
1704 "The master rank has to participate, as it should pass an up to date prev. COM "
1705 "to bcast here as well as to e.g. checkpointing");
1707 gmx_bcast(sizeof(dvec), group.x_prev_step, cr);
1713 /* Since the PBC of atoms might have changed, we need to update the PBC */
1714 pull->bSetPBCatoms = TRUE;
1717 static void init_pull_group_index(FILE *fplog, const t_commrec *cr,
1718 int g, pull_group_work_t *pg,
1719 gmx_bool bConstraint, const ivec pulldim_con,
1720 const gmx_mtop_t *mtop,
1721 const t_inputrec *ir, real lambda)
1723 /* With EM and BD there are no masses in the integrator.
1724 * But we still want to have the correct mass-weighted COMs.
1725 * So we store the real masses in the weights.
1727 const bool setWeights = (pg->params.nweight > 0 ||
1728 EI_ENERGY_MINIMIZATION(ir->eI) ||
1729 ir->eI == eiBD);
1731 /* In parallel, store we need to extract localWeights from weights at DD time */
1732 std::vector<real> &weights = ((cr && PAR(cr)) ? pg->globalWeights : pg->localWeights);
1734 const gmx_groups_t *groups = &mtop->groups;
1736 /* Count frozen dimensions and (weighted) mass */
1737 int nfrozen = 0;
1738 double tmass = 0;
1739 double wmass = 0;
1740 double wwmass = 0;
1741 int molb = 0;
1742 for (int i = 0; i < pg->params.nat; i++)
1744 int ii = pg->params.ind[i];
1745 if (bConstraint && ir->opts.nFreeze)
1747 for (int d = 0; d < DIM; d++)
1749 if (pulldim_con[d] == 1 &&
1750 ir->opts.nFreeze[getGroupType(groups, egcFREEZE, ii)][d])
1752 nfrozen++;
1756 const t_atom &atom = mtopGetAtomParameters(mtop, ii, &molb);
1757 real m;
1758 if (ir->efep == efepNO)
1760 m = atom.m;
1762 else
1764 m = (1 - lambda)*atom.m + lambda*atom.mB;
1766 real w;
1767 if (pg->params.nweight > 0)
1769 w = pg->params.weight[i];
1771 else
1773 w = 1;
1775 if (EI_ENERGY_MINIMIZATION(ir->eI))
1777 /* Move the mass to the weight */
1778 w *= m;
1779 m = 1;
1781 else if (ir->eI == eiBD)
1783 real mbd;
1784 if (ir->bd_fric != 0.0f)
1786 mbd = ir->bd_fric*ir->delta_t;
1788 else
1790 if (groups->grpnr[egcTC] == nullptr)
1792 mbd = ir->delta_t/ir->opts.tau_t[0];
1794 else
1796 mbd = ir->delta_t/ir->opts.tau_t[groups->grpnr[egcTC][ii]];
1799 w *= m/mbd;
1800 m = mbd;
1802 if (setWeights)
1804 weights.push_back(w);
1806 tmass += m;
1807 wmass += m*w;
1808 wwmass += m*w*w;
1811 if (wmass == 0)
1813 /* We can have single atom groups with zero mass with potential pulling
1814 * without cosine weighting.
1816 if (pg->params.nat == 1 && !bConstraint && pg->epgrppbc != epgrppbcCOS)
1818 /* With one atom the mass doesn't matter */
1819 wwmass = 1;
1821 else
1823 gmx_fatal(FARGS, "The total%s mass of pull group %d is zero",
1824 pg->params.weight ? " weighted" : "", g);
1827 if (fplog)
1829 fprintf(fplog,
1830 "Pull group %d: %5d atoms, mass %9.3f",
1831 g, pg->params.nat, tmass);
1832 if (pg->params.weight ||
1833 EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD)
1835 fprintf(fplog, ", weighted mass %9.3f", wmass*wmass/wwmass);
1837 if (pg->epgrppbc == epgrppbcCOS)
1839 fprintf(fplog, ", cosine weighting will be used");
1841 fprintf(fplog, "\n");
1844 if (nfrozen == 0)
1846 /* A value != 0 signals not frozen, it is updated later */
1847 pg->invtm = -1.0;
1849 else
1851 int ndim = 0;
1852 for (int d = 0; d < DIM; d++)
1854 ndim += pulldim_con[d]*pg->params.nat;
1856 if (fplog && nfrozen > 0 && nfrozen < ndim)
1858 fprintf(fplog,
1859 "\nWARNING: In pull group %d some, but not all of the degrees of freedom\n"
1860 " that are subject to pulling are frozen.\n"
1861 " For constraint pulling the whole group will be frozen.\n\n",
1864 pg->invtm = 0.0;
1865 pg->wscale = 1.0;
1869 struct pull_t *
1870 init_pull(FILE *fplog, const pull_params_t *pull_params, const t_inputrec *ir,
1871 const gmx_mtop_t *mtop, const t_commrec *cr, gmx::LocalAtomSetManager *atomSets,
1872 real lambda)
1874 struct pull_t *pull;
1875 pull_comm_t *comm;
1877 pull = new pull_t;
1879 /* Copy the pull parameters */
1880 pull->params = *pull_params;
1881 /* Avoid pointer copies */
1882 pull->params.group = nullptr;
1883 pull->params.coord = nullptr;
1885 for (int i = 0; i < pull_params->ngroup; ++i)
1887 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}),
1888 pull_params->bSetPbcRefToPrevStepCOM);
1891 if (cr != nullptr && DOMAINDECOMP(cr))
1893 /* Set up the global to local atom mapping for PBC atoms */
1894 for (pull_group_work_t &group : pull->group)
1896 if (group.epgrppbc == epgrppbcREFAT || group.epgrppbc == epgrppbcPREVSTEPCOM)
1898 /* pbcAtomSet consists of a single atom */
1899 group.pbcAtomSet = gmx::compat::make_unique<gmx::LocalAtomSet>(atomSets->add({&group.params.pbcatom, &group.params.pbcatom + 1}));
1904 pull->bPotential = FALSE;
1905 pull->bConstraint = FALSE;
1906 pull->bCylinder = FALSE;
1907 pull->bAngle = FALSE;
1908 pull->bXOutAverage = pull_params->bXOutAverage;
1909 pull->bFOutAverage = pull_params->bFOutAverage;
1911 GMX_RELEASE_ASSERT(pull->group[0].params.nat == 0, "pull group 0 is an absolute reference group and should not contain atoms");
1913 pull->numCoordinatesWithExternalPotential = 0;
1915 for (int c = 0; c < pull->params.ncoord; c++)
1917 /* Construct a pull coordinate, copying all coordinate parameters */
1918 pull->coord.emplace_back(pull_params->coord[c]);
1920 pull_coord_work_t *pcrd = &pull->coord.back();
1922 switch (pcrd->params.eGeom)
1924 case epullgDIST:
1925 case epullgDIRRELATIVE: /* Direction vector is determined at each step */
1926 case epullgANGLE:
1927 case epullgDIHEDRAL:
1928 break;
1929 case epullgDIR:
1930 case epullgDIRPBC:
1931 case epullgCYL:
1932 case epullgANGLEAXIS:
1933 copy_rvec_to_dvec(pull_params->coord[c].vec, pcrd->spatialData.vec);
1934 break;
1935 default:
1936 /* We allow reading of newer tpx files with new pull geometries,
1937 * but with the same tpx format, with old code. A new geometry
1938 * only adds a new enum value, which will be out of range for
1939 * old code. The first place we need to generate an error is
1940 * here, since the pull code can't handle this.
1941 * The enum can be used before arriving here only for printing
1942 * the string corresponding to the geometry, which will result
1943 * in printing "UNDEFINED".
1945 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.",
1946 c + 1, pcrd->params.eGeom, epullgNR - 1);
1949 if (pcrd->params.eType == epullCONSTRAINT)
1951 /* Check restrictions of the constraint pull code */
1952 if (pcrd->params.eGeom == epullgCYL ||
1953 pcrd->params.eGeom == epullgDIRRELATIVE ||
1954 pcrd->params.eGeom == epullgANGLE ||
1955 pcrd->params.eGeom == epullgDIHEDRAL ||
1956 pcrd->params.eGeom == epullgANGLEAXIS)
1958 gmx_fatal(FARGS, "Pulling of type %s can not be combined with geometry %s. Consider using pull type %s.",
1959 epull_names[pcrd->params.eType],
1960 epullg_names[pcrd->params.eGeom],
1961 epull_names[epullUMBRELLA]);
1964 pull->bConstraint = TRUE;
1966 else
1968 pull->bPotential = TRUE;
1971 if (pcrd->params.eGeom == epullgCYL)
1973 pull->bCylinder = TRUE;
1975 else if (pcrd->params.eGeom == epullgANGLE || pcrd->params.eGeom == epullgDIHEDRAL || pcrd->params.eGeom == epullgANGLEAXIS)
1977 pull->bAngle = TRUE;
1980 /* We only need to calculate the plain COM of a group
1981 * when it is not only used as a cylinder group.
1982 * Also the absolute reference group 0 needs no COM computation.
1984 for (int i = 0; i < pcrd->params.ngroup; i++)
1986 int groupIndex = pcrd->params.group[i];
1987 if (groupIndex > 0 &&
1988 !(pcrd->params.eGeom == epullgCYL && i == 0))
1990 pull->group[groupIndex].needToCalcCom = true;
1994 /* With non-zero rate the reference value is set at every step */
1995 if (pcrd->params.rate == 0)
1997 /* Initialize the constant reference value */
1998 if (pcrd->params.eType != epullEXTERNAL)
2000 low_set_pull_coord_reference_value(pcrd, c, pcrd->params.init*pull_conversion_factor_userinput2internal(&pcrd->params));
2002 else
2004 /* With an external pull potential, the reference value loses
2005 * it's meaning and should not be used. Setting it to zero
2006 * makes any terms dependent on it disappear.
2007 * The only issue this causes is that with cylinder pulling
2008 * no atoms of the cylinder group within the cylinder radius
2009 * should be more than half a box length away from the COM of
2010 * the pull group along the axial direction.
2012 pcrd->value_ref = 0.0;
2016 if (pcrd->params.eType == epullEXTERNAL)
2018 GMX_RELEASE_ASSERT(pcrd->params.rate == 0, "With an external potential, a pull coordinate should have rate = 0");
2020 /* This external potential needs to be registered later */
2021 pull->numCoordinatesWithExternalPotential++;
2023 pcrd->bExternalPotentialProviderHasBeenRegistered = false;
2026 pull->numUnregisteredExternalPotentials =
2027 pull->numCoordinatesWithExternalPotential;
2028 pull->numExternalPotentialsStillToBeAppliedThisStep = 0;
2030 pull->ePBC = ir->ePBC;
2031 switch (pull->ePBC)
2033 case epbcNONE: pull->npbcdim = 0; break;
2034 case epbcXY: pull->npbcdim = 2; break;
2035 default: pull->npbcdim = 3; break;
2038 if (fplog)
2040 gmx_bool bAbs, bCos;
2042 bAbs = FALSE;
2043 for (const pull_coord_work_t &coord : pull->coord)
2045 if (pull->group[coord.params.group[0]].params.nat == 0 ||
2046 pull->group[coord.params.group[1]].params.nat == 0)
2048 bAbs = TRUE;
2052 fprintf(fplog, "\n");
2053 if (pull->bPotential)
2055 fprintf(fplog, "Will apply potential COM pulling\n");
2057 if (pull->bConstraint)
2059 fprintf(fplog, "Will apply constraint COM pulling\n");
2061 // Don't include the reference group 0 in output, so we report ngroup-1
2062 int numRealGroups = pull->group.size() - 1;
2063 GMX_RELEASE_ASSERT(numRealGroups > 0, "The reference absolute position pull group should always be present");
2064 fprintf(fplog, "with %zu pull coordinate%s and %d group%s\n",
2065 pull->coord.size(), pull->coord.size() == 1 ? "" : "s",
2066 numRealGroups, numRealGroups == 1 ? "" : "s");
2067 if (bAbs)
2069 fprintf(fplog, "with an absolute reference\n");
2071 bCos = FALSE;
2072 // Don't include the reference group 0 in loop
2073 for (size_t g = 1; g < pull->group.size(); g++)
2075 if (pull->group[g].params.nat > 1 &&
2076 pull->group[g].params.pbcatom < 0)
2078 /* We are using cosine weighting */
2079 fprintf(fplog, "Cosine weighting is used for group %zu\n", g);
2080 bCos = TRUE;
2083 if (bCos)
2085 please_cite(fplog, "Engin2010");
2089 pull->bRefAt = FALSE;
2090 pull->cosdim = -1;
2091 for (size_t g = 0; g < pull->group.size(); g++)
2093 pull_group_work_t *pgrp;
2095 pgrp = &pull->group[g];
2096 if (pgrp->params.nat > 0)
2098 /* There is an issue when a group is used in multiple coordinates
2099 * and constraints are applied in different dimensions with atoms
2100 * frozen in some, but not all dimensions.
2101 * Since there is only one mass array per group, we can't have
2102 * frozen/non-frozen atoms for different coords at the same time.
2103 * But since this is a very exotic case, we don't check for this.
2104 * A warning is printed in init_pull_group_index.
2107 gmx_bool bConstraint;
2108 ivec pulldim, pulldim_con;
2110 /* Loop over all pull coordinates to see along which dimensions
2111 * this group is pulled and if it is involved in constraints.
2113 bConstraint = FALSE;
2114 clear_ivec(pulldim);
2115 clear_ivec(pulldim_con);
2116 for (const pull_coord_work_t &coord : pull->coord)
2118 gmx_bool bGroupUsed = FALSE;
2119 for (int gi = 0; gi < coord.params.ngroup; gi++)
2121 if (coord.params.group[gi] == static_cast<int>(g))
2123 bGroupUsed = TRUE;
2127 if (bGroupUsed)
2129 for (int m = 0; m < DIM; m++)
2131 if (coord.params.dim[m] == 1)
2133 pulldim[m] = 1;
2135 if (coord.params.eType == epullCONSTRAINT)
2137 bConstraint = TRUE;
2138 pulldim_con[m] = 1;
2145 /* Determine if we need to take PBC into account for calculating
2146 * the COM's of the pull groups.
2148 switch (pgrp->epgrppbc)
2150 case epgrppbcREFAT:
2151 pull->bRefAt = TRUE;
2152 break;
2153 case epgrppbcCOS:
2154 if (pgrp->params.weight != nullptr)
2156 gmx_fatal(FARGS, "Pull groups can not have relative weights and cosine weighting at same time");
2158 for (int m = 0; m < DIM; m++)
2160 if (m < pull->npbcdim && pulldim[m] == 1)
2162 if (pull->cosdim >= 0 && pull->cosdim != m)
2164 gmx_fatal(FARGS, "Can only use cosine weighting with pulling in one dimension (use mdp option pull-coord?-dim)");
2166 pull->cosdim = m;
2169 break;
2170 case epgrppbcNONE:
2171 break;
2174 /* Set the indices */
2175 init_pull_group_index(fplog, cr, g, pgrp,
2176 bConstraint, pulldim_con,
2177 mtop, ir, lambda);
2179 else
2181 /* Absolute reference, set the inverse mass to zero.
2182 * This is only relevant (and used) with constraint pulling.
2184 pgrp->invtm = 0;
2185 pgrp->wscale = 1;
2189 /* If we use cylinder coordinates, do some initialising for them */
2190 if (pull->bCylinder)
2192 for (const pull_coord_work_t &coord : pull->coord)
2194 if (coord.params.eGeom == epullgCYL)
2196 if (pull->group[coord.params.group[0]].params.nat == 0)
2198 gmx_fatal(FARGS, "A cylinder pull group is not supported when using absolute reference!\n");
2201 const auto &referenceGroup = pull->group[coord.params.group[0]];
2202 pull->dyna.emplace_back(referenceGroup.params, referenceGroup.atomSet, pull->params.bSetPbcRefToPrevStepCOM);
2206 /* The gmx_omp_nthreads module might not be initialized here, so max(1,) */
2207 pull->nthreads = std::max(1, gmx_omp_nthreads_get(emntDefault));
2208 pull->comSums.resize(pull->nthreads);
2210 comm = &pull->comm;
2212 #if GMX_MPI
2213 /* Use a sub-communicator when we have more than 32 ranks, but not
2214 * when we have an external pull potential, since then the external
2215 * potential provider expects each rank to have the coordinate.
2217 comm->bParticipateAll = (cr == nullptr || !DOMAINDECOMP(cr) ||
2218 cr->dd->nnodes <= 32 ||
2219 pull->numCoordinatesWithExternalPotential > 0 ||
2220 getenv("GMX_PULL_PARTICIPATE_ALL") != nullptr);
2221 /* This sub-commicator is not used with comm->bParticipateAll,
2222 * so we can always initialize it to NULL.
2224 comm->mpi_comm_com = MPI_COMM_NULL;
2225 comm->nparticipate = 0;
2226 comm->isMasterRank = (cr == nullptr || MASTER(cr));
2227 #else
2228 /* No MPI: 1 rank: all ranks pull */
2229 comm->bParticipateAll = TRUE;
2230 comm->isMasterRank = true;
2231 #endif
2232 comm->bParticipate = comm->bParticipateAll;
2233 comm->setup_count = 0;
2234 comm->must_count = 0;
2236 if (!comm->bParticipateAll && fplog != nullptr)
2238 fprintf(fplog, "Will use a sub-communicator for pull communication\n");
2241 comm->pbcAtomBuffer.resize(pull->group.size());
2242 comm->comBuffer.resize(pull->group.size()*c_comBufferStride);
2243 if (pull->bCylinder)
2245 comm->cylinderBuffer.resize(pull->coord.size()*c_cylinderBufferStride);
2248 /* We still need to initialize the PBC reference coordinates */
2249 pull->bSetPBCatoms = TRUE;
2251 pull->out_x = nullptr;
2252 pull->out_f = nullptr;
2254 return pull;
2257 static void destroy_pull(struct pull_t *pull)
2259 #if GMX_MPI
2260 if (pull->comm.mpi_comm_com != MPI_COMM_NULL)
2262 MPI_Comm_free(&pull->comm.mpi_comm_com);
2264 #endif
2266 delete pull;
2269 void preparePrevStepPullCom(const t_inputrec *ir, const t_mdatoms *md, t_state *state, const t_state *state_global, const t_commrec *cr, bool startingFromCheckpoint)
2271 if (!ir->pull || !ir->pull->bSetPbcRefToPrevStepCOM)
2273 return;
2275 allocStatePrevStepPullCom(state, ir->pull_work);
2276 if (startingFromCheckpoint)
2278 if (MASTER(cr))
2280 state->pull_com_prev_step = state_global->pull_com_prev_step;
2282 if (PAR(cr))
2284 /* Only the master rank has the checkpointed COM from the previous step */
2285 gmx_bcast(sizeof(double) * state->pull_com_prev_step.size(), &state->pull_com_prev_step[0], cr);
2287 setPrevStepPullComFromState(ir->pull_work, state);
2289 else
2291 t_pbc pbc;
2292 set_pbc(&pbc, ir->ePBC, state->box);
2293 initPullComFromPrevStep(cr, ir->pull_work, md, &pbc, state->x.rvec_array());
2294 updatePrevStepPullCom(ir->pull_work, state);
2298 void finish_pull(struct pull_t *pull)
2300 check_external_potential_registration(pull);
2302 if (pull->out_x)
2304 gmx_fio_fclose(pull->out_x);
2306 if (pull->out_f)
2308 gmx_fio_fclose(pull->out_f);
2311 destroy_pull(pull);
2314 gmx_bool pull_have_potential(const struct pull_t *pull)
2316 return pull->bPotential;
2319 gmx_bool pull_have_constraint(const struct pull_t *pull)
2321 return pull->bConstraint;