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