135edf649cc8017e2425e56e997304850f847ec3
[alexxy/gromacs.git] / src / gromacs / pulling / pullutil.c
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, 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 <assert.h>
40 #include <stdlib.h>
41
42 #include "gromacs/fileio/confio.h"
43 #include "gromacs/legacyheaders/gmx_ga2la.h"
44 #include "gromacs/legacyheaders/macros.h"
45 #include "gromacs/legacyheaders/names.h"
46 #include "gromacs/legacyheaders/network.h"
47 #include "gromacs/legacyheaders/typedefs.h"
48 #include "gromacs/legacyheaders/types/commrec.h"
49 #include "gromacs/math/vec.h"
50 #include "gromacs/pbcutil/pbc.h"
51 #include "gromacs/pulling/pull.h"
52 #include "gromacs/utility/fatalerror.h"
53 #include "gromacs/utility/futil.h"
54 #include "gromacs/utility/smalloc.h"
55
56 static void pull_set_pbcatom(t_commrec *cr, t_pull_group *pgrp,
57                              rvec *x,
58                              rvec x_pbc)
59 {
60     int a;
61
62     if (cr != NULL && DOMAINDECOMP(cr))
63     {
64         if (ga2la_get_home(cr->dd->ga2la, pgrp->pbcatom, &a))
65         {
66             copy_rvec(x[a], x_pbc);
67         }
68         else
69         {
70             clear_rvec(x_pbc);
71         }
72     }
73     else
74     {
75         copy_rvec(x[pgrp->pbcatom], x_pbc);
76     }
77 }
78
79 static void pull_set_pbcatoms(t_commrec *cr, t_pull *pull,
80                               rvec *x,
81                               rvec *x_pbc)
82 {
83     int g, n, m;
84
85     n = 0;
86     for (g = 0; g < pull->ngroup; g++)
87     {
88         if (!pull->group[g].bCalcCOM || pull->group[g].pbcatom == -1)
89         {
90             clear_rvec(x_pbc[g]);
91         }
92         else
93         {
94             pull_set_pbcatom(cr, &pull->group[g], x, x_pbc[g]);
95             n++;
96         }
97     }
98
99     if (cr && PAR(cr) && n > 0)
100     {
101         /* Sum over the nodes to get x_pbc from the home node of pbcatom */
102         gmx_sum(pull->ngroup*DIM, x_pbc[0], cr);
103     }
104 }
105
106 static void make_cyl_refgrps(t_commrec *cr, t_pull *pull, t_mdatoms *md,
107                              t_pbc *pbc, double t, rvec *x)
108 {
109     /* The size and stride per coord for the reduction buffer */
110     const int     stride = 9;
111     int           c, i, ii, m, start, end;
112     rvec          g_x, dx, dir;
113     double        inv_cyl_r2;
114     t_pull_coord *pcrd;
115     t_pull_group *pref, *pgrp, *pdyna;
116     gmx_ga2la_t   ga2la = NULL;
117
118     if (pull->dbuf_cyl == NULL)
119     {
120         snew(pull->dbuf_cyl, pull->ncoord*stride);
121     }
122
123     if (cr && DOMAINDECOMP(cr))
124     {
125         ga2la = cr->dd->ga2la;
126     }
127
128     start = 0;
129     end   = md->homenr;
130
131     inv_cyl_r2 = 1/dsqr(pull->cylinder_r);
132
133     /* loop over all groups to make a reference group for each*/
134     for (c = 0; c < pull->ncoord; c++)
135     {
136         double sum_a, wmass, wwmass;
137         dvec   radf_fac0, radf_fac1;
138
139         pcrd   = &pull->coord[c];
140
141         sum_a  = 0;
142         wmass  = 0;
143         wwmass = 0;
144         clear_dvec(radf_fac0);
145         clear_dvec(radf_fac1);
146
147         if (pcrd->eGeom == epullgCYL)
148         {
149             /* pref will be the same group for all pull coordinates */
150             pref  = &pull->group[pcrd->group[0]];
151             pgrp  = &pull->group[pcrd->group[1]];
152             pdyna = &pull->dyna[c];
153             copy_rvec(pcrd->vec, dir);
154             pdyna->nat_loc = 0;
155
156             /* We calculate distances with respect to the reference location
157              * of this cylinder group (g_x), which we already have now since
158              * we reduced the other group COM over the ranks. This resolves
159              * any PBC issues and we don't need to use a PBC-atom here.
160              */
161             for (m = 0; m < DIM; m++)
162             {
163                 g_x[m] = pgrp->x[m] - pcrd->vec[m]*(pcrd->init + pcrd->rate*t);
164             }
165
166             /* loop over all atoms in the main ref group */
167             for (i = 0; i < pref->nat; i++)
168             {
169                 ii = pref->ind[i];
170                 if (ga2la)
171                 {
172                     if (!ga2la_get_home(ga2la, pref->ind[i], &ii))
173                     {
174                         ii = -1;
175                     }
176                 }
177                 if (ii >= start && ii < end)
178                 {
179                     double dr2, dr2_rel, inp;
180                     dvec   dr;
181
182                     pbc_dx_aiuc(pbc, x[ii], g_x, dx);
183                     inp = iprod(dir, dx);
184                     dr2 = 0;
185                     for (m = 0; m < DIM; m++)
186                     {
187                         /* Determine the radial components */
188                         dr[m] = dx[m] - inp*dir[m];
189                         dr2  += dr[m]*dr[m];
190                     }
191                     dr2_rel = dr2*inv_cyl_r2;
192
193                     if (dr2_rel < 1)
194                     {
195                         double mass, weight, dweight_r;
196                         dvec   mdw;
197
198                         /* add to index, to sum of COM, to weight array */
199                         if (pdyna->nat_loc >= pdyna->nalloc_loc)
200                         {
201                             pdyna->nalloc_loc = over_alloc_large(pdyna->nat_loc+1);
202                             srenew(pdyna->ind_loc,    pdyna->nalloc_loc);
203                             srenew(pdyna->weight_loc, pdyna->nalloc_loc);
204                             srenew(pdyna->mdw,        pdyna->nalloc_loc);
205                             srenew(pdyna->dv,         pdyna->nalloc_loc);
206                         }
207                         pdyna->ind_loc[pdyna->nat_loc] = ii;
208
209                         mass      = md->massT[ii];
210                         /* The radial weight function is 1-2x^2+x^4,
211                          * where x=r/cylinder_r. Since this function depends
212                          * on the radial component, we also get radial forces
213                          * on both groups.
214                          */
215                         weight    = 1 + (-2 + dr2_rel)*dr2_rel;
216                         dweight_r = (-4 + 4*dr2_rel)*inv_cyl_r2;
217                         pdyna->weight_loc[pdyna->nat_loc] = weight;
218                         sum_a    += mass*weight*inp;
219                         wmass    += mass*weight;
220                         wwmass   += mass*weight*weight;
221                         dsvmul(mass*dweight_r, dr, mdw);
222                         copy_dvec(mdw, pdyna->mdw[pdyna->nat_loc]);
223                         /* Currently we only have the axial component of the
224                          * distance (inp) up to an unkown offset. We add this
225                          * offset after the reduction needs to determine the
226                          * COM of the cylinder group.
227                          */
228                         pdyna->dv[pdyna->nat_loc] = inp;
229                         for (m = 0; m < DIM; m++)
230                         {
231                             radf_fac0[m] += mdw[m];
232                             radf_fac1[m] += mdw[m]*inp;
233                         }
234                         pdyna->nat_loc++;
235                     }
236                 }
237             }
238         }
239         pull->dbuf_cyl[c*stride+0] = wmass;
240         pull->dbuf_cyl[c*stride+1] = wwmass;
241         pull->dbuf_cyl[c*stride+2] = sum_a;
242         pull->dbuf_cyl[c*stride+3] = radf_fac0[XX];
243         pull->dbuf_cyl[c*stride+4] = radf_fac0[YY];
244         pull->dbuf_cyl[c*stride+5] = radf_fac0[ZZ];
245         pull->dbuf_cyl[c*stride+6] = radf_fac1[XX];
246         pull->dbuf_cyl[c*stride+7] = radf_fac1[YY];
247         pull->dbuf_cyl[c*stride+8] = radf_fac1[ZZ];
248     }
249
250     if (cr != NULL && PAR(cr))
251     {
252         /* Sum the contributions over the ranks */
253         gmx_sumd(pull->ncoord*stride, pull->dbuf_cyl, cr);
254     }
255
256     for (c = 0; c < pull->ncoord; c++)
257     {
258         pcrd  = &pull->coord[c];
259
260         if (pcrd->eGeom == epullgCYL)
261         {
262             double wmass, wwmass, inp, dist;
263
264             pdyna = &pull->dyna[c];
265             pgrp  = &pull->group[pcrd->group[1]];
266
267             wmass          = pull->dbuf_cyl[c*stride+0];
268             wwmass         = pull->dbuf_cyl[c*stride+1];
269             pdyna->mwscale = 1.0/wmass;
270             /* Cylinder pulling can't be used with constraints, but we set
271              * wscale and invtm anyhow, in case someone would like to use them.
272              */
273             pdyna->wscale  = wmass/wwmass;
274             pdyna->invtm   = wwmass/(wmass*wmass);
275
276             /* We store the deviation of the COM from the reference location
277              * used above, since we need it when we apply the radial forces
278              * to the atoms in the cylinder group.
279              */
280             pcrd->cyl_dev  = 0;
281             for (m = 0; m < DIM; m++)
282             {
283                 g_x[m]         = pgrp->x[m] - pcrd->vec[m]*(pcrd->init + pcrd->rate*t);
284                 dist           = -pcrd->vec[m]*pull->dbuf_cyl[c*stride+2]*pdyna->mwscale;
285                 pdyna->x[m]    = g_x[m] - dist;
286                 pcrd->cyl_dev += dist;
287             }
288             /* Now we know the exact COM of the cylinder reference group,
289              * we can determine the radial force factor (ffrad) that when
290              * multiplied with the axial pull force will give the radial
291              * force on the pulled (non-cylinder) group.
292              */
293             for (m = 0; m < DIM; m++)
294             {
295                 pcrd->ffrad[m] = (pull->dbuf_cyl[c*stride+6+m] +
296                                   pull->dbuf_cyl[c*stride+3+m]*pcrd->cyl_dev)/wmass;
297             }
298
299             if (debug)
300             {
301                 fprintf(debug, "Pull cylinder group %d:%8.3f%8.3f%8.3f m:%8.3f\n",
302                         c, pdyna->x[0], pdyna->x[1],
303                         pdyna->x[2], 1.0/pdyna->invtm);
304                 fprintf(debug, "ffrad %8.3f %8.3f %8.3f\n",
305                         pcrd->ffrad[XX], pcrd->ffrad[YY], pcrd->ffrad[ZZ]);
306             }
307         }
308     }
309 }
310
311 static double atan2_0_2pi(double y, double x)
312 {
313     double a;
314
315     a = atan2(y, x);
316     if (a < 0)
317     {
318         a += 2.0*M_PI;
319     }
320     return a;
321 }
322
323 /* calculates center of mass of selection index from all coordinates x */
324 void pull_calc_coms(t_commrec *cr,
325                     t_pull *pull, t_mdatoms *md, t_pbc *pbc, double t,
326                     rvec x[], rvec *xp)
327 {
328     int  g;
329     real twopi_box = 0;
330
331     if (pull->rbuf == NULL)
332     {
333         snew(pull->rbuf, pull->ngroup);
334     }
335     if (pull->dbuf == NULL)
336     {
337         snew(pull->dbuf, 3*pull->ngroup);
338     }
339
340     if (pull->bRefAt && pull->bSetPBCatoms)
341     {
342         pull_set_pbcatoms(cr, pull, x, pull->rbuf);
343
344         if (cr != NULL && DOMAINDECOMP(cr))
345         {
346             /* We can keep these PBC reference coordinates fixed for nstlist
347              * steps, since atoms won't jump over PBC.
348              * This avoids a global reduction at the next nstlist-1 steps.
349              * Note that the exact values of the pbc reference coordinates
350              * are irrelevant, as long all atoms in the group are within
351              * half a box distance of the reference coordinate.
352              */
353             pull->bSetPBCatoms = FALSE;
354         }
355     }
356
357     if (pull->cosdim >= 0)
358     {
359         int m;
360
361         assert(pull->npbcdim <= DIM);
362
363         for (m = pull->cosdim+1; m < pull->npbcdim; m++)
364         {
365             if (pbc->box[m][pull->cosdim] != 0)
366             {
367                 gmx_fatal(FARGS, "Can not do cosine weighting for trilinic dimensions");
368             }
369         }
370         twopi_box = 2.0*M_PI/pbc->box[pull->cosdim][pull->cosdim];
371     }
372
373     for (g = 0; g < pull->ngroup; g++)
374     {
375         t_pull_group *pgrp;
376
377         pgrp = &pull->group[g];
378
379         if (pgrp->bCalcCOM)
380         {
381             if (pgrp->epgrppbc != epgrppbcCOS)
382             {
383                 dvec   com, comp;
384                 double wmass, wwmass;
385                 rvec   x_pbc = { 0, 0, 0 };
386                 int    i;
387
388                 clear_dvec(com);
389                 clear_dvec(comp);
390                 wmass  = 0;
391                 wwmass = 0;
392
393                 if (pgrp->epgrppbc == epgrppbcREFAT)
394                 {
395                     /* Set the pbc atom */
396                     copy_rvec(pull->rbuf[g], x_pbc);
397                 }
398
399                 for (i = 0; i < pgrp->nat_loc; i++)
400                 {
401                     int  ii, m;
402                     real mass, wm;
403
404                     ii   = pgrp->ind_loc[i];
405                     mass = md->massT[ii];
406                     if (pgrp->weight_loc == NULL)
407                     {
408                         wm     = mass;
409                         wmass += wm;
410                     }
411                     else
412                     {
413                         real w;
414
415                         w       = pgrp->weight_loc[i];
416                         wm      = w*mass;
417                         wmass  += wm;
418                         wwmass += wm*w;
419                     }
420                     if (pgrp->epgrppbc == epgrppbcNONE)
421                     {
422                         /* Plain COM: sum the coordinates */
423                         for (m = 0; m < DIM; m++)
424                         {
425                             com[m]    += wm*x[ii][m];
426                         }
427                         if (xp)
428                         {
429                             for (m = 0; m < DIM; m++)
430                             {
431                                 comp[m] += wm*xp[ii][m];
432                             }
433                         }
434                     }
435                     else
436                     {
437                         rvec dx;
438
439                         /* Sum the difference with the reference atom */
440                         pbc_dx(pbc, x[ii], x_pbc, dx);
441                         for (m = 0; m < DIM; m++)
442                         {
443                             com[m]    += wm*dx[m];
444                         }
445                         if (xp)
446                         {
447                             /* For xp add the difference between xp and x to dx,
448                              * such that we use the same periodic image,
449                              * also when xp has a large displacement.
450                              */
451                             for (m = 0; m < DIM; m++)
452                             {
453                                 comp[m] += wm*(dx[m] + xp[ii][m] - x[ii][m]);
454                             }
455                         }
456                     }
457                 }
458
459                 /* We do this check after the loop above to avoid more nesting.
460                  * If we have a single-atom group the mass is irrelevant, so
461                  * we can remove the mass factor to avoid division by zero.
462                  * Note that with constraint pulling the mass does matter, but
463                  * in that case a check group mass != 0 has been done before.
464                  */
465                 if (pgrp->nat == 1 && pgrp->nat_loc == 1 && wmass == 0)
466                 {
467                     int m;
468
469                     /* Copy the single atom coordinate */
470                     for (m = 0; m < DIM; m++)
471                     {
472                         com[m] = x[pgrp->ind_loc[0]][m];
473                     }
474                     /* Set all mass factors to 1 to get the correct COM */
475                     wmass  = 1;
476                     wwmass = 1;
477                 }
478
479                 if (pgrp->weight_loc == NULL)
480                 {
481                     wwmass = wmass;
482                 }
483
484                 /* Copy local sums to a buffer for global summing */
485                 copy_dvec(com, pull->dbuf[g*3]);
486                 copy_dvec(comp, pull->dbuf[g*3+1]);
487                 pull->dbuf[g*3+2][0] = wmass;
488                 pull->dbuf[g*3+2][1] = wwmass;
489                 pull->dbuf[g*3+2][2] = 0;
490             }
491             else
492             {
493                 /* Cosine weighting geometry */
494                 double cm, sm, cmp, smp, ccm, csm, ssm, csw, snw;
495                 int    i;
496
497                 cm  = 0;
498                 sm  = 0;
499                 cmp = 0;
500                 smp = 0;
501                 ccm = 0;
502                 csm = 0;
503                 ssm = 0;
504
505                 for (i = 0; i < pgrp->nat_loc; i++)
506                 {
507                     int  ii;
508                     real mass;
509
510                     ii   = pgrp->ind_loc[i];
511                     mass = md->massT[ii];
512                     /* Determine cos and sin sums */
513                     csw  = cos(x[ii][pull->cosdim]*twopi_box);
514                     snw  = sin(x[ii][pull->cosdim]*twopi_box);
515                     cm  += csw*mass;
516                     sm  += snw*mass;
517                     ccm += csw*csw*mass;
518                     csm += csw*snw*mass;
519                     ssm += snw*snw*mass;
520
521                     if (xp)
522                     {
523                         csw  = cos(xp[ii][pull->cosdim]*twopi_box);
524                         snw  = sin(xp[ii][pull->cosdim]*twopi_box);
525                         cmp += csw*mass;
526                         smp += snw*mass;
527                     }
528                 }
529
530                 /* Copy local sums to a buffer for global summing */
531                 pull->dbuf[g*3  ][0] = cm;
532                 pull->dbuf[g*3  ][1] = sm;
533                 pull->dbuf[g*3  ][2] = 0;
534                 pull->dbuf[g*3+1][0] = ccm;
535                 pull->dbuf[g*3+1][1] = csm;
536                 pull->dbuf[g*3+1][2] = ssm;
537                 pull->dbuf[g*3+2][0] = cmp;
538                 pull->dbuf[g*3+2][1] = smp;
539                 pull->dbuf[g*3+2][2] = 0;
540             }
541         }
542     }
543
544     if (cr && PAR(cr))
545     {
546         /* Sum the contributions over the nodes */
547         gmx_sumd(pull->ngroup*3*DIM, pull->dbuf[0], cr);
548     }
549
550     for (g = 0; g < pull->ngroup; g++)
551     {
552         t_pull_group *pgrp;
553
554         pgrp = &pull->group[g];
555         if (pgrp->nat > 0 && pgrp->bCalcCOM)
556         {
557             if (pgrp->epgrppbc != epgrppbcCOS)
558             {
559                 double wmass, wwmass;
560                 int    m;
561
562                 /* Determine the inverse mass */
563                 wmass             = pull->dbuf[g*3+2][0];
564                 wwmass            = pull->dbuf[g*3+2][1];
565                 pgrp->mwscale     = 1.0/wmass;
566                 /* invtm==0 signals a frozen group, so then we should keep it zero */
567                 if (pgrp->invtm != 0)
568                 {
569                     pgrp->wscale  = wmass/wwmass;
570                     pgrp->invtm   = wwmass/(wmass*wmass);
571                 }
572                 /* Divide by the total mass */
573                 for (m = 0; m < DIM; m++)
574                 {
575                     pgrp->x[m]    = pull->dbuf[g*3  ][m]*pgrp->mwscale;
576                     if (xp)
577                     {
578                         pgrp->xp[m] = pull->dbuf[g*3+1][m]*pgrp->mwscale;
579                     }
580                     if (pgrp->epgrppbc == epgrppbcREFAT)
581                     {
582                         pgrp->x[m]    += pull->rbuf[g][m];
583                         if (xp)
584                         {
585                             pgrp->xp[m] += pull->rbuf[g][m];
586                         }
587                     }
588                 }
589             }
590             else
591             {
592                 /* Cosine weighting geometry */
593                 double csw, snw, wmass, wwmass;
594                 int    i, ii;
595
596                 /* Determine the optimal location of the cosine weight */
597                 csw                   = pull->dbuf[g*3][0];
598                 snw                   = pull->dbuf[g*3][1];
599                 pgrp->x[pull->cosdim] = atan2_0_2pi(snw, csw)/twopi_box;
600                 /* Set the weights for the local atoms */
601                 wmass  = sqrt(csw*csw + snw*snw);
602                 wwmass = (pull->dbuf[g*3+1][0]*csw*csw +
603                           pull->dbuf[g*3+1][1]*csw*snw +
604                           pull->dbuf[g*3+1][2]*snw*snw)/(wmass*wmass);
605
606                 pgrp->mwscale = 1.0/wmass;
607                 pgrp->wscale  = wmass/wwmass;
608                 pgrp->invtm   = wwmass/(wmass*wmass);
609                 /* Set the weights for the local atoms */
610                 csw *= pgrp->invtm;
611                 snw *= pgrp->invtm;
612                 for (i = 0; i < pgrp->nat_loc; i++)
613                 {
614                     ii                  = pgrp->ind_loc[i];
615                     pgrp->weight_loc[i] = csw*cos(twopi_box*x[ii][pull->cosdim]) +
616                         snw*sin(twopi_box*x[ii][pull->cosdim]);
617                 }
618                 if (xp)
619                 {
620                     csw                    = pull->dbuf[g*3+2][0];
621                     snw                    = pull->dbuf[g*3+2][1];
622                     pgrp->xp[pull->cosdim] = atan2_0_2pi(snw, csw)/twopi_box;
623                 }
624             }
625             if (debug)
626             {
627                 fprintf(debug, "Pull group %d wmass %f invtm %f\n",
628                         g, 1.0/pgrp->mwscale, pgrp->invtm);
629             }
630         }
631     }
632
633     if (pull->bCylinder)
634     {
635         /* Calculate the COMs for the cyclinder reference groups */
636         make_cyl_refgrps(cr, pull, md, pbc, t, x);
637     }
638 }