6d5ea22148c886d7c377ba22a4bfa942ffa953a3
[alexxy/gromacs.git] / src / gromacs / pulling / pull.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
7  * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
8  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9  * and including many others, as listed in the AUTHORS file in the
10  * top-level source directory and at http://www.gromacs.org.
11  *
12  * GROMACS is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * as published by the Free Software Foundation; either version 2.1
15  * of the License, or (at your option) any later version.
16  *
17  * GROMACS is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with GROMACS; if not, see
24  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
26  *
27  * If you want to redistribute modifications to GROMACS, please
28  * consider that scientific software is very special. Version
29  * control is crucial - bugs must be traceable. We will be happy to
30  * consider code for inclusion in the official distribution, but
31  * derived work must not be called official GROMACS. Details are found
32  * in the README & COPYING files - if they are missing, get the
33  * official version at http://www.gromacs.org.
34  *
35  * To help us fund GROMACS development, we humbly ask that you cite
36  * the research papers on the package. Check out http://www.gromacs.org.
37  */
38 #include "gmxpre.h"
39
40 #include "pull.h"
41
42 #include "config.h"
43
44 #include <cassert>
45 #include <cinttypes>
46 #include <cmath>
47 #include <cstdlib>
48
49 #include <algorithm>
50 #include <memory>
51 #include <mutex>
52
53 #include "gromacs/commandline/filenm.h"
54 #include "gromacs/domdec/domdec_struct.h"
55 #include "gromacs/domdec/localatomset.h"
56 #include "gromacs/domdec/localatomsetmanager.h"
57 #include "gromacs/fileio/gmxfio.h"
58 #include "gromacs/gmxlib/network.h"
59 #include "gromacs/math/functions.h"
60 #include "gromacs/math/units.h"
61 #include "gromacs/math/utilities.h"
62 #include "gromacs/math/vec.h"
63 #include "gromacs/math/vectypes.h"
64 #include "gromacs/mdlib/gmx_omp_nthreads.h"
65 #include "gromacs/mdtypes/commrec.h"
66 #include "gromacs/mdtypes/forceoutput.h"
67 #include "gromacs/mdtypes/inputrec.h"
68 #include "gromacs/mdtypes/md_enums.h"
69 #include "gromacs/mdtypes/state.h"
70 #include "gromacs/pbcutil/pbc.h"
71 #include "gromacs/topology/mtop_lookup.h"
72 #include "gromacs/topology/topology.h"
73 #include "gromacs/utility/arrayref.h"
74 #include "gromacs/utility/cstringutil.h"
75 #include "gromacs/utility/exceptions.h"
76 #include "gromacs/utility/fatalerror.h"
77 #include "gromacs/utility/futil.h"
78 #include "gromacs/utility/gmxassert.h"
79 #include "gromacs/utility/pleasecite.h"
80 #include "gromacs/utility/real.h"
81 #include "gromacs/utility/smalloc.h"
82 #include "gromacs/utility/strconvert.h"
83 #include "gromacs/utility/stringutil.h"
84
85 #include "pull_internal.h"
86
87 namespace gmx
88 {
89 extern template LocalAtomSet LocalAtomSetManager::add<void, void>(ArrayRef<const int> globalAtomIndex);
90 } // namespace gmx
91
92 using gmx::ArrayRef;
93
94 static int groupPbcFromParams(const t_pull_group& params, bool setPbcRefToPrevStepCOM)
95 {
96     if (params.ind.size() <= 1)
97     {
98         /* no PBC required */
99         return epgrppbcNONE;
100     }
101     else if (params.pbcatom >= 0)
102     {
103         if (setPbcRefToPrevStepCOM)
104         {
105             return epgrppbcPREVSTEPCOM;
106         }
107         else
108         {
109             return epgrppbcREFAT;
110         }
111     }
112     else
113     {
114         return epgrppbcCOS;
115     }
116 }
117
118 /* NOTE: The params initialization currently copies pointers.
119  *       So the lifetime of the source, currently always inputrec,
120  *       should not end before that of this object.
121  *       This will be fixed when the pointers are replacted by std::vector.
122  */
123 pull_group_work_t::pull_group_work_t(const t_pull_group& params,
124                                      gmx::LocalAtomSet   atomSet,
125                                      bool                bSetPbcRefToPrevStepCOM,
126                                      int                 maxNumThreads) :
127     params(params),
128     epgrppbc(groupPbcFromParams(params, bSetPbcRefToPrevStepCOM)),
129     maxNumThreads(maxNumThreads),
130     needToCalcCom(false),
131     atomSet(atomSet),
132     mwscale(0),
133     wscale(0),
134     invtm(0)
135 {
136     clear_dvec(x);
137     clear_dvec(xp);
138 };
139
140 bool pull_coordinate_is_angletype(const t_pull_coord* pcrd)
141 {
142     return (pcrd->eGeom == PullGroupGeometry::Angle || pcrd->eGeom == PullGroupGeometry::Dihedral
143             || pcrd->eGeom == PullGroupGeometry::AngleAxis);
144 }
145
146 static bool pull_coordinate_is_directional(const t_pull_coord* pcrd)
147 {
148     return (pcrd->eGeom == PullGroupGeometry::Direction || pcrd->eGeom == PullGroupGeometry::DirectionPBC
149             || pcrd->eGeom == PullGroupGeometry::DirectionRelative
150             || pcrd->eGeom == PullGroupGeometry::Cylinder);
151 }
152
153 const char* pull_coordinate_units(const t_pull_coord* pcrd)
154 {
155     return pull_coordinate_is_angletype(pcrd) ? "deg" : "nm";
156 }
157
158 double pull_conversion_factor_userinput2internal(const t_pull_coord* pcrd)
159 {
160     if (pull_coordinate_is_angletype(pcrd))
161     {
162         return gmx::c_deg2Rad;
163     }
164     else
165     {
166         return 1.0;
167     }
168 }
169
170 double pull_conversion_factor_internal2userinput(const t_pull_coord* pcrd)
171 {
172     if (pull_coordinate_is_angletype(pcrd))
173     {
174         return gmx::c_rad2Deg;
175     }
176     else
177     {
178         return 1.0;
179     }
180 }
181
182 /* Apply forces in a mass weighted fashion for part of the pull group */
183 static void apply_forces_grp_part(const pull_group_work_t&  pgrp,
184                                   int                       ind_start,
185                                   int                       ind_end,
186                                   gmx::ArrayRef<const real> masses,
187                                   const dvec                f_pull,
188                                   const int                 sign,
189                                   rvec*                     f)
190 {
191     const double inv_wm = pgrp.mwscale;
192
193     auto localAtomIndices = pgrp.atomSet.localIndex();
194     for (int i = ind_start; i < ind_end; i++)
195     {
196         int    ii    = localAtomIndices[i];
197         double wmass = masses[ii];
198         if (!pgrp.localWeights.empty())
199         {
200             wmass *= pgrp.localWeights[i];
201         }
202
203         for (int d = 0; d < DIM; d++)
204         {
205             f[ii][d] += sign * wmass * f_pull[d] * inv_wm;
206         }
207     }
208 }
209
210 /* Apply forces in a mass weighted fashion */
211 static void apply_forces_grp(const pull_group_work_t&  pgrp,
212                              gmx::ArrayRef<const real> masses,
213                              const dvec                f_pull,
214                              const int                 sign,
215                              rvec*                     f)
216 {
217     auto localAtomIndices = pgrp.atomSet.localIndex();
218
219     if (pgrp.params.ind.size() == 1 && pgrp.atomSet.numAtomsLocal() == 1)
220     {
221         /* Only one atom and our rank has this atom: we can skip
222          * the mass weighting, which means that this code also works
223          * for mass=0, e.g. with a virtual site.
224          */
225         for (int d = 0; d < DIM; d++)
226         {
227             f[localAtomIndices[0]][d] += sign * f_pull[d];
228         }
229     }
230     else
231     {
232         const int numThreads = pgrp.numThreads();
233         if (numThreads == 1)
234         {
235             apply_forces_grp_part(pgrp, 0, localAtomIndices.size(), masses, f_pull, sign, f);
236         }
237         else
238         {
239 #pragma omp parallel for num_threads(numThreads) schedule(static)
240             for (int th = 0; th < numThreads; th++)
241             {
242                 int ind_start = (localAtomIndices.size() * (th + 0)) / numThreads;
243                 int ind_end   = (localAtomIndices.size() * (th + 1)) / numThreads;
244                 apply_forces_grp_part(pgrp, ind_start, ind_end, masses, f_pull, sign, f);
245             }
246         }
247     }
248 }
249
250 /* Apply forces in a mass weighted fashion to a cylinder group */
251 static void apply_forces_cyl_grp(const pull_group_work_t&  pgrp,
252                                  const double              dv_corr,
253                                  gmx::ArrayRef<const real> masses,
254                                  const dvec                f_pull,
255                                  double                    f_scal,
256                                  const int                 sign,
257                                  rvec*                     f)
258 {
259     const double inv_wm = pgrp.mwscale;
260
261     auto localAtomIndices = pgrp.atomSet.localIndex();
262
263     /* The cylinder group is always a slab in the system, thus large.
264      * Therefore we always thread-parallelize this group.
265      */
266     const int numAtomsLocal         = localAtomIndices.size();
267     const int gmx_unused numThreads = pgrp.numThreads();
268 #pragma omp parallel for num_threads(numThreads) schedule(static)
269     for (int i = 0; i < numAtomsLocal; i++)
270     {
271         double weight = pgrp.localWeights[i];
272         if (weight == 0)
273         {
274             continue;
275         }
276         int    ii   = localAtomIndices[i];
277         double mass = masses[ii];
278         /* The stored axial distance from the cylinder center (dv) needs
279          * to be corrected for an offset (dv_corr), which was unknown when
280          * we calculated dv.
281          */
282         double dv_com = pgrp.dv[i] + dv_corr;
283
284         /* Here we not only add the pull force working along vec (f_pull),
285          * but also a radial component, due to the dependence of the weights
286          * on the radial distance.
287          */
288         for (int m = 0; m < DIM; m++)
289         {
290             f[ii][m] += sign * inv_wm * (mass * weight * f_pull[m] + pgrp.mdw[i][m] * dv_com * f_scal);
291         }
292     }
293 }
294
295 /* Apply torque forces in a mass weighted fashion to the groups that define
296  * the pull vector direction for pull coordinate pcrd.
297  */
298 static void apply_forces_vec_torque(const pull_coord_work_t&          pcrd,
299                                     ArrayRef<const pull_group_work_t> pullGroups,
300                                     gmx::ArrayRef<const real>         masses,
301                                     rvec*                             f)
302 {
303     const PullCoordSpatialData& spatialData = pcrd.spatialData;
304
305     /* The component inpr along the pull vector is accounted for in the usual
306      * way. Here we account for the component perpendicular to vec.
307      */
308     double inpr = 0;
309     for (int m = 0; m < DIM; m++)
310     {
311         inpr += spatialData.dr01[m] * spatialData.vec[m];
312     }
313     /* The torque force works along the component of the distance vector
314      * of between the two "usual" pull groups that is perpendicular to
315      * the pull vector. The magnitude of this force is the "usual" scale force
316      * multiplied with the ratio of the distance between the two "usual" pull
317      * groups and the distance between the two groups that define the vector.
318      */
319     dvec f_perp;
320     for (int m = 0; m < DIM; m++)
321     {
322         f_perp[m] = (spatialData.dr01[m] - inpr * spatialData.vec[m]) / spatialData.vec_len * pcrd.scalarForce;
323     }
324
325     /* Apply the force to the groups defining the vector using opposite signs */
326     apply_forces_grp(pullGroups[pcrd.params.group[2]], masses, f_perp, -1, f);
327     apply_forces_grp(pullGroups[pcrd.params.group[3]], masses, f_perp, 1, f);
328 }
329
330 /* Apply forces in a mass weighted fashion */
331 static void apply_forces_coord(const pull_coord_work_t&          pcrd,
332                                ArrayRef<const pull_group_work_t> pullGroups,
333                                const PullCoordVectorForces&      forces,
334                                gmx::ArrayRef<const real>         masses,
335                                rvec*                             f)
336 {
337     /* Here it would be more efficient to use one large thread-parallel
338      * region instead of potential parallel regions within apply_forces_grp.
339      * But there could be overlap between pull groups and this would lead
340      * to data races.
341      */
342
343     if (pcrd.params.eGeom == PullGroupGeometry::Cylinder)
344     {
345         apply_forces_cyl_grp(
346                 *pcrd.dynamicGroup0, pcrd.spatialData.cyl_dev, masses, forces.force01, pcrd.scalarForce, -1, f);
347
348         /* Sum the force along the vector and the radial force */
349         dvec f_tot;
350         for (int m = 0; m < DIM; m++)
351         {
352             f_tot[m] = forces.force01[m] + pcrd.scalarForce * pcrd.spatialData.ffrad[m];
353         }
354         apply_forces_grp(pullGroups[pcrd.params.group[1]], masses, f_tot, 1, f);
355     }
356     else
357     {
358         if (pcrd.params.eGeom == PullGroupGeometry::DirectionRelative)
359         {
360             /* We need to apply the torque forces to the pull groups
361              * that define the pull vector.
362              */
363             apply_forces_vec_torque(pcrd, pullGroups, masses, f);
364         }
365
366         if (!pullGroups[pcrd.params.group[0]].params.ind.empty())
367         {
368             apply_forces_grp(pullGroups[pcrd.params.group[0]], masses, forces.force01, -1, f);
369         }
370         apply_forces_grp(pullGroups[pcrd.params.group[1]], masses, forces.force01, 1, f);
371
372         if (pcrd.params.ngroup >= 4)
373         {
374             apply_forces_grp(pullGroups[pcrd.params.group[2]], masses, forces.force23, -1, f);
375             apply_forces_grp(pullGroups[pcrd.params.group[3]], masses, forces.force23, 1, f);
376         }
377         if (pcrd.params.ngroup >= 6)
378         {
379             apply_forces_grp(pullGroups[pcrd.params.group[4]], masses, forces.force45, -1, f);
380             apply_forces_grp(pullGroups[pcrd.params.group[5]], masses, forces.force45, 1, f);
381         }
382     }
383 }
384
385 real max_pull_distance2(const pull_coord_work_t* pcrd, const t_pbc* pbc)
386 {
387     /* Note that this maximum distance calculation is more complex than
388      * most other cases in GROMACS, since here we have to take care of
389      * distance calculations that don't involve all three dimensions.
390      * For example, we can use distances that are larger than the
391      * box X and Y dimensions for a box that is elongated along Z.
392      */
393
394     real max_d2 = GMX_REAL_MAX;
395
396     if (pull_coordinate_is_directional(&pcrd->params))
397     {
398         /* Directional pulling along along direction pcrd->vec.
399          * Calculating the exact maximum distance is complex and bug-prone.
400          * So we take a safe approach by not allowing distances that
401          * are larger than half the distance between unit cell faces
402          * along dimensions involved in pcrd->vec.
403          */
404         for (int m = 0; m < DIM; m++)
405         {
406             if (m < pbc->ndim_ePBC && pcrd->spatialData.vec[m] != 0)
407             {
408                 real imageDistance2 = gmx::square(pbc->box[m][m]);
409                 for (int d = m + 1; d < DIM; d++)
410                 {
411                     imageDistance2 -= gmx::square(pbc->box[d][m]);
412                 }
413                 max_d2 = std::min(max_d2, imageDistance2);
414             }
415         }
416     }
417     else
418     {
419         /* Distance pulling along dimensions with pcrd->params.dim[d]==1.
420          * We use half the minimum box vector length of the dimensions involved.
421          * This is correct for all cases, except for corner cases with
422          * triclinic boxes where e.g. dim[XX]=1 and dim[YY]=0 with
423          * box[YY][XX]!=0 and box[YY][YY] < box[XX][XX]. But since even
424          * in such corner cases the user could get correct results,
425          * depending on the details of the setup, we avoid further
426          * code complications.
427          */
428         for (int m = 0; m < DIM; m++)
429         {
430             if (m < pbc->ndim_ePBC && pcrd->params.dim[m] != 0)
431             {
432                 real imageDistance2 = gmx::square(pbc->box[m][m]);
433                 for (int d = 0; d < m; d++)
434                 {
435                     if (pcrd->params.dim[d] != 0)
436                     {
437                         imageDistance2 += gmx::square(pbc->box[m][d]);
438                     }
439                 }
440                 max_d2 = std::min(max_d2, imageDistance2);
441             }
442         }
443     }
444
445     return 0.25 * max_d2;
446 }
447
448 /* This function returns the distance based on coordinates xg and xref.
449  * Note that the pull coordinate struct pcrd is not modified.
450  *
451  * \param[in]  pull  The pull struct
452  * \param[in]  pcrd  The pull coordinate to compute a distance for
453  * \param[in]  pbc   The periodic boundary conditions
454  * \param[in]  xg    The coordinate of group 1
455  * \param[in]  xref  The coordinate of group 0
456  * \param[in]  groupIndex0  The index of group 0 in the pcrd->params.group
457  * \param[in]  groupIndex1  The index of group 1 in the pcrd->params.group
458  * \param[in]  max_dist2    The maximum distance squared
459  * \param[out] dr           The distance vector
460  */
461 static void low_get_pull_coord_dr(const pull_t&            pull,
462                                   const pull_coord_work_t* pcrd,
463                                   const t_pbc*             pbc,
464                                   const dvec               xg,
465                                   const dvec               xref,
466                                   const int                groupIndex0,
467                                   const int                groupIndex1,
468                                   const double             max_dist2,
469                                   dvec                     dr)
470 {
471     const pull_group_work_t& pgrp0 = pull.group[pcrd->params.group[0]];
472
473     // Group coordinate, to be updated with the reference position
474     dvec xrefr;
475
476     /* Only the first group can be an absolute reference, in that case nat=0 */
477     if (pgrp0.params.ind.empty())
478     {
479         for (int m = 0; m < DIM; m++)
480         {
481             xrefr[m] = pcrd->params.origin[m];
482         }
483     }
484     else
485     {
486         copy_dvec(xref, xrefr);
487     }
488
489     dvec dref = { 0, 0, 0 };
490     if (pcrd->params.eGeom == PullGroupGeometry::DirectionPBC)
491     {
492         for (int m = 0; m < DIM; m++)
493         {
494             dref[m] = pcrd->value_ref * pcrd->spatialData.vec[m];
495         }
496         /* Add the reference position, so we use the correct periodic image */
497         dvec_inc(xrefr, dref);
498     }
499
500     pbc_dx_d(pbc, xg, xrefr, dr);
501
502     bool   directional = pull_coordinate_is_directional(&pcrd->params);
503     double dr2         = 0;
504     for (int m = 0; m < DIM; m++)
505     {
506         dr[m] *= pcrd->params.dim[m];
507         if (pcrd->params.dim[m] && !(directional && pcrd->spatialData.vec[m] == 0))
508         {
509             dr2 += dr[m] * dr[m];
510         }
511     }
512     /* Check if we are close to switching to another periodic image */
513     if (max_dist2 > 0 && dr2 > 0.98 * 0.98 * max_dist2)
514     {
515         /* Note that technically there is no issue with switching periodic
516          * image, as pbc_dx_d returns the distance to the closest periodic
517          * image. However in all cases where periodic image switches occur,
518          * the pull results will be useless in practice.
519          */
520         gmx_fatal(FARGS,
521                   "Distance between pull groups %d and %d (%f nm) is larger than 0.49 times the "
522                   "box size (%f).\n%s",
523                   pcrd->params.group[groupIndex0],
524                   pcrd->params.group[groupIndex1],
525                   sqrt(dr2),
526                   sqrt(0.98 * 0.98 * max_dist2),
527                   pcrd->params.eGeom == PullGroupGeometry::Direction
528                           ? "You might want to consider using \"pull-geometry = "
529                             "direction-periodic\" instead.\n"
530                           : "");
531     }
532
533     if (pcrd->params.eGeom == PullGroupGeometry::DirectionPBC)
534     {
535         dvec_inc(dr, dref);
536     }
537 }
538
539 /* This function returns the distance based on the contents of the pull struct.
540  * pull->coord[coord_ind].dr, and potentially vector, are updated.
541  */
542 static void get_pull_coord_dr(const pull_t& pull, pull_coord_work_t* pcrd, const t_pbc* pbc)
543 {
544     const int coord_ind = pcrd->params.coordIndex;
545
546     PullCoordSpatialData& spatialData = pcrd->spatialData;
547
548     double md2;
549     /* With AWH pulling we allow for periodic pulling with geometry=direction.
550      * TODO: Store a periodicity flag instead of checking for external pull provider.
551      */
552     if (pcrd->params.eGeom == PullGroupGeometry::DirectionPBC
553         || (pcrd->params.eGeom == PullGroupGeometry::Direction
554             && pcrd->params.eType == PullingAlgorithm::External))
555     {
556         md2 = -1;
557     }
558     else
559     {
560         md2 = static_cast<double>(max_pull_distance2(pcrd, pbc));
561     }
562
563     if (pcrd->params.eGeom == PullGroupGeometry::DirectionRelative)
564     {
565         /* We need to determine the pull vector */
566         dvec vec;
567         int  m;
568
569         const pull_group_work_t& pgrp2 = pull.group[pcrd->params.group[2]];
570         const pull_group_work_t& pgrp3 = pull.group[pcrd->params.group[3]];
571
572         pbc_dx_d(pbc, pgrp3.x, pgrp2.x, vec);
573
574         for (m = 0; m < DIM; m++)
575         {
576             vec[m] *= pcrd->params.dim[m];
577         }
578         spatialData.vec_len = dnorm(vec);
579         for (m = 0; m < DIM; m++)
580         {
581             spatialData.vec[m] = vec[m] / spatialData.vec_len;
582         }
583         if (debug)
584         {
585             fprintf(debug,
586                     "pull coord %d vector: %6.3f %6.3f %6.3f normalized: %6.3f %6.3f %6.3f\n",
587                     coord_ind,
588                     vec[XX],
589                     vec[YY],
590                     vec[ZZ],
591                     spatialData.vec[XX],
592                     spatialData.vec[YY],
593                     spatialData.vec[ZZ]);
594         }
595     }
596
597     const pull_group_work_t& pgrp0 = pull.group[pcrd->params.group[0]];
598     const pull_group_work_t& pgrp1 = pull.group[pcrd->params.group[1]];
599
600     low_get_pull_coord_dr(
601             pull,
602             pcrd,
603             pbc,
604             pgrp1.x,
605             pcrd->params.eGeom == PullGroupGeometry::Cylinder ? pcrd->dynamicGroup0->x : pgrp0.x,
606             0,
607             1,
608             md2,
609             spatialData.dr01);
610
611     if (pcrd->params.ngroup >= 4)
612     {
613         const pull_group_work_t& pgrp2 = pull.group[pcrd->params.group[2]];
614         const pull_group_work_t& pgrp3 = pull.group[pcrd->params.group[3]];
615
616         low_get_pull_coord_dr(pull, pcrd, pbc, pgrp3.x, pgrp2.x, 2, 3, md2, spatialData.dr23);
617     }
618     if (pcrd->params.ngroup >= 6)
619     {
620         const pull_group_work_t& pgrp4 = pull.group[pcrd->params.group[4]];
621         const pull_group_work_t& pgrp5 = pull.group[pcrd->params.group[5]];
622
623         low_get_pull_coord_dr(pull, pcrd, pbc, pgrp5.x, pgrp4.x, 4, 5, md2, spatialData.dr45);
624     }
625 }
626
627 /* Modify x so that it is periodic in [-pi, pi)
628  * It is assumed that x is in [-3pi, 3pi) so that x
629  * needs to be shifted by at most one period.
630  */
631 static void make_periodic_2pi(double* x)
632 {
633     if (*x >= M_PI)
634     {
635         *x -= M_2PI;
636     }
637     else if (*x < -M_PI)
638     {
639         *x += M_2PI;
640     }
641 }
642
643 /* This function should always be used to get values for setting value_ref in pull_coord_work_t */
644 static double sanitizePullCoordReferenceValue(const t_pull_coord& pcrdParams, double value_ref)
645 {
646     GMX_ASSERT(pcrdParams.eType != PullingAlgorithm::External,
647                "The pull coord reference value should not be used with type external-potential");
648
649     if (pcrdParams.eGeom == PullGroupGeometry::Distance)
650     {
651         if (value_ref < 0)
652         {
653             gmx_fatal(FARGS,
654                       "Pull reference distance for coordinate %d (%f) needs to be non-negative",
655                       pcrdParams.coordIndex + 1,
656                       value_ref);
657         }
658     }
659     else if (pcrdParams.eGeom == PullGroupGeometry::Angle || pcrdParams.eGeom == PullGroupGeometry::AngleAxis)
660     {
661         if (value_ref < 0 || value_ref > M_PI)
662         {
663             gmx_fatal(FARGS,
664                       "Pull reference angle for coordinate %d (%f) needs to be in the allowed "
665                       "interval [0,180] deg",
666                       pcrdParams.coordIndex + 1,
667                       value_ref * pull_conversion_factor_internal2userinput(&pcrdParams));
668         }
669     }
670     else if (pcrdParams.eGeom == PullGroupGeometry::Dihedral)
671     {
672         /* Allow pulling to be periodic for dihedral angles by remapping the reference value to the interval [-pi, pi). */
673         make_periodic_2pi(&value_ref);
674     }
675
676     return value_ref;
677 }
678
679 static void updatePullCoordReferenceValue(double* referenceValue, const t_pull_coord& pcrdParams, double t)
680 {
681     GMX_ASSERT(referenceValue, "Need a valid reference value object");
682
683     /* With zero rate the reference value is set initially and doesn't change */
684     if (pcrdParams.rate != 0)
685     {
686         const double inputValue = (pcrdParams.init + pcrdParams.rate * t)
687                                   * pull_conversion_factor_userinput2internal(&pcrdParams);
688         *referenceValue = sanitizePullCoordReferenceValue(pcrdParams, inputValue);
689     }
690 }
691
692 /* Returns the dihedral angle. Updates the plane normal vectors m, n. */
693 static double get_dihedral_angle_coord(PullCoordSpatialData* spatialData)
694 {
695     double phi, sign;
696     dvec   dr32; /* store instead of dr23? */
697
698     dsvmul(-1, spatialData->dr23, dr32);
699     dcprod(spatialData->dr01, dr32, spatialData->planevec_m); /* Normal of first plane */
700     dcprod(dr32, spatialData->dr45, spatialData->planevec_n); /* Normal of second plane */
701     phi = gmx_angle_between_dvecs(spatialData->planevec_m, spatialData->planevec_n);
702
703     /* Note 1: the sign below is opposite of that in the bondeds or Bekker 1994
704      * because there r_ij = ri - rj, while here dr01 = r_1 - r_0
705      * Note 2: the angle between the plane normal vectors equals pi only when
706      * both planes coincide. Thus, when phi = pi dr01 will lie in both planes and
707      * we get a positive sign below. Thus, the range of the dihedral angle is (-180, 180].
708      */
709     sign = (diprod(spatialData->dr01, spatialData->planevec_n) < 0.0) ? 1.0 : -1.0;
710     return sign * phi;
711 }
712
713 /* Calculates pull->coord[coord_ind].value.
714  * This function also updates pull->coord[coord_ind].dr.
715  */
716 static void get_pull_coord_distance(const pull_t& pull, pull_coord_work_t* pcrd, const t_pbc* pbc)
717 {
718     get_pull_coord_dr(pull, pcrd, pbc);
719
720     PullCoordSpatialData& spatialData = pcrd->spatialData;
721
722     switch (pcrd->params.eGeom)
723     {
724         case PullGroupGeometry::Distance:
725             /* Pull along the vector between the com's */
726             spatialData.value = dnorm(spatialData.dr01);
727             break;
728         case PullGroupGeometry::Direction:
729         case PullGroupGeometry::DirectionPBC:
730         case PullGroupGeometry::DirectionRelative:
731         case PullGroupGeometry::Cylinder:
732             /* Pull along vec */
733             spatialData.value = 0;
734             for (int m = 0; m < DIM; m++)
735             {
736                 spatialData.value += spatialData.vec[m] * spatialData.dr01[m];
737             }
738             break;
739         case PullGroupGeometry::Angle:
740             spatialData.value = gmx_angle_between_dvecs(spatialData.dr01, spatialData.dr23);
741             break;
742         case PullGroupGeometry::Dihedral:
743             spatialData.value = get_dihedral_angle_coord(&spatialData);
744             break;
745         case PullGroupGeometry::AngleAxis:
746             spatialData.value = gmx_angle_between_dvecs(spatialData.dr01, spatialData.vec);
747             break;
748         default: gmx_incons("Unsupported pull type in get_pull_coord_distance");
749     }
750 }
751
752 /* Returns the deviation from the reference value.
753  * Updates pull->coord[coord_ind].dr, .value and .value_ref.
754  */
755 static double get_pull_coord_deviation(const pull_t& pull, pull_coord_work_t* pcrd, const t_pbc* pbc, double t)
756 {
757     double dev = 0;
758
759     /* Update the reference value before computing the distance,
760      * since it is used in the distance computation with periodic pulling.
761      */
762     updatePullCoordReferenceValue(&pcrd->value_ref, pcrd->params, t);
763
764     get_pull_coord_distance(pull, pcrd, pbc);
765
766     get_pull_coord_distance(pull, pcrd, pbc);
767
768     /* Determine the deviation */
769     dev = pcrd->spatialData.value - pcrd->value_ref;
770
771     /* Check that values are allowed */
772     if (pcrd->params.eGeom == PullGroupGeometry::Distance && pcrd->spatialData.value == 0)
773     {
774         /* With no vector we can not determine the direction for the force,
775          * so we set the force to zero.
776          */
777         dev = 0;
778     }
779     else if (pcrd->params.eGeom == PullGroupGeometry::Dihedral)
780     {
781         /* The reference value is in [-pi, pi). The coordinate value is in (-pi, pi].
782            Thus, the unwrapped deviation is here in (-2pi, 2pi].
783            After making it periodic, the deviation will be in [-pi, pi). */
784         make_periodic_2pi(&dev);
785     }
786
787     return dev;
788 }
789
790 double get_pull_coord_value(pull_t* pull, int coord_ind, const t_pbc* pbc)
791 {
792     get_pull_coord_distance(*pull, &pull->coord[coord_ind], pbc);
793
794     return pull->coord[coord_ind].spatialData.value;
795 }
796
797 void clear_pull_forces(pull_t* pull)
798 {
799     /* Zeroing the forces is only required for constraint pulling.
800      * It can happen that multiple constraint steps need to be applied
801      * and therefore the constraint forces need to be accumulated.
802      */
803     for (pull_coord_work_t& coord : pull->coord)
804     {
805         coord.scalarForce = 0;
806     }
807 }
808
809 /* Apply constraint using SHAKE */
810 static void do_constraint(struct pull_t*           pull,
811                           t_pbc*                   pbc,
812                           gmx::ArrayRef<gmx::RVec> x,
813                           gmx::ArrayRef<gmx::RVec> v,
814                           gmx_bool                 bMaster,
815                           tensor                   vir,
816                           double                   dt,
817                           double                   t)
818 {
819
820     dvec*    r_ij;   /* x[i] com of i in prev. step. Obeys constr. -> r_ij[i] */
821     dvec     unc_ij; /* xp[i] com of i this step, before constr.   -> unc_ij  */
822     dvec*    rnew;   /* current 'new' positions of the groups */
823     double*  dr_tot; /* the total update of the coords */
824     dvec     vec;
825     double   inpr;
826     double   lambda, rm, invdt = 0;
827     gmx_bool bConverged_all, bConverged = FALSE;
828     int      niter = 0, ii, j, m, max_iter = 100;
829     double   a;
830     dvec     tmp, tmp3;
831
832     snew(r_ij, pull->coord.size());
833     snew(dr_tot, pull->coord.size());
834
835     snew(rnew, pull->group.size());
836
837     /* copy the current unconstrained positions for use in iterations. We
838        iterate until rinew[i] and rjnew[j] obey the constraints. Then
839        rinew - pull.x_unc[i] is the correction dr to group i */
840     for (size_t g = 0; g < pull->group.size(); g++)
841     {
842         copy_dvec(pull->group[g].xp, rnew[g]);
843     }
844
845     /* Determine the constraint directions from the old positions */
846     for (size_t c = 0; c < pull->coord.size(); c++)
847     {
848         pull_coord_work_t* pcrd;
849
850         pcrd = &pull->coord[c];
851
852         if (pcrd->params.eType != PullingAlgorithm::Constraint)
853         {
854             continue;
855         }
856
857         /* Note that get_pull_coord_distance also sets pcrd->dr and pcrd->value.
858          * We don't modify dr and value anymore, so these values are also used
859          * for printing.
860          */
861         get_pull_coord_distance(*pull, pcrd, pbc);
862
863         const PullCoordSpatialData& spatialData = pcrd->spatialData;
864         if (debug)
865         {
866             fprintf(debug,
867                     "Pull coord %zu dr %f %f %f\n",
868                     c,
869                     spatialData.dr01[XX],
870                     spatialData.dr01[YY],
871                     spatialData.dr01[ZZ]);
872         }
873
874         if (pcrd->params.eGeom == PullGroupGeometry::Direction
875             || pcrd->params.eGeom == PullGroupGeometry::DirectionPBC)
876         {
877             /* Select the component along vec */
878             a = 0;
879             for (m = 0; m < DIM; m++)
880             {
881                 a += spatialData.vec[m] * spatialData.dr01[m];
882             }
883             for (m = 0; m < DIM; m++)
884             {
885                 r_ij[c][m] = a * spatialData.vec[m];
886             }
887         }
888         else
889         {
890             copy_dvec(spatialData.dr01, r_ij[c]);
891         }
892
893         if (dnorm2(r_ij[c]) == 0)
894         {
895             gmx_fatal(FARGS,
896                       "Distance for pull coordinate %zu is zero with constraint pulling, which is "
897                       "not allowed.",
898                       c + 1);
899         }
900     }
901
902     bConverged_all = FALSE;
903     while (!bConverged_all && niter < max_iter)
904     {
905         bConverged_all = TRUE;
906
907         /* loop over all constraints */
908         for (size_t c = 0; c < pull->coord.size(); c++)
909         {
910             pull_coord_work_t* pcrd;
911             pull_group_work_t *pgrp0, *pgrp1;
912             dvec               dr0, dr1;
913
914             pcrd = &pull->coord[c];
915
916             if (pcrd->params.eType != PullingAlgorithm::Constraint)
917             {
918                 continue;
919             }
920
921             updatePullCoordReferenceValue(&pcrd->value_ref, pcrd->params, t);
922
923             pgrp0 = &pull->group[pcrd->params.group[0]];
924             pgrp1 = &pull->group[pcrd->params.group[1]];
925
926             /* Get the current difference vector */
927             low_get_pull_coord_dr(
928                     *pull, pcrd, pbc, rnew[pcrd->params.group[1]], rnew[pcrd->params.group[0]], 0, 1, -1, unc_ij);
929
930             if (debug)
931             {
932                 fprintf(debug, "Pull coord %zu, iteration %d\n", c, niter);
933             }
934
935             rm = 1.0 / (pgrp0->invtm + pgrp1->invtm);
936
937             switch (pcrd->params.eGeom)
938             {
939                 case PullGroupGeometry::Distance:
940                     if (pcrd->value_ref <= 0)
941                     {
942                         gmx_fatal(
943                                 FARGS,
944                                 "The pull constraint reference distance for group %zu is <= 0 (%f)",
945                                 c,
946                                 pcrd->value_ref);
947                     }
948
949                     {
950                         double q, c_a, c_b, c_c;
951
952                         c_a = diprod(r_ij[c], r_ij[c]);
953                         c_b = diprod(unc_ij, r_ij[c]) * 2;
954                         c_c = diprod(unc_ij, unc_ij) - gmx::square(pcrd->value_ref);
955
956                         if (c_b < 0)
957                         {
958                             q      = -0.5 * (c_b - std::sqrt(c_b * c_b - 4 * c_a * c_c));
959                             lambda = -q / c_a;
960                         }
961                         else
962                         {
963                             q      = -0.5 * (c_b + std::sqrt(c_b * c_b - 4 * c_a * c_c));
964                             lambda = -c_c / q;
965                         }
966
967                         if (debug)
968                         {
969                             fprintf(debug, "Pull ax^2+bx+c=0: a=%e b=%e c=%e lambda=%e\n", c_a, c_b, c_c, lambda);
970                         }
971                     }
972
973                     /* The position corrections dr due to the constraints */
974                     dsvmul(-lambda * rm * pgrp1->invtm, r_ij[c], dr1);
975                     dsvmul(lambda * rm * pgrp0->invtm, r_ij[c], dr0);
976                     dr_tot[c] += -lambda * dnorm(r_ij[c]);
977                     break;
978                 case PullGroupGeometry::Direction:
979                 case PullGroupGeometry::DirectionPBC:
980                 case PullGroupGeometry::Cylinder:
981                     /* A 1-dimensional constraint along a vector */
982                     a = 0;
983                     for (m = 0; m < DIM; m++)
984                     {
985                         vec[m] = pcrd->spatialData.vec[m];
986                         a += unc_ij[m] * vec[m];
987                     }
988                     /* Select only the component along the vector */
989                     dsvmul(a, vec, unc_ij);
990                     lambda = a - pcrd->value_ref;
991                     if (debug)
992                     {
993                         fprintf(debug, "Pull inpr %e lambda: %e\n", a, lambda);
994                     }
995
996                     /* The position corrections dr due to the constraints */
997                     dsvmul(-lambda * rm * pgrp1->invtm, vec, dr1);
998                     dsvmul(lambda * rm * pgrp0->invtm, vec, dr0);
999                     dr_tot[c] += -lambda;
1000                     break;
1001                 default: gmx_incons("Invalid enumeration value for eGeom");
1002             }
1003
1004             /* DEBUG */
1005             if (debug)
1006             {
1007                 int g0, g1;
1008
1009                 g0 = pcrd->params.group[0];
1010                 g1 = pcrd->params.group[1];
1011                 low_get_pull_coord_dr(*pull, pcrd, pbc, rnew[g1], rnew[g0], 0, 1, -1, tmp);
1012                 low_get_pull_coord_dr(*pull, pcrd, pbc, dr1, dr0, 0, 1, -1, tmp3);
1013                 fprintf(debug,
1014                         "Pull cur %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
1015                         rnew[g0][0],
1016                         rnew[g0][1],
1017                         rnew[g0][2],
1018                         rnew[g1][0],
1019                         rnew[g1][1],
1020                         rnew[g1][2],
1021                         dnorm(tmp));
1022                 fprintf(debug,
1023                         "Pull ref %8s %8s %8s   %8s %8s %8s d: %8.5f\n",
1024                         "",
1025                         "",
1026                         "",
1027                         "",
1028                         "",
1029                         "",
1030                         pcrd->value_ref);
1031                 fprintf(debug,
1032                         "Pull cor %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
1033                         dr0[0],
1034                         dr0[1],
1035                         dr0[2],
1036                         dr1[0],
1037                         dr1[1],
1038                         dr1[2],
1039                         dnorm(tmp3));
1040             } /* END DEBUG */
1041
1042             /* Update the COMs with dr */
1043             dvec_inc(rnew[pcrd->params.group[1]], dr1);
1044             dvec_inc(rnew[pcrd->params.group[0]], dr0);
1045         }
1046
1047         /* Check if all constraints are fullfilled now */
1048         for (pull_coord_work_t& coord : pull->coord)
1049         {
1050             if (coord.params.eType != PullingAlgorithm::Constraint)
1051             {
1052                 continue;
1053             }
1054
1055             low_get_pull_coord_dr(
1056                     *pull, &coord, pbc, rnew[coord.params.group[1]], rnew[coord.params.group[0]], 0, 1, -1, unc_ij);
1057
1058             switch (coord.params.eGeom)
1059             {
1060                 case PullGroupGeometry::Distance:
1061                     bConverged = fabs(dnorm(unc_ij) - coord.value_ref) < pull->params.constr_tol;
1062                     break;
1063                 case PullGroupGeometry::Direction:
1064                 case PullGroupGeometry::DirectionPBC:
1065                 case PullGroupGeometry::Cylinder:
1066                     for (m = 0; m < DIM; m++)
1067                     {
1068                         vec[m] = coord.spatialData.vec[m];
1069                     }
1070                     inpr = diprod(unc_ij, vec);
1071                     dsvmul(inpr, vec, unc_ij);
1072                     bConverged = fabs(diprod(unc_ij, vec) - coord.value_ref) < pull->params.constr_tol;
1073                     break;
1074                 default:
1075                     GMX_ASSERT(false,
1076                                gmx::formatString("Geometry %s not handled here",
1077                                                  enumValueToString(coord.params.eGeom))
1078                                        .c_str());
1079             }
1080
1081             if (!bConverged)
1082             {
1083                 if (debug)
1084                 {
1085                     fprintf(debug,
1086                             "Pull constraint not converged: "
1087                             "groups %d %d,"
1088                             "d_ref = %f, current d = %f\n",
1089                             coord.params.group[0],
1090                             coord.params.group[1],
1091                             coord.value_ref,
1092                             dnorm(unc_ij));
1093                 }
1094
1095                 bConverged_all = FALSE;
1096             }
1097         }
1098
1099         niter++;
1100         /* if after all constraints are dealt with and bConverged is still TRUE
1101            we're finished, if not we do another iteration */
1102     }
1103     if (niter > max_iter)
1104     {
1105         gmx_fatal(FARGS, "Too many iterations for constraint run: %d", niter);
1106     }
1107
1108     /* DONE ITERATING, NOW UPDATE COORDINATES AND CALC. CONSTRAINT FORCES */
1109
1110     if (!v.empty())
1111     {
1112         invdt = 1 / dt;
1113     }
1114
1115     /* update atoms in the groups */
1116     for (size_t g = 0; g < pull->group.size(); g++)
1117     {
1118         const pull_group_work_t* pgrp;
1119         dvec                     dr;
1120
1121         pgrp = &pull->group[g];
1122
1123         /* get the final constraint displacement dr for group g */
1124         dvec_sub(rnew[g], pgrp->xp, dr);
1125
1126         if (dnorm2(dr) == 0)
1127         {
1128             /* No displacement, no update necessary */
1129             continue;
1130         }
1131
1132         /* update the atom positions */
1133         auto localAtomIndices = pgrp->atomSet.localIndex();
1134         copy_dvec(dr, tmp);
1135         for (gmx::index j = 0; j < localAtomIndices.ssize(); j++)
1136         {
1137             ii = localAtomIndices[j];
1138             if (!pgrp->localWeights.empty())
1139             {
1140                 dsvmul(pgrp->wscale * pgrp->localWeights[j], dr, tmp);
1141             }
1142             for (m = 0; m < DIM; m++)
1143             {
1144                 x[ii][m] += tmp[m];
1145             }
1146             if (!v.empty())
1147             {
1148                 for (m = 0; m < DIM; m++)
1149                 {
1150                     v[ii][m] += invdt * tmp[m];
1151                 }
1152             }
1153         }
1154     }
1155
1156     /* calculate the constraint forces, used for output and virial only */
1157     for (size_t c = 0; c < pull->coord.size(); c++)
1158     {
1159         pull_coord_work_t* pcrd;
1160
1161         pcrd = &pull->coord[c];
1162
1163         if (pcrd->params.eType != PullingAlgorithm::Constraint)
1164         {
1165             continue;
1166         }
1167
1168         /* Accumulate the forces, in case we have multiple constraint steps */
1169         double force =
1170                 dr_tot[c]
1171                 / ((pull->group[pcrd->params.group[0]].invtm + pull->group[pcrd->params.group[1]].invtm)
1172                    * dt * dt);
1173         pcrd->scalarForce += force;
1174
1175         if (vir != nullptr && pcrd->params.eGeom != PullGroupGeometry::DirectionPBC && bMaster)
1176         {
1177             double f_invr;
1178
1179             /* Add the pull contribution to the virial */
1180             /* We have already checked above that r_ij[c] != 0 */
1181             f_invr = pcrd->scalarForce / dnorm(r_ij[c]);
1182
1183             for (j = 0; j < DIM; j++)
1184             {
1185                 for (m = 0; m < DIM; m++)
1186                 {
1187                     vir[j][m] -= 0.5 * f_invr * r_ij[c][j] * r_ij[c][m];
1188                 }
1189             }
1190         }
1191     }
1192
1193     /* finished! I hope. Give back some memory */
1194     sfree(r_ij);
1195     sfree(dr_tot);
1196     sfree(rnew);
1197 }
1198
1199 static void add_virial_coord_dr(tensor vir, const dvec dr, const dvec f)
1200 {
1201     for (int j = 0; j < DIM; j++)
1202     {
1203         for (int m = 0; m < DIM; m++)
1204         {
1205             vir[j][m] -= 0.5 * f[j] * dr[m];
1206         }
1207     }
1208 }
1209
1210 /* Adds the pull contribution to the virial */
1211 static void add_virial_coord(tensor vir, const pull_coord_work_t& pcrd, const PullCoordVectorForces& forces)
1212 {
1213     if (vir != nullptr && pcrd.params.eGeom != PullGroupGeometry::DirectionPBC)
1214     {
1215         /* Add the pull contribution for each distance vector to the virial. */
1216         add_virial_coord_dr(vir, pcrd.spatialData.dr01, forces.force01);
1217         if (pcrd.params.ngroup >= 4)
1218         {
1219             add_virial_coord_dr(vir, pcrd.spatialData.dr23, forces.force23);
1220         }
1221         if (pcrd.params.ngroup >= 6)
1222         {
1223             add_virial_coord_dr(vir, pcrd.spatialData.dr45, forces.force45);
1224         }
1225     }
1226 }
1227
1228 static void calc_pull_coord_scalar_force_and_potential(pull_coord_work_t* pcrd,
1229                                                        double             dev,
1230                                                        real               lambda,
1231                                                        real*              V,
1232                                                        real*              dVdl)
1233 {
1234     real k, dkdl;
1235
1236     k    = (1.0 - lambda) * pcrd->params.k + lambda * pcrd->params.kB;
1237     dkdl = pcrd->params.kB - pcrd->params.k;
1238
1239     switch (pcrd->params.eType)
1240     {
1241         case PullingAlgorithm::Umbrella:
1242         case PullingAlgorithm::FlatBottom:
1243         case PullingAlgorithm::FlatBottomHigh:
1244             /* The only difference between an umbrella and a flat-bottom
1245              * potential is that a flat-bottom is zero above or below
1246                the reference value.
1247              */
1248             if ((pcrd->params.eType == PullingAlgorithm::FlatBottom && dev < 0)
1249                 || (pcrd->params.eType == PullingAlgorithm::FlatBottomHigh && dev > 0))
1250             {
1251                 dev = 0;
1252             }
1253
1254             pcrd->scalarForce = -k * dev;
1255             *V += 0.5 * k * gmx::square(dev);
1256             *dVdl += 0.5 * dkdl * gmx::square(dev);
1257             break;
1258         case PullingAlgorithm::ConstantForce:
1259             pcrd->scalarForce = -k;
1260             *V += k * pcrd->spatialData.value;
1261             *dVdl += dkdl * pcrd->spatialData.value;
1262             break;
1263         case PullingAlgorithm::External:
1264             gmx_incons(
1265                     "the scalar pull force should not be calculated internally for pull type "
1266                     "external");
1267         default: gmx_incons("Unsupported pull type in do_pull_pot");
1268     }
1269 }
1270
1271 static PullCoordVectorForces calculateVectorForces(const pull_coord_work_t& pcrd)
1272 {
1273     const t_pull_coord&         params      = pcrd.params;
1274     const PullCoordSpatialData& spatialData = pcrd.spatialData;
1275
1276     /* The geometry of the coordinate determines how the scalar force relates to the force on each group */
1277     PullCoordVectorForces forces;
1278
1279     if (params.eGeom == PullGroupGeometry::Distance)
1280     {
1281         double invdr01 = spatialData.value > 0 ? 1. / spatialData.value : 0.;
1282         for (int m = 0; m < DIM; m++)
1283         {
1284             forces.force01[m] = pcrd.scalarForce * spatialData.dr01[m] * invdr01;
1285         }
1286     }
1287     else if (params.eGeom == PullGroupGeometry::Angle)
1288     {
1289
1290         double cos_theta, cos_theta2;
1291
1292         cos_theta  = cos(spatialData.value);
1293         cos_theta2 = gmx::square(cos_theta);
1294
1295         /* The force at theta = 0, pi is undefined so we should not apply any force.
1296          * cos(theta) = 1 for theta = 0, pi so this is what we check for below.
1297          * Note that dr01 = 0 or dr23 = 0 evaluates to theta = 0 so we do not
1298          * have to check for this before dividing by their norm below.
1299          */
1300         if (cos_theta2 < 1)
1301         {
1302             /* The forces f01 and f23 can be decomposed into one vector parallel to dr01
1303              * and another vector parallel to dr23:
1304              * f01 = -dV/dtheta*(a*dr23/|dr23| - b*dr01*|dr01|)/|dr01|,
1305              * f23 = -dV/dtheta*(a*dr01/|dr01| - b*dr23*|dr23|)/|dr23|,
1306              */
1307             double a       = -gmx::invsqrt(1 - cos_theta2); /* comes from d/dx acos(x) */
1308             double b       = a * cos_theta;
1309             double invdr01 = 1. / dnorm(spatialData.dr01);
1310             double invdr23 = 1. / dnorm(spatialData.dr23);
1311             dvec   normalized_dr01, normalized_dr23;
1312             dsvmul(invdr01, spatialData.dr01, normalized_dr01);
1313             dsvmul(invdr23, spatialData.dr23, normalized_dr23);
1314
1315             for (int m = 0; m < DIM; m++)
1316             {
1317                 /* Here, f_scal is -dV/dtheta */
1318                 forces.force01[m] =
1319                         pcrd.scalarForce * invdr01 * (a * normalized_dr23[m] - b * normalized_dr01[m]);
1320                 forces.force23[m] =
1321                         pcrd.scalarForce * invdr23 * (a * normalized_dr01[m] - b * normalized_dr23[m]);
1322             }
1323         }
1324         else
1325         {
1326             /* No forces to apply for ill-defined cases*/
1327             clear_dvec(forces.force01);
1328             clear_dvec(forces.force23);
1329         }
1330     }
1331     else if (params.eGeom == PullGroupGeometry::AngleAxis)
1332     {
1333         double cos_theta, cos_theta2;
1334
1335         /* The angle-axis force is exactly the same as the angle force (above) except that in
1336            this case the second vector (dr23) is replaced by the pull vector. */
1337         cos_theta  = cos(spatialData.value);
1338         cos_theta2 = gmx::square(cos_theta);
1339
1340         if (cos_theta2 < 1)
1341         {
1342             double a, b;
1343             double invdr01;
1344             dvec   normalized_dr01;
1345
1346             invdr01 = 1. / dnorm(spatialData.dr01);
1347             dsvmul(invdr01, spatialData.dr01, normalized_dr01);
1348             a = -gmx::invsqrt(1 - cos_theta2); /* comes from d/dx acos(x) */
1349             b = a * cos_theta;
1350
1351             for (int m = 0; m < DIM; m++)
1352             {
1353                 forces.force01[m] =
1354                         pcrd.scalarForce * invdr01 * (a * spatialData.vec[m] - b * normalized_dr01[m]);
1355             }
1356         }
1357         else
1358         {
1359             clear_dvec(forces.force01);
1360         }
1361     }
1362     else if (params.eGeom == PullGroupGeometry::Dihedral)
1363     {
1364         double m2, n2, tol, sqrdist_32;
1365         dvec   dr32;
1366         /* Note: there is a small difference here compared to the
1367            dihedral force calculations in the bondeds (ref: Bekker 1994).
1368            There rij = ri - rj, while here dr01 = r1 - r0.
1369            However, all distance vectors occur in form of cross or inner products
1370            so that two signs cancel and we end up with the same expressions.
1371            Also, we treat the more general case of 6 groups (0..5) instead of 4 (i, j, k, l).
1372          */
1373         m2 = diprod(spatialData.planevec_m, spatialData.planevec_m);
1374         n2 = diprod(spatialData.planevec_n, spatialData.planevec_n);
1375         dsvmul(-1, spatialData.dr23, dr32);
1376         sqrdist_32 = diprod(dr32, dr32);
1377         tol        = sqrdist_32 * GMX_REAL_EPS; /* Avoid tiny angles */
1378         if ((m2 > tol) && (n2 > tol))
1379         {
1380             double a_01, a_23_01, a_23_45, a_45;
1381             double inv_dist_32, inv_sqrdist_32, dist_32;
1382             dvec   u, v;
1383             inv_dist_32    = gmx::invsqrt(sqrdist_32);
1384             inv_sqrdist_32 = inv_dist_32 * inv_dist_32;
1385             dist_32        = sqrdist_32 * inv_dist_32;
1386
1387             /* Forces on groups 0, 1 */
1388             a_01 = pcrd.scalarForce * dist_32 / m2;                /* scalarForce is -dV/dphi */
1389             dsvmul(-a_01, spatialData.planevec_m, forces.force01); /* added sign to get force on group 1, not 0 */
1390
1391             /* Forces on groups 4, 5 */
1392             a_45 = -pcrd.scalarForce * dist_32 / n2;
1393             dsvmul(a_45, spatialData.planevec_n, forces.force45); /* force on group 5 */
1394
1395             /* Force on groups 2, 3 (defining the axis) */
1396             a_23_01 = -diprod(spatialData.dr01, dr32) * inv_sqrdist_32;
1397             a_23_45 = -diprod(spatialData.dr45, dr32) * inv_sqrdist_32;
1398             dsvmul(-a_23_01, forces.force01, u); /* added sign to get force from group 0, not 1 */
1399             dsvmul(a_23_45, forces.force45, v);
1400             dvec_sub(u, v, forces.force23); /* force on group 3 */
1401         }
1402         else
1403         {
1404             /* No force to apply for ill-defined cases */
1405             clear_dvec(forces.force01);
1406             clear_dvec(forces.force23);
1407             clear_dvec(forces.force45);
1408         }
1409     }
1410     else
1411     {
1412         for (int m = 0; m < DIM; m++)
1413         {
1414             forces.force01[m] = pcrd.scalarForce * spatialData.vec[m];
1415         }
1416     }
1417
1418     return forces;
1419 }
1420
1421
1422 /* We use a global mutex for locking access to the pull data structure
1423  * during registration of external pull potential providers.
1424  * We could use a different, local mutex for each pull object, but the overhead
1425  * is extremely small here and registration is only done during initialization.
1426  */
1427 static std::mutex registrationMutex;
1428
1429 using Lock = std::lock_guard<std::mutex>;
1430
1431 void register_external_pull_potential(struct pull_t* pull, int coord_index, const char* provider)
1432 {
1433     GMX_RELEASE_ASSERT(pull != nullptr, "register_external_pull_potential called before init_pull");
1434     GMX_RELEASE_ASSERT(provider != nullptr,
1435                        "register_external_pull_potential called with NULL as provider name");
1436
1437     if (coord_index < 0 || coord_index >= gmx::ssize(pull->coord))
1438     {
1439         gmx_fatal(FARGS,
1440                   "Module '%s' attempted to register an external potential for pull coordinate %d "
1441                   "which is out of the pull coordinate range %d - %zu\n",
1442                   provider,
1443                   coord_index + 1,
1444                   1,
1445                   pull->coord.size());
1446     }
1447
1448     pull_coord_work_t* pcrd = &pull->coord[coord_index];
1449
1450     if (pcrd->params.eType != PullingAlgorithm::External)
1451     {
1452         gmx_fatal(
1453                 FARGS,
1454                 "Module '%s' attempted to register an external potential for pull coordinate %d "
1455                 "which of type '%s', whereas external potentials are only supported with type '%s'",
1456                 provider,
1457                 coord_index + 1,
1458                 enumValueToString(pcrd->params.eType),
1459                 enumValueToString(PullingAlgorithm::External));
1460     }
1461
1462     GMX_RELEASE_ASSERT(!pcrd->params.externalPotentialProvider.empty(),
1463                        "The external potential provider string for a pull coordinate is NULL");
1464
1465     if (gmx_strcasecmp(provider, pcrd->params.externalPotentialProvider.c_str()) != 0)
1466     {
1467         gmx_fatal(FARGS,
1468                   "Module '%s' attempted to register an external potential for pull coordinate %d "
1469                   "which expects the external potential to be provided by a module named '%s'",
1470                   provider,
1471                   coord_index + 1,
1472                   pcrd->params.externalPotentialProvider.c_str());
1473     }
1474
1475     /* Lock to avoid (extremely unlikely) simultaneous reading and writing of
1476      * pcrd->bExternalPotentialProviderHasBeenRegistered and
1477      * pull->numUnregisteredExternalPotentials.
1478      */
1479     Lock registrationLock(registrationMutex);
1480
1481     if (pcrd->bExternalPotentialProviderHasBeenRegistered)
1482     {
1483         gmx_fatal(FARGS,
1484                   "Module '%s' attempted to register an external potential for pull coordinate %d "
1485                   "more than once",
1486                   provider,
1487                   coord_index + 1);
1488     }
1489
1490     pcrd->bExternalPotentialProviderHasBeenRegistered = true;
1491     pull->numUnregisteredExternalPotentials--;
1492
1493     GMX_RELEASE_ASSERT(pull->numUnregisteredExternalPotentials >= 0,
1494                        "Negative unregistered potentials, the pull code is inconsistent");
1495 }
1496
1497
1498 static void check_external_potential_registration(const struct pull_t* pull)
1499 {
1500     if (pull->numUnregisteredExternalPotentials > 0)
1501     {
1502         size_t c;
1503         for (c = 0; c < pull->coord.size(); c++)
1504         {
1505             if (pull->coord[c].params.eType == PullingAlgorithm::External
1506                 && !pull->coord[c].bExternalPotentialProviderHasBeenRegistered)
1507             {
1508                 break;
1509             }
1510         }
1511
1512         GMX_RELEASE_ASSERT(c < pull->coord.size(),
1513                            "Internal inconsistency in the pull potential provider counting");
1514
1515         gmx_fatal(FARGS,
1516                   "No external provider for external pull potentials have been provided for %d "
1517                   "pull coordinates. The first coordinate without provider is number %zu, which "
1518                   "expects a module named '%s' to provide the external potential.",
1519                   pull->numUnregisteredExternalPotentials,
1520                   c + 1,
1521                   pull->coord[c].params.externalPotentialProvider.c_str());
1522     }
1523 }
1524
1525 /* Pull takes care of adding the forces of the external potential.
1526  * The external potential module  has to make sure that the corresponding
1527  * potential energy is added either to the pull term or to a term
1528  * specific to the external module.
1529  */
1530 void apply_external_pull_coord_force(struct pull_t*            pull,
1531                                      int                       coord_index,
1532                                      double                    coord_force,
1533                                      gmx::ArrayRef<const real> masses,
1534                                      gmx::ForceWithVirial*     forceWithVirial)
1535 {
1536     pull_coord_work_t* pcrd;
1537
1538     GMX_ASSERT(coord_index >= 0 && coord_index < gmx::ssize(pull->coord),
1539                "apply_external_pull_coord_force called with coord_index out of range");
1540
1541     if (pull->comm.bParticipate)
1542     {
1543         pcrd = &pull->coord[coord_index];
1544
1545         GMX_RELEASE_ASSERT(
1546                 pcrd->params.eType == PullingAlgorithm::External,
1547                 "The pull force can only be set externally on pull coordinates of external type");
1548
1549         GMX_ASSERT(pcrd->bExternalPotentialProviderHasBeenRegistered,
1550                    "apply_external_pull_coord_force called for an unregistered pull coordinate");
1551
1552         /* Set the force */
1553         pcrd->scalarForce = coord_force;
1554
1555         /* Calculate the forces on the pull groups */
1556         PullCoordVectorForces pullCoordForces = calculateVectorForces(*pcrd);
1557
1558         /* Add the forces for this coordinate to the total virial and force */
1559         if (forceWithVirial->computeVirial_ && pull->comm.isMasterRank)
1560         {
1561             matrix virial = { { 0 } };
1562             add_virial_coord(virial, *pcrd, pullCoordForces);
1563             forceWithVirial->addVirialContribution(virial);
1564         }
1565
1566         apply_forces_coord(
1567                 *pcrd, pull->group, pullCoordForces, masses, as_rvec_array(forceWithVirial->force_.data()));
1568     }
1569
1570     pull->numExternalPotentialsStillToBeAppliedThisStep--;
1571 }
1572
1573 /* Calculate the pull potential and scalar force for a pull coordinate.
1574  * Returns the vector forces for the pull coordinate.
1575  */
1576 static PullCoordVectorForces do_pull_pot_coord(const pull_t&      pull,
1577                                                pull_coord_work_t* pcrd,
1578                                                t_pbc*             pbc,
1579                                                double             t,
1580                                                real               lambda,
1581                                                real*              V,
1582                                                tensor             vir,
1583                                                real*              dVdl)
1584 {
1585     assert(pcrd->params.eType != PullingAlgorithm::Constraint);
1586
1587     double dev = get_pull_coord_deviation(pull, pcrd, pbc, t);
1588
1589     calc_pull_coord_scalar_force_and_potential(pcrd, dev, lambda, V, dVdl);
1590
1591     PullCoordVectorForces pullCoordForces = calculateVectorForces(*pcrd);
1592
1593     add_virial_coord(vir, *pcrd, pullCoordForces);
1594
1595     return pullCoordForces;
1596 }
1597
1598 real pull_potential(struct pull_t*                 pull,
1599                     gmx::ArrayRef<const real>      masses,
1600                     t_pbc*                         pbc,
1601                     const t_commrec*               cr,
1602                     double                         t,
1603                     real                           lambda,
1604                     gmx::ArrayRef<const gmx::RVec> x,
1605                     gmx::ForceWithVirial*          force,
1606                     real*                          dvdlambda)
1607 {
1608     real V = 0;
1609
1610     assert(pull != nullptr);
1611
1612     /* Ideally we should check external potential registration only during
1613      * the initialization phase, but that requires another function call
1614      * that should be done exactly in the right order. So we check here.
1615      */
1616     check_external_potential_registration(pull);
1617
1618     if (pull->comm.bParticipate)
1619     {
1620         real dVdl = 0;
1621
1622         pull_calc_coms(cr, pull, masses, pbc, t, x, {});
1623
1624         rvec*      f             = as_rvec_array(force->force_.data());
1625         matrix     virial        = { { 0 } };
1626         const bool computeVirial = (force->computeVirial_ && MASTER(cr));
1627         for (size_t c = 0; c < pull->coord.size(); c++)
1628         {
1629             pull_coord_work_t* pcrd;
1630             pcrd = &pull->coord[c];
1631
1632             /* For external potential the force is assumed to be given by an external module by a
1633                call to apply_pull_coord_external_force */
1634             if (pcrd->params.eType == PullingAlgorithm::Constraint
1635                 || pcrd->params.eType == PullingAlgorithm::External)
1636             {
1637                 continue;
1638             }
1639
1640             PullCoordVectorForces pullCoordForces = do_pull_pot_coord(
1641                     *pull, pcrd, pbc, t, lambda, &V, computeVirial ? virial : nullptr, &dVdl);
1642
1643             /* Distribute the force over the atoms in the pulled groups */
1644             apply_forces_coord(*pcrd, pull->group, pullCoordForces, masses, f);
1645         }
1646
1647         if (MASTER(cr))
1648         {
1649             force->addVirialContribution(virial);
1650             *dvdlambda += dVdl;
1651         }
1652     }
1653
1654     GMX_ASSERT(pull->numExternalPotentialsStillToBeAppliedThisStep == 0,
1655                "Too few or too many external pull potentials have been applied the previous step");
1656     /* All external pull potentials still need to be applied */
1657     pull->numExternalPotentialsStillToBeAppliedThisStep = pull->numCoordinatesWithExternalPotential;
1658
1659     return (MASTER(cr) ? V : 0.0);
1660 }
1661
1662 void pull_constraint(struct pull_t*            pull,
1663                      gmx::ArrayRef<const real> masses,
1664                      t_pbc*                    pbc,
1665                      const t_commrec*          cr,
1666                      double                    dt,
1667                      double                    t,
1668                      gmx::ArrayRef<gmx::RVec>  x,
1669                      gmx::ArrayRef<gmx::RVec>  xp,
1670                      gmx::ArrayRef<gmx::RVec>  v,
1671                      tensor                    vir)
1672 {
1673     assert(pull != nullptr);
1674
1675     if (pull->comm.bParticipate)
1676     {
1677         pull_calc_coms(cr, pull, masses, pbc, t, x, xp);
1678
1679         do_constraint(pull, pbc, xp, v, MASTER(cr), vir, dt, t);
1680     }
1681 }
1682
1683 void dd_make_local_pull_groups(const t_commrec* cr, struct pull_t* pull)
1684 {
1685     gmx_domdec_t* dd;
1686     pull_comm_t*  comm;
1687     gmx_bool      bMustParticipate;
1688
1689     dd = cr->dd;
1690
1691     comm = &pull->comm;
1692
1693     /* We always make the master node participate, such that it can do i/o,
1694      * add the virial and to simplify MC type extensions people might have.
1695      */
1696     bMustParticipate = (comm->bParticipateAll || comm->isMasterRank);
1697
1698     for (pull_group_work_t& group : pull->group)
1699     {
1700         if (!group.globalWeights.empty())
1701         {
1702             group.localWeights.resize(group.atomSet.numAtomsLocal());
1703             for (size_t i = 0; i < group.atomSet.numAtomsLocal(); ++i)
1704             {
1705                 group.localWeights[i] = group.globalWeights[group.atomSet.collectiveIndex()[i]];
1706             }
1707         }
1708
1709         GMX_ASSERT(bMustParticipate || dd != nullptr,
1710                    "Either all ranks (including this rank) participate, or we use DD and need to "
1711                    "have access to dd here");
1712
1713         /* We should participate if we have pull or pbc atoms */
1714         if (!bMustParticipate
1715             && (group.atomSet.numAtomsLocal() > 0
1716                 || (group.epgrppbc == epgrppbcREFAT && group.pbcAtomSet->numAtomsLocal() > 0)))
1717         {
1718             bMustParticipate = TRUE;
1719         }
1720     }
1721
1722     if (!comm->bParticipateAll)
1723     {
1724         /* Keep currently not required ranks in the communicator
1725          * if they needed to participate up to 20 decompositions ago.
1726          * This avoids frequent rebuilds due to atoms jumping back and forth.
1727          */
1728         const int64_t history_count = 20;
1729         gmx_bool      bWillParticipate;
1730         int           count[2];
1731
1732         /* Increase the decomposition counter for the current call */
1733         comm->setup_count++;
1734
1735         if (bMustParticipate)
1736         {
1737             comm->must_count = comm->setup_count;
1738         }
1739
1740         bWillParticipate =
1741                 bMustParticipate
1742                 || (comm->bParticipate && comm->must_count >= comm->setup_count - history_count);
1743
1744         if (debug && dd != nullptr)
1745         {
1746             fprintf(debug,
1747                     "Our DD rank (%3d) pull #atoms>0 or master: %s, will be part %s\n",
1748                     dd->rank,
1749                     gmx::boolToString(bMustParticipate),
1750                     gmx::boolToString(bWillParticipate));
1751         }
1752
1753         if (bWillParticipate)
1754         {
1755             /* Count the number of ranks that we want to have participating */
1756             count[0] = 1;
1757             /* Count the number of ranks that need to be added */
1758             count[1] = comm->bParticipate ? 0 : 1;
1759         }
1760         else
1761         {
1762             count[0] = 0;
1763             count[1] = 0;
1764         }
1765
1766         /* The cost of this global operation will be less that the cost
1767          * of the extra MPI_Comm_split calls that we can avoid.
1768          */
1769         gmx_sumi(2, count, cr);
1770
1771         /* If we are missing ranks or if we have 20% more ranks than needed
1772          * we make a new sub-communicator.
1773          */
1774         if (count[1] > 0 || 6 * count[0] < 5 * comm->nparticipate)
1775         {
1776             if (debug)
1777             {
1778                 fprintf(debug, "Creating new pull subcommunicator of size %d\n", count[0]);
1779             }
1780
1781 #if GMX_MPI
1782             if (comm->mpi_comm_com != MPI_COMM_NULL)
1783             {
1784                 MPI_Comm_free(&comm->mpi_comm_com);
1785             }
1786
1787             /* This might be an extremely expensive operation, so we try
1788              * to avoid this splitting as much as possible.
1789              */
1790             assert(dd != nullptr);
1791             MPI_Comm_split(dd->mpi_comm_all, bWillParticipate ? 0 : 1, dd->rank, &comm->mpi_comm_com);
1792 #endif
1793
1794             comm->bParticipate = bWillParticipate;
1795             comm->nparticipate = count[0];
1796
1797             /* When we use the previous COM for PBC, we need to broadcast
1798              * the previous COM to ranks that have joined the communicator.
1799              */
1800             for (pull_group_work_t& group : pull->group)
1801             {
1802                 if (group.epgrppbc == epgrppbcPREVSTEPCOM)
1803                 {
1804                     GMX_ASSERT(comm->bParticipate || !MASTER(cr),
1805                                "The master rank has to participate, as it should pass an up to "
1806                                "date prev. COM "
1807                                "to bcast here as well as to e.g. checkpointing");
1808
1809                     gmx_bcast(sizeof(dvec), group.x_prev_step, cr->mpi_comm_mygroup);
1810                 }
1811             }
1812         }
1813     }
1814
1815     /* Since the PBC of atoms might have changed, we need to update the PBC */
1816     pull->bSetPBCatoms = TRUE;
1817 }
1818
1819 static void init_pull_group_index(FILE*              fplog,
1820                                   const t_commrec*   cr,
1821                                   int                g,
1822                                   pull_group_work_t* pg,
1823                                   gmx_bool           bConstraint,
1824                                   const ivec         pulldim_con,
1825                                   const gmx_mtop_t&  mtop,
1826                                   const t_inputrec*  ir,
1827                                   real               lambda)
1828 {
1829     /* With EM and BD there are no masses in the integrator.
1830      * But we still want to have the correct mass-weighted COMs.
1831      * So we store the real masses in the weights.
1832      */
1833     const bool setWeights = (!pg->params.weight.empty() || EI_ENERGY_MINIMIZATION(ir->eI)
1834                              || ir->eI == IntegrationAlgorithm::BD);
1835
1836     /* In parallel, store we need to extract localWeights from weights at DD time */
1837     std::vector<real>& weights = ((cr && PAR(cr)) ? pg->globalWeights : pg->localWeights);
1838
1839     const SimulationGroups& groups = mtop.groups;
1840
1841     /* Count frozen dimensions and (weighted) mass */
1842     int    nfrozen = 0;
1843     double tmass   = 0;
1844     double wmass   = 0;
1845     double wwmass  = 0;
1846     int    molb    = 0;
1847     for (int i = 0; i < int(pg->params.ind.size()); i++)
1848     {
1849         int ii = pg->params.ind[i];
1850         if (bConstraint && ir->opts.nFreeze)
1851         {
1852             for (int d = 0; d < DIM; d++)
1853             {
1854                 if (pulldim_con[d] == 1
1855                     && ir->opts.nFreeze[getGroupType(groups, SimulationAtomGroupType::Freeze, ii)][d])
1856                 {
1857                     nfrozen++;
1858                 }
1859             }
1860         }
1861         const t_atom& atom = mtopGetAtomParameters(mtop, ii, &molb);
1862         real          m;
1863         if (ir->efep == FreeEnergyPerturbationType::No)
1864         {
1865             m = atom.m;
1866         }
1867         else
1868         {
1869             m = (1 - lambda) * atom.m + lambda * atom.mB;
1870         }
1871         real w;
1872         if (!pg->params.weight.empty())
1873         {
1874             w = pg->params.weight[i];
1875         }
1876         else
1877         {
1878             w = 1;
1879         }
1880         if (EI_ENERGY_MINIMIZATION(ir->eI))
1881         {
1882             /* Move the mass to the weight */
1883             w *= m;
1884             m = 1;
1885         }
1886         else if (ir->eI == IntegrationAlgorithm::BD)
1887         {
1888             real mbd;
1889             if (ir->bd_fric != 0.0F)
1890             {
1891                 mbd = ir->bd_fric * ir->delta_t;
1892             }
1893             else
1894             {
1895                 if (groups.groupNumbers[SimulationAtomGroupType::TemperatureCoupling].empty())
1896                 {
1897                     mbd = ir->delta_t / ir->opts.tau_t[0];
1898                 }
1899                 else
1900                 {
1901                     mbd = ir->delta_t
1902                           / ir->opts.tau_t[groups.groupNumbers[SimulationAtomGroupType::TemperatureCoupling][ii]];
1903                 }
1904             }
1905             w *= m / mbd;
1906             m = mbd;
1907         }
1908         if (setWeights)
1909         {
1910             weights.push_back(w);
1911         }
1912         tmass += m;
1913         wmass += m * w;
1914         wwmass += m * w * w;
1915     }
1916
1917     if (wmass == 0)
1918     {
1919         /* We can have single atom groups with zero mass with potential pulling
1920          * without cosine weighting.
1921          */
1922         if (pg->params.ind.size() == 1 && !bConstraint && pg->epgrppbc != epgrppbcCOS)
1923         {
1924             /* With one atom the mass doesn't matter */
1925             wwmass = 1;
1926         }
1927         else
1928         {
1929             gmx_fatal(FARGS,
1930                       "The total%s mass of pull group %d is zero",
1931                       !pg->params.weight.empty() ? " weighted" : "",
1932                       g);
1933         }
1934     }
1935     if (fplog)
1936     {
1937         fprintf(fplog, "Pull group %d: %5zu atoms, mass %9.3f", g, pg->params.ind.size(), tmass);
1938         if (!pg->params.weight.empty() || EI_ENERGY_MINIMIZATION(ir->eI)
1939             || ir->eI == IntegrationAlgorithm::BD)
1940         {
1941             fprintf(fplog, ", weighted mass %9.3f", wmass * wmass / wwmass);
1942         }
1943         if (pg->epgrppbc == epgrppbcCOS)
1944         {
1945             fprintf(fplog, ", cosine weighting will be used");
1946         }
1947         fprintf(fplog, "\n");
1948     }
1949
1950     if (nfrozen == 0)
1951     {
1952         /* A value != 0 signals not frozen, it is updated later */
1953         pg->invtm = -1.0;
1954     }
1955     else
1956     {
1957         int ndim = 0;
1958         for (int d = 0; d < DIM; d++)
1959         {
1960             ndim += pulldim_con[d] * pg->params.ind.size();
1961         }
1962         if (fplog && nfrozen > 0 && nfrozen < ndim)
1963         {
1964             fprintf(fplog,
1965                     "\nWARNING: In pull group %d some, but not all of the degrees of freedom\n"
1966                     "         that are subject to pulling are frozen.\n"
1967                     "         For constraint pulling the whole group will be frozen.\n\n",
1968                     g);
1969         }
1970         pg->invtm  = 0.0;
1971         pg->wscale = 1.0;
1972     }
1973 }
1974
1975 struct pull_t* init_pull(FILE*                     fplog,
1976                          const pull_params_t*      pull_params,
1977                          const t_inputrec*         ir,
1978                          const gmx_mtop_t&         mtop,
1979                          const t_commrec*          cr,
1980                          gmx::LocalAtomSetManager* atomSets,
1981                          real                      lambda)
1982 {
1983     struct pull_t* pull;
1984     pull_comm_t*   comm;
1985
1986     pull = new pull_t;
1987
1988     /* Copy the pull parameters */
1989     pull->params = *pull_params;
1990
1991     /* The gmx_omp_nthreads module might not be initialized here, so max(1,) */
1992     const int maxNumThreads = std::max(1, gmx_omp_nthreads_get(emntDefault));
1993
1994     for (int i = 0; i < pull_params->ngroup; ++i)
1995     {
1996         pull->group.emplace_back(pull_params->group[i],
1997                                  atomSets->add(pull_params->group[i].ind),
1998                                  pull_params->bSetPbcRefToPrevStepCOM,
1999                                  maxNumThreads);
2000     }
2001
2002     if (cr != nullptr && DOMAINDECOMP(cr))
2003     {
2004         /* Set up the global to local atom mapping for PBC atoms */
2005         for (pull_group_work_t& group : pull->group)
2006         {
2007             if (group.epgrppbc == epgrppbcREFAT || group.epgrppbc == epgrppbcPREVSTEPCOM)
2008             {
2009                 /* pbcAtomSet consists of a single atom */
2010                 group.pbcAtomSet = std::make_unique<gmx::LocalAtomSet>(
2011                         atomSets->add({ &group.params.pbcatom, &group.params.pbcatom + 1 }));
2012             }
2013         }
2014     }
2015
2016     pull->bPotential   = FALSE;
2017     pull->bConstraint  = FALSE;
2018     pull->bCylinder    = FALSE;
2019     pull->bAngle       = FALSE;
2020     pull->bXOutAverage = pull_params->bXOutAverage;
2021     pull->bFOutAverage = pull_params->bFOutAverage;
2022
2023     GMX_RELEASE_ASSERT(pull->group[0].params.ind.empty(),
2024                        "pull group 0 is an absolute reference group and should not contain atoms");
2025
2026     pull->numCoordinatesWithExternalPotential = 0;
2027
2028     for (int c = 0; c < pull->params.ncoord; c++)
2029     {
2030         GMX_RELEASE_ASSERT(pull_params->coord[c].coordIndex == c,
2031                            "The stored index should match the position in the vector");
2032
2033         /* Construct a pull coordinate, copying all coordinate parameters */
2034         pull->coord.emplace_back(pull_params->coord[c]);
2035
2036         pull_coord_work_t* pcrd = &pull->coord.back();
2037
2038         switch (pcrd->params.eGeom)
2039         {
2040             case PullGroupGeometry::Distance:
2041             case PullGroupGeometry::DirectionRelative: /* Direction vector is determined at each step */
2042             case PullGroupGeometry::Angle:
2043             case PullGroupGeometry::Dihedral: break;
2044             case PullGroupGeometry::Direction:
2045             case PullGroupGeometry::DirectionPBC:
2046             case PullGroupGeometry::Cylinder:
2047             case PullGroupGeometry::AngleAxis:
2048                 copy_rvec_to_dvec(pull_params->coord[c].vec, pcrd->spatialData.vec);
2049                 break;
2050             default:
2051                 /* We allow reading of newer tpx files with new pull geometries,
2052                  * but with the same tpx format, with old code. A new geometry
2053                  * only adds a new enum value, which will be out of range for
2054                  * old code. The first place we need to generate an error is
2055                  * here, since the pull code can't handle this.
2056                  * The enum can be used before arriving here only for printing
2057                  * the string corresponding to the geometry, which will result
2058                  * in printing "UNDEFINED".
2059                  */
2060                 gmx_fatal(FARGS,
2061                           "Pull geometry not supported for pull coordinate %d. The geometry enum "
2062                           "%s in the input is larger than that supported by the code (up to %d). "
2063                           "You are probably reading a tpr file generated with a newer version of "
2064                           "GROMACS with an binary from an older version of Gromacs.",
2065                           c + 1,
2066                           enumValueToString(pcrd->params.eGeom),
2067                           static_cast<int>(PullGroupGeometry::Count) - 1);
2068         }
2069
2070         if (pcrd->params.eType == PullingAlgorithm::Constraint)
2071         {
2072             /* Check restrictions of the constraint pull code */
2073             if (pcrd->params.eGeom == PullGroupGeometry::Cylinder
2074                 || pcrd->params.eGeom == PullGroupGeometry::DirectionRelative
2075                 || pcrd->params.eGeom == PullGroupGeometry::Angle
2076                 || pcrd->params.eGeom == PullGroupGeometry::Dihedral
2077                 || pcrd->params.eGeom == PullGroupGeometry::AngleAxis)
2078             {
2079                 gmx_fatal(FARGS,
2080                           "Pulling of type %s can not be combined with geometry %s. Consider using "
2081                           "pull type %s.",
2082                           enumValueToString(pcrd->params.eType),
2083                           enumValueToString(pcrd->params.eGeom),
2084                           enumValueToString(PullingAlgorithm::Umbrella));
2085             }
2086
2087             GMX_RELEASE_ASSERT(
2088                     !ir->useMts,
2089                     "Constraint pulling can not be combined with multiple time stepping");
2090
2091             pull->bConstraint = TRUE;
2092         }
2093         else
2094         {
2095             pull->bPotential = TRUE;
2096         }
2097
2098         if (pcrd->params.eGeom == PullGroupGeometry::Cylinder)
2099         {
2100             pull->bCylinder = TRUE;
2101         }
2102         else if (pcrd->params.eGeom == PullGroupGeometry::Angle
2103                  || pcrd->params.eGeom == PullGroupGeometry::Dihedral
2104                  || pcrd->params.eGeom == PullGroupGeometry::AngleAxis)
2105         {
2106             pull->bAngle = TRUE;
2107         }
2108
2109         /* We only need to calculate the plain COM of a group
2110          * when it is not only used as a cylinder group.
2111          * Also the absolute reference group 0 needs no COM computation.
2112          */
2113         for (int i = 0; i < pcrd->params.ngroup; i++)
2114         {
2115             int groupIndex = pcrd->params.group[i];
2116             if (groupIndex > 0 && !(pcrd->params.eGeom == PullGroupGeometry::Cylinder && i == 0))
2117             {
2118                 pull->group[groupIndex].needToCalcCom = true;
2119             }
2120         }
2121
2122         /* With non-zero rate the reference value is set at every step */
2123         if (pcrd->params.rate == 0)
2124         {
2125             /* Initialize the constant reference value */
2126             if (pcrd->params.eType != PullingAlgorithm::External)
2127             {
2128                 pcrd->value_ref = sanitizePullCoordReferenceValue(
2129                         pcrd->params,
2130                         pcrd->params.init * pull_conversion_factor_userinput2internal(&pcrd->params));
2131             }
2132             else
2133             {
2134                 /* With an external pull potential, the reference value loses
2135                  * it's meaning and should not be used. Setting it to zero
2136                  * makes any terms dependent on it disappear.
2137                  * The only issue this causes is that with cylinder pulling
2138                  * no atoms of the cylinder group within the cylinder radius
2139                  * should be more than half a box length away from the COM of
2140                  * the pull group along the axial direction.
2141                  */
2142                 pcrd->value_ref = 0.0;
2143             }
2144         }
2145
2146         if (pcrd->params.eType == PullingAlgorithm::External)
2147         {
2148             GMX_RELEASE_ASSERT(
2149                     pcrd->params.rate == 0,
2150                     "With an external potential, a pull coordinate should have rate = 0");
2151
2152             /* This external potential needs to be registered later */
2153             pull->numCoordinatesWithExternalPotential++;
2154         }
2155         pcrd->bExternalPotentialProviderHasBeenRegistered = false;
2156     }
2157
2158     pull->numUnregisteredExternalPotentials             = pull->numCoordinatesWithExternalPotential;
2159     pull->numExternalPotentialsStillToBeAppliedThisStep = 0;
2160
2161     pull->pbcType = ir->pbcType;
2162     switch (pull->pbcType)
2163     {
2164         case PbcType::No: pull->npbcdim = 0; break;
2165         case PbcType::XY: pull->npbcdim = 2; break;
2166         default: pull->npbcdim = 3; break;
2167     }
2168
2169     if (fplog)
2170     {
2171         gmx_bool bAbs, bCos;
2172
2173         bAbs = FALSE;
2174         for (const pull_coord_work_t& coord : pull->coord)
2175         {
2176             if (pull->group[coord.params.group[0]].params.ind.empty()
2177                 || pull->group[coord.params.group[1]].params.ind.empty())
2178             {
2179                 bAbs = TRUE;
2180             }
2181         }
2182
2183         fprintf(fplog, "\n");
2184         if (pull->bPotential)
2185         {
2186             fprintf(fplog, "Will apply potential COM pulling\n");
2187         }
2188         if (pull->bConstraint)
2189         {
2190             fprintf(fplog, "Will apply constraint COM pulling\n");
2191         }
2192         // Don't include the reference group 0 in output, so we report ngroup-1
2193         int numRealGroups = pull->group.size() - 1;
2194         GMX_RELEASE_ASSERT(numRealGroups > 0,
2195                            "The reference absolute position pull group should always be present");
2196         fprintf(fplog,
2197                 "with %zu pull coordinate%s and %d group%s\n",
2198                 pull->coord.size(),
2199                 pull->coord.size() == 1 ? "" : "s",
2200                 numRealGroups,
2201                 numRealGroups == 1 ? "" : "s");
2202         if (bAbs)
2203         {
2204             fprintf(fplog, "with an absolute reference\n");
2205         }
2206         bCos = FALSE;
2207         // Don't include the reference group 0 in loop
2208         for (size_t g = 1; g < pull->group.size(); g++)
2209         {
2210             if (pull->group[g].params.ind.size() > 1 && pull->group[g].params.pbcatom < 0)
2211             {
2212                 /* We are using cosine weighting */
2213                 fprintf(fplog, "Cosine weighting is used for group %zu\n", g);
2214                 bCos = TRUE;
2215             }
2216         }
2217         if (bCos)
2218         {
2219             please_cite(fplog, "Engin2010");
2220         }
2221     }
2222
2223     pull->bRefAt = FALSE;
2224     pull->cosdim = -1;
2225     for (size_t g = 0; g < pull->group.size(); g++)
2226     {
2227         pull_group_work_t* pgrp;
2228
2229         pgrp = &pull->group[g];
2230         if (!pgrp->params.ind.empty())
2231         {
2232             /* There is an issue when a group is used in multiple coordinates
2233              * and constraints are applied in different dimensions with atoms
2234              * frozen in some, but not all dimensions.
2235              * Since there is only one mass array per group, we can't have
2236              * frozen/non-frozen atoms for different coords at the same time.
2237              * But since this is a very exotic case, we don't check for this.
2238              * A warning is printed in init_pull_group_index.
2239              */
2240
2241             gmx_bool bConstraint;
2242             ivec     pulldim, pulldim_con;
2243
2244             /* Loop over all pull coordinates to see along which dimensions
2245              * this group is pulled and if it is involved in constraints.
2246              */
2247             bConstraint = FALSE;
2248             clear_ivec(pulldim);
2249             clear_ivec(pulldim_con);
2250             for (const pull_coord_work_t& coord : pull->coord)
2251             {
2252                 gmx_bool bGroupUsed = FALSE;
2253                 for (int gi = 0; gi < coord.params.ngroup; gi++)
2254                 {
2255                     if (coord.params.group[gi] == static_cast<int>(g))
2256                     {
2257                         bGroupUsed = TRUE;
2258                     }
2259                 }
2260
2261                 if (bGroupUsed)
2262                 {
2263                     for (int m = 0; m < DIM; m++)
2264                     {
2265                         if (coord.params.dim[m] == 1)
2266                         {
2267                             pulldim[m] = 1;
2268
2269                             if (coord.params.eType == PullingAlgorithm::Constraint)
2270                             {
2271                                 bConstraint    = TRUE;
2272                                 pulldim_con[m] = 1;
2273                             }
2274                         }
2275                     }
2276                 }
2277             }
2278
2279             /* Determine if we need to take PBC into account for calculating
2280              * the COM's of the pull groups.
2281              */
2282             switch (pgrp->epgrppbc)
2283             {
2284                 case epgrppbcREFAT: pull->bRefAt = TRUE; break;
2285                 case epgrppbcCOS:
2286                     if (!pgrp->params.weight.empty())
2287                     {
2288                         gmx_fatal(FARGS,
2289                                   "Pull groups can not have relative weights and cosine weighting "
2290                                   "at same time");
2291                     }
2292                     for (int m = 0; m < DIM; m++)
2293                     {
2294                         if (m < pull->npbcdim && pulldim[m] == 1)
2295                         {
2296                             if (pull->cosdim >= 0 && pull->cosdim != m)
2297                             {
2298                                 gmx_fatal(FARGS,
2299                                           "Can only use cosine weighting with pulling in one "
2300                                           "dimension (use mdp option pull-coord?-dim)");
2301                             }
2302                             pull->cosdim = m;
2303                         }
2304                     }
2305                     break;
2306                 case epgrppbcNONE: break;
2307             }
2308
2309             /* Set the indices */
2310             init_pull_group_index(fplog, cr, g, pgrp, bConstraint, pulldim_con, mtop, ir, lambda);
2311         }
2312         else
2313         {
2314             /* Absolute reference, set the inverse mass to zero.
2315              * This is only relevant (and used) with constraint pulling.
2316              */
2317             pgrp->invtm  = 0;
2318             pgrp->wscale = 1;
2319         }
2320     }
2321
2322     /* If we use cylinder coordinates, do some initialising for them */
2323     if (pull->bCylinder)
2324     {
2325         for (pull_coord_work_t& coord : pull->coord)
2326         {
2327             if (coord.params.eGeom == PullGroupGeometry::Cylinder)
2328             {
2329                 if (pull->group[coord.params.group[0]].params.ind.empty())
2330                 {
2331                     gmx_fatal(FARGS,
2332                               "A cylinder pull group is not supported when using absolute "
2333                               "reference!\n");
2334                 }
2335             }
2336             const auto& group0  = pull->group[coord.params.group[0]];
2337             coord.dynamicGroup0 = std::make_unique<pull_group_work_t>(
2338                     group0.params, group0.atomSet, pull->params.bSetPbcRefToPrevStepCOM, maxNumThreads);
2339         }
2340     }
2341
2342     pull->comSums.resize(maxNumThreads);
2343
2344     comm = &pull->comm;
2345
2346 #if GMX_MPI
2347     /* Use a sub-communicator when we have more than 32 ranks, but not
2348      * when we have an external pull potential, since then the external
2349      * potential provider expects each rank to have the coordinate.
2350      */
2351     comm->bParticipateAll = (cr == nullptr || !DOMAINDECOMP(cr) || cr->dd->nnodes <= 32
2352                              || pull->numCoordinatesWithExternalPotential > 0
2353                              || getenv("GMX_PULL_PARTICIPATE_ALL") != nullptr);
2354     /* This sub-commicator is not used with comm->bParticipateAll,
2355      * so we can always initialize it to NULL.
2356      */
2357     comm->mpi_comm_com = MPI_COMM_NULL;
2358     comm->nparticipate = 0;
2359     comm->isMasterRank = (cr == nullptr || MASTER(cr));
2360 #else
2361     /* No MPI: 1 rank: all ranks pull */
2362     comm->bParticipateAll = TRUE;
2363     comm->isMasterRank    = true;
2364 #endif
2365     comm->bParticipate = comm->bParticipateAll;
2366     comm->setup_count  = 0;
2367     comm->must_count   = 0;
2368
2369     if (!comm->bParticipateAll && fplog != nullptr)
2370     {
2371         fprintf(fplog, "Will use a sub-communicator for pull communication\n");
2372     }
2373
2374     comm->pbcAtomBuffer.resize(pull->group.size());
2375     comm->comBuffer.resize(pull->group.size() * c_comBufferStride);
2376     if (pull->bCylinder)
2377     {
2378         comm->cylinderBuffer.resize(pull->coord.size() * c_cylinderBufferStride);
2379     }
2380
2381     /* We still need to initialize the PBC reference coordinates */
2382     pull->bSetPBCatoms = TRUE;
2383
2384     pull->out_x = nullptr;
2385     pull->out_f = nullptr;
2386
2387     return pull;
2388 }
2389
2390 static void destroy_pull(struct pull_t* pull)
2391 {
2392 #if GMX_MPI
2393     if (pull->comm.mpi_comm_com != MPI_COMM_NULL)
2394     {
2395         MPI_Comm_free(&pull->comm.mpi_comm_com);
2396     }
2397 #endif
2398
2399     delete pull;
2400 }
2401
2402 void preparePrevStepPullCom(const t_inputrec*         ir,
2403                             pull_t*                   pull_work,
2404                             gmx::ArrayRef<const real> masses,
2405                             t_state*                  state,
2406                             const t_state*            state_global,
2407                             const t_commrec*          cr,
2408                             bool                      startingFromCheckpoint)
2409 {
2410     if (!ir->pull || !ir->pull->bSetPbcRefToPrevStepCOM)
2411     {
2412         return;
2413     }
2414     allocStatePrevStepPullCom(state, pull_work);
2415     if (startingFromCheckpoint)
2416     {
2417         if (MASTER(cr))
2418         {
2419             state->pull_com_prev_step = state_global->pull_com_prev_step;
2420         }
2421         if (PAR(cr))
2422         {
2423             /* Only the master rank has the checkpointed COM from the previous step */
2424             gmx_bcast(sizeof(double) * state->pull_com_prev_step.size(),
2425                       &state->pull_com_prev_step[0],
2426                       cr->mpi_comm_mygroup);
2427         }
2428         setPrevStepPullComFromState(pull_work, state);
2429     }
2430     else
2431     {
2432         t_pbc pbc;
2433         set_pbc(&pbc, ir->pbcType, state->box);
2434         initPullComFromPrevStep(
2435                 cr, pull_work, masses, &pbc, state->x.arrayRefWithPadding().unpaddedArrayRef());
2436         updatePrevStepPullCom(pull_work, state);
2437     }
2438 }
2439
2440 void finish_pull(struct pull_t* pull)
2441 {
2442     check_external_potential_registration(pull);
2443
2444     if (pull->out_x)
2445     {
2446         gmx_fio_fclose(pull->out_x);
2447     }
2448     if (pull->out_f)
2449     {
2450         gmx_fio_fclose(pull->out_f);
2451     }
2452
2453     destroy_pull(pull);
2454 }
2455
2456 bool pull_have_potential(const pull_t& pull)
2457 {
2458     return pull.bPotential;
2459 }
2460
2461 bool pull_have_constraint(const pull_t& pull)
2462 {
2463     return pull.bConstraint;
2464 }
2465
2466 bool pull_have_constraint(const pull_params_t& pullParameters)
2467 {
2468     for (int c = 0; c < pullParameters.ncoord; c++)
2469     {
2470         if (pullParameters.coord[c].eType == PullingAlgorithm::Constraint)
2471         {
2472             return true;
2473         }
2474     }
2475     return false;
2476 }