C++ math function cleanup
[gromacs/AngularHB.git] / src / gromacs / gmxana / gmx_msd.cpp
blob7f4eac4286d9b25d3cfc922aa8481b4a1b561ba9
1 /*
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.
37 #include "gmxpre.h"
39 #include <cmath>
40 #include <cstring>
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"
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
67 typedef enum {
68 NOT_USED, NORMAL, X, Y, Z, LATERAL
69 } msd_type;
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;
99 typedef real t_calc_func (t_corr *curr, int nx, int index[], int nx0, rvec xc[],
100 rvec dcom, gmx_bool bTen, matrix mat);
102 static real thistime(t_corr *curr)
104 return curr->time[curr->nframes];
107 static gmx_bool in_data(t_corr *curr, int nx00)
109 return curr->nframes-curr->n_offs[nx00];
112 t_corr *init_corr(int nrgrp, int type, int axis, real dim_factor,
113 int nmol, gmx_bool bTen, gmx_bool bMass, real dt, t_topology *top,
114 real beginfit, real endfit)
116 t_corr *curr;
117 t_atoms *atoms;
118 int i;
120 snew(curr, 1);
121 curr->type = (msd_type)type;
122 curr->axis = axis;
123 curr->ngrp = nrgrp;
124 curr->nrestart = 0;
125 curr->delta_t = dt;
126 curr->beginfit = (1 - 2*GMX_REAL_EPS)*beginfit;
127 curr->endfit = (1 + 2*GMX_REAL_EPS)*endfit;
128 curr->x0 = NULL;
129 curr->n_offs = NULL;
130 curr->nframes = 0;
131 curr->nlast = 0;
132 curr->dim_factor = dim_factor;
134 snew(curr->ndata, nrgrp);
135 snew(curr->data, nrgrp);
136 if (bTen)
138 snew(curr->datam, nrgrp);
140 for (i = 0; (i < nrgrp); i++)
142 curr->ndata[i] = NULL;
143 curr->data[i] = NULL;
144 if (bTen)
146 curr->datam[i] = NULL;
149 curr->time = NULL;
150 curr->lsq = NULL;
151 curr->nmol = nmol;
152 if (curr->nmol > 0)
154 snew(curr->mass, curr->nmol);
155 for (i = 0; i < curr->nmol; i++)
157 curr->mass[i] = 1;
160 else
162 if (bMass)
164 atoms = &top->atoms;
165 snew(curr->mass, atoms->nr);
166 for (i = 0; (i < atoms->nr); i++)
168 curr->mass[i] = atoms->atom[i].m;
173 return curr;
176 static void corr_print(t_corr *curr, gmx_bool bTen, const char *fn, const char *title,
177 const char *yaxis,
178 real msdtime, real beginfit, real endfit,
179 real *DD, real *SigmaD, char *grpname[],
180 const gmx_output_env_t *oenv)
182 FILE *out;
183 int i, j;
185 out = xvgropen(fn, title, output_env_get_xvgr_tlabel(oenv), yaxis, oenv);
186 if (DD)
188 fprintf(out, "# MSD gathered over %g %s with %d restarts\n",
189 msdtime, output_env_get_time_unit(oenv), curr->nrestart);
190 fprintf(out, "# Diffusion constants fitted from time %g to %g %s\n",
191 beginfit, endfit, output_env_get_time_unit(oenv));
192 for (i = 0; i < curr->ngrp; i++)
194 fprintf(out, "# D[%10s] = %.4f (+/- %.4f) (1e-5 cm^2/s)\n",
195 grpname[i], DD[i], SigmaD[i]);
198 for (i = 0; i < curr->nframes; i++)
200 fprintf(out, "%10g", output_env_conv_time(oenv, curr->time[i]));
201 for (j = 0; j < curr->ngrp; j++)
203 fprintf(out, " %10g", curr->data[j][i]);
204 if (bTen)
206 fprintf(out, " %10g %10g %10g %10g %10g %10g",
207 curr->datam[j][i][XX][XX],
208 curr->datam[j][i][YY][YY],
209 curr->datam[j][i][ZZ][ZZ],
210 curr->datam[j][i][YY][XX],
211 curr->datam[j][i][ZZ][XX],
212 curr->datam[j][i][ZZ][YY]);
215 fprintf(out, "\n");
217 xvgrclose(out);
220 /* called from corr_loop, to do the main calculations */
221 static void calc_corr(t_corr *curr, int nr, int nx, int index[], rvec xc[],
222 gmx_bool bRmCOMM, rvec com, t_calc_func *calc1, gmx_bool bTen)
224 int nx0;
225 real g;
226 matrix mat;
227 rvec dcom;
229 /* Check for new starting point */
230 if (curr->nlast < curr->nrestart)
232 if ((thistime(curr) >= (curr->nlast*curr->delta_t)) && (nr == 0))
234 std::memcpy(curr->x0[curr->nlast], xc, curr->ncoords*sizeof(xc[0]));
235 curr->n_offs[curr->nlast] = curr->nframes;
236 copy_rvec(com, curr->com[curr->nlast]);
237 curr->nlast++;
241 /* nx0 appears to be the number of new starting points,
242 * so for all starting points, call calc1.
244 for (nx0 = 0; (nx0 < curr->nlast); nx0++)
246 if (bRmCOMM)
248 rvec_sub(com, curr->com[nx0], dcom);
250 else
252 clear_rvec(dcom);
254 g = calc1(curr, nx, index, nx0, xc, dcom, bTen, mat);
255 #ifdef DEBUG2
256 printf("g[%d]=%g\n", nx0, g);
257 #endif
258 curr->data[nr][in_data(curr, nx0)] += g;
259 if (bTen)
261 m_add(curr->datam[nr][in_data(curr, nx0)], mat,
262 curr->datam[nr][in_data(curr, nx0)]);
264 curr->ndata[nr][in_data(curr, nx0)]++;
268 /* the non-mass-weighted mean-squared displacement calcuation */
269 static real calc1_norm(t_corr *curr, int nx, int index[], int nx0, rvec xc[],
270 rvec dcom, gmx_bool bTen, matrix mat)
272 int i, ix, m, m2;
273 real g, r, r2;
274 rvec rv;
276 g = 0.0;
277 clear_mat(mat);
279 for (i = 0; (i < nx); i++)
281 ix = index[i];
282 r2 = 0.0;
283 switch (curr->type)
285 case NORMAL:
286 for (m = 0; (m < DIM); m++)
288 rv[m] = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
289 r2 += rv[m]*rv[m];
290 if (bTen)
292 for (m2 = 0; m2 <= m; m2++)
294 mat[m][m2] += rv[m]*rv[m2];
298 break;
299 case X:
300 case Y:
301 case Z:
302 r = xc[ix][curr->type-X] - curr->x0[nx0][ix][curr->type-X] -
303 dcom[curr->type-X];
304 r2 += r*r;
305 break;
306 case LATERAL:
307 for (m = 0; (m < DIM); m++)
309 if (m != curr->axis)
311 r = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
312 r2 += r*r;
315 break;
316 default:
317 gmx_fatal(FARGS, "Error: did not expect option value %d", curr->type);
319 g += r2;
321 g /= nx;
322 msmul(mat, 1.0/nx, mat);
324 return g;
327 /* calculate the com of molecules in x and put it into xa */
328 static void calc_mol_com(int nmol, int *molindex, t_block *mols, t_atoms *atoms,
329 rvec *x, rvec *xa)
331 int m, mol, i, d;
332 rvec xm;
333 real mass, mtot;
335 for (m = 0; m < nmol; m++)
337 mol = molindex[m];
338 clear_rvec(xm);
339 mtot = 0;
340 for (i = mols->index[mol]; i < mols->index[mol+1]; i++)
342 mass = atoms->atom[i].m;
343 for (d = 0; d < DIM; d++)
345 xm[d] += mass*x[i][d];
347 mtot += mass;
349 svmul(1/mtot, xm, xa[m]);
353 static real calc_one_mw(t_corr *curr, int ix, int nx0, rvec xc[], real *tm,
354 rvec dcom, gmx_bool bTen, matrix mat)
356 real r2, r, mm;
357 rvec rv;
358 int m, m2;
360 mm = curr->mass[ix];
361 if (mm == 0)
363 return 0;
365 (*tm) += mm;
366 r2 = 0.0;
367 switch (curr->type)
369 case NORMAL:
370 for (m = 0; (m < DIM); m++)
372 rv[m] = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
373 r2 += mm*rv[m]*rv[m];
374 if (bTen)
376 for (m2 = 0; m2 <= m; m2++)
378 mat[m][m2] += mm*rv[m]*rv[m2];
382 break;
383 case X:
384 case Y:
385 case Z:
386 r = xc[ix][curr->type-X] - curr->x0[nx0][ix][curr->type-X] -
387 dcom[curr->type-X];
388 r2 = mm*r*r;
389 break;
390 case LATERAL:
391 for (m = 0; (m < DIM); m++)
393 if (m != curr->axis)
395 r = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
396 r2 += mm*r*r;
399 break;
400 default:
401 gmx_fatal(FARGS, "Options got screwed. Did not expect value %d\n", curr->type);
402 } /* end switch */
403 return r2;
406 /* the normal, mass-weighted mean-squared displacement calcuation */
407 static real calc1_mw(t_corr *curr, int nx, int index[], int nx0, rvec xc[],
408 rvec dcom, gmx_bool bTen, matrix mat)
410 int i;
411 real g, tm;
413 g = tm = 0.0;
414 clear_mat(mat);
415 for (i = 0; (i < nx); i++)
417 g += calc_one_mw(curr, index[i], nx0, xc, &tm, dcom, bTen, mat);
420 g /= tm;
421 if (bTen)
423 msmul(mat, 1/tm, mat);
426 return g;
429 /* prepare the coordinates by removing periodic boundary crossings.
430 gnx = the number of atoms/molecules
431 index = the indices
432 xcur = the current coordinates
433 xprev = the previous coordinates
434 box = the box matrix */
435 static void prep_data(gmx_bool bMol, int gnx, int index[],
436 rvec xcur[], rvec xprev[], matrix box)
438 int i, m, ind;
439 rvec hbox;
441 /* Remove periodicity */
442 for (m = 0; (m < DIM); m++)
444 hbox[m] = 0.5*box[m][m];
447 for (i = 0; (i < gnx); i++)
449 if (bMol)
451 ind = i;
453 else
455 ind = index[i];
458 for (m = DIM-1; m >= 0; m--)
460 if (hbox[m] == 0)
462 continue;
464 while (xcur[ind][m]-xprev[ind][m] <= -hbox[m])
466 rvec_inc(xcur[ind], box[m]);
468 while (xcur[ind][m]-xprev[ind][m] > hbox[m])
470 rvec_dec(xcur[ind], box[m]);
476 /* calculate the center of mass for a group
477 gnx = the number of atoms/molecules
478 index = the indices
479 xcur = the current coordinates
480 xprev = the previous coordinates
481 box = the box matrix
482 atoms = atom data (for mass)
483 com(output) = center of mass */
484 static void calc_com(gmx_bool bMol, int gnx, int index[],
485 rvec xcur[], rvec xprev[], matrix box, t_atoms *atoms,
486 rvec com)
488 int i, m, ind;
489 real mass;
490 double tmass;
491 dvec sx;
493 clear_dvec(sx);
494 tmass = 0;
496 prep_data(bMol, gnx, index, xcur, xprev, box);
497 for (i = 0; (i < gnx); i++)
499 if (bMol)
501 ind = i;
503 else
505 ind = index[i];
509 mass = atoms->atom[ind].m;
510 for (m = 0; m < DIM; m++)
512 sx[m] += mass*xcur[ind][m];
514 tmass += mass;
516 for (m = 0; m < DIM; m++)
518 com[m] = sx[m]/tmass;
523 static real calc1_mol(t_corr *curr, int nx, int gmx_unused index[], int nx0, rvec xc[],
524 rvec dcom, gmx_bool bTen, matrix mat)
526 int i;
527 real g, tm, gtot, tt;
529 tt = curr->time[in_data(curr, nx0)];
530 gtot = 0;
531 tm = 0;
532 clear_mat(mat);
533 for (i = 0; (i < nx); i++)
535 g = calc_one_mw(curr, i, nx0, xc, &tm, dcom, bTen, mat);
536 /* We don't need to normalize as the mass was set to 1 */
537 gtot += g;
538 if (tt >= curr->beginfit && (curr->endfit < 0 || tt <= curr->endfit))
540 gmx_stats_add_point(curr->lsq[nx0][i], tt, g, 0, 0);
543 msmul(mat, 1.0/nx, mat);
545 return gtot/nx;
548 void printmol(t_corr *curr, const char *fn,
549 const char *fn_pdb, int *molindex, t_topology *top,
550 rvec *x, int ePBC, matrix box, const gmx_output_env_t *oenv)
552 #define NDIST 100
553 FILE *out;
554 gmx_stats_t lsq1;
555 int i, j;
556 real a, b, D, Dav, D2av, VarD, sqrtD, sqrtD_max, scale;
557 t_pdbinfo *pdbinfo = NULL;
558 int *mol2a = NULL;
560 out = xvgropen(fn, "Diffusion Coefficients / Molecule", "Molecule", "D", oenv);
562 if (fn_pdb)
564 if (top->atoms.pdbinfo == NULL)
566 snew(top->atoms.pdbinfo, top->atoms.nr);
568 pdbinfo = top->atoms.pdbinfo;
569 mol2a = top->mols.index;
572 Dav = D2av = 0;
573 sqrtD_max = 0;
574 for (i = 0; (i < curr->nmol); i++)
576 lsq1 = gmx_stats_init();
577 for (j = 0; (j < curr->nrestart); j++)
579 real xx, yy, dx, dy;
581 while (gmx_stats_get_point(curr->lsq[j][i], &xx, &yy, &dx, &dy, 0) == estatsOK)
583 gmx_stats_add_point(lsq1, xx, yy, dx, dy);
586 gmx_stats_get_ab(lsq1, elsqWEIGHT_NONE, &a, &b, NULL, NULL, NULL, NULL);
587 gmx_stats_free(lsq1);
588 D = a*FACTOR/curr->dim_factor;
589 if (D < 0)
591 D = 0;
593 Dav += D;
594 D2av += gmx::square(D);
595 fprintf(out, "%10d %10g\n", i, D);
596 if (pdbinfo)
598 sqrtD = std::sqrt(D);
599 if (sqrtD > sqrtD_max)
601 sqrtD_max = sqrtD;
603 for (j = mol2a[molindex[i]]; j < mol2a[molindex[i]+1]; j++)
605 pdbinfo[j].bfac = sqrtD;
609 xvgrclose(out);
610 do_view(oenv, fn, "-graphtype bar");
612 /* Compute variance, stddev and error */
613 Dav /= curr->nmol;
614 D2av /= curr->nmol;
615 VarD = D2av - gmx::square(Dav);
616 printf("<D> = %.4f Std. Dev. = %.4f Error = %.4f\n",
617 Dav, std::sqrt(VarD), std::sqrt(VarD/curr->nmol));
619 if (fn_pdb && x)
621 scale = 1;
622 while (scale*sqrtD_max > 10)
624 scale *= 0.1;
626 while (scale*sqrtD_max < 0.1)
628 scale *= 10;
630 GMX_RELEASE_ASSERT(pdbinfo != NULL, "Internal error - pdbinfo not set for PDB input");
631 for (i = 0; i < top->atoms.nr; i++)
633 pdbinfo[i].bfac *= scale;
635 write_sto_conf(fn_pdb, "molecular MSD", &top->atoms, x, NULL, ePBC, box);
639 /* this is the main loop for the correlation type functions
640 * fx and nx are file pointers to things like read_first_x and
641 * read_next_x
643 int corr_loop(t_corr *curr, const char *fn, t_topology *top, int ePBC,
644 gmx_bool bMol, int gnx[], int *index[],
645 t_calc_func *calc1, gmx_bool bTen, int *gnx_com, int *index_com[],
646 real dt, real t_pdb, rvec **x_pdb, matrix box_pdb,
647 const gmx_output_env_t *oenv)
649 rvec *x[2]; /* the coordinates to read */
650 rvec *xa[2]; /* the coordinates to calculate displacements for */
651 rvec com = {0};
652 real t, t_prev = 0;
653 int natoms, i, j, cur = 0, maxframes = 0;
654 t_trxstatus *status;
655 #define prev (1-cur)
656 matrix box;
657 gmx_bool bFirst;
658 gmx_rmpbc_t gpbc = NULL;
660 natoms = read_first_x(oenv, &status, fn, &curr->t0, &(x[cur]), box);
661 #ifdef DEBUG
662 fprintf(stderr, "Read %d atoms for first frame\n", natoms);
663 #endif
664 if ((gnx_com != NULL) && natoms < top->atoms.nr)
666 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);
669 snew(x[prev], natoms);
671 if (bMol)
673 curr->ncoords = curr->nmol;
674 snew(xa[0], curr->ncoords);
675 snew(xa[1], curr->ncoords);
677 else
679 curr->ncoords = natoms;
680 xa[0] = x[0];
681 xa[1] = x[1];
684 bFirst = TRUE;
685 t = curr->t0;
686 if (x_pdb)
688 *x_pdb = NULL;
691 if (bMol)
693 gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
696 /* the loop over all frames */
699 if (x_pdb && ((bFirst && t_pdb < t) ||
700 (!bFirst &&
701 t_pdb > t - 0.5*(t - t_prev) &&
702 t_pdb < t + 0.5*(t - t_prev))))
704 if (*x_pdb == NULL)
706 snew(*x_pdb, natoms);
708 for (i = 0; i < natoms; i++)
710 copy_rvec(x[cur][i], (*x_pdb)[i]);
712 copy_mat(box, box_pdb);
716 /* check whether we've reached a restart point */
717 if (bRmod(t, curr->t0, dt))
719 curr->nrestart++;
721 srenew(curr->x0, curr->nrestart);
722 snew(curr->x0[curr->nrestart-1], curr->ncoords);
723 srenew(curr->com, curr->nrestart);
724 srenew(curr->n_offs, curr->nrestart);
725 srenew(curr->lsq, curr->nrestart);
726 snew(curr->lsq[curr->nrestart-1], curr->nmol);
727 for (i = 0; i < curr->nmol; i++)
729 curr->lsq[curr->nrestart-1][i] = gmx_stats_init();
732 if (debug)
734 fprintf(debug, "Extended data structures because of new restart %d\n",
735 curr->nrestart);
738 /* create or extend the frame-based arrays */
739 if (curr->nframes >= maxframes-1)
741 if (maxframes == 0)
743 for (i = 0; (i < curr->ngrp); i++)
745 curr->ndata[i] = NULL;
746 curr->data[i] = NULL;
747 if (bTen)
749 curr->datam[i] = NULL;
752 curr->time = NULL;
754 maxframes += 10;
755 for (i = 0; (i < curr->ngrp); i++)
757 srenew(curr->ndata[i], maxframes);
758 srenew(curr->data[i], maxframes);
759 if (bTen)
761 srenew(curr->datam[i], maxframes);
763 for (j = maxframes-10; j < maxframes; j++)
765 curr->ndata[i][j] = 0;
766 curr->data[i][j] = 0;
767 if (bTen)
769 clear_mat(curr->datam[i][j]);
773 srenew(curr->time, maxframes);
776 /* set the time */
777 curr->time[curr->nframes] = t - curr->t0;
779 /* for the first frame, the previous frame is a copy of the first frame */
780 if (bFirst)
782 std::memcpy(xa[prev], xa[cur], curr->ncoords*sizeof(xa[prev][0]));
783 bFirst = FALSE;
786 /* make the molecules whole */
787 if (bMol)
789 gmx_rmpbc(gpbc, natoms, box, x[cur]);
792 /* calculate the molecules' centers of masses and put them into xa */
793 if (bMol)
795 calc_mol_com(gnx[0], index[0], &top->mols, &top->atoms, x[cur], xa[cur]);
798 /* first remove the periodic boundary condition crossings */
799 for (i = 0; i < curr->ngrp; i++)
801 prep_data(bMol, gnx[i], index[i], xa[cur], xa[prev], box);
804 /* calculate the center of mass */
805 if (gnx_com)
807 prep_data(bMol, gnx_com[0], index_com[0], xa[cur], xa[prev], box);
808 calc_com(bMol, gnx_com[0], index_com[0], xa[cur], xa[prev], box,
809 &top->atoms, com);
812 /* loop over all groups in index file */
813 for (i = 0; (i < curr->ngrp); i++)
815 /* calculate something useful, like mean square displacements */
816 calc_corr(curr, i, gnx[i], index[i], xa[cur], (gnx_com != NULL), com,
817 calc1, bTen);
819 cur = prev;
820 t_prev = t;
822 curr->nframes++;
824 while (read_next_x(oenv, status, &t, x[cur], box));
825 fprintf(stderr, "\nUsed %d restart points spaced %g %s over %g %s\n\n",
826 curr->nrestart,
827 output_env_conv_time(oenv, dt), output_env_get_time_unit(oenv),
828 output_env_conv_time(oenv, curr->time[curr->nframes-1]),
829 output_env_get_time_unit(oenv) );
831 if (bMol)
833 gmx_rmpbc_done(gpbc);
836 close_trj(status);
838 return natoms;
841 static void index_atom2mol(int *n, int *index, t_block *mols)
843 int nat, i, nmol, mol, j;
845 nat = *n;
846 i = 0;
847 nmol = 0;
848 mol = 0;
849 while (i < nat)
851 while (index[i] > mols->index[mol])
853 mol++;
854 if (mol >= mols->nr)
856 gmx_fatal(FARGS, "Atom index out of range: %d", index[i]+1);
859 for (j = mols->index[mol]; j < mols->index[mol+1]; j++)
861 if (i >= nat || index[i] != j)
863 gmx_fatal(FARGS, "The index group does not consist of whole molecules");
865 i++;
867 index[nmol++] = mol;
870 fprintf(stderr, "Split group of %d atoms into %d molecules\n", nat, nmol);
872 *n = nmol;
875 void do_corr(const char *trx_file, const char *ndx_file, const char *msd_file,
876 const char *mol_file, const char *pdb_file, real t_pdb,
877 int nrgrp, t_topology *top, int ePBC,
878 gmx_bool bTen, gmx_bool bMW, gmx_bool bRmCOMM,
879 int type, real dim_factor, int axis,
880 real dt, real beginfit, real endfit, const gmx_output_env_t *oenv)
882 t_corr *msd;
883 int *gnx; /* the selected groups' sizes */
884 int **index; /* selected groups' indices */
885 char **grpname;
886 int i, i0, i1, j, N, nat_trx;
887 real *DD, *SigmaD, a, a2, b, r, chi2;
888 rvec *x;
889 matrix box;
890 int *gnx_com = NULL; /* the COM removal group size */
891 int **index_com = NULL; /* the COM removal group atom indices */
892 char **grpname_com = NULL; /* the COM removal group name */
894 snew(gnx, nrgrp);
895 snew(index, nrgrp);
896 snew(grpname, nrgrp);
898 fprintf(stderr, "\nSelect a group to calculate mean squared displacement for:\n");
899 get_index(&top->atoms, ndx_file, nrgrp, gnx, index, grpname);
901 if (bRmCOMM)
903 snew(gnx_com, 1);
904 snew(index_com, 1);
905 snew(grpname_com, 1);
907 fprintf(stderr, "\nNow select a group for center of mass removal:\n");
908 get_index(&top->atoms, ndx_file, 1, gnx_com, index_com, grpname_com);
911 if (mol_file)
913 index_atom2mol(&gnx[0], index[0], &top->mols);
916 msd = init_corr(nrgrp, type, axis, dim_factor,
917 mol_file == NULL ? 0 : gnx[0], bTen, bMW, dt, top,
918 beginfit, endfit);
920 nat_trx =
921 corr_loop(msd, trx_file, top, ePBC, mol_file ? gnx[0] : 0, gnx, index,
922 (mol_file != NULL) ? calc1_mol : (bMW ? calc1_mw : calc1_norm),
923 bTen, gnx_com, index_com, dt, t_pdb,
924 pdb_file ? &x : NULL, box, oenv);
926 /* Correct for the number of points */
927 for (j = 0; (j < msd->ngrp); j++)
929 for (i = 0; (i < msd->nframes); i++)
931 msd->data[j][i] /= msd->ndata[j][i];
932 if (bTen)
934 msmul(msd->datam[j][i], 1.0/msd->ndata[j][i], msd->datam[j][i]);
939 if (mol_file)
941 if (pdb_file && x == NULL)
943 fprintf(stderr, "\nNo frame found need time tpdb = %g ps\n"
944 "Can not write %s\n\n", t_pdb, pdb_file);
946 i = top->atoms.nr;
947 top->atoms.nr = nat_trx;
948 printmol(msd, mol_file, pdb_file, index[0], top, x, ePBC, box, oenv);
949 top->atoms.nr = i;
952 DD = NULL;
953 SigmaD = NULL;
955 if (beginfit == -1)
957 i0 = static_cast<int>(0.1*(msd->nframes - 1) + 0.5);
958 beginfit = msd->time[i0];
960 else
962 for (i0 = 0; i0 < msd->nframes && msd->time[i0] < beginfit; i0++)
968 if (endfit == -1)
970 i1 = static_cast<int>(0.9*(msd->nframes - 1) + 0.5) + 1;
971 endfit = msd->time[i1-1];
973 else
975 for (i1 = i0; i1 < msd->nframes && msd->time[i1] <= endfit; i1++)
980 fprintf(stdout, "Fitting from %g to %g %s\n\n", beginfit, endfit,
981 output_env_get_time_unit(oenv));
983 N = i1-i0;
984 if (N <= 2)
986 fprintf(stdout, "Not enough points for fitting (%d).\n"
987 "Can not determine the diffusion constant.\n", N);
989 else
991 snew(DD, msd->ngrp);
992 snew(SigmaD, msd->ngrp);
993 for (j = 0; j < msd->ngrp; j++)
995 if (N >= 4)
997 lsq_y_ax_b(N/2, &(msd->time[i0]), &(msd->data[j][i0]), &a, &b, &r, &chi2);
998 lsq_y_ax_b(N/2, &(msd->time[i0+N/2]), &(msd->data[j][i0+N/2]), &a2, &b, &r, &chi2);
999 SigmaD[j] = std::abs(a-a2);
1001 else
1003 SigmaD[j] = 0;
1005 lsq_y_ax_b(N, &(msd->time[i0]), &(msd->data[j][i0]), &(DD[j]), &b, &r, &chi2);
1006 DD[j] *= FACTOR/msd->dim_factor;
1007 SigmaD[j] *= FACTOR/msd->dim_factor;
1008 if (DD[j] > 0.01 && DD[j] < 1e4)
1010 fprintf(stdout, "D[%10s] %.4f (+/- %.4f) 1e-5 cm^2/s\n",
1011 grpname[j], DD[j], SigmaD[j]);
1013 else
1015 fprintf(stdout, "D[%10s] %.4g (+/- %.4g) 1e-5 cm^2/s\n",
1016 grpname[j], DD[j], SigmaD[j]);
1020 /* Print mean square displacement */
1021 corr_print(msd, bTen, msd_file,
1022 "Mean Square Displacement",
1023 "MSD (nm\\S2\\N)",
1024 msd->time[msd->nframes-1], beginfit, endfit, DD, SigmaD, grpname, oenv);
1027 int gmx_msd(int argc, char *argv[])
1029 const char *desc[] = {
1030 "[THISMODULE] computes the mean square displacement (MSD) of atoms from",
1031 "a set of initial positions. This provides an easy way to compute",
1032 "the diffusion constant using the Einstein relation.",
1033 "The time between the reference points for the MSD calculation",
1034 "is set with [TT]-trestart[tt].",
1035 "The diffusion constant is calculated by least squares fitting a",
1036 "straight line (D*t + c) through the MSD(t) from [TT]-beginfit[tt] to",
1037 "[TT]-endfit[tt] (note that t is time from the reference positions,",
1038 "not simulation time). An error estimate given, which is the difference",
1039 "of the diffusion coefficients obtained from fits over the two halves",
1040 "of the fit interval.[PAR]",
1041 "There are three, mutually exclusive, options to determine different",
1042 "types of mean square displacement: [TT]-type[tt], [TT]-lateral[tt]",
1043 "and [TT]-ten[tt]. Option [TT]-ten[tt] writes the full MSD tensor for",
1044 "each group, the order in the output is: trace xx yy zz yx zx zy.[PAR]",
1045 "If [TT]-mol[tt] is set, [THISMODULE] plots the MSD for individual molecules",
1046 "(including making molecules whole across periodic boundaries): ",
1047 "for each individual molecule a diffusion constant is computed for ",
1048 "its center of mass. The chosen index group will be split into ",
1049 "molecules.[PAR]",
1050 "The default way to calculate a MSD is by using mass-weighted averages.",
1051 "This can be turned off with [TT]-nomw[tt].[PAR]",
1052 "With the option [TT]-rmcomm[tt], the center of mass motion of a ",
1053 "specific group can be removed. For trajectories produced with ",
1054 "GROMACS this is usually not necessary, ",
1055 "as [gmx-mdrun] usually already removes the center of mass motion.",
1056 "When you use this option be sure that the whole system is stored",
1057 "in the trajectory file.[PAR]",
1058 "The diffusion coefficient is determined by linear regression of the MSD,",
1059 "where, unlike for the normal output of D, the times are weighted",
1060 "according to the number of reference points, i.e. short times have",
1061 "a higher weight. Also when [TT]-beginfit[tt]=-1,fitting starts at 10%",
1062 "and when [TT]-endfit[tt]=-1, fitting goes to 90%.",
1063 "Using this option one also gets an accurate error estimate",
1064 "based on the statistics between individual molecules.",
1065 "Note that this diffusion coefficient and error estimate are only",
1066 "accurate when the MSD is completely linear between",
1067 "[TT]-beginfit[tt] and [TT]-endfit[tt].[PAR]",
1068 "Option [TT]-pdb[tt] writes a [REF].pdb[ref] file with the coordinates of the frame",
1069 "at time [TT]-tpdb[tt] with in the B-factor field the square root of",
1070 "the diffusion coefficient of the molecule.",
1071 "This option implies option [TT]-mol[tt]."
1073 static const char *normtype[] = { NULL, "no", "x", "y", "z", NULL };
1074 static const char *axtitle[] = { NULL, "no", "x", "y", "z", NULL };
1075 static int ngroup = 1;
1076 static real dt = 10;
1077 static real t_pdb = 0;
1078 static real beginfit = -1;
1079 static real endfit = -1;
1080 static gmx_bool bTen = FALSE;
1081 static gmx_bool bMW = TRUE;
1082 static gmx_bool bRmCOMM = FALSE;
1083 t_pargs pa[] = {
1084 { "-type", FALSE, etENUM, {normtype},
1085 "Compute diffusion coefficient in one direction" },
1086 { "-lateral", FALSE, etENUM, {axtitle},
1087 "Calculate the lateral diffusion in a plane perpendicular to" },
1088 { "-ten", FALSE, etBOOL, {&bTen},
1089 "Calculate the full tensor" },
1090 { "-ngroup", FALSE, etINT, {&ngroup},
1091 "Number of groups to calculate MSD for" },
1092 { "-mw", FALSE, etBOOL, {&bMW},
1093 "Mass weighted MSD" },
1094 { "-rmcomm", FALSE, etBOOL, {&bRmCOMM},
1095 "Remove center of mass motion" },
1096 { "-tpdb", FALSE, etTIME, {&t_pdb},
1097 "The frame to use for option [TT]-pdb[tt] (%t)" },
1098 { "-trestart", FALSE, etTIME, {&dt},
1099 "Time between restarting points in trajectory (%t)" },
1100 { "-beginfit", FALSE, etTIME, {&beginfit},
1101 "Start time for fitting the MSD (%t), -1 is 10%" },
1102 { "-endfit", FALSE, etTIME, {&endfit},
1103 "End time for fitting the MSD (%t), -1 is 90%" }
1106 t_filenm fnm[] = {
1107 { efTRX, NULL, NULL, ffREAD },
1108 { efTPS, NULL, NULL, ffREAD },
1109 { efNDX, NULL, NULL, ffOPTRD },
1110 { efXVG, NULL, "msd", ffWRITE },
1111 { efXVG, "-mol", "diff_mol", ffOPTWR },
1112 { efPDB, "-pdb", "diff_mol", ffOPTWR }
1114 #define NFILE asize(fnm)
1116 t_topology top;
1117 int ePBC;
1118 matrix box;
1119 const char *trx_file, *tps_file, *ndx_file, *msd_file, *mol_file, *pdb_file;
1120 rvec *xdum;
1121 gmx_bool bTop;
1122 int axis, type;
1123 real dim_factor;
1124 gmx_output_env_t *oenv;
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))
1130 return 0;
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);
1137 if (pdb_file)
1139 mol_file = opt2fn("-mol", NFILE, fnm);
1141 else
1143 mol_file = opt2fn_null("-mol", NFILE, fnm);
1146 if (ngroup < 1)
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)",
1153 ngroup);
1157 if (mol_file)
1159 bMW = TRUE;
1160 fprintf(stderr, "Calculating diffusion coefficients for molecules.\n");
1163 GMX_RELEASE_ASSERT(normtype[0] != 0, "Options inconsistency; normtype[0] is NULL");
1164 GMX_RELEASE_ASSERT(axtitle[0] != 0, "Options inconsistency; axtitle[0] is NULL");
1166 if (normtype[0][0] != 'n')
1168 type = normtype[0][0] - 'x' + X; /* See defines above */
1169 dim_factor = 2.0;
1171 else
1173 type = NORMAL;
1174 dim_factor = 6.0;
1176 if ((type == NORMAL) && (axtitle[0][0] != 'n'))
1178 type = LATERAL;
1179 dim_factor = 4.0;
1180 axis = (axtitle[0][0] - 'x'); /* See defines above */
1182 else
1184 axis = 0;
1187 if (bTen && type != NORMAL)
1189 gmx_fatal(FARGS, "Can only calculate the full tensor for 3D msd");
1192 bTop = read_tps_conf(tps_file, &top, &ePBC, &xdum, NULL, box, bMW || bRmCOMM);
1193 if (mol_file && !bTop)
1195 gmx_fatal(FARGS,
1196 "Could not read a topology from %s. Try a tpr file instead.",
1197 tps_file);
1200 do_corr(trx_file, ndx_file, msd_file, mol_file, pdb_file, t_pdb, ngroup,
1201 &top, ePBC, bTen, bMW, bRmCOMM, type, dim_factor, axis, dt, beginfit, endfit,
1202 oenv);
1204 view_all(oenv, NFILE, fnm);
1206 return 0;