6818ac285589168ab1f1c93aaeb13c95d9507512
[alexxy/gromacs.git] / src / gromacs / gmxana / gmx_msd.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 <cmath>
40 #include <cstring>
41
42 #include "gromacs/commandline/pargs.h"
43 #include "gromacs/commandline/viewit.h"
44 #include "gromacs/fileio/confio.h"
45 #include "gromacs/fileio/trxio.h"
46 #include "gromacs/fileio/xvgr.h"
47 #include "gromacs/gmxana/gmx_ana.h"
48 #include "gromacs/gmxana/gstat.h"
49 #include "gromacs/math/functions.h"
50 #include "gromacs/math/utilities.h"
51 #include "gromacs/math/vec.h"
52 #include "gromacs/pbcutil/rmpbc.h"
53 #include "gromacs/statistics/statistics.h"
54 #include "gromacs/topology/index.h"
55 #include "gromacs/topology/topology.h"
56 #include "gromacs/utility/arraysize.h"
57 #include "gromacs/utility/fatalerror.h"
58 #include "gromacs/utility/futil.h"
59 #include "gromacs/utility/gmxassert.h"
60 #include "gromacs/utility/smalloc.h"
61
62 #define FACTOR  1000.0  /* Convert nm^2/ps to 10e-5 cm^2/s */
63 /* NORMAL = total diffusion coefficient (default). X,Y,Z is diffusion
64    coefficient in X,Y,Z direction. LATERAL is diffusion coefficient in
65    plane perpendicular to axis
66  */
67 typedef enum {
68     NOT_USED, NORMAL, X, Y, Z, LATERAL
69 } msd_type;
70
71 typedef struct {
72     real          t0;         /* start time and time increment between  */
73     real          delta_t;    /* time between restart points */
74     real          beginfit,   /* the begin/end time for fits as reals between */
75                   endfit;     /* 0 and 1 */
76     real          dim_factor; /* the dimensionality factor for the diffusion
77                                  constant */
78     real        **data;       /* the displacement data. First index is the group
79                                  number, second is frame number */
80     real         *time;       /* frame time */
81     real         *mass;       /* masses for mass-weighted msd */
82     matrix      **datam;
83     rvec        **x0;         /* original positions */
84     rvec         *com;        /* center of mass correction for each frame */
85     gmx_stats_t **lsq;        /* fitting stats for individual molecule msds */
86     msd_type      type;       /* the type of msd to calculate (lateral, etc.)*/
87     int           axis;       /* the axis along which to calculate */
88     int           ncoords;
89     int           nrestart;   /* number of restart points */
90     int           nmol;       /* number of molecules (for bMol) */
91     int           nframes;    /* number of frames */
92     int           nlast;
93     int           ngrp;       /* number of groups to use for msd calculation */
94     int          *n_offs;
95     int         **ndata;      /* the number of msds (particles/mols) per data
96                                  point. */
97 } t_corr;
98
99 typedef real t_calc_func (t_corr *curr, int nx, const int index[], int nx0, rvec xc[],
100                           const rvec dcom, gmx_bool bTen, matrix mat);
101
102 static real thistime(t_corr *curr)
103 {
104     return curr->time[curr->nframes];
105 }
106
107 static gmx_bool in_data(t_corr *curr, int nx00)
108 {
109     return curr->nframes-curr->n_offs[nx00];
110 }
111
112 static t_corr *init_corr(int nrgrp, int type, int axis, real dim_factor,
113                          int nmol, gmx_bool bTen, gmx_bool bMass, real dt, const t_topology *top,
114                          real beginfit, real endfit)
115 {
116     t_corr  *curr;
117     int      i;
118
119     snew(curr, 1);
120     curr->type       = (msd_type)type;
121     curr->axis       = axis;
122     curr->ngrp       = nrgrp;
123     curr->nrestart   = 0;
124     curr->delta_t    = dt;
125     curr->beginfit   = (1 - 2*GMX_REAL_EPS)*beginfit;
126     curr->endfit     = (1 + 2*GMX_REAL_EPS)*endfit;
127     curr->x0         = nullptr;
128     curr->n_offs     = nullptr;
129     curr->nframes    = 0;
130     curr->nlast      = 0;
131     curr->dim_factor = dim_factor;
132
133     snew(curr->ndata, nrgrp);
134     snew(curr->data, nrgrp);
135     if (bTen)
136     {
137         snew(curr->datam, nrgrp);
138     }
139     for (i = 0; (i < nrgrp); i++)
140     {
141         curr->ndata[i] = nullptr;
142         curr->data[i]  = nullptr;
143         if (bTen)
144         {
145             curr->datam[i] = nullptr;
146         }
147     }
148     curr->time = nullptr;
149     curr->lsq  = nullptr;
150     curr->nmol = nmol;
151     if (curr->nmol > 0)
152     {
153         snew(curr->mass, curr->nmol);
154         for (i = 0; i < curr->nmol; i++)
155         {
156             curr->mass[i] = 1;
157         }
158     }
159     else
160     {
161         if (bMass)
162         {
163             const t_atoms *atoms = &top->atoms;
164             snew(curr->mass, atoms->nr);
165             for (i = 0; (i < atoms->nr); i++)
166             {
167                 curr->mass[i] = atoms->atom[i].m;
168             }
169         }
170     }
171
172     return curr;
173 }
174
175 static void corr_print(t_corr *curr, gmx_bool bTen, const char *fn, const char *title,
176                        const char *yaxis,
177                        real msdtime, real beginfit, real endfit,
178                        real *DD, real *SigmaD, char *grpname[],
179                        const gmx_output_env_t *oenv)
180 {
181     FILE *out;
182     int   i, j;
183
184     out = xvgropen(fn, title, output_env_get_xvgr_tlabel(oenv), yaxis, oenv);
185     if (DD)
186     {
187         fprintf(out, "# MSD gathered over %g %s with %d restarts\n",
188                 msdtime, output_env_get_time_unit(oenv).c_str(), curr->nrestart);
189         fprintf(out, "# Diffusion constants fitted from time %g to %g %s\n",
190                 beginfit, endfit, output_env_get_time_unit(oenv).c_str());
191         for (i = 0; i < curr->ngrp; i++)
192         {
193             fprintf(out, "# D[%10s] = %.4f (+/- %.4f) (1e-5 cm^2/s)\n",
194                     grpname[i], DD[i], SigmaD[i]);
195         }
196     }
197     for (i = 0; i < curr->nframes; i++)
198     {
199         fprintf(out, "%10g", output_env_conv_time(oenv, curr->time[i]));
200         for (j = 0; j < curr->ngrp; j++)
201         {
202             fprintf(out, "  %10g", curr->data[j][i]);
203             if (bTen)
204             {
205                 fprintf(out, " %10g %10g %10g %10g %10g %10g",
206                         curr->datam[j][i][XX][XX],
207                         curr->datam[j][i][YY][YY],
208                         curr->datam[j][i][ZZ][ZZ],
209                         curr->datam[j][i][YY][XX],
210                         curr->datam[j][i][ZZ][XX],
211                         curr->datam[j][i][ZZ][YY]);
212             }
213         }
214         fprintf(out, "\n");
215     }
216     xvgrclose(out);
217 }
218
219 /* called from corr_loop, to do the main calculations */
220 static void calc_corr(t_corr *curr, int nr, int nx, int index[], rvec xc[],
221                       gmx_bool bRmCOMM, rvec com, t_calc_func *calc1, gmx_bool bTen)
222 {
223     int    nx0;
224     real   g;
225     matrix mat;
226     rvec   dcom;
227
228     /* Check for new starting point */
229     if (curr->nlast < curr->nrestart)
230     {
231         if ((thistime(curr) >= (curr->nlast*curr->delta_t)) && (nr == 0))
232         {
233             std::memcpy(curr->x0[curr->nlast], xc, curr->ncoords*sizeof(xc[0]));
234             curr->n_offs[curr->nlast] = curr->nframes;
235             copy_rvec(com, curr->com[curr->nlast]);
236             curr->nlast++;
237         }
238     }
239
240     /* nx0 appears to be the number of new starting points,
241      * so for all starting points, call calc1.
242      */
243     for (nx0 = 0; (nx0 < curr->nlast); nx0++)
244     {
245         if (bRmCOMM)
246         {
247             rvec_sub(com, curr->com[nx0], dcom);
248         }
249         else
250         {
251             clear_rvec(dcom);
252         }
253         g = calc1(curr, nx, index, nx0, xc, dcom, bTen, mat);
254 #ifdef DEBUG2
255         printf("g[%d]=%g\n", nx0, g);
256 #endif
257         curr->data[nr][in_data(curr, nx0)] += g;
258         if (bTen)
259         {
260             m_add(curr->datam[nr][in_data(curr, nx0)], mat,
261                   curr->datam[nr][in_data(curr, nx0)]);
262         }
263         curr->ndata[nr][in_data(curr, nx0)]++;
264     }
265 }
266
267 /* the non-mass-weighted mean-squared displacement calcuation */
268 static real calc1_norm(t_corr *curr, int nx, const int index[], int nx0, rvec xc[],
269                        const rvec dcom, gmx_bool bTen, matrix mat)
270 {
271     int  i, ix, m, m2;
272     real g, r, r2;
273     rvec rv;
274
275     g = 0.0;
276     clear_mat(mat);
277
278     for (i = 0; (i < nx); i++)
279     {
280         ix = index[i];
281         r2 = 0.0;
282         switch (curr->type)
283         {
284             case NORMAL:
285                 for (m = 0; (m < DIM); m++)
286                 {
287                     rv[m] = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
288                     r2   += rv[m]*rv[m];
289                     if (bTen)
290                     {
291                         for (m2 = 0; m2 <= m; m2++)
292                         {
293                             mat[m][m2] += rv[m]*rv[m2];
294                         }
295                     }
296                 }
297                 break;
298             case X:
299             case Y:
300             case Z:
301                 r = xc[ix][curr->type-X] - curr->x0[nx0][ix][curr->type-X] -
302                     dcom[curr->type-X];
303                 r2 += r*r;
304                 break;
305             case LATERAL:
306                 for (m = 0; (m < DIM); m++)
307                 {
308                     if (m != curr->axis)
309                     {
310                         r   = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
311                         r2 += r*r;
312                     }
313                 }
314                 break;
315             default:
316                 gmx_fatal(FARGS, "Error: did not expect option value %d", curr->type);
317         }
318         g += r2;
319     }
320     g /= nx;
321     msmul(mat, 1.0/nx, mat);
322
323     return g;
324 }
325
326 /* calculate the com of molecules in x and put it into xa */
327 static void calc_mol_com(int nmol, const int *molindex, const t_block *mols, const t_atoms *atoms,
328                          rvec *x, rvec *xa)
329 {
330     int  m, mol, i, d;
331     rvec xm;
332     real mass, mtot;
333
334     for (m = 0; m < nmol; m++)
335     {
336         mol = molindex[m];
337         clear_rvec(xm);
338         mtot = 0;
339         for (i = mols->index[mol]; i < mols->index[mol+1]; i++)
340         {
341             mass = atoms->atom[i].m;
342             for (d = 0; d < DIM; d++)
343             {
344                 xm[d] += mass*x[i][d];
345             }
346             mtot += mass;
347         }
348         svmul(1/mtot, xm, xa[m]);
349     }
350 }
351
352 static real calc_one_mw(t_corr *curr, int ix, int nx0, rvec xc[], real *tm,
353                         const rvec dcom, gmx_bool bTen, matrix mat)
354 {
355     real r2, r, mm;
356     rvec rv;
357     int  m, m2;
358
359     mm = curr->mass[ix];
360     if (mm == 0)
361     {
362         return 0;
363     }
364     (*tm) += mm;
365     r2     = 0.0;
366     switch (curr->type)
367     {
368         case NORMAL:
369             for (m = 0; (m < DIM); m++)
370             {
371                 rv[m] = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
372                 r2   += mm*rv[m]*rv[m];
373                 if (bTen)
374                 {
375                     for (m2 = 0; m2 <= m; m2++)
376                     {
377                         mat[m][m2] += mm*rv[m]*rv[m2];
378                     }
379                 }
380             }
381             break;
382         case X:
383         case Y:
384         case Z:
385             r  = xc[ix][curr->type-X] - curr->x0[nx0][ix][curr->type-X] -
386                 dcom[curr->type-X];
387             r2 = mm*r*r;
388             break;
389         case LATERAL:
390             for (m = 0; (m < DIM); m++)
391             {
392                 if (m != curr->axis)
393                 {
394                     r   = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
395                     r2 += mm*r*r;
396                 }
397             }
398             break;
399         default:
400             gmx_fatal(FARGS, "Options got screwed. Did not expect value %d\n", curr->type);
401     } /* end switch */
402     return r2;
403 }
404
405 /* the normal, mass-weighted mean-squared displacement calcuation */
406 static real calc1_mw(t_corr *curr, int nx, const int index[], int nx0, rvec xc[],
407                      const rvec dcom, gmx_bool bTen, matrix mat)
408 {
409     int  i;
410     real g, tm;
411
412     g = tm = 0.0;
413     clear_mat(mat);
414     for (i = 0; (i < nx); i++)
415     {
416         g += calc_one_mw(curr, index[i], nx0, xc, &tm, dcom, bTen, mat);
417     }
418
419     g /= tm;
420     if (bTen)
421     {
422         msmul(mat, 1/tm, mat);
423     }
424
425     return g;
426 }
427
428 /* prepare the coordinates by removing periodic boundary crossings.
429    gnx = the number of atoms/molecules
430    index = the indices
431    xcur = the current coordinates
432    xprev = the previous coordinates
433    box = the box matrix */
434 static void prep_data(gmx_bool bMol, int gnx, const int index[],
435                       rvec xcur[], rvec xprev[], matrix box)
436 {
437     int  i, m, ind;
438     rvec hbox;
439
440     /* Remove periodicity */
441     for (m = 0; (m < DIM); m++)
442     {
443         hbox[m] = 0.5*box[m][m];
444     }
445
446     for (i = 0; (i < gnx); i++)
447     {
448         if (bMol)
449         {
450             ind = i;
451         }
452         else
453         {
454             ind = index[i];
455         }
456
457         for (m = DIM-1; m >= 0; m--)
458         {
459             if (hbox[m] == 0)
460             {
461                 continue;
462             }
463             while (xcur[ind][m]-xprev[ind][m] <= -hbox[m])
464             {
465                 rvec_inc(xcur[ind], box[m]);
466             }
467             while (xcur[ind][m]-xprev[ind][m] >  hbox[m])
468             {
469                 rvec_dec(xcur[ind], box[m]);
470             }
471         }
472     }
473 }
474
475 /* calculate the center of mass for a group
476    gnx = the number of atoms/molecules
477    index = the indices
478    xcur = the current coordinates
479    xprev = the previous coordinates
480    box = the box matrix
481    atoms = atom data (for mass)
482    com(output) = center of mass  */
483 static void calc_com(gmx_bool bMol, int gnx, int index[],
484                      rvec xcur[], rvec xprev[], matrix box, const t_atoms *atoms,
485                      rvec com)
486 {
487     int    i, m, ind;
488     real   mass;
489     double tmass;
490     dvec   sx;
491
492     clear_dvec(sx);
493     tmass = 0;
494
495     prep_data(bMol, gnx, index, xcur, xprev, box);
496     for (i = 0; (i < gnx); i++)
497     {
498         if (bMol)
499         {
500             ind = i;
501         }
502         else
503         {
504             ind = index[i];
505         }
506
507
508         mass = atoms->atom[ind].m;
509         for (m = 0; m < DIM; m++)
510         {
511             sx[m] += mass*xcur[ind][m];
512         }
513         tmass += mass;
514     }
515     for (m = 0; m < DIM; m++)
516     {
517         com[m] = sx[m]/tmass;
518     }
519 }
520
521
522 static real calc1_mol(t_corr *curr, int nx, const int gmx_unused index[], int nx0, rvec xc[],
523                       const rvec dcom, gmx_bool bTen, matrix mat)
524 {
525     int  i;
526     real g, tm, gtot, tt;
527
528     tt   = curr->time[in_data(curr, nx0)];
529     gtot = 0;
530     tm   = 0;
531     clear_mat(mat);
532     for (i = 0; (i < nx); i++)
533     {
534         g = calc_one_mw(curr, i, nx0, xc, &tm, dcom, bTen, mat);
535         /* We don't need to normalize as the mass was set to 1 */
536         gtot += g;
537         if (tt >= curr->beginfit && (curr->endfit < 0 || tt <= curr->endfit))
538         {
539             gmx_stats_add_point(curr->lsq[nx0][i], tt, g, 0, 0);
540         }
541     }
542     msmul(mat, 1.0/nx, mat);
543
544     return gtot/nx;
545 }
546
547 static void printmol(t_corr *curr, const char *fn,
548                      const char *fn_pdb, const int *molindex, const t_topology *top,
549                      rvec *x, int ePBC, matrix box, const gmx_output_env_t *oenv)
550 {
551 #define NDIST 100
552     FILE       *out;
553     gmx_stats_t lsq1;
554     int         i, j;
555     real        a, b, D, Dav, D2av, VarD, sqrtD, sqrtD_max, scale;
556     t_pdbinfo  *pdbinfo = nullptr;
557     const int  *mol2a   = nullptr;
558
559     out = xvgropen(fn, "Diffusion Coefficients / Molecule", "Molecule", "D", oenv);
560
561     if (fn_pdb)
562     {
563         pdbinfo = top->atoms.pdbinfo;
564         mol2a   = top->mols.index;
565     }
566
567     Dav       = D2av = 0;
568     sqrtD_max = 0;
569     for (i = 0; (i < curr->nmol); i++)
570     {
571         lsq1 = gmx_stats_init();
572         for (j = 0; (j < curr->nrestart); j++)
573         {
574             real xx, yy, dx, dy;
575
576             while (gmx_stats_get_point(curr->lsq[j][i], &xx, &yy, &dx, &dy, 0) == estatsOK)
577             {
578                 gmx_stats_add_point(lsq1, xx, yy, dx, dy);
579             }
580         }
581         gmx_stats_get_ab(lsq1, elsqWEIGHT_NONE, &a, &b, nullptr, nullptr, nullptr, nullptr);
582         gmx_stats_free(lsq1);
583         D     = a*FACTOR/curr->dim_factor;
584         if (D < 0)
585         {
586             D   = 0;
587         }
588         Dav  += D;
589         D2av += gmx::square(D);
590         fprintf(out, "%10d  %10g\n", i, D);
591         if (pdbinfo)
592         {
593             sqrtD = std::sqrt(D);
594             if (sqrtD > sqrtD_max)
595             {
596                 sqrtD_max = sqrtD;
597             }
598             for (j = mol2a[molindex[i]]; j < mol2a[molindex[i]+1]; j++)
599             {
600                 pdbinfo[j].bfac = sqrtD;
601             }
602         }
603     }
604     xvgrclose(out);
605     do_view(oenv, fn, "-graphtype bar");
606
607     /* Compute variance, stddev and error */
608     Dav  /= curr->nmol;
609     D2av /= curr->nmol;
610     VarD  = D2av - gmx::square(Dav);
611     printf("<D> = %.4f Std. Dev. = %.4f Error = %.4f\n",
612            Dav, std::sqrt(VarD), std::sqrt(VarD/curr->nmol));
613
614     if (fn_pdb && x)
615     {
616         scale = 1;
617         while (scale*sqrtD_max > 10)
618         {
619             scale *= 0.1;
620         }
621         while (scale*sqrtD_max < 0.1)
622         {
623             scale *= 10;
624         }
625         GMX_RELEASE_ASSERT(pdbinfo != nullptr, "Internal error - pdbinfo not set for PDB input");
626         for (i = 0; i < top->atoms.nr; i++)
627         {
628             pdbinfo[i].bfac *= scale;
629         }
630         write_sto_conf(fn_pdb, "molecular MSD", &top->atoms, x, nullptr, ePBC, box);
631     }
632 }
633
634 /* this is the main loop for the correlation type functions
635  * fx and nx are file pointers to things like read_first_x and
636  * read_next_x
637  */
638 static int corr_loop(t_corr *curr, const char *fn, const t_topology *top, int ePBC,
639                      gmx_bool bMol, int gnx[], int *index[],
640                      t_calc_func *calc1, gmx_bool bTen, int *gnx_com, int *index_com[],
641                      real dt, real t_pdb, rvec **x_pdb, matrix box_pdb,
642                      const gmx_output_env_t *oenv)
643 {
644     rvec            *x[2];  /* the coordinates to read */
645     rvec            *xa[2]; /* the coordinates to calculate displacements for */
646     rvec             com = {0};
647     real             t, t_prev = 0;
648     int              natoms, i, j, cur = 0, maxframes = 0;
649     t_trxstatus     *status;
650 #define        prev (1-cur)
651     matrix           box;
652     gmx_bool         bFirst;
653     gmx_rmpbc_t      gpbc = nullptr;
654
655     natoms = read_first_x(oenv, &status, fn, &curr->t0, &(x[cur]), box);
656 #ifdef DEBUG
657     fprintf(stderr, "Read %d atoms for first frame\n", natoms);
658 #endif
659     if ((gnx_com != nullptr) && natoms < top->atoms.nr)
660     {
661         fprintf(stderr, "WARNING: The trajectory only contains part of the system (%d of %d atoms) and therefore the COM motion of only this part of the system will be removed\n", natoms, top->atoms.nr);
662     }
663
664     snew(x[prev], natoms);
665
666     // if com is requested, the data structure needs to be large enough to do this
667     // to prevent overflow
668     if (bMol && !gnx_com)
669     {
670         curr->ncoords = curr->nmol;
671         snew(xa[0], curr->ncoords);
672         snew(xa[1], curr->ncoords);
673     }
674     else
675     {
676         curr->ncoords = natoms;
677         xa[0]         = x[0];
678         xa[1]         = x[1];
679     }
680
681     bFirst = TRUE;
682     t      = curr->t0;
683     if (x_pdb)
684     {
685         *x_pdb = nullptr;
686     }
687
688     if (bMol)
689     {
690         gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
691     }
692
693     /* the loop over all frames */
694     do
695     {
696         if (x_pdb && ((bFirst && t_pdb < t) ||
697                       (!bFirst &&
698                        t_pdb > t - 0.5*(t - t_prev) &&
699                        t_pdb < t + 0.5*(t - t_prev))))
700         {
701             if (*x_pdb == nullptr)
702             {
703                 snew(*x_pdb, natoms);
704             }
705             for (i = 0; i < natoms; i++)
706             {
707                 copy_rvec(x[cur][i], (*x_pdb)[i]);
708             }
709             copy_mat(box, box_pdb);
710         }
711
712
713         /* check whether we've reached a restart point */
714         if (bRmod(t, curr->t0, dt))
715         {
716             curr->nrestart++;
717
718             srenew(curr->x0, curr->nrestart);
719             snew(curr->x0[curr->nrestart-1], curr->ncoords);
720             srenew(curr->com, curr->nrestart);
721             srenew(curr->n_offs, curr->nrestart);
722             srenew(curr->lsq, curr->nrestart);
723             snew(curr->lsq[curr->nrestart-1], curr->nmol);
724             for (i = 0; i < curr->nmol; i++)
725             {
726                 curr->lsq[curr->nrestart-1][i]  = gmx_stats_init();
727             }
728
729             if (debug)
730             {
731                 fprintf(debug, "Extended data structures because of new restart %d\n",
732                         curr->nrestart);
733             }
734         }
735         /* create or extend the frame-based arrays */
736         if (curr->nframes >= maxframes-1)
737         {
738             if (maxframes == 0)
739             {
740                 for (i = 0; (i < curr->ngrp); i++)
741                 {
742                     curr->ndata[i] = nullptr;
743                     curr->data[i]  = nullptr;
744                     if (bTen)
745                     {
746                         curr->datam[i] = nullptr;
747                     }
748                 }
749                 curr->time = nullptr;
750             }
751             maxframes += 10;
752             for (i = 0; (i < curr->ngrp); i++)
753             {
754                 srenew(curr->ndata[i], maxframes);
755                 srenew(curr->data[i], maxframes);
756                 if (bTen)
757                 {
758                     srenew(curr->datam[i], maxframes);
759                 }
760                 for (j = maxframes-10; j < maxframes; j++)
761                 {
762                     curr->ndata[i][j] = 0;
763                     curr->data[i][j]  = 0;
764                     if (bTen)
765                     {
766                         clear_mat(curr->datam[i][j]);
767                     }
768                 }
769             }
770             srenew(curr->time, maxframes);
771         }
772
773         /* set the time */
774         curr->time[curr->nframes] = t - curr->t0;
775
776         /* make the molecules whole */
777         if (bMol)
778         {
779             gmx_rmpbc(gpbc, natoms, box, x[cur]);
780         }
781
782         /* calculate the molecules' centers of masses and put them into xa */
783         // NOTE and WARNING! If above both COM removal and individual molecules have been
784         // requested, x and xa point to the same memory, and the coordinate
785         // data becomes overwritten by the molecule data.
786         if (bMol)
787         {
788             calc_mol_com(gnx[0], index[0], &top->mols, &top->atoms, x[cur], xa[cur]);
789         }
790
791         /* for the first frame, the previous frame is a copy of the first frame */
792         if (bFirst)
793         {
794             std::memcpy(xa[prev], xa[cur], curr->ncoords*sizeof(xa[prev][0]));
795             bFirst = FALSE;
796         }
797
798         /* first remove the periodic boundary condition crossings */
799         for (i = 0; i < curr->ngrp; i++)
800         {
801             prep_data(bMol, gnx[i], index[i], xa[cur], xa[prev], box);
802         }
803
804         /* calculate the center of mass */
805         if (gnx_com)
806         {
807             calc_com(bMol, gnx_com[0], index_com[0], xa[cur], xa[prev], box,
808                      &top->atoms, com);
809         }
810
811         /* loop over all groups in index file */
812         for (i = 0; (i < curr->ngrp); i++)
813         {
814             /* calculate something useful, like mean square displacements */
815             calc_corr(curr, i, gnx[i], index[i], xa[cur], (gnx_com != nullptr), com,
816                       calc1, bTen);
817         }
818         cur    = prev;
819         t_prev = t;
820
821         curr->nframes++;
822     }
823     while (read_next_x(oenv, status, &t, x[cur], box));
824     fprintf(stderr, "\nUsed %d restart points spaced %g %s over %g %s\n\n",
825             curr->nrestart,
826             output_env_conv_time(oenv, dt), output_env_get_time_unit(oenv).c_str(),
827             output_env_conv_time(oenv, curr->time[curr->nframes-1]),
828             output_env_get_time_unit(oenv).c_str() );
829
830     if (bMol)
831     {
832         gmx_rmpbc_done(gpbc);
833     }
834
835     close_trx(status);
836
837     return natoms;
838 }
839
840 static void index_atom2mol(int *n, int *index, const t_block *mols)
841 {
842     int nat, i, nmol, mol, j;
843
844     nat  = *n;
845     i    = 0;
846     nmol = 0;
847     mol  = 0;
848     while (i < nat)
849     {
850         while (index[i] > mols->index[mol])
851         {
852             mol++;
853             if (mol >= mols->nr)
854             {
855                 gmx_fatal(FARGS, "Atom index out of range: %d", index[i]+1);
856             }
857         }
858         for (j = mols->index[mol]; j < mols->index[mol+1]; j++)
859         {
860             if (i >= nat || index[i] != j)
861             {
862                 gmx_fatal(FARGS, "The index group does not consist of whole molecules");
863             }
864             i++;
865         }
866         index[nmol++] = mol;
867     }
868
869     fprintf(stderr, "Split group of %d atoms into %d molecules\n", nat, nmol);
870
871     *n = nmol;
872 }
873
874 static void do_corr(const char *trx_file, const char *ndx_file, const char *msd_file,
875                     const char *mol_file, const char *pdb_file, real t_pdb,
876                     int nrgrp, t_topology *top, int ePBC,
877                     gmx_bool bTen, gmx_bool bMW, gmx_bool bRmCOMM,
878                     int type, real dim_factor, int axis,
879                     real dt, real beginfit, real endfit, const gmx_output_env_t *oenv)
880 {
881     t_corr        *msd;
882     int           *gnx;   /* the selected groups' sizes */
883     int          **index; /* selected groups' indices */
884     char         **grpname;
885     int            i, i0, i1, j, N, nat_trx;
886     real          *DD, *SigmaD, a, a2, b, r, chi2;
887     rvec          *x;
888     matrix         box;
889     int           *gnx_com     = nullptr; /* the COM removal group size  */
890     int          **index_com   = nullptr; /* the COM removal group atom indices */
891     char         **grpname_com = nullptr; /* the COM removal group name */
892
893     snew(gnx, nrgrp);
894     snew(index, nrgrp);
895     snew(grpname, nrgrp);
896
897     fprintf(stderr, "\nSelect a group to calculate mean squared displacement for:\n");
898     get_index(&top->atoms, ndx_file, nrgrp, gnx, index, grpname);
899
900     if (bRmCOMM)
901     {
902         snew(gnx_com, 1);
903         snew(index_com, 1);
904         snew(grpname_com, 1);
905
906         fprintf(stderr, "\nNow select a group for center of mass removal:\n");
907         get_index(&top->atoms, ndx_file, 1, gnx_com, index_com, grpname_com);
908     }
909
910     if (mol_file)
911     {
912         index_atom2mol(&gnx[0], index[0], &top->mols);
913     }
914
915     msd = init_corr(nrgrp, type, axis, dim_factor,
916                     mol_file == nullptr ? 0 : gnx[0], bTen, bMW, dt, top,
917                     beginfit, endfit);
918
919     nat_trx =
920         corr_loop(msd, trx_file, top, ePBC, mol_file ? gnx[0] : 0, gnx, index,
921                   (mol_file != nullptr) ? calc1_mol : (bMW ? calc1_mw : calc1_norm),
922                   bTen, gnx_com, index_com, dt, t_pdb,
923                   pdb_file ? &x : nullptr, box, oenv);
924
925     /* Correct for the number of points */
926     for (j = 0; (j < msd->ngrp); j++)
927     {
928         for (i = 0; (i < msd->nframes); i++)
929         {
930             msd->data[j][i] /= msd->ndata[j][i];
931             if (bTen)
932             {
933                 msmul(msd->datam[j][i], 1.0/msd->ndata[j][i], msd->datam[j][i]);
934             }
935         }
936     }
937
938     if (mol_file)
939     {
940         if (pdb_file && x == nullptr)
941         {
942             fprintf(stderr, "\nNo frame found need time tpdb = %g ps\n"
943                     "Can not write %s\n\n", t_pdb, pdb_file);
944         }
945         i             = top->atoms.nr;
946         top->atoms.nr = nat_trx;
947         if (pdb_file && top->atoms.pdbinfo == nullptr)
948         {
949             snew(top->atoms.pdbinfo, top->atoms.nr);
950         }
951         printmol(msd, mol_file, pdb_file, index[0], top, x, ePBC, box, oenv);
952         top->atoms.nr = i;
953     }
954
955     DD     = nullptr;
956     SigmaD = nullptr;
957
958     if (beginfit == -1)
959     {
960         i0       = static_cast<int>(0.1*(msd->nframes - 1) + 0.5);
961         beginfit = msd->time[i0];
962     }
963     else
964     {
965         for (i0 = 0; i0 < msd->nframes && msd->time[i0] < beginfit; i0++)
966         {
967             ;
968         }
969     }
970
971     if (endfit == -1)
972     {
973         i1     = static_cast<int>(0.9*(msd->nframes - 1) + 0.5) + 1;
974         endfit = msd->time[i1-1];
975     }
976     else
977     {
978         for (i1 = i0; i1 < msd->nframes && msd->time[i1] <= endfit; i1++)
979         {
980             ;
981         }
982     }
983     fprintf(stdout, "Fitting from %g to %g %s\n\n", beginfit, endfit,
984             output_env_get_time_unit(oenv).c_str());
985
986     N = i1-i0;
987     if (N <= 2)
988     {
989         fprintf(stdout, "Not enough points for fitting (%d).\n"
990                 "Can not determine the diffusion constant.\n", N);
991     }
992     else
993     {
994         snew(DD, msd->ngrp);
995         snew(SigmaD, msd->ngrp);
996         for (j = 0; j < msd->ngrp; j++)
997         {
998             if (N >= 4)
999             {
1000                 lsq_y_ax_b(N/2, &(msd->time[i0]), &(msd->data[j][i0]), &a, &b, &r, &chi2);
1001                 lsq_y_ax_b(N/2, &(msd->time[i0+N/2]), &(msd->data[j][i0+N/2]), &a2, &b, &r, &chi2);
1002                 SigmaD[j] = std::abs(a-a2);
1003             }
1004             else
1005             {
1006                 SigmaD[j] = 0;
1007             }
1008             lsq_y_ax_b(N, &(msd->time[i0]), &(msd->data[j][i0]), &(DD[j]), &b, &r, &chi2);
1009             DD[j]     *= FACTOR/msd->dim_factor;
1010             SigmaD[j] *= FACTOR/msd->dim_factor;
1011             if (DD[j] > 0.01 && DD[j] < 1e4)
1012             {
1013                 fprintf(stdout, "D[%10s] %.4f (+/- %.4f) 1e-5 cm^2/s\n",
1014                         grpname[j], DD[j], SigmaD[j]);
1015             }
1016             else
1017             {
1018                 fprintf(stdout, "D[%10s] %.4g (+/- %.4g) 1e-5 cm^2/s\n",
1019                         grpname[j], DD[j], SigmaD[j]);
1020             }
1021         }
1022     }
1023     /* Print mean square displacement */
1024     corr_print(msd, bTen, msd_file,
1025                "Mean Square Displacement",
1026                "MSD (nm\\S2\\N)",
1027                msd->time[msd->nframes-1], beginfit, endfit, DD, SigmaD, grpname, oenv);
1028 }
1029
1030 int gmx_msd(int argc, char *argv[])
1031 {
1032     const char        *desc[] = {
1033         "[THISMODULE] computes the mean square displacement (MSD) of atoms from",
1034         "a set of initial positions. This provides an easy way to compute",
1035         "the diffusion constant using the Einstein relation.",
1036         "The time between the reference points for the MSD calculation",
1037         "is set with [TT]-trestart[tt].",
1038         "The diffusion constant is calculated by least squares fitting a",
1039         "straight line (D*t + c) through the MSD(t) from [TT]-beginfit[tt] to",
1040         "[TT]-endfit[tt] (note that t is time from the reference positions,",
1041         "not simulation time). An error estimate given, which is the difference",
1042         "of the diffusion coefficients obtained from fits over the two halves",
1043         "of the fit interval.[PAR]",
1044         "There are three, mutually exclusive, options to determine different",
1045         "types of mean square displacement: [TT]-type[tt], [TT]-lateral[tt]",
1046         "and [TT]-ten[tt]. Option [TT]-ten[tt] writes the full MSD tensor for",
1047         "each group, the order in the output is: trace xx yy zz yx zx zy.[PAR]",
1048         "If [TT]-mol[tt] is set, [THISMODULE] plots the MSD for individual molecules",
1049         "(including making molecules whole across periodic boundaries): ",
1050         "for each individual molecule a diffusion constant is computed for ",
1051         "its center of mass. The chosen index group will be split into ",
1052         "molecules.[PAR]",
1053         "The default way to calculate a MSD is by using mass-weighted averages.",
1054         "This can be turned off with [TT]-nomw[tt].[PAR]",
1055         "With the option [TT]-rmcomm[tt], the center of mass motion of a ",
1056         "specific group can be removed. For trajectories produced with ",
1057         "GROMACS this is usually not necessary, ",
1058         "as [gmx-mdrun] usually already removes the center of mass motion.",
1059         "When you use this option be sure that the whole system is stored",
1060         "in the trajectory file.[PAR]",
1061         "The diffusion coefficient is determined by linear regression of the MSD,",
1062         "where, unlike for the normal output of D, the times are weighted",
1063         "according to the number of reference points, i.e. short times have",
1064         "a higher weight. Also when [TT]-beginfit[tt] is -1, fitting starts at 10%",
1065         "and when [TT]-endfit[tt] is -1, fitting goes to 90%.",
1066         "Using this option one also gets an accurate error estimate",
1067         "based on the statistics between individual molecules.",
1068         "Note that this diffusion coefficient and error estimate are only",
1069         "accurate when the MSD is completely linear between",
1070         "[TT]-beginfit[tt] and [TT]-endfit[tt].[PAR]",
1071         "Option [TT]-pdb[tt] writes a [REF].pdb[ref] file with the coordinates of the frame",
1072         "at time [TT]-tpdb[tt] with in the B-factor field the square root of",
1073         "the diffusion coefficient of the molecule.",
1074         "This option implies option [TT]-mol[tt]."
1075     };
1076     static const char *normtype[] = { nullptr, "no", "x", "y", "z", nullptr };
1077     static const char *axtitle[]  = { nullptr, "no", "x", "y", "z", nullptr };
1078     static int         ngroup     = 1;
1079     static real        dt         = 10;
1080     static real        t_pdb      = 0;
1081     static real        beginfit   = -1;
1082     static real        endfit     = -1;
1083     static gmx_bool    bTen       = FALSE;
1084     static gmx_bool    bMW        = TRUE;
1085     static gmx_bool    bRmCOMM    = FALSE;
1086     t_pargs            pa[]       = {
1087         { "-type",    FALSE, etENUM, {normtype},
1088           "Compute diffusion coefficient in one direction" },
1089         { "-lateral", FALSE, etENUM, {axtitle},
1090           "Calculate the lateral diffusion in a plane perpendicular to" },
1091         { "-ten",      FALSE, etBOOL, {&bTen},
1092           "Calculate the full tensor" },
1093         { "-ngroup",  FALSE, etINT,  {&ngroup},
1094           "Number of groups to calculate MSD for" },
1095         { "-mw",      FALSE, etBOOL, {&bMW},
1096           "Mass weighted MSD" },
1097         { "-rmcomm",      FALSE, etBOOL, {&bRmCOMM},
1098           "Remove center of mass motion" },
1099         { "-tpdb", FALSE, etTIME, {&t_pdb},
1100           "The frame to use for option [TT]-pdb[tt] (%t)" },
1101         { "-trestart", FALSE, etTIME, {&dt},
1102           "Time between restarting points in trajectory (%t)" },
1103         { "-beginfit", FALSE, etTIME, {&beginfit},
1104           "Start time for fitting the MSD (%t), -1 is 10%" },
1105         { "-endfit", FALSE, etTIME, {&endfit},
1106           "End time for fitting the MSD (%t), -1 is 90%" }
1107     };
1108
1109     t_filenm           fnm[] = {
1110         { efTRX, nullptr, nullptr,  ffREAD },
1111         { efTPS, nullptr, nullptr,  ffREAD },
1112         { efNDX, nullptr, nullptr,  ffOPTRD },
1113         { efXVG, nullptr, "msd", ffWRITE },
1114         { efXVG, "-mol", "diff_mol", ffOPTWR },
1115         { efPDB, "-pdb", "diff_mol", ffOPTWR }
1116     };
1117 #define NFILE asize(fnm)
1118
1119     t_topology        top;
1120     int               ePBC;
1121     matrix            box;
1122     const char       *trx_file, *tps_file, *ndx_file, *msd_file, *mol_file, *pdb_file;
1123     rvec             *xdum;
1124     gmx_bool          bTop;
1125     int               axis, type;
1126     real              dim_factor;
1127     gmx_output_env_t *oenv;
1128
1129     if (!parse_common_args(&argc, argv,
1130                            PCA_CAN_VIEW | PCA_CAN_BEGIN | PCA_CAN_END | PCA_TIME_UNIT,
1131                            NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv))
1132     {
1133         return 0;
1134     }
1135     trx_file = ftp2fn_null(efTRX, NFILE, fnm);
1136     tps_file = ftp2fn_null(efTPS, NFILE, fnm);
1137     ndx_file = ftp2fn_null(efNDX, NFILE, fnm);
1138     msd_file = ftp2fn_null(efXVG, NFILE, fnm);
1139     pdb_file = opt2fn_null("-pdb", NFILE, fnm);
1140     if (pdb_file)
1141     {
1142         mol_file = opt2fn("-mol", NFILE, fnm);
1143     }
1144     else
1145     {
1146         mol_file = opt2fn_null("-mol", NFILE, fnm);
1147     }
1148
1149     if (ngroup < 1)
1150     {
1151         gmx_fatal(FARGS, "Must have at least 1 group (now %d)", ngroup);
1152     }
1153     if (mol_file && ngroup > 1)
1154     {
1155         gmx_fatal(FARGS, "With molecular msd can only have 1 group (now %d)",
1156                   ngroup);
1157     }
1158
1159
1160     if (mol_file)
1161     {
1162         bMW  = TRUE;
1163         fprintf(stderr, "Calculating diffusion coefficients for molecules.\n");
1164     }
1165
1166     GMX_RELEASE_ASSERT(normtype[0] != nullptr, "Options inconsistency; normtype[0] is NULL");
1167     GMX_RELEASE_ASSERT(axtitle[0] != nullptr, "Options inconsistency; axtitle[0] is NULL");
1168
1169     if (normtype[0][0] != 'n')
1170     {
1171         type       = normtype[0][0] - 'x' + X; /* See defines above */
1172         dim_factor = 2.0;
1173     }
1174     else
1175     {
1176         type       = NORMAL;
1177         dim_factor = 6.0;
1178     }
1179     if ((type == NORMAL) && (axtitle[0][0] != 'n'))
1180     {
1181         type       = LATERAL;
1182         dim_factor = 4.0;
1183         axis       = (axtitle[0][0] - 'x'); /* See defines above */
1184     }
1185     else
1186     {
1187         axis = 0;
1188     }
1189
1190     if (bTen && type != NORMAL)
1191     {
1192         gmx_fatal(FARGS, "Can only calculate the full tensor for 3D msd");
1193     }
1194
1195     bTop = read_tps_conf(tps_file, &top, &ePBC, &xdum, nullptr, box, bMW || bRmCOMM);
1196     if (mol_file && !bTop)
1197     {
1198         gmx_fatal(FARGS,
1199                   "Could not read a topology from %s. Try a tpr file instead.",
1200                   tps_file);
1201     }
1202
1203     do_corr(trx_file, ndx_file, msd_file, mol_file, pdb_file, t_pdb, ngroup,
1204             &top, ePBC, bTen, bMW, bRmCOMM, type, dim_factor, axis, dt, beginfit, endfit,
1205             oenv);
1206
1207     view_all(oenv, NFILE, fnm);
1208
1209     return 0;
1210 }