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,2016,2017 by the GROMACS development team.
7 * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
8 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9 * and including many others, as listed in the AUTHORS file in the
10 * top-level source directory and at http://www.gromacs.org.
12 * GROMACS is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 2.1
15 * of the License, or (at your option) any later version.
17 * GROMACS is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with GROMACS; if not, see
24 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 * If you want to redistribute modifications to GROMACS, please
28 * consider that scientific software is very special. Version
29 * control is crucial - bugs must be traceable. We will be happy to
30 * consider code for inclusion in the official distribution, but
31 * derived work must not be called official GROMACS. Details are found
32 * in the README & COPYING files - if they are missing, get the
33 * official version at http://www.gromacs.org.
35 * To help us fund GROMACS development, we humbly ask that you cite
36 * the research papers on the package. Check out http://www.gromacs.org.
45 #include "gromacs/commandline/pargs.h"
46 #include "gromacs/commandline/viewit.h"
47 #include "gromacs/fileio/confio.h"
48 #include "gromacs/fileio/trxio.h"
49 #include "gromacs/fileio/xvgr.h"
50 #include "gromacs/gmxana/gmx_ana.h"
51 #include "gromacs/gmxana/gstat.h"
52 #include "gromacs/math/functions.h"
53 #include "gromacs/math/utilities.h"
54 #include "gromacs/math/vec.h"
55 #include "gromacs/math/vectypes.h"
56 #include "gromacs/pbcutil/rmpbc.h"
57 #include "gromacs/statistics/statistics.h"
58 #include "gromacs/topology/index.h"
59 #include "gromacs/topology/topology.h"
60 #include "gromacs/utility/arraysize.h"
61 #include "gromacs/utility/fatalerror.h"
62 #include "gromacs/utility/futil.h"
63 #include "gromacs/utility/gmxassert.h"
64 #include "gromacs/utility/smalloc.h"
66 static constexpr double diffusionConversionFactor = 1000.0; /* Convert nm^2/ps to 10e-5 cm^2/s */
67 /* NORMAL = total diffusion coefficient (default). X,Y,Z is diffusion
68 coefficient in X,Y,Z direction. LATERAL is diffusion coefficient in
69 plane perpendicular to axis
81 // TODO : Group related fields into a struct
84 real t0; /* start time and time increment between */
85 real delta_t; /* time between restart points */
86 real beginfit, /* the begin/end time for fits as reals between */
88 real dim_factor; /* the dimensionality factor for the diffusion
90 std::vector<std::vector<real>> data; /* the displacement data. First index is the group
91 number, second is frame number */
92 std::vector<real> time; /* frame time */
93 std::vector<real> mass; /* masses for mass-weighted msd */
95 std::vector<std::vector<gmx::RVec>> x0; /* original positions */
96 std::vector<gmx::RVec> com; /* center of mass correction for each frame */
97 gmx_stats_t** lsq; /* fitting stats for individual molecule msds */
98 msd_type type; /* the type of msd to calculate (lateral, etc.)*/
99 int axis; /* the axis along which to calculate */
101 int nrestart; /* number of restart points */
102 int nmol; /* number of molecules (for bMol) */
103 int nframes; /* number of frames */
105 int ngrp; /* number of groups to use for msd calculation */
106 std::vector<int> n_offs;
107 std::vector<std::vector<int>> ndata; /* the number of msds (particles/mols) per data
117 const t_topology* top,
122 beginfit((1 - 2 * GMX_REAL_EPS) * beginfit),
123 endfit((1 + 2 * GMX_REAL_EPS) * endfit),
124 dim_factor(dim_factor),
125 data(nrgrp, std::vector<real>()),
128 type(static_cast<msd_type>(type)),
136 ndata(nrgrp, std::vector<int>())
142 for (int i = 0; i < nrgrp; i++)
150 mass.resize(nmol, 1);
156 const t_atoms* atoms = &top->atoms;
157 mass.resize(atoms->nr);
158 for (int i = 0; (i < atoms->nr); i++)
160 mass[i] = atoms->atom[i].m;
167 for (int i = 0; i < nrestart; i++)
169 for (int j = 0; j < nmol; j++)
171 gmx_stats_free(lsq[i][j]);
179 t_calc_func(t_corr* curr, int nx, const int index[], int nx0, rvec xc[], const rvec dcom, gmx_bool bTen, matrix mat);
181 static real thistime(t_corr* curr)
183 return curr->time[curr->nframes];
186 static int in_data(t_corr* curr, int nx00)
188 return curr->nframes - curr->n_offs[nx00];
191 static void corr_print(t_corr* curr,
202 const gmx_output_env_t* oenv)
207 out = xvgropen(fn, title, output_env_get_xvgr_tlabel(oenv), yaxis, oenv);
211 "# MSD gathered over %g %s with %d restarts\n",
213 output_env_get_time_unit(oenv).c_str(),
216 "# Diffusion constants fitted from time %g to %g %s\n",
219 output_env_get_time_unit(oenv).c_str());
220 for (i = 0; i < curr->ngrp; i++)
222 fprintf(out, "# D[%10s] = %.4f (+/- %.4f) (1e-5 cm^2/s)\n", grpname[i], DD[i], SigmaD[i]);
225 for (i = 0; i < curr->nframes; i++)
227 fprintf(out, "%10g", output_env_conv_time(oenv, curr->time[i]));
228 for (j = 0; j < curr->ngrp; j++)
230 fprintf(out, " %10g", curr->data[j][i]);
234 " %10g %10g %10g %10g %10g %10g",
235 curr->datam[j][i][XX][XX],
236 curr->datam[j][i][YY][YY],
237 curr->datam[j][i][ZZ][ZZ],
238 curr->datam[j][i][YY][XX],
239 curr->datam[j][i][ZZ][XX],
240 curr->datam[j][i][ZZ][YY]);
248 /* called from corr_loop, to do the main calculations */
250 calc_corr(t_corr* curr, int nr, int nx, int index[], rvec xc[], gmx_bool bRmCOMM, rvec com, t_calc_func* calc1, gmx_bool bTen)
257 /* Check for new starting point */
258 if (curr->nlast < curr->nrestart)
260 if ((thistime(curr) >= (curr->nlast * curr->delta_t)) && (nr == 0))
262 std::memcpy(curr->x0[curr->nlast].data()->as_vec(), xc, curr->ncoords * sizeof(xc[0]));
263 curr->n_offs[curr->nlast] = curr->nframes;
264 copy_rvec(com, curr->com[curr->nlast]);
269 /* nx0 appears to be the number of new starting points,
270 * so for all starting points, call calc1.
272 for (nx0 = 0; (nx0 < curr->nlast); nx0++)
276 rvec_sub(com, curr->com[nx0], dcom);
282 g = calc1(curr, nx, index, nx0, xc, dcom, bTen, mat);
284 printf("g[%d]=%g\n", nx0, g);
286 curr->data[nr][in_data(curr, nx0)] += g;
289 m_add(curr->datam[nr][in_data(curr, nx0)], mat, curr->datam[nr][in_data(curr, nx0)]);
291 curr->ndata[nr][in_data(curr, nx0)]++;
295 /* the non-mass-weighted mean-squared displacement calculation */
297 calc1_norm(t_corr* curr, int nx, const int index[], int nx0, rvec xc[], const rvec dcom, gmx_bool bTen, matrix mat)
306 for (i = 0; (i < nx); i++)
313 for (m = 0; (m < DIM); m++)
315 rv[m] = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
319 for (m2 = 0; m2 <= m; m2++)
321 mat[m][m2] += rv[m] * rv[m2];
329 r = xc[ix][curr->type - X] - curr->x0[nx0][ix][curr->type - X] - dcom[curr->type - X];
333 for (m = 0; (m < DIM); m++)
337 r = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
342 default: gmx_fatal(FARGS, "Error: did not expect option value %d", curr->type);
347 msmul(mat, 1.0 / nx, mat);
352 /* calculate the com of molecules in x and put it into xa */
354 calc_mol_com(int nmol, const int* molindex, const t_block* mols, const t_atoms* atoms, rvec* x, rvec* xa)
360 for (m = 0; m < nmol; m++)
365 for (i = mols->index[mol]; i < mols->index[mol + 1]; i++)
367 mass = atoms->atom[i].m;
368 for (d = 0; d < DIM; d++)
370 xm[d] += mass * x[i][d];
374 svmul(1 / mtot, xm, xa[m]);
378 static real calc_one_mw(t_corr* curr, int ix, int nx0, rvec xc[], real* tm, const rvec dcom, gmx_bool bTen, matrix mat)
394 for (m = 0; (m < DIM); m++)
396 rv[m] = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
397 r2 += mm * rv[m] * rv[m];
400 for (m2 = 0; m2 <= m; m2++)
402 mat[m][m2] += mm * rv[m] * rv[m2];
410 r = xc[ix][curr->type - X] - curr->x0[nx0][ix][curr->type - X] - dcom[curr->type - X];
414 for (m = 0; (m < DIM); m++)
418 r = xc[ix][m] - curr->x0[nx0][ix][m] - dcom[m];
423 default: gmx_fatal(FARGS, "Options got screwed. Did not expect value %d\n", curr->type);
428 /* the normal, mass-weighted mean-squared displacement calcuation */
429 static real calc1_mw(t_corr* curr, int nx, const int index[], int nx0, rvec xc[], const rvec dcom, gmx_bool bTen, matrix mat)
436 for (i = 0; (i < nx); i++)
438 g += calc_one_mw(curr, index[i], nx0, xc, &tm, dcom, bTen, mat);
444 msmul(mat, 1 / tm, mat);
450 /* prepare the coordinates by removing periodic boundary crossings.
451 gnx = the number of atoms/molecules
453 xcur = the current coordinates
454 xprev = the previous coordinates
455 box = the box matrix */
456 static void prep_data(gmx_bool bMol, int gnx, const int index[], rvec xcur[], rvec xprev[], matrix box)
461 /* Remove periodicity */
462 for (m = 0; (m < DIM); m++)
464 hbox[m] = 0.5 * box[m][m];
467 for (i = 0; (i < gnx); i++)
478 for (m = DIM - 1; m >= 0; m--)
484 while (xcur[ind][m] - xprev[ind][m] <= -hbox[m])
486 rvec_inc(xcur[ind], box[m]);
488 while (xcur[ind][m] - xprev[ind][m] > hbox[m])
490 rvec_dec(xcur[ind], box[m]);
496 /* calculate the center of mass for a group
497 gnx = the number of atoms/molecules
499 xcur = the current coordinates
500 xprev = the previous coordinates
502 atoms = atom data (for mass)
503 com(output) = center of mass */
505 calc_com(gmx_bool bMol, int gnx, int index[], rvec xcur[], rvec xprev[], matrix box, const t_atoms* atoms, rvec com)
515 prep_data(bMol, gnx, index, xcur, xprev, box);
516 for (i = 0; (i < gnx); i++)
528 mass = atoms->atom[ind].m;
529 for (m = 0; m < DIM; m++)
531 sx[m] += mass * xcur[ind][m];
535 for (m = 0; m < DIM; m++)
537 com[m] = sx[m] / tmass;
542 static real calc1_mol(t_corr* curr,
544 const int gmx_unused index[],
552 real g, tm, gtot, tt;
554 tt = curr->time[in_data(curr, nx0)];
558 for (i = 0; (i < nx); i++)
560 g = calc_one_mw(curr, i, nx0, xc, &tm, dcom, bTen, mat);
561 /* We don't need to normalize as the mass was set to 1 */
563 if (tt >= curr->beginfit && (curr->endfit < 0 || tt <= curr->endfit))
565 gmx_stats_add_point(curr->lsq[nx0][i], tt, g, 0, 0);
568 msmul(mat, 1.0 / nx, mat);
573 static void printmol(t_corr* curr,
577 const t_topology* top,
581 const gmx_output_env_t* oenv)
586 real a, b, D, Dav, D2av, VarD, sqrtD, sqrtD_max, scale;
587 t_pdbinfo* pdbinfo = nullptr;
588 const int* mol2a = nullptr;
590 out = xvgropen(fn, "Diffusion Coefficients / Molecule", "Molecule", "D (1e-5 cm^2/s)", oenv);
594 pdbinfo = top->atoms.pdbinfo;
595 mol2a = top->mols.index;
600 for (i = 0; (i < curr->nmol); i++)
602 lsq1 = gmx_stats_init();
603 for (j = 0; (j < curr->nrestart); j++)
607 while (gmx_stats_get_point(curr->lsq[j][i], &xx, &yy, &dx, &dy, 0) == estatsOK)
609 gmx_stats_add_point(lsq1, xx, yy, dx, dy);
612 gmx_stats_get_ab(lsq1, elsqWEIGHT_NONE, &a, &b, nullptr, nullptr, nullptr, nullptr);
613 gmx_stats_free(lsq1);
614 D = a * diffusionConversionFactor / curr->dim_factor;
620 D2av += gmx::square(D);
621 fprintf(out, "%10d %10g\n", i, D);
624 sqrtD = std::sqrt(D);
625 if (sqrtD > sqrtD_max)
629 for (j = mol2a[molindex[i]]; j < mol2a[molindex[i] + 1]; j++)
631 pdbinfo[j].bfac = sqrtD;
636 fprintf(stdout, "Wrote per-molecule output to %s\n", fn);
637 do_view(oenv, fn, "-graphtype bar");
639 /* Compute variance, stddev and error */
642 VarD = D2av - gmx::square(Dav);
643 printf("<D> = %.4f Std. Dev. = %.4f Error = %.4f\n", Dav, std::sqrt(VarD), std::sqrt(VarD / curr->nmol));
648 while (scale * sqrtD_max > 10)
652 while (scale * sqrtD_max < 0.1)
656 GMX_RELEASE_ASSERT(pdbinfo != nullptr, "Internal error - pdbinfo not set for PDB input");
657 for (i = 0; i < top->atoms.nr; i++)
659 pdbinfo[i].bfac *= scale;
661 write_sto_conf(fn_pdb, "molecular MSD", &top->atoms, x, nullptr, pbcType, box);
662 fprintf(stdout, "Wrote frame for -tpdb to %s\n", fn_pdb);
666 /* this is the main loop for the correlation type functions
667 * fx and nx are file pointers to things like read_first_x and
670 static int corr_loop(t_corr* curr,
672 const t_topology* top,
679 gmx::ArrayRef<const int> gnx_com,
685 const gmx_output_env_t* oenv)
687 rvec* x[2]; /* the coordinates to read */
688 rvec* xa[2]; /* the coordinates to calculate displacements for */
691 int natoms, i, j, cur = 0, maxframes = 0;
693 #define prev (1 - cur)
696 gmx_rmpbc_t gpbc = nullptr;
698 natoms = read_first_x(oenv, &status, fn, &curr->t0, &(x[cur]), box);
700 fprintf(stderr, "Read %d atoms for first frame\n", natoms);
702 if ((!gnx_com.empty()) && natoms < top->atoms.nr)
705 "WARNING: The trajectory only contains part of the system (%d of %d atoms) and "
706 "therefore the COM motion of only this part of the system will be removed\n",
711 snew(x[prev], natoms);
713 // if com is requested, the data structure needs to be large enough to do this
714 // to prevent overflow
715 if (bMol && gnx_com.empty())
717 curr->ncoords = curr->nmol;
718 snew(xa[0], curr->ncoords);
719 snew(xa[1], curr->ncoords);
723 curr->ncoords = natoms;
737 gpbc = gmx_rmpbc_init(&top->idef, pbcType, natoms);
740 /* the loop over all frames */
744 && ((bFirst && t_pdb < t)
745 || (!bFirst && t_pdb > t - 0.5 * (t - t_prev) && t_pdb < t + 0.5 * (t - t_prev))))
747 if (*x_pdb == nullptr)
749 snew(*x_pdb, natoms);
751 for (i = 0; i < natoms; i++)
753 copy_rvec(x[cur][i], (*x_pdb)[i]);
755 copy_mat(box, box_pdb);
759 /* check whether we've reached a restart point */
760 if (bRmod(t, curr->t0, dt))
764 curr->x0.resize(curr->nrestart);
765 curr->x0[curr->nrestart - 1].resize(curr->ncoords);
766 curr->com.resize(curr->nrestart);
767 curr->n_offs.resize(curr->nrestart);
768 srenew(curr->lsq, curr->nrestart);
769 snew(curr->lsq[curr->nrestart - 1], curr->nmol);
770 for (i = 0; i < curr->nmol; i++)
772 curr->lsq[curr->nrestart - 1][i] = gmx_stats_init();
777 fprintf(debug, "Extended data structures because of new restart %d\n", curr->nrestart);
780 /* create or extend the frame-based arrays */
781 if (curr->nframes >= maxframes - 1)
785 for (i = 0; (i < curr->ngrp); i++)
789 curr->datam[i] = nullptr;
794 for (i = 0; (i < curr->ngrp); i++)
796 curr->ndata[i].resize(maxframes);
797 curr->data[i].resize(maxframes);
800 srenew(curr->datam[i], maxframes);
802 for (j = maxframes - 10; j < maxframes; j++)
804 curr->ndata[i][j] = 0;
805 curr->data[i][j] = 0;
808 clear_mat(curr->datam[i][j]);
812 curr->time.resize(maxframes);
816 curr->time[curr->nframes] = t - curr->t0;
818 /* make the molecules whole */
821 gmx_rmpbc(gpbc, natoms, box, x[cur]);
824 /* calculate the molecules' centers of masses and put them into xa */
825 // NOTE and WARNING! If above both COM removal and individual molecules have been
826 // requested, x and xa point to the same memory, and the coordinate
827 // data becomes overwritten by the molecule data.
830 calc_mol_com(gnx[0], index[0], &top->mols, &top->atoms, x[cur], xa[cur]);
833 /* for the first frame, the previous frame is a copy of the first frame */
836 std::memcpy(xa[prev], xa[cur], curr->ncoords * sizeof(xa[prev][0]));
840 /* first remove the periodic boundary condition crossings */
841 for (i = 0; i < curr->ngrp; i++)
843 prep_data(bMol, gnx[i], index[i], xa[cur], xa[prev], box);
846 /* calculate the center of mass */
847 if (!gnx_com.empty())
849 GMX_RELEASE_ASSERT(index_com != nullptr,
850 "Center-of-mass removal must have valid index group");
851 calc_com(bMol, gnx_com[0], index_com[0], xa[cur], xa[prev], box, &top->atoms, com);
854 /* loop over all groups in index file */
855 for (i = 0; (i < curr->ngrp); i++)
857 /* calculate something useful, like mean square displacements */
858 calc_corr(curr, i, gnx[i], index[i], xa[cur], (!gnx_com.empty()), com, calc1, bTen);
864 } while (read_next_x(oenv, status, &t, x[cur], box));
866 "\nUsed %d restart points spaced %g %s over %g %s\n\n",
868 output_env_conv_time(oenv, dt),
869 output_env_get_time_unit(oenv).c_str(),
870 output_env_conv_time(oenv, curr->time[curr->nframes - 1]),
871 output_env_get_time_unit(oenv).c_str());
875 gmx_rmpbc_done(gpbc);
883 static void index_atom2mol(int* n, int* index, const t_block* mols)
885 int nat, i, nmol, mol, j;
893 while (index[i] > mols->index[mol])
898 gmx_fatal(FARGS, "Atom index out of range: %d", index[i] + 1);
901 for (j = mols->index[mol]; j < mols->index[mol + 1]; j++)
903 if (i >= nat || index[i] != j)
905 gmx_fatal(FARGS, "The index group does not consist of whole molecules");
912 fprintf(stderr, "Split group of %d atoms into %d molecules\n", nat, nmol);
917 static void do_corr(const char* trx_file,
918 const char* ndx_file,
919 const char* msd_file,
920 const char* mol_file,
921 const char* pdb_file,
935 const gmx_output_env_t* oenv)
937 std::unique_ptr<t_corr> msd;
938 std::vector<int> gnx, gnx_com; /* the selected groups' sizes */
939 int** index; /* selected groups' indices */
941 int i, i0, i1, j, N, nat_trx;
942 std::vector<real> SigmaD, DD;
943 real a, a2, b, r, chi2;
946 int** index_com = nullptr; /* the COM removal group atom indices */
947 char** grpname_com = nullptr; /* the COM removal group name */
951 snew(grpname, nrgrp);
953 fprintf(stderr, "\nSelect a group to calculate mean squared displacement for:\n");
954 get_index(&top->atoms, ndx_file, nrgrp, gnx.data(), index, grpname);
960 snew(grpname_com, 1);
962 fprintf(stderr, "\nNow select a group for center of mass removal:\n");
963 get_index(&top->atoms, ndx_file, 1, gnx_com.data(), index_com, grpname_com);
968 index_atom2mol(&gnx[0], index[0], &top->mols);
971 msd = std::make_unique<t_corr>(
972 nrgrp, type, axis, dim_factor, mol_file == nullptr ? 0 : gnx[0], bTen, bMW, dt, top, beginfit, endfit);
974 nat_trx = corr_loop(msd.get(),
978 mol_file ? gnx[0] != 0 : false,
981 (mol_file != nullptr) ? calc1_mol : (bMW ? calc1_mw : calc1_norm),
987 pdb_file ? &x : nullptr,
991 /* Correct for the number of points */
992 for (j = 0; (j < msd->ngrp); j++)
994 for (i = 0; (i < msd->nframes); i++)
996 msd->data[j][i] /= msd->ndata[j][i];
999 msmul(msd->datam[j][i], 1.0 / msd->ndata[j][i], msd->datam[j][i]);
1006 if (pdb_file && x == nullptr)
1009 "\nNo frame found need time tpdb = %g ps\n"
1010 "Can not write %s\n\n",
1015 top->atoms.nr = nat_trx;
1016 if (pdb_file && top->atoms.pdbinfo == nullptr)
1018 snew(top->atoms.pdbinfo, top->atoms.nr);
1020 printmol(msd.get(), mol_file, pdb_file, index[0], top, x, pbcType, box, oenv);
1026 i0 = gmx::roundToInt(0.1 * (msd->nframes - 1));
1027 beginfit = msd->time[i0];
1031 for (i0 = 0; i0 < msd->nframes && msd->time[i0] < beginfit; i0++) {}
1036 i1 = gmx::roundToInt(0.9 * (msd->nframes - 1)) + 1;
1037 endfit = msd->time[i1 - 1];
1041 for (i1 = i0; i1 < msd->nframes && msd->time[i1] <= endfit; i1++) {}
1043 fprintf(stdout, "Fitting from %g to %g %s\n\n", beginfit, endfit, output_env_get_time_unit(oenv).c_str());
1049 "Not enough points for fitting (%d).\n"
1050 "Can not determine the diffusion constant.\n",
1055 DD.resize(msd->ngrp);
1056 SigmaD.resize(msd->ngrp);
1057 for (j = 0; j < msd->ngrp; j++)
1061 lsq_y_ax_b(N / 2, &(msd->time[i0]), &(msd->data[j][i0]), &a, &b, &r, &chi2);
1062 lsq_y_ax_b(N / 2, &(msd->time[i0 + N / 2]), &(msd->data[j][i0 + N / 2]), &a2, &b, &r, &chi2);
1063 SigmaD[j] = std::abs(a - a2);
1069 lsq_y_ax_b(N, &(msd->time[i0]), &(msd->data[j][i0]), &(DD[j]), &b, &r, &chi2);
1070 DD[j] *= diffusionConversionFactor / msd->dim_factor;
1071 SigmaD[j] *= diffusionConversionFactor / msd->dim_factor;
1072 if (DD[j] > 0.01 && DD[j] < 1e4)
1074 fprintf(stdout, "D[%10s] %.4f (+/- %.4f) 1e-5 cm^2/s\n", grpname[j], DD[j], SigmaD[j]);
1078 fprintf(stdout, "D[%10s] %.4g (+/- %.4g) 1e-5 cm^2/s\n", grpname[j], DD[j], SigmaD[j]);
1082 /* Print mean square displacement */
1083 corr_print(msd.get(),
1086 "Mean Square Displacement",
1088 msd->time[msd->nframes - 1],
1097 int gmx_msd(int argc, char* argv[])
1099 const char* desc[] = {
1100 "[THISMODULE] computes the mean square displacement (MSD) of atoms from",
1101 "a set of initial positions. This provides an easy way to compute",
1102 "the diffusion constant using the Einstein relation.",
1103 "The time between the reference points for the MSD calculation",
1104 "is set with [TT]-trestart[tt].",
1105 "The diffusion constant is calculated by least squares fitting a",
1106 "straight line (D*t + c) through the MSD(t) from [TT]-beginfit[tt] to",
1107 "[TT]-endfit[tt] (note that t is time from the reference positions,",
1108 "not simulation time). An error estimate given, which is the difference",
1109 "of the diffusion coefficients obtained from fits over the two halves",
1110 "of the fit interval.[PAR]",
1111 "There are three, mutually exclusive, options to determine different",
1112 "types of mean square displacement: [TT]-type[tt], [TT]-lateral[tt]",
1113 "and [TT]-ten[tt]. Option [TT]-ten[tt] writes the full MSD tensor for",
1114 "each group, the order in the output is: trace xx yy zz yx zx zy.[PAR]",
1115 "If [TT]-mol[tt] is set, [THISMODULE] plots the MSD for individual molecules",
1116 "(including making molecules whole across periodic boundaries): ",
1117 "for each individual molecule a diffusion constant is computed for ",
1118 "its center of mass. The chosen index group will be split into ",
1120 "The default way to calculate a MSD is by using mass-weighted averages.",
1121 "This can be turned off with [TT]-nomw[tt].[PAR]",
1122 "With the option [TT]-rmcomm[tt], the center of mass motion of a ",
1123 "specific group can be removed. For trajectories produced with ",
1124 "GROMACS this is usually not necessary, ",
1125 "as [gmx-mdrun] usually already removes the center of mass motion.",
1126 "When you use this option be sure that the whole system is stored",
1127 "in the trajectory file.[PAR]",
1128 "The diffusion coefficient is determined by linear regression of the MSD.",
1129 "When [TT]-beginfit[tt] is -1, fitting starts at 10%",
1130 "and when [TT]-endfit[tt] is -1, fitting goes to 90%.",
1131 "Using this option one also gets an accurate error estimate",
1132 "based on the statistics between individual molecules.",
1133 "Note that this diffusion coefficient and error estimate are only",
1134 "accurate when the MSD is completely linear between",
1135 "[TT]-beginfit[tt] and [TT]-endfit[tt].[PAR]",
1136 "Option [TT]-pdb[tt] writes a [REF].pdb[ref] file with the coordinates of the frame",
1137 "at time [TT]-tpdb[tt] with in the B-factor field the square root of",
1138 "the diffusion coefficient of the molecule.",
1139 "This option implies option [TT]-mol[tt]."
1141 static const char* normtype[] = { nullptr, "no", "x", "y", "z", nullptr };
1142 static const char* axtitle[] = { nullptr, "no", "x", "y", "z", nullptr };
1143 static int ngroup = 1;
1144 static real dt = 10;
1145 static real t_pdb = 0;
1146 static real beginfit = -1;
1147 static real endfit = -1;
1148 static gmx_bool bTen = FALSE;
1149 static gmx_bool bMW = TRUE;
1150 static gmx_bool bRmCOMM = FALSE;
1152 { "-type", FALSE, etENUM, { normtype }, "Compute diffusion coefficient in one direction" },
1157 "Calculate the lateral diffusion in a plane perpendicular to" },
1158 { "-ten", FALSE, etBOOL, { &bTen }, "Calculate the full tensor" },
1159 { "-ngroup", FALSE, etINT, { &ngroup }, "Number of groups to calculate MSD for" },
1160 { "-mw", FALSE, etBOOL, { &bMW }, "Mass weighted MSD" },
1161 { "-rmcomm", FALSE, etBOOL, { &bRmCOMM }, "Remove center of mass motion" },
1162 { "-tpdb", FALSE, etTIME, { &t_pdb }, "The frame to use for option [TT]-pdb[tt] (%t)" },
1163 { "-trestart", FALSE, etTIME, { &dt }, "Time between restarting points in trajectory (%t)" },
1168 "Start time for fitting the MSD (%t), -1 is 10%" },
1169 { "-endfit", FALSE, etTIME, { &endfit }, "End time for fitting the MSD (%t), -1 is 90%" }
1173 { efTRX, nullptr, nullptr, ffREAD }, { efTPS, nullptr, nullptr, ffREAD },
1174 { efNDX, nullptr, nullptr, ffOPTRD }, { efXVG, nullptr, "msd", ffWRITE },
1175 { efXVG, "-mol", "diff_mol", ffOPTWR }, { efPDB, "-pdb", "diff_mol", ffOPTWR }
1177 #define NFILE asize(fnm)
1182 const char * trx_file, *tps_file, *ndx_file, *msd_file, *mol_file, *pdb_file;
1187 gmx_output_env_t* oenv;
1189 if (!parse_common_args(&argc,
1191 PCA_CAN_VIEW | PCA_CAN_BEGIN | PCA_CAN_END | PCA_TIME_UNIT,
1204 trx_file = ftp2fn_null(efTRX, NFILE, fnm);
1205 tps_file = ftp2fn_null(efTPS, NFILE, fnm);
1206 ndx_file = ftp2fn_null(efNDX, NFILE, fnm);
1207 msd_file = ftp2fn_null(efXVG, NFILE, fnm);
1208 pdb_file = opt2fn_null("-pdb", NFILE, fnm);
1211 mol_file = opt2fn("-mol", NFILE, fnm);
1215 mol_file = opt2fn_null("-mol", NFILE, fnm);
1220 gmx_fatal(FARGS, "Must have at least 1 group (now %d)", ngroup);
1222 if (mol_file && ngroup > 1)
1224 gmx_fatal(FARGS, "With molecular msd can only have 1 group (now %d)", ngroup);
1231 fprintf(stderr, "Calculating diffusion coefficients for molecules.\n");
1234 GMX_RELEASE_ASSERT(normtype[0] != nullptr, "Options inconsistency; normtype[0] is NULL");
1235 GMX_RELEASE_ASSERT(axtitle[0] != nullptr, "Options inconsistency; axtitle[0] is NULL");
1237 if (normtype[0][0] != 'n')
1239 type = normtype[0][0] - 'x' + X; /* See defines above */
1247 if ((type == NORMAL) && (axtitle[0][0] != 'n'))
1251 axis = (axtitle[0][0] - 'x'); /* See defines above */
1258 if (bTen && type != NORMAL)
1260 gmx_fatal(FARGS, "Can only calculate the full tensor for 3D msd");
1263 bTop = read_tps_conf(tps_file, &top, &pbcType, &xdum, nullptr, box, bMW || bRmCOMM);
1264 if (mol_file && !bTop)
1266 gmx_fatal(FARGS, "Could not read a topology from %s. Try a tpr file instead.", tps_file);
1290 view_all(oenv, NFILE, fnm);