2 * This file is part of the GROMACS molecular simulation package.
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.
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.
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.
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.
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.
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.
42 #include "gromacs/commandline/pargs.h"
43 #include "gromacs/fileio/confio.h"
44 #include "gromacs/fileio/trxio.h"
45 #include "gromacs/fileio/xvgr.h"
46 #include "gromacs/gmxana/gmx_ana.h"
47 #include "gromacs/gmxana/gstat.h"
48 #include "gromacs/legacyheaders/macros.h"
49 #include "gromacs/legacyheaders/typedefs.h"
50 #include "gromacs/legacyheaders/viewit.h"
51 #include "gromacs/math/utilities.h"
52 #include "gromacs/math/vec.h"
53 #include "gromacs/pbcutil/rmpbc.h"
54 #include "gromacs/statistics/statistics.h"
55 #include "gromacs/topology/index.h"
56 #include "gromacs/utility/fatalerror.h"
57 #include "gromacs/utility/futil.h"
58 #include "gromacs/utility/smalloc.h"
60 #define FACTOR 1000.0 /* Convert nm^2/ps to 10e-5 cm^2/s */
61 /* NORMAL = total diffusion coefficient (default). X,Y,Z is diffusion
62 coefficient in X,Y,Z direction. LATERAL is diffusion coefficient in
63 plane perpendicular to axis
66 NOT_USED, NORMAL, X, Y, Z, LATERAL
70 real t0; /* start time and time increment between */
71 real delta_t; /* time between restart points */
72 real beginfit, /* the begin/end time for fits as reals between */
74 real dim_factor; /* the dimensionality factor for the diffusion
76 real **data; /* the displacement data. First index is the group
77 number, second is frame number */
78 real *time; /* frame time */
79 real *mass; /* masses for mass-weighted msd */
81 rvec **x0; /* original positions */
82 rvec *com; /* center of mass correction for each frame */
83 gmx_stats_t **lsq; /* fitting stats for individual molecule msds */
84 msd_type type; /* the type of msd to calculate (lateral, etc.)*/
85 int axis; /* the axis along which to calculate */
87 int nrestart; /* number of restart points */
88 int nmol; /* number of molecules (for bMol) */
89 int nframes; /* number of frames */
91 int ngrp; /* number of groups to use for msd calculation */
93 int **ndata; /* the number of msds (particles/mols) per data
97 typedef real t_calc_func (t_corr *curr, int nx, atom_id index[], int nx0, rvec xc[],
98 rvec dcom, gmx_bool bTen, matrix mat);
100 static real thistime(t_corr *curr)
102 return curr->time[curr->nframes];
105 static gmx_bool in_data(t_corr *curr, int nx00)
107 return curr->nframes-curr->n_offs[nx00];
110 t_corr *init_corr(int nrgrp, int type, int axis, real dim_factor,
111 int nmol, gmx_bool bTen, gmx_bool bMass, real dt, t_topology *top,
112 real beginfit, real endfit)
119 curr->type = (msd_type)type;
124 curr->beginfit = (1 - 2*GMX_REAL_EPS)*beginfit;
125 curr->endfit = (1 + 2*GMX_REAL_EPS)*endfit;
130 curr->dim_factor = dim_factor;
132 snew(curr->ndata, nrgrp);
133 snew(curr->data, nrgrp);
136 snew(curr->datam, nrgrp);
138 for (i = 0; (i < nrgrp); i++)
140 curr->ndata[i] = NULL;
141 curr->data[i] = NULL;
144 curr->datam[i] = NULL;
152 snew(curr->mass, curr->nmol);
153 for (i = 0; i < curr->nmol; i++)
163 snew(curr->mass, atoms->nr);
164 for (i = 0; (i < atoms->nr); i++)
166 curr->mass[i] = atoms->atom[i].m;
174 static void corr_print(t_corr *curr, gmx_bool bTen, const char *fn, const char *title,
176 real msdtime, real beginfit, real endfit,
177 real *DD, real *SigmaD, char *grpname[],
178 const output_env_t oenv)
183 out = xvgropen(fn, title, output_env_get_xvgr_tlabel(oenv), yaxis, oenv);
186 fprintf(out, "# MSD gathered over %g %s with %d restarts\n",
187 msdtime, output_env_get_time_unit(oenv), curr->nrestart);
188 fprintf(out, "# Diffusion constants fitted from time %g to %g %s\n",
189 beginfit, endfit, output_env_get_time_unit(oenv));
190 for (i = 0; i < curr->ngrp; i++)
192 fprintf(out, "# D[%10s] = %.4f (+/- %.4f) (1e-5 cm^2/s)\n",
193 grpname[i], DD[i], SigmaD[i]);
196 for (i = 0; i < curr->nframes; i++)
198 fprintf(out, "%10g", output_env_conv_time(oenv, curr->time[i]));
199 for (j = 0; j < curr->ngrp; j++)
201 fprintf(out, " %10g", curr->data[j][i]);
204 fprintf(out, " %10g %10g %10g %10g %10g %10g",
205 curr->datam[j][i][XX][XX],
206 curr->datam[j][i][YY][YY],
207 curr->datam[j][i][ZZ][ZZ],
208 curr->datam[j][i][YY][XX],
209 curr->datam[j][i][ZZ][XX],
210 curr->datam[j][i][ZZ][YY]);
218 /* called from corr_loop, to do the main calculations */
219 static void calc_corr(t_corr *curr, int nr, int nx, atom_id index[], rvec xc[],
220 gmx_bool bRmCOMM, rvec com, t_calc_func *calc1, gmx_bool bTen)
227 /* Check for new starting point */
228 if (curr->nlast < curr->nrestart)
230 if ((thistime(curr) >= (curr->nlast*curr->delta_t)) && (nr == 0))
232 memcpy(curr->x0[curr->nlast], xc, curr->ncoords*sizeof(xc[0]));
233 curr->n_offs[curr->nlast] = curr->nframes;
234 copy_rvec(com, curr->com[curr->nlast]);
239 /* nx0 appears to be the number of new starting points,
240 * so for all starting points, call calc1.
242 for (nx0 = 0; (nx0 < curr->nlast); nx0++)
246 rvec_sub(com, curr->com[nx0], dcom);
252 g = calc1(curr, nx, index, nx0, xc, dcom, bTen, mat);
254 printf("g[%d]=%g\n", nx0, g);
256 curr->data[nr][in_data(curr, nx0)] += g;
259 m_add(curr->datam[nr][in_data(curr, nx0)], mat,
260 curr->datam[nr][in_data(curr, nx0)]);
262 curr->ndata[nr][in_data(curr, nx0)]++;
266 /* the non-mass-weighted mean-squared displacement calcuation */
267 static real calc1_norm(t_corr *curr, int nx, atom_id index[], int nx0, rvec xc[],
268 rvec dcom, gmx_bool bTen, matrix mat)
277 for (i = 0; (i < nx); i++)
284 for (m = 0; (m < DIM); m++)
286 rv[m] = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
290 for (m2 = 0; m2 <= m; m2++)
292 mat[m][m2] += rv[m]*rv[m2];
300 r = xc[ix][curr->type-X] - curr->x0[nx0][ix][curr->type-X] -
305 for (m = 0; (m < DIM); m++)
309 r = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
315 gmx_fatal(FARGS, "Error: did not expect option value %d", curr->type);
320 msmul(mat, 1.0/nx, mat);
325 /* calculate the com of molecules in x and put it into xa */
326 static void calc_mol_com(int nmol, int *molindex, t_block *mols, t_atoms *atoms,
333 for (m = 0; m < nmol; m++)
338 for (i = mols->index[mol]; i < mols->index[mol+1]; i++)
340 mass = atoms->atom[i].m;
341 for (d = 0; d < DIM; d++)
343 xm[d] += mass*x[i][d];
347 svmul(1/mtot, xm, xa[m]);
351 static real calc_one_mw(t_corr *curr, int ix, int nx0, rvec xc[], real *tm,
352 rvec dcom, gmx_bool bTen, matrix mat)
368 for (m = 0; (m < DIM); m++)
370 rv[m] = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
371 r2 += mm*rv[m]*rv[m];
374 for (m2 = 0; m2 <= m; m2++)
376 mat[m][m2] += mm*rv[m]*rv[m2];
384 r = xc[ix][curr->type-X] - curr->x0[nx0][ix][curr->type-X] -
389 for (m = 0; (m < DIM); m++)
393 r = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
399 gmx_fatal(FARGS, "Options got screwed. Did not expect value %d\n", curr->type);
404 /* the normal, mass-weighted mean-squared displacement calcuation */
405 static real calc1_mw(t_corr *curr, int nx, atom_id index[], int nx0, rvec xc[],
406 rvec dcom, gmx_bool bTen, matrix mat)
413 for (i = 0; (i < nx); i++)
415 g += calc_one_mw(curr, index[i], nx0, xc, &tm, dcom, bTen, mat);
421 msmul(mat, 1/tm, mat);
427 /* prepare the coordinates by removing periodic boundary crossings.
428 gnx = the number of atoms/molecules
430 xcur = the current coordinates
431 xprev = the previous coordinates
432 box = the box matrix */
433 static void prep_data(gmx_bool bMol, int gnx, atom_id index[],
434 rvec xcur[], rvec xprev[], matrix box)
439 /* Remove periodicity */
440 for (m = 0; (m < DIM); m++)
442 hbox[m] = 0.5*box[m][m];
445 for (i = 0; (i < gnx); i++)
456 for (m = DIM-1; m >= 0; m--)
462 while (xcur[ind][m]-xprev[ind][m] <= -hbox[m])
464 rvec_inc(xcur[ind], box[m]);
466 while (xcur[ind][m]-xprev[ind][m] > hbox[m])
468 rvec_dec(xcur[ind], box[m]);
474 /* calculate the center of mass for a group
475 gnx = the number of atoms/molecules
477 xcur = the current coordinates
478 xprev = the previous coordinates
480 atoms = atom data (for mass)
481 com(output) = center of mass */
482 static void calc_com(gmx_bool bMol, int gnx, atom_id index[],
483 rvec xcur[], rvec xprev[], matrix box, t_atoms *atoms,
495 prep_data(bMol, gnx, index, xcur, xprev, box);
496 for (i = 0; (i < gnx); i++)
508 mass = atoms->atom[ind].m;
509 for (m = 0; m < DIM; m++)
511 sx[m] += mass*xcur[ind][m];
515 for (m = 0; m < DIM; m++)
517 com[m] = sx[m]/tmass;
522 static real calc1_mol(t_corr *curr, int nx, atom_id gmx_unused index[], int nx0, rvec xc[],
523 rvec dcom, gmx_bool bTen, matrix mat)
526 real g, tm, gtot, tt;
528 tt = curr->time[in_data(curr, nx0)];
532 for (i = 0; (i < nx); i++)
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 */
537 if (tt >= curr->beginfit && (curr->endfit < 0 || tt <= curr->endfit))
539 gmx_stats_add_point(curr->lsq[nx0][i], tt, g, 0, 0);
542 msmul(mat, 1.0/nx, mat);
547 void printmol(t_corr *curr, const char *fn,
548 const char *fn_pdb, int *molindex, t_topology *top,
549 rvec *x, int ePBC, matrix box, const output_env_t oenv)
555 real a, b, D, Dav, D2av, VarD, sqrtD, sqrtD_max, scale;
556 t_pdbinfo *pdbinfo = NULL;
559 out = xvgropen(fn, "Diffusion Coefficients / Molecule", "Molecule", "D", oenv);
563 if (top->atoms.pdbinfo == NULL)
565 snew(top->atoms.pdbinfo, top->atoms.nr);
567 pdbinfo = top->atoms.pdbinfo;
568 mol2a = top->mols.index;
573 for (i = 0; (i < curr->nmol); i++)
575 lsq1 = gmx_stats_init();
576 for (j = 0; (j < curr->nrestart); j++)
580 while (gmx_stats_get_point(curr->lsq[j][i], &xx, &yy, &dx, &dy, 0) == estatsOK)
582 gmx_stats_add_point(lsq1, xx, yy, dx, dy);
585 gmx_stats_get_ab(lsq1, elsqWEIGHT_NONE, &a, &b, NULL, NULL, NULL, NULL);
586 gmx_stats_done(lsq1);
588 D = a*FACTOR/curr->dim_factor;
595 fprintf(out, "%10d %10g\n", i, D);
599 if (sqrtD > sqrtD_max)
603 for (j = mol2a[molindex[i]]; j < mol2a[molindex[i]+1]; j++)
605 pdbinfo[j].bfac = sqrtD;
610 do_view(oenv, fn, "-graphtype bar");
612 /* Compute variance, stddev and error */
615 VarD = D2av - sqr(Dav);
616 printf("<D> = %.4f Std. Dev. = %.4f Error = %.4f\n",
617 Dav, sqrt(VarD), sqrt(VarD/curr->nmol));
622 while (scale*sqrtD_max > 10)
626 while (scale*sqrtD_max < 0.1)
630 for (i = 0; i < top->atoms.nr; i++)
632 pdbinfo[i].bfac *= scale;
634 write_sto_conf(fn_pdb, "molecular MSD", &top->atoms, x, NULL, ePBC, box);
638 /* this is the main loop for the correlation type functions
639 * fx and nx are file pointers to things like read_first_x and
642 int corr_loop(t_corr *curr, const char *fn, t_topology *top, int ePBC,
643 gmx_bool bMol, int gnx[], atom_id *index[],
644 t_calc_func *calc1, gmx_bool bTen, int *gnx_com, atom_id *index_com[],
645 real dt, real t_pdb, rvec **x_pdb, matrix box_pdb,
646 const output_env_t oenv)
648 rvec *x[2]; /* the coordinates to read */
649 rvec *xa[2]; /* the coordinates to calculate displacements for */
652 int natoms, i, j, cur = 0, maxframes = 0;
657 gmx_rmpbc_t gpbc = NULL;
659 natoms = read_first_x(oenv, &status, fn, &curr->t0, &(x[cur]), box);
661 fprintf(stderr, "Read %d atoms for first frame\n", natoms);
663 if ((gnx_com != NULL) && natoms < top->atoms.nr)
665 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);
668 snew(x[prev], natoms);
672 curr->ncoords = curr->nmol;
673 snew(xa[0], curr->ncoords);
674 snew(xa[1], curr->ncoords);
678 curr->ncoords = natoms;
692 gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
695 /* the loop over all frames */
698 if (x_pdb && ((bFirst && t_pdb < t) ||
700 t_pdb > t - 0.5*(t - t_prev) &&
701 t_pdb < t + 0.5*(t - t_prev))))
705 snew(*x_pdb, natoms);
707 for (i = 0; i < natoms; i++)
709 copy_rvec(x[cur][i], (*x_pdb)[i]);
711 copy_mat(box, box_pdb);
715 /* check whether we've reached a restart point */
716 if (bRmod(t, curr->t0, dt))
720 srenew(curr->x0, curr->nrestart);
721 snew(curr->x0[curr->nrestart-1], curr->ncoords);
722 srenew(curr->com, curr->nrestart);
723 srenew(curr->n_offs, curr->nrestart);
724 srenew(curr->lsq, curr->nrestart);
725 snew(curr->lsq[curr->nrestart-1], curr->nmol);
726 for (i = 0; i < curr->nmol; i++)
728 curr->lsq[curr->nrestart-1][i] = gmx_stats_init();
733 fprintf(debug, "Extended data structures because of new restart %d\n",
737 /* create or extend the frame-based arrays */
738 if (curr->nframes >= maxframes-1)
742 for (i = 0; (i < curr->ngrp); i++)
744 curr->ndata[i] = NULL;
745 curr->data[i] = NULL;
748 curr->datam[i] = NULL;
754 for (i = 0; (i < curr->ngrp); i++)
756 srenew(curr->ndata[i], maxframes);
757 srenew(curr->data[i], maxframes);
760 srenew(curr->datam[i], maxframes);
762 for (j = maxframes-10; j < maxframes; j++)
764 curr->ndata[i][j] = 0;
765 curr->data[i][j] = 0;
768 clear_mat(curr->datam[i][j]);
772 srenew(curr->time, maxframes);
776 curr->time[curr->nframes] = t - curr->t0;
778 /* for the first frame, the previous frame is a copy of the first frame */
781 memcpy(xa[prev], xa[cur], curr->ncoords*sizeof(xa[prev][0]));
785 /* make the molecules whole */
788 gmx_rmpbc(gpbc, natoms, box, x[cur]);
791 /* calculate the molecules' centers of masses and put them into xa */
794 calc_mol_com(gnx[0], index[0], &top->mols, &top->atoms, x[cur], xa[cur]);
797 /* first remove the periodic boundary condition crossings */
798 for (i = 0; i < curr->ngrp; i++)
800 prep_data(bMol, gnx[i], index[i], xa[cur], xa[prev], box);
803 /* calculate the center of mass */
806 prep_data(bMol, gnx_com[0], index_com[0], xa[cur], xa[prev], box);
807 calc_com(bMol, gnx_com[0], index_com[0], xa[cur], xa[prev], box,
811 /* loop over all groups in index file */
812 for (i = 0; (i < curr->ngrp); i++)
814 /* calculate something useful, like mean square displacements */
815 calc_corr(curr, i, gnx[i], index[i], xa[cur], (gnx_com != NULL), com,
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",
826 output_env_conv_time(oenv, dt), output_env_get_time_unit(oenv),
827 output_env_conv_time(oenv, curr->time[curr->nframes-1]),
828 output_env_get_time_unit(oenv) );
832 gmx_rmpbc_done(gpbc);
840 static void index_atom2mol(int *n, int *index, t_block *mols)
842 int nat, i, nmol, mol, j;
850 while (index[i] > mols->index[mol])
855 gmx_fatal(FARGS, "Atom index out of range: %d", index[i]+1);
858 for (j = mols->index[mol]; j < mols->index[mol+1]; j++)
860 if (i >= nat || index[i] != j)
862 gmx_fatal(FARGS, "The index group does not consist of whole molecules");
869 fprintf(stderr, "Split group of %d atoms into %d molecules\n", nat, nmol);
874 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 output_env_t oenv)
882 int *gnx; /* the selected groups' sizes */
883 atom_id **index; /* selected groups' indices */
885 int i, i0, i1, j, N, nat_trx;
886 real *DD, *SigmaD, a, a2, b, r, chi2;
889 int *gnx_com = NULL; /* the COM removal group size */
890 atom_id **index_com = NULL; /* the COM removal group atom indices */
891 char **grpname_com = NULL; /* the COM removal group name */
895 snew(grpname, nrgrp);
897 fprintf(stderr, "\nSelect a group to calculate mean squared displacement for:\n");
898 get_index(&top->atoms, ndx_file, nrgrp, gnx, index, grpname);
904 snew(grpname_com, 1);
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);
912 index_atom2mol(&gnx[0], index[0], &top->mols);
915 msd = init_corr(nrgrp, type, axis, dim_factor,
916 mol_file == NULL ? 0 : gnx[0], bTen, bMW, dt, top,
920 corr_loop(msd, trx_file, top, ePBC, mol_file ? gnx[0] : 0, gnx, index,
921 (mol_file != NULL) ? calc1_mol : (bMW ? calc1_mw : calc1_norm),
922 bTen, gnx_com, index_com, dt, t_pdb,
923 pdb_file ? &x : NULL, box, oenv);
925 /* Correct for the number of points */
926 for (j = 0; (j < msd->ngrp); j++)
928 for (i = 0; (i < msd->nframes); i++)
930 msd->data[j][i] /= msd->ndata[j][i];
933 msmul(msd->datam[j][i], 1.0/msd->ndata[j][i], msd->datam[j][i]);
940 if (pdb_file && x == NULL)
942 fprintf(stderr, "\nNo frame found need time tpdb = %g ps\n"
943 "Can not write %s\n\n", t_pdb, pdb_file);
946 top->atoms.nr = nat_trx;
947 printmol(msd, mol_file, pdb_file, index[0], top, x, ePBC, box, oenv);
956 i0 = (int)(0.1*(msd->nframes - 1) + 0.5);
957 beginfit = msd->time[i0];
961 for (i0 = 0; i0 < msd->nframes && msd->time[i0] < beginfit; i0++)
969 i1 = (int)(0.9*(msd->nframes - 1) + 0.5) + 1;
970 endfit = msd->time[i1-1];
974 for (i1 = i0; i1 < msd->nframes && msd->time[i1] <= endfit; i1++)
979 fprintf(stdout, "Fitting from %g to %g %s\n\n", beginfit, endfit,
980 output_env_get_time_unit(oenv));
985 fprintf(stdout, "Not enough points for fitting (%d).\n"
986 "Can not determine the diffusion constant.\n", N);
991 snew(SigmaD, msd->ngrp);
992 for (j = 0; j < msd->ngrp; j++)
996 lsq_y_ax_b(N/2, &(msd->time[i0]), &(msd->data[j][i0]), &a, &b, &r, &chi2);
997 lsq_y_ax_b(N/2, &(msd->time[i0+N/2]), &(msd->data[j][i0+N/2]), &a2, &b, &r, &chi2);
998 SigmaD[j] = fabs(a-a2);
1004 lsq_y_ax_b(N, &(msd->time[i0]), &(msd->data[j][i0]), &(DD[j]), &b, &r, &chi2);
1005 DD[j] *= FACTOR/msd->dim_factor;
1006 SigmaD[j] *= FACTOR/msd->dim_factor;
1007 if (DD[j] > 0.01 && DD[j] < 1e4)
1009 fprintf(stdout, "D[%10s] %.4f (+/- %.4f) 1e-5 cm^2/s\n",
1010 grpname[j], DD[j], SigmaD[j]);
1014 fprintf(stdout, "D[%10s] %.4g (+/- %.4g) 1e-5 cm^2/s\n",
1015 grpname[j], DD[j], SigmaD[j]);
1019 /* Print mean square displacement */
1020 corr_print(msd, bTen, msd_file,
1021 "Mean Square Displacement",
1023 msd->time[msd->nframes-1], beginfit, endfit, DD, SigmaD, grpname, oenv);
1026 int gmx_msd(int argc, char *argv[])
1028 const char *desc[] = {
1029 "[THISMODULE] computes the mean square displacement (MSD) of atoms from",
1030 "a set of initial positions. This provides an easy way to compute",
1031 "the diffusion constant using the Einstein relation.",
1032 "The time between the reference points for the MSD calculation",
1033 "is set with [TT]-trestart[tt].",
1034 "The diffusion constant is calculated by least squares fitting a",
1035 "straight line (D*t + c) through the MSD(t) from [TT]-beginfit[tt] to",
1036 "[TT]-endfit[tt] (note that t is time from the reference positions,",
1037 "not simulation time). An error estimate given, which is the difference",
1038 "of the diffusion coefficients obtained from fits over the two halves",
1039 "of the fit interval.[PAR]",
1040 "There are three, mutually exclusive, options to determine different",
1041 "types of mean square displacement: [TT]-type[tt], [TT]-lateral[tt]",
1042 "and [TT]-ten[tt]. Option [TT]-ten[tt] writes the full MSD tensor for",
1043 "each group, the order in the output is: trace xx yy zz yx zx zy.[PAR]",
1044 "If [TT]-mol[tt] is set, [THISMODULE] plots the MSD for individual molecules",
1045 "(including making molecules whole across periodic boundaries): ",
1046 "for each individual molecule a diffusion constant is computed for ",
1047 "its center of mass. The chosen index group will be split into ",
1049 "The default way to calculate a MSD is by using mass-weighted averages.",
1050 "This can be turned off with [TT]-nomw[tt].[PAR]",
1051 "With the option [TT]-rmcomm[tt], the center of mass motion of a ",
1052 "specific group can be removed. For trajectories produced with ",
1053 "GROMACS this is usually not necessary, ",
1054 "as [gmx-mdrun] usually already removes the center of mass motion.",
1055 "When you use this option be sure that the whole system is stored",
1056 "in the trajectory file.[PAR]",
1057 "The diffusion coefficient is determined by linear regression of the MSD,",
1058 "where, unlike for the normal output of D, the times are weighted",
1059 "according to the number of reference points, i.e. short times have",
1060 "a higher weight. Also when [TT]-beginfit[tt]=-1,fitting starts at 10%",
1061 "and when [TT]-endfit[tt]=-1, fitting goes to 90%.",
1062 "Using this option one also gets an accurate error estimate",
1063 "based on the statistics between individual molecules.",
1064 "Note that this diffusion coefficient and error estimate are only",
1065 "accurate when the MSD is completely linear between",
1066 "[TT]-beginfit[tt] and [TT]-endfit[tt].[PAR]",
1067 "Option [TT]-pdb[tt] writes a [REF].pdb[ref] file with the coordinates of the frame",
1068 "at time [TT]-tpdb[tt] with in the B-factor field the square root of",
1069 "the diffusion coefficient of the molecule.",
1070 "This option implies option [TT]-mol[tt]."
1072 static const char *normtype[] = { NULL, "no", "x", "y", "z", NULL };
1073 static const char *axtitle[] = { NULL, "no", "x", "y", "z", NULL };
1074 static int ngroup = 1;
1075 static real dt = 10;
1076 static real t_pdb = 0;
1077 static real beginfit = -1;
1078 static real endfit = -1;
1079 static gmx_bool bTen = FALSE;
1080 static gmx_bool bMW = TRUE;
1081 static gmx_bool bRmCOMM = FALSE;
1083 { "-type", FALSE, etENUM, {normtype},
1084 "Compute diffusion coefficient in one direction" },
1085 { "-lateral", FALSE, etENUM, {axtitle},
1086 "Calculate the lateral diffusion in a plane perpendicular to" },
1087 { "-ten", FALSE, etBOOL, {&bTen},
1088 "Calculate the full tensor" },
1089 { "-ngroup", FALSE, etINT, {&ngroup},
1090 "Number of groups to calculate MSD for" },
1091 { "-mw", FALSE, etBOOL, {&bMW},
1092 "Mass weighted MSD" },
1093 { "-rmcomm", FALSE, etBOOL, {&bRmCOMM},
1094 "Remove center of mass motion" },
1095 { "-tpdb", FALSE, etTIME, {&t_pdb},
1096 "The frame to use for option [TT]-pdb[tt] (%t)" },
1097 { "-trestart", FALSE, etTIME, {&dt},
1098 "Time between restarting points in trajectory (%t)" },
1099 { "-beginfit", FALSE, etTIME, {&beginfit},
1100 "Start time for fitting the MSD (%t), -1 is 10%" },
1101 { "-endfit", FALSE, etTIME, {&endfit},
1102 "End time for fitting the MSD (%t), -1 is 90%" }
1106 { efTRX, NULL, NULL, ffREAD },
1107 { efTPS, NULL, NULL, ffREAD },
1108 { efNDX, NULL, NULL, ffOPTRD },
1109 { efXVG, NULL, "msd", ffWRITE },
1110 { efXVG, "-mol", "diff_mol", ffOPTWR },
1111 { efPDB, "-pdb", "diff_mol", ffOPTWR }
1113 #define NFILE asize(fnm)
1119 const char *trx_file, *tps_file, *ndx_file, *msd_file, *mol_file, *pdb_file;
1126 if (!parse_common_args(&argc, argv,
1127 PCA_CAN_VIEW | PCA_CAN_BEGIN | PCA_CAN_END | PCA_TIME_UNIT,
1128 NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
1132 trx_file = ftp2fn_null(efTRX, NFILE, fnm);
1133 tps_file = ftp2fn_null(efTPS, NFILE, fnm);
1134 ndx_file = ftp2fn_null(efNDX, NFILE, fnm);
1135 msd_file = ftp2fn_null(efXVG, NFILE, fnm);
1136 pdb_file = opt2fn_null("-pdb", NFILE, fnm);
1139 mol_file = opt2fn("-mol", NFILE, fnm);
1143 mol_file = opt2fn_null("-mol", NFILE, fnm);
1148 gmx_fatal(FARGS, "Must have at least 1 group (now %d)", ngroup);
1150 if (mol_file && ngroup > 1)
1152 gmx_fatal(FARGS, "With molecular msd can only have 1 group (now %d)",
1160 fprintf(stderr, "Calculating diffusion coefficients for molecules.\n");
1163 if (normtype[0][0] != 'n')
1165 type = normtype[0][0] - 'x' + X; /* See defines above */
1173 if ((type == NORMAL) && (axtitle[0][0] != 'n'))
1177 axis = (axtitle[0][0] - 'x'); /* See defines above */
1184 if (bTen && type != NORMAL)
1186 gmx_fatal(FARGS, "Can only calculate the full tensor for 3D msd");
1189 bTop = read_tps_conf(tps_file, title, &top, &ePBC, &xdum, NULL, box, bMW || bRmCOMM);
1190 if (mol_file && !bTop)
1193 "Could not read a topology from %s. Try a tpr file instead.",
1197 do_corr(trx_file, ndx_file, msd_file, mol_file, pdb_file, t_pdb, ngroup,
1198 &top, ePBC, bTen, bMW, bRmCOMM, type, dim_factor, axis, dt, beginfit, endfit,
1201 view_all(oenv, NFILE, fnm);