clang-tidy modernize
[alexxy/gromacs.git] / src / gromacs / pulling / pullutil.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,2018, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #include "gmxpre.h"
38
39 #include "config.h"
40
41 #include <cassert>
42 #include <cstdlib>
43
44 #include "gromacs/fileio/confio.h"
45 #include "gromacs/gmxlib/network.h"
46 #include "gromacs/math/functions.h"
47 #include "gromacs/math/utilities.h"
48 #include "gromacs/math/vec.h"
49 #include "gromacs/mdtypes/commrec.h"
50 #include "gromacs/mdtypes/md_enums.h"
51 #include "gromacs/mdtypes/mdatom.h"
52 #include "gromacs/pbcutil/pbc.h"
53 #include "gromacs/pulling/pull.h"
54 #include "gromacs/utility/fatalerror.h"
55 #include "gromacs/utility/futil.h"
56 #include "gromacs/utility/gmxassert.h"
57 #include "gromacs/utility/real.h"
58 #include "gromacs/utility/smalloc.h"
59
60 #include "pull_internal.h"
61
62 #if GMX_MPI
63
64 // Helper function to deduce MPI datatype from the type of data
65 gmx_unused static MPI_Datatype mpiDatatype(const float gmx_unused *data)
66 {
67     return MPI_FLOAT;
68 }
69
70 // Helper function to deduce MPI datatype from the type of data
71 gmx_unused static MPI_Datatype mpiDatatype(const double gmx_unused *data)
72 {
73     return MPI_DOUBLE;
74 }
75
76 #endif // GMX_MPI
77
78 #if !GMX_DOUBLE
79 // Helper function; note that gmx_sum(d) should actually be templated
80 gmx_unused static void gmxAllReduce(int n, real *data, const t_commrec *cr)
81 {
82     gmx_sum(n, data, cr);
83 }
84 #endif
85
86 // Helper function; note that gmx_sum(d) should actually be templated
87 gmx_unused static void gmxAllReduce(int n, double *data, const t_commrec *cr)
88 {
89     gmx_sumd(n, data, cr);
90 }
91
92 // Reduce data of n elements over all ranks currently participating in pull
93 template <typename T>
94 static void pullAllReduce(const t_commrec *cr,
95                           pull_comm_t     *comm,
96                           int              n,
97                           T               *data)
98 {
99     if (cr != nullptr && PAR(cr))
100     {
101         if (comm->bParticipateAll)
102         {
103             /* Sum the contributions over all DD ranks */
104             gmxAllReduce(n, data, cr);
105         }
106         else
107         {
108             /* Separate branch because gmx_sum uses cr->mpi_comm_mygroup */
109 #if GMX_MPI
110 #if MPI_IN_PLACE_EXISTS
111             MPI_Allreduce(MPI_IN_PLACE, data, n, mpiDatatype(data), MPI_SUM,
112                           comm->mpi_comm_com);
113 #else
114             std::vector<T> buf(n);
115
116             MPI_Allreduce(data, buf, n, mpiDatatype(data), MPI_SUM,
117                           comm->mpi_comm_com);
118
119             /* Copy the result from the buffer to the input/output data */
120             for (int i = 0; i < n; i++)
121             {
122                 data[i] = buf[i];
123             }
124 #endif
125 #else
126             gmx_incons("comm->bParticipateAll=FALSE without GMX_MPI");
127 #endif
128         }
129     }
130 }
131
132 /* Copies the coordinates of the PBC atom of pgrp to x_pbc.
133  * When those coordinates are not available on this rank, clears x_pbc.
134  */
135 static void setPbcAtomCoords(const pull_group_work_t &pgrp,
136                              const rvec              *x,
137                              rvec                     x_pbc)
138 {
139     if (pgrp.pbcAtomSet != nullptr)
140     {
141         if (pgrp.pbcAtomSet->numAtomsLocal() > 0)
142         {
143             /* We have the atom locally, copy its coordinates */
144             copy_rvec(x[pgrp.pbcAtomSet->localIndex()[0]], x_pbc);
145         }
146         else
147         {
148             /* Another rank has it, clear the coordinates for MPI_Allreduce */
149             clear_rvec(x_pbc);
150         }
151     }
152     else
153     {
154         copy_rvec(x[pgrp.params.pbcatom], x_pbc);
155     }
156 }
157
158 static void pull_set_pbcatoms(const t_commrec *cr, struct pull_t *pull,
159                               const rvec *x,
160                               rvec *x_pbc)
161 {
162     int numPbcAtoms = 0;
163     for (size_t g = 0; g < pull->group.size(); g++)
164     {
165         const pull_group_work_t &group = pull->group[g];
166         if (group.needToCalcCom && group.epgrppbc == epgrppbcREFAT)
167         {
168             setPbcAtomCoords(pull->group[g], x, x_pbc[g]);
169             numPbcAtoms++;
170         }
171         else
172         {
173             clear_rvec(x_pbc[g]);
174         }
175     }
176
177     if (cr && PAR(cr) && numPbcAtoms > 0)
178     {
179         /* Sum over participating ranks to get x_pbc from the home ranks.
180          * This can be very expensive at high parallelization, so we only
181          * do this after each DD repartitioning.
182          */
183         pullAllReduce(cr, &pull->comm, pull->group.size()*DIM, x_pbc[0]);
184     }
185 }
186
187 static void make_cyl_refgrps(const t_commrec *cr,
188                              pull_t          *pull,
189                              const t_mdatoms *md,
190                              t_pbc           *pbc,
191                              double           t,
192                              const rvec      *x)
193 {
194     /* The size and stride per coord for the reduction buffer */
195     const int       stride = 9;
196     double          inv_cyl_r2;
197     pull_comm_t    *comm;
198
199     comm = &pull->comm;
200
201     if (comm->dbuf_cyl == nullptr)
202     {
203         snew(comm->dbuf_cyl, pull->coord.size()*stride);
204     }
205
206     inv_cyl_r2 = 1.0/gmx::square(pull->params.cylinder_r);
207
208     /* loop over all groups to make a reference group for each*/
209     for (size_t c = 0; c < pull->coord.size(); c++)
210     {
211         pull_coord_work_t *pcrd;
212         double             sum_a, wmass, wwmass;
213         dvec               radf_fac0, radf_fac1;
214
215         pcrd   = &pull->coord[c];
216
217         sum_a  = 0;
218         wmass  = 0;
219         wwmass = 0;
220         clear_dvec(radf_fac0);
221         clear_dvec(radf_fac1);
222
223         if (pcrd->params.eGeom == epullgCYL)
224         {
225             /* pref will be the same group for all pull coordinates */
226             const pull_group_work_t &pref  = pull->group[pcrd->params.group[0]];
227             const pull_group_work_t &pgrp  = pull->group[pcrd->params.group[1]];
228             pull_group_work_t       &pdyna = pull->dyna[c];
229             rvec                     direction;
230             copy_dvec_to_rvec(pcrd->spatialData.vec, direction);
231
232             /* Since we have not calculated the COM of the cylinder group yet,
233              * we calculate distances with respect to location of the pull
234              * group minus the reference position along the vector.
235              * here we already have the COM of the pull group. This resolves
236              * any PBC issues and we don't need to use a PBC-atom here.
237              */
238             if (pcrd->params.rate != 0)
239             {
240                 /* With rate=0, value_ref is set initially */
241                 pcrd->value_ref = pcrd->params.init + pcrd->params.rate*t;
242             }
243             rvec reference;
244             for (int m = 0; m < DIM; m++)
245             {
246                 reference[m] = pgrp.x[m] - pcrd->spatialData.vec[m]*pcrd->value_ref;
247             }
248
249             auto localAtomIndices = pref.atomSet.localIndex();
250
251             /* This actually only needs to be done at init or DD time,
252              * but resizing with the same size does not cause much overhead.
253              */
254             pdyna.localWeights.resize(localAtomIndices.size());
255             pdyna.mdw.resize(localAtomIndices.size());
256             pdyna.dv.resize(localAtomIndices.size());
257
258             /* loop over all atoms in the main ref group */
259             for (gmx::index indexInSet = 0; indexInSet < localAtomIndices.size(); indexInSet++)
260             {
261                 int    atomIndex = localAtomIndices[indexInSet];
262                 rvec   dx;
263                 pbc_dx_aiuc(pbc, x[atomIndex], reference, dx);
264                 double axialLocation = iprod(direction, dx);
265                 dvec   radialLocation;
266                 double dr2 = 0;
267                 for (int m = 0; m < DIM; m++)
268                 {
269                     /* Determine the radial components */
270                     radialLocation[m]  = dx[m] - axialLocation*direction[m];
271                     dr2               += gmx::square(radialLocation[m]);
272                 }
273                 double dr2_rel = dr2*inv_cyl_r2;
274
275                 if (dr2_rel < 1)
276                 {
277                     /* add atom to sum of COM and to weight array */
278
279                     double mass                     = md->massT[atomIndex];
280                     /* The radial weight function is 1-2x^2+x^4,
281                      * where x=r/cylinder_r. Since this function depends
282                      * on the radial component, we also get radial forces
283                      * on both groups.
284                      */
285                     double weight                   =  1 + (-2 + dr2_rel)*dr2_rel;
286                     double dweight_r                = (-4 + 4*dr2_rel)*inv_cyl_r2;
287                     pdyna.localWeights[indexInSet]  = weight;
288                     sum_a                          += mass*weight*axialLocation;
289                     wmass                          += mass*weight;
290                     wwmass                         += mass*weight*weight;
291                     dvec mdw;
292                     dsvmul(mass*dweight_r, radialLocation, mdw);
293                     copy_dvec(mdw, pdyna.mdw[indexInSet]);
294                     /* Currently we only have the axial component of the
295                      * offset from the cylinder COM up to an unkown offset.
296                      * We add this offset after the reduction needed
297                      * for determining the COM of the cylinder group.
298                      */
299                     pdyna.dv[indexInSet] = axialLocation;
300                     for (int m = 0; m < DIM; m++)
301                     {
302                         radf_fac0[m] += mdw[m];
303                         radf_fac1[m] += mdw[m]*axialLocation;
304                     }
305                 }
306                 else
307                 {
308                     pdyna.localWeights[indexInSet] = 0;
309                 }
310             }
311         }
312         comm->dbuf_cyl[c*stride+0] = wmass;
313         comm->dbuf_cyl[c*stride+1] = wwmass;
314         comm->dbuf_cyl[c*stride+2] = sum_a;
315         comm->dbuf_cyl[c*stride+3] = radf_fac0[XX];
316         comm->dbuf_cyl[c*stride+4] = radf_fac0[YY];
317         comm->dbuf_cyl[c*stride+5] = radf_fac0[ZZ];
318         comm->dbuf_cyl[c*stride+6] = radf_fac1[XX];
319         comm->dbuf_cyl[c*stride+7] = radf_fac1[YY];
320         comm->dbuf_cyl[c*stride+8] = radf_fac1[ZZ];
321     }
322
323     if (cr != nullptr && PAR(cr))
324     {
325         /* Sum the contributions over the ranks */
326         pullAllReduce(cr, comm, pull->coord.size()*stride, comm->dbuf_cyl);
327     }
328
329     for (size_t c = 0; c < pull->coord.size(); c++)
330     {
331         pull_coord_work_t *pcrd;
332
333         pcrd  = &pull->coord[c];
334
335         if (pcrd->params.eGeom == epullgCYL)
336         {
337             pull_group_work_t      *pdyna       = &pull->dyna[c];
338             pull_group_work_t      *pgrp        = &pull->group[pcrd->params.group[1]];
339             PullCoordSpatialData   &spatialData = pcrd->spatialData;
340
341             double                  wmass       = comm->dbuf_cyl[c*stride+0];
342             double                  wwmass      = comm->dbuf_cyl[c*stride+1];
343             pdyna->mwscale                    = 1.0/wmass;
344             /* Cylinder pulling can't be used with constraints, but we set
345              * wscale and invtm anyhow, in case someone would like to use them.
346              */
347             pdyna->wscale  = wmass/wwmass;
348             pdyna->invtm   = wwmass/(wmass*wmass);
349
350             /* We store the deviation of the COM from the reference location
351              * used above, since we need it when we apply the radial forces
352              * to the atoms in the cylinder group.
353              */
354             spatialData.cyl_dev = 0;
355             for (int m = 0; m < DIM; m++)
356             {
357                 double reference     = pgrp->x[m] - spatialData.vec[m]*pcrd->value_ref;
358                 double dist          = -spatialData.vec[m]*comm->dbuf_cyl[c*stride+2]*pdyna->mwscale;
359                 pdyna->x[m]          = reference - dist;
360                 spatialData.cyl_dev += dist;
361             }
362             /* Now we know the exact COM of the cylinder reference group,
363              * we can determine the radial force factor (ffrad) that when
364              * multiplied with the axial pull force will give the radial
365              * force on the pulled (non-cylinder) group.
366              */
367             for (int m = 0; m < DIM; m++)
368             {
369                 spatialData.ffrad[m] = (comm->dbuf_cyl[c*stride+6+m] +
370                                         comm->dbuf_cyl[c*stride+3+m]*spatialData.cyl_dev)/wmass;
371             }
372
373             if (debug)
374             {
375                 fprintf(debug, "Pull cylinder group %zu:%8.3f%8.3f%8.3f m:%8.3f\n",
376                         c, pdyna->x[0], pdyna->x[1],
377                         pdyna->x[2], 1.0/pdyna->invtm);
378                 fprintf(debug, "ffrad %8.3f %8.3f %8.3f\n",
379                         spatialData.ffrad[XX], spatialData.ffrad[YY], spatialData.ffrad[ZZ]);
380             }
381         }
382     }
383 }
384
385 static double atan2_0_2pi(double y, double x)
386 {
387     double a;
388
389     a = atan2(y, x);
390     if (a < 0)
391     {
392         a += 2.0*M_PI;
393     }
394     return a;
395 }
396
397 static void sum_com_part(const pull_group_work_t *pgrp,
398                          int ind_start, int ind_end,
399                          const rvec *x, const rvec *xp,
400                          const real *mass,
401                          const t_pbc *pbc,
402                          const rvec x_pbc,
403                          pull_sum_com_t *sum_com)
404 {
405     double sum_wm   = 0;
406     double sum_wwm  = 0;
407     dvec   sum_wmx  = { 0, 0, 0 };
408     dvec   sum_wmxp = { 0, 0, 0 };
409
410     auto   localAtomIndices = pgrp->atomSet.localIndex();
411     for (int i = ind_start; i < ind_end; i++)
412     {
413         int  ii = localAtomIndices[i];
414         real wm;
415         if (pgrp->localWeights.empty())
416         {
417             wm      = mass[ii];
418             sum_wm += wm;
419         }
420         else
421         {
422             real w;
423
424             w        = pgrp->localWeights[i];
425             wm       = w*mass[ii];
426             sum_wm  += wm;
427             sum_wwm += wm*w;
428         }
429         if (pgrp->epgrppbc == epgrppbcNONE)
430         {
431             /* Plain COM: sum the coordinates */
432             for (int d = 0; d < DIM; d++)
433             {
434                 sum_wmx[d]      += wm*x[ii][d];
435             }
436             if (xp)
437             {
438                 for (int d = 0; d < DIM; d++)
439                 {
440                     sum_wmxp[d] += wm*xp[ii][d];
441                 }
442             }
443         }
444         else
445         {
446             rvec dx;
447
448             /* Sum the difference with the reference atom */
449             pbc_dx(pbc, x[ii], x_pbc, dx);
450             for (int d = 0; d < DIM; d++)
451             {
452                 sum_wmx[d]     += wm*dx[d];
453             }
454             if (xp)
455             {
456                 /* For xp add the difference between xp and x to dx,
457                  * such that we use the same periodic image,
458                  * also when xp has a large displacement.
459                  */
460                 for (int d = 0; d < DIM; d++)
461                 {
462                     sum_wmxp[d] += wm*(dx[d] + xp[ii][d] - x[ii][d]);
463                 }
464             }
465         }
466     }
467
468     sum_com->sum_wm  = sum_wm;
469     sum_com->sum_wwm = sum_wwm;
470     copy_dvec(sum_wmx, sum_com->sum_wmx);
471     if (xp)
472     {
473         copy_dvec(sum_wmxp, sum_com->sum_wmxp);
474     }
475 }
476
477 static void sum_com_part_cosweight(const pull_group_work_t *pgrp,
478                                    int ind_start, int ind_end,
479                                    int cosdim, real twopi_box,
480                                    const rvec *x, const rvec *xp,
481                                    const real *mass,
482                                    pull_sum_com_t *sum_com)
483 {
484     /* Cosine weighting geometry */
485     double sum_cm  = 0;
486     double sum_sm  = 0;
487     double sum_ccm = 0;
488     double sum_csm = 0;
489     double sum_ssm = 0;
490     double sum_cmp = 0;
491     double sum_smp = 0;
492
493     auto   localAtomIndices = pgrp->atomSet.localIndex();
494
495     for (int i = ind_start; i < ind_end; i++)
496     {
497         int  ii  = localAtomIndices[i];
498         real m   = mass[ii];
499         /* Determine cos and sin sums */
500         real cw  = std::cos(x[ii][cosdim]*twopi_box);
501         real sw  = std::sin(x[ii][cosdim]*twopi_box);
502         sum_cm  += static_cast<double>(cw*m);
503         sum_sm  += static_cast<double>(sw*m);
504         sum_ccm += static_cast<double>(cw*cw*m);
505         sum_csm += static_cast<double>(cw*sw*m);
506         sum_ssm += static_cast<double>(sw*sw*m);
507
508         if (xp != nullptr)
509         {
510             real cw  = std::cos(xp[ii][cosdim]*twopi_box);
511             real sw  = std::sin(xp[ii][cosdim]*twopi_box);
512             sum_cmp += static_cast<double>(cw*m);
513             sum_smp += static_cast<double>(sw*m);
514         }
515     }
516
517     sum_com->sum_cm  = sum_cm;
518     sum_com->sum_sm  = sum_sm;
519     sum_com->sum_ccm = sum_ccm;
520     sum_com->sum_csm = sum_csm;
521     sum_com->sum_ssm = sum_ssm;
522     sum_com->sum_cmp = sum_cmp;
523     sum_com->sum_smp = sum_smp;
524 }
525
526 /* calculates center of mass of selection index from all coordinates x */
527 void pull_calc_coms(const t_commrec *cr,
528                     pull_t *pull,
529                     const t_mdatoms *md,
530                     t_pbc *pbc,
531                     double t,
532                     const rvec x[], rvec *xp)
533 {
534     real         twopi_box = 0;
535     pull_comm_t *comm;
536
537     comm = &pull->comm;
538
539     if (comm->rbuf == nullptr)
540     {
541         snew(comm->rbuf, pull->group.size());
542     }
543     if (comm->dbuf == nullptr)
544     {
545         snew(comm->dbuf, pull->group.size()*DIM);
546     }
547
548     if (pull->bRefAt && pull->bSetPBCatoms)
549     {
550         pull_set_pbcatoms(cr, pull, x, comm->rbuf);
551
552         if (cr != nullptr && DOMAINDECOMP(cr))
553         {
554             /* We can keep these PBC reference coordinates fixed for nstlist
555              * steps, since atoms won't jump over PBC.
556              * This avoids a global reduction at the next nstlist-1 steps.
557              * Note that the exact values of the pbc reference coordinates
558              * are irrelevant, as long all atoms in the group are within
559              * half a box distance of the reference coordinate.
560              */
561             pull->bSetPBCatoms = FALSE;
562         }
563     }
564
565     if (pull->cosdim >= 0)
566     {
567         int m;
568
569         assert(pull->npbcdim <= DIM);
570
571         for (m = pull->cosdim+1; m < pull->npbcdim; m++)
572         {
573             if (pbc->box[m][pull->cosdim] != 0)
574             {
575                 gmx_fatal(FARGS, "Can not do cosine weighting for trilinic dimensions");
576             }
577         }
578         twopi_box = 2.0*M_PI/pbc->box[pull->cosdim][pull->cosdim];
579     }
580
581     for (size_t g = 0; g < pull->group.size(); g++)
582     {
583         pull_group_work_t *pgrp;
584
585         pgrp = &pull->group[g];
586
587         if (pgrp->needToCalcCom)
588         {
589             if (pgrp->epgrppbc != epgrppbcCOS)
590             {
591                 rvec   x_pbc = { 0, 0, 0 };
592
593                 if (pgrp->epgrppbc == epgrppbcREFAT)
594                 {
595                     /* Set the pbc atom */
596                     copy_rvec(comm->rbuf[g], x_pbc);
597                 }
598
599                 /* The final sums should end up in sum_com[0] */
600                 pull_sum_com_t *sum_com = &pull->sum_com[0];
601
602                 /* If we have a single-atom group the mass is irrelevant, so
603                  * we can remove the mass factor to avoid division by zero.
604                  * Note that with constraint pulling the mass does matter, but
605                  * in that case a check group mass != 0 has been done before.
606                  */
607                 if (pgrp->params.nat == 1 &&
608                     pgrp->atomSet.numAtomsLocal() == 1 &&
609                     md->massT[pgrp->atomSet.localIndex()[0]] == 0)
610                 {
611                     GMX_ASSERT(xp == nullptr, "We should not have groups with zero mass with constraints, i.e. xp!=NULL");
612
613                     /* Copy the single atom coordinate */
614                     for (int d = 0; d < DIM; d++)
615                     {
616                         sum_com->sum_wmx[d] = x[pgrp->atomSet.localIndex()[0]][d];
617                     }
618                     /* Set all mass factors to 1 to get the correct COM */
619                     sum_com->sum_wm  = 1;
620                     sum_com->sum_wwm = 1;
621                 }
622                 else if (pgrp->atomSet.numAtomsLocal() <= c_pullMaxNumLocalAtomsSingleThreaded)
623                 {
624                     sum_com_part(pgrp, 0, pgrp->atomSet.numAtomsLocal(),
625                                  x, xp, md->massT,
626                                  pbc, x_pbc,
627                                  sum_com);
628                 }
629                 else
630                 {
631 #pragma omp parallel for num_threads(pull->nthreads) schedule(static)
632                     for (int t = 0; t < pull->nthreads; t++)
633                     {
634                         int ind_start = (pgrp->atomSet.numAtomsLocal()*(t + 0))/pull->nthreads;
635                         int ind_end   = (pgrp->atomSet.numAtomsLocal()*(t + 1))/pull->nthreads;
636                         sum_com_part(pgrp, ind_start, ind_end,
637                                      x, xp, md->massT,
638                                      pbc, x_pbc,
639                                      &pull->sum_com[t]);
640                     }
641
642                     /* Reduce the thread contributions to sum_com[0] */
643                     for (int t = 1; t < pull->nthreads; t++)
644                     {
645                         sum_com->sum_wm  += pull->sum_com[t].sum_wm;
646                         sum_com->sum_wwm += pull->sum_com[t].sum_wwm;
647                         dvec_inc(sum_com->sum_wmx, pull->sum_com[t].sum_wmx);
648                         dvec_inc(sum_com->sum_wmxp, pull->sum_com[t].sum_wmxp);
649                     }
650                 }
651
652                 if (pgrp->localWeights.empty())
653                 {
654                     sum_com->sum_wwm = sum_com->sum_wm;
655                 }
656
657                 /* Copy local sums to a buffer for global summing */
658                 copy_dvec(sum_com->sum_wmx,  comm->dbuf[g*3]);
659                 copy_dvec(sum_com->sum_wmxp, comm->dbuf[g*3 + 1]);
660                 comm->dbuf[g*3 + 2][0] = sum_com->sum_wm;
661                 comm->dbuf[g*3 + 2][1] = sum_com->sum_wwm;
662                 comm->dbuf[g*3 + 2][2] = 0;
663             }
664             else
665             {
666                 /* Cosine weighting geometry.
667                  * This uses a slab of the system, thus we always have many
668                  * atoms in the pull groups. Therefore, always use threads.
669                  */
670 #pragma omp parallel for num_threads(pull->nthreads) schedule(static)
671                 for (int t = 0; t < pull->nthreads; t++)
672                 {
673                     int ind_start = (pgrp->atomSet.numAtomsLocal()*(t + 0))/pull->nthreads;
674                     int ind_end   = (pgrp->atomSet.numAtomsLocal()*(t + 1))/pull->nthreads;
675                     sum_com_part_cosweight(pgrp, ind_start, ind_end,
676                                            pull->cosdim, twopi_box,
677                                            x, xp, md->massT,
678                                            &pull->sum_com[t]);
679                 }
680
681                 /* Reduce the thread contributions to sum_com[0] */
682                 pull_sum_com_t *sum_com = &pull->sum_com[0];
683                 for (int t = 1; t < pull->nthreads; t++)
684                 {
685                     sum_com->sum_cm  += pull->sum_com[t].sum_cm;
686                     sum_com->sum_sm  += pull->sum_com[t].sum_sm;
687                     sum_com->sum_ccm += pull->sum_com[t].sum_ccm;
688                     sum_com->sum_csm += pull->sum_com[t].sum_csm;
689                     sum_com->sum_ssm += pull->sum_com[t].sum_ssm;
690                     sum_com->sum_cmp += pull->sum_com[t].sum_cmp;
691                     sum_com->sum_smp += pull->sum_com[t].sum_smp;
692                 }
693
694                 /* Copy local sums to a buffer for global summing */
695                 comm->dbuf[g*3    ][0] = sum_com->sum_cm;
696                 comm->dbuf[g*3    ][1] = sum_com->sum_sm;
697                 comm->dbuf[g*3    ][2] = 0;
698                 comm->dbuf[g*3 + 1][0] = sum_com->sum_ccm;
699                 comm->dbuf[g*3 + 1][1] = sum_com->sum_csm;
700                 comm->dbuf[g*3 + 1][2] = sum_com->sum_ssm;
701                 comm->dbuf[g*3 + 2][0] = sum_com->sum_cmp;
702                 comm->dbuf[g*3 + 2][1] = sum_com->sum_smp;
703                 comm->dbuf[g*3 + 2][2] = 0;
704             }
705         }
706     }
707
708     pullAllReduce(cr, comm, pull->group.size()*3*DIM, comm->dbuf[0]);
709
710     for (size_t g = 0; g < pull->group.size(); g++)
711     {
712         pull_group_work_t *pgrp;
713
714         pgrp = &pull->group[g];
715         if (pgrp->needToCalcCom)
716         {
717             GMX_ASSERT(pgrp->params.nat > 0, "Normal pull groups should have atoms, only group 0, which should have bCalcCom=FALSE has nat=0");
718
719             if (pgrp->epgrppbc != epgrppbcCOS)
720             {
721                 double wmass, wwmass;
722                 int    m;
723
724                 /* Determine the inverse mass */
725                 wmass             = comm->dbuf[g*3+2][0];
726                 wwmass            = comm->dbuf[g*3+2][1];
727                 pgrp->mwscale     = 1.0/wmass;
728                 /* invtm==0 signals a frozen group, so then we should keep it zero */
729                 if (pgrp->invtm != 0)
730                 {
731                     pgrp->wscale  = wmass/wwmass;
732                     pgrp->invtm   = wwmass/(wmass*wmass);
733                 }
734                 /* Divide by the total mass */
735                 for (m = 0; m < DIM; m++)
736                 {
737                     pgrp->x[m]      = comm->dbuf[g*3  ][m]*pgrp->mwscale;
738                     if (xp)
739                     {
740                         pgrp->xp[m] = comm->dbuf[g*3+1][m]*pgrp->mwscale;
741                     }
742                     if (pgrp->epgrppbc == epgrppbcREFAT)
743                     {
744                         pgrp->x[m]      += comm->rbuf[g][m];
745                         if (xp)
746                         {
747                             pgrp->xp[m] += comm->rbuf[g][m];
748                         }
749                     }
750                 }
751             }
752             else
753             {
754                 /* Cosine weighting geometry */
755                 double csw, snw, wmass, wwmass;
756
757                 /* Determine the optimal location of the cosine weight */
758                 csw                   = comm->dbuf[g*3][0];
759                 snw                   = comm->dbuf[g*3][1];
760                 pgrp->x[pull->cosdim] = atan2_0_2pi(snw, csw)/twopi_box;
761                 /* Set the weights for the local atoms */
762                 wmass  = sqrt(csw*csw + snw*snw);
763                 wwmass = (comm->dbuf[g*3+1][0]*csw*csw +
764                           comm->dbuf[g*3+1][1]*csw*snw +
765                           comm->dbuf[g*3+1][2]*snw*snw)/(wmass*wmass);
766
767                 pgrp->mwscale = 1.0/wmass;
768                 pgrp->wscale  = wmass/wwmass;
769                 pgrp->invtm   = wwmass/(wmass*wmass);
770                 /* Set the weights for the local atoms */
771                 csw *= pgrp->invtm;
772                 snw *= pgrp->invtm;
773                 for (size_t i = 0; i < pgrp->atomSet.numAtomsLocal(); i++)
774                 {
775                     int ii = pgrp->atomSet.localIndex()[i];
776                     pgrp->localWeights[i] = csw*std::cos(twopi_box*x[ii][pull->cosdim]) +
777                         snw*std::sin(twopi_box*x[ii][pull->cosdim]);
778                 }
779                 if (xp)
780                 {
781                     csw                    = comm->dbuf[g*3+2][0];
782                     snw                    = comm->dbuf[g*3+2][1];
783                     pgrp->xp[pull->cosdim] = atan2_0_2pi(snw, csw)/twopi_box;
784                 }
785             }
786             if (debug)
787             {
788                 fprintf(debug, "Pull group %zu wmass %f invtm %f\n",
789                         g, 1.0/pgrp->mwscale, pgrp->invtm);
790             }
791         }
792     }
793
794     if (pull->bCylinder)
795     {
796         /* Calculate the COMs for the cyclinder reference groups */
797         make_cyl_refgrps(cr, pull, md, pbc, t, x);
798     }
799 }