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 * check out http://www.gromacs.org for more information.
7 * Copyright (c) 2012,2013, by the GROMACS development team, led by
8 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
9 * others, as listed in the AUTHORS file in the top-level source
10 * 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 #ifdef HAVE_SYS_TIME_H
50 #ifdef GMX_NATIVE_WINDOWS
55 #include "visibility.h"
76 #include "eigensolver.h"
81 /* Portable version of ctime_r implemented in src/gmxlib/string2.c, but we do not want it declared in public installed headers */
84 gmx_ctime_r(const time_t *clock, char *buf, int n);
87 int gmx_covar(int argc, char *argv[])
89 const char *desc[] = {
90 "[TT]g_covar[tt] calculates and diagonalizes the (mass-weighted)",
92 "All structures are fitted to the structure in the structure file.",
93 "When this is not a run input file periodicity will not be taken into",
94 "account. When the fit and analysis groups are identical and the analysis",
95 "is non mass-weighted, the fit will also be non mass-weighted.",
97 "The eigenvectors are written to a trajectory file ([TT]-v[tt]).",
98 "When the same atoms are used for the fit and the covariance analysis,",
99 "the reference structure for the fit is written first with t=-1.",
100 "The average (or reference when [TT]-ref[tt] is used) structure is",
101 "written with t=0, the eigenvectors",
102 "are written as frames with the eigenvector number as timestamp.",
104 "The eigenvectors can be analyzed with [TT]g_anaeig[tt].",
106 "Option [TT]-ascii[tt] writes the whole covariance matrix to",
107 "an ASCII file. The order of the elements is: x1x1, x1y1, x1z1, x1x2, ...",
109 "Option [TT]-xpm[tt] writes the whole covariance matrix to an [TT].xpm[tt] file.",
111 "Option [TT]-xpma[tt] writes the atomic covariance matrix to an [TT].xpm[tt] file,",
112 "i.e. for each atom pair the sum of the xx, yy and zz covariances is",
115 "Note that the diagonalization of a matrix requires memory and time",
116 "that will increase at least as fast as than the square of the number",
117 "of atoms involved. It is easy to run out of memory, in which",
118 "case this tool will probably exit with a 'Segmentation fault'. You",
119 "should consider carefully whether a reduced set of atoms will meet",
120 "your needs for lower costs."
122 static gmx_bool bFit = TRUE, bRef = FALSE, bM = FALSE, bPBC = TRUE;
125 { "-fit", FALSE, etBOOL, {&bFit},
126 "Fit to a reference structure"},
127 { "-ref", FALSE, etBOOL, {&bRef},
128 "Use the deviation from the conformation in the structure file instead of from the average" },
129 { "-mwa", FALSE, etBOOL, {&bM},
130 "Mass-weighted covariance analysis"},
131 { "-last", FALSE, etINT, {&end},
132 "Last eigenvector to write away (-1 is till the last)" },
133 { "-pbc", FALSE, etBOOL, {&bPBC},
134 "Apply corrections for periodic boundary conditions" }
142 rvec *x, *xread, *xref, *xav, *xproj;
144 real *sqrtm, *mat, *eigenvalues, sum, trace, inv_nframes;
145 real t, tstart, tend, **mat2;
146 real xj, *w_rls = NULL;
147 real min, max, *axis;
149 int natoms, nat, count, nframes0, nframes, nlevels;
150 gmx_large_int_t ndim, i, j, k, l;
152 const char *fitfile, *trxfile, *ndxfile;
153 const char *eigvalfile, *eigvecfile, *averfile, *logfile;
154 const char *asciifile, *xpmfile, *xpmafile;
155 char str[STRLEN], *fitname, *ananame, *pcwd;
157 atom_id *index, *ifit;
158 gmx_bool bDiffMass1, bDiffMass2;
160 char timebuf[STRLEN];
164 gmx_rmpbc_t gpbc = NULL;
167 { efTRX, "-f", NULL, ffREAD },
168 { efTPS, NULL, NULL, ffREAD },
169 { efNDX, NULL, NULL, ffOPTRD },
170 { efXVG, NULL, "eigenval", ffWRITE },
171 { efTRN, "-v", "eigenvec", ffWRITE },
172 { efSTO, "-av", "average.pdb", ffWRITE },
173 { efLOG, NULL, "covar", ffWRITE },
174 { efDAT, "-ascii", "covar", ffOPTWR },
175 { efXPM, "-xpm", "covar", ffOPTWR },
176 { efXPM, "-xpma", "covara", ffOPTWR }
178 #define NFILE asize(fnm)
180 CopyRight(stderr, argv[0]);
181 parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
182 NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv);
186 fitfile = ftp2fn(efTPS, NFILE, fnm);
187 trxfile = ftp2fn(efTRX, NFILE, fnm);
188 ndxfile = ftp2fn_null(efNDX, NFILE, fnm);
189 eigvalfile = ftp2fn(efXVG, NFILE, fnm);
190 eigvecfile = ftp2fn(efTRN, NFILE, fnm);
191 averfile = ftp2fn(efSTO, NFILE, fnm);
192 logfile = ftp2fn(efLOG, NFILE, fnm);
193 asciifile = opt2fn_null("-ascii", NFILE, fnm);
194 xpmfile = opt2fn_null("-xpm", NFILE, fnm);
195 xpmafile = opt2fn_null("-xpma", NFILE, fnm);
197 read_tps_conf(fitfile, str, &top, &ePBC, &xref, NULL, box, TRUE);
202 printf("\nChoose a group for the least squares fit\n");
203 get_index(atoms, ndxfile, 1, &nfit, &ifit, &fitname);
206 gmx_fatal(FARGS, "Need >= 3 points to fit!\n");
213 printf("\nChoose a group for the covariance analysis\n");
214 get_index(atoms, ndxfile, 1, &natoms, &index, &ananame);
219 snew(w_rls, atoms->nr);
220 for (i = 0; (i < nfit); i++)
222 w_rls[ifit[i]] = atoms->atom[ifit[i]].m;
225 bDiffMass1 = bDiffMass1 || (w_rls[ifit[i]] != w_rls[ifit[i-1]]);
231 for (i = 0; (i < natoms); i++)
235 sqrtm[i] = sqrt(atoms->atom[index[i]].m);
238 bDiffMass2 = bDiffMass2 || (sqrtm[i] != sqrtm[i-1]);
247 if (bFit && bDiffMass1 && !bDiffMass2)
249 bDiffMass1 = natoms != nfit;
251 for (i = 0; (i < natoms) && !bDiffMass1; i++)
253 bDiffMass1 = index[i] != ifit[i];
258 "Note: the fit and analysis group are identical,\n"
259 " while the fit is mass weighted and the analysis is not.\n"
260 " Making the fit non mass weighted.\n\n");
261 for (i = 0; (i < nfit); i++)
263 w_rls[ifit[i]] = 1.0;
268 /* Prepare reference frame */
271 gpbc = gmx_rmpbc_init(&top.idef, ePBC, atoms->nr, box);
272 gmx_rmpbc(gpbc, atoms->nr, box, xref);
276 reset_x(nfit, ifit, atoms->nr, NULL, xref, w_rls);
282 if (sqrt(GMX_LARGE_INT_MAX) < ndim)
284 gmx_fatal(FARGS, "Number of degrees of freedoms to large for matrix.\n");
286 snew(mat, ndim*ndim);
288 fprintf(stderr, "Calculating the average structure ...\n");
290 nat = read_first_x(oenv, &status, trxfile, &t, &xread, box);
291 if (nat != atoms->nr)
293 fprintf(stderr, "\nWARNING: number of atoms in tpx (%d) and trajectory (%d) do not match\n", natoms, nat);
298 /* calculate x: a fitted struture of the selected atoms */
301 gmx_rmpbc(gpbc, nat, box, xread);
305 reset_x(nfit, ifit, nat, NULL, xread, w_rls);
306 do_fit(nat, w_rls, xref, xread);
308 for (i = 0; i < natoms; i++)
310 rvec_inc(xav[i], xread[index[i]]);
313 while (read_next_x(oenv, status, &t, nat, xread, box));
316 inv_nframes = 1.0/nframes0;
317 for (i = 0; i < natoms; i++)
319 for (d = 0; d < DIM; d++)
321 xav[i][d] *= inv_nframes;
322 xread[index[i]][d] = xav[i][d];
325 write_sto_conf_indexed(opt2fn("-av", NFILE, fnm), "Average structure",
326 atoms, xread, NULL, epbcNONE, zerobox, natoms, index);
329 fprintf(stderr, "Constructing covariance matrix (%dx%d) ...\n", (int)ndim, (int)ndim);
331 nat = read_first_x(oenv, &status, trxfile, &t, &xread, box);
337 /* calculate x: a (fitted) structure of the selected atoms */
340 gmx_rmpbc(gpbc, nat, box, xread);
344 reset_x(nfit, ifit, nat, NULL, xread, w_rls);
345 do_fit(nat, w_rls, xref, xread);
349 for (i = 0; i < natoms; i++)
351 rvec_sub(xread[index[i]], xref[index[i]], x[i]);
356 for (i = 0; i < natoms; i++)
358 rvec_sub(xread[index[i]], xav[i], x[i]);
362 for (j = 0; j < natoms; j++)
364 for (dj = 0; dj < DIM; dj++)
368 for (i = j; i < natoms; i++)
371 for (d = 0; d < DIM; d++)
373 mat[l+d] += x[i][d]*xj;
379 while (read_next_x(oenv, status, &t, nat, xread, box) &&
380 (bRef || nframes < nframes0));
382 gmx_rmpbc_done(gpbc);
384 fprintf(stderr, "Read %d frames\n", nframes);
388 /* copy the reference structure to the ouput array x */
390 for (i = 0; i < natoms; i++)
392 copy_rvec(xref[index[i]], xproj[i]);
400 /* correct the covariance matrix for the mass */
401 inv_nframes = 1.0/nframes;
402 for (j = 0; j < natoms; j++)
404 for (dj = 0; dj < DIM; dj++)
406 for (i = j; i < natoms; i++)
408 k = ndim*(DIM*j+dj)+DIM*i;
409 for (d = 0; d < DIM; d++)
411 mat[k+d] = mat[k+d]*inv_nframes*sqrtm[i]*sqrtm[j];
417 /* symmetrize the matrix */
418 for (j = 0; j < ndim; j++)
420 for (i = j; i < ndim; i++)
422 mat[ndim*i+j] = mat[ndim*j+i];
427 for (i = 0; i < ndim; i++)
429 trace += mat[i*ndim+i];
431 fprintf(stderr, "\nTrace of the covariance matrix: %g (%snm^2)\n",
432 trace, bM ? "u " : "");
436 out = ffopen(asciifile, "w");
437 for (j = 0; j < ndim; j++)
439 for (i = 0; i < ndim; i += 3)
441 fprintf(out, "%g %g %g\n",
442 mat[ndim*j+i], mat[ndim*j+i+1], mat[ndim*j+i+2]);
453 for (j = 0; j < ndim; j++)
455 mat2[j] = &(mat[ndim*j]);
456 for (i = 0; i <= j; i++)
458 if (mat2[j][i] < min)
462 if (mat2[j][j] > max)
469 for (i = 0; i < ndim; i++)
473 rlo.r = 0; rlo.g = 0; rlo.b = 1;
474 rmi.r = 1; rmi.g = 1; rmi.b = 1;
475 rhi.r = 1; rhi.g = 0; rhi.b = 0;
476 out = ffopen(xpmfile, "w");
478 write_xpm3(out, 0, "Covariance", bM ? "u nm^2" : "nm^2",
479 "dim", "dim", ndim, ndim, axis, axis,
480 mat2, min, 0.0, max, rlo, rmi, rhi, &nlevels);
490 snew(mat2, ndim/DIM);
491 for (i = 0; i < ndim/DIM; i++)
493 snew(mat2[i], ndim/DIM);
495 for (j = 0; j < ndim/DIM; j++)
497 for (i = 0; i <= j; i++)
500 for (d = 0; d < DIM; d++)
502 mat2[j][i] += mat[ndim*(DIM*j+d)+DIM*i+d];
504 if (mat2[j][i] < min)
508 if (mat2[j][j] > max)
512 mat2[i][j] = mat2[j][i];
515 snew(axis, ndim/DIM);
516 for (i = 0; i < ndim/DIM; i++)
520 rlo.r = 0; rlo.g = 0; rlo.b = 1;
521 rmi.r = 1; rmi.g = 1; rmi.b = 1;
522 rhi.r = 1; rhi.g = 0; rhi.b = 0;
523 out = ffopen(xpmafile, "w");
525 write_xpm3(out, 0, "Covariance", bM ? "u nm^2" : "nm^2",
526 "atom", "atom", ndim/DIM, ndim/DIM, axis, axis,
527 mat2, min, 0.0, max, rlo, rmi, rhi, &nlevels);
530 for (i = 0; i < ndim/DIM; i++)
538 /* call diagonalization routine */
540 snew(eigenvalues, ndim);
541 snew(eigenvectors, ndim*ndim);
543 memcpy(eigenvectors, mat, ndim*ndim*sizeof(real));
544 fprintf(stderr, "\nDiagonalizing ...\n");
546 eigensolver(eigenvectors, ndim, 0, ndim, eigenvalues, mat);
549 /* now write the output */
552 for (i = 0; i < ndim; i++)
554 sum += eigenvalues[i];
556 fprintf(stderr, "\nSum of the eigenvalues: %g (%snm^2)\n",
557 sum, bM ? "u " : "");
558 if (fabs(trace-sum) > 0.01*trace)
560 fprintf(stderr, "\nWARNING: eigenvalue sum deviates from the trace of the covariance matrix\n");
563 fprintf(stderr, "\nWriting eigenvalues to %s\n", eigvalfile);
565 sprintf(str, "(%snm\\S2\\N)", bM ? "u " : "");
566 out = xvgropen(eigvalfile,
567 "Eigenvalues of the covariance matrix",
568 "Eigenvector index", str, oenv);
569 for (i = 0; (i < ndim); i++)
571 fprintf (out, "%10d %g\n", (int)i+1, eigenvalues[ndim-1-i]);
577 if (nframes-1 < ndim)
588 /* misuse lambda: 0/1 mass weighted analysis no/yes */
591 WriteXref = eWXR_YES;
592 for (i = 0; i < nfit; i++)
594 copy_rvec(xref[ifit[i]], x[i]);
604 /* misuse lambda: -1 for no fit */
605 WriteXref = eWXR_NOFIT;
608 write_eigenvectors(eigvecfile, natoms, mat, TRUE, 1, end,
609 WriteXref, x, bDiffMass1, xproj, bM, eigenvalues);
611 out = ffopen(logfile, "w");
614 gmx_ctime_r(&now, timebuf, STRLEN);
615 fprintf(out, "Covariance analysis log, written %s\n", timebuf);
617 fprintf(out, "Program: %s\n", argv[0]);
618 #ifdef GMX_NATIVE_WINDOWS
619 pcwd = _getcwd(str, STRLEN);
621 pcwd = getcwd(str, STRLEN);
625 gmx_fatal(FARGS, "Current working directory is undefined");
628 fprintf(out, "Working directory: %s\n\n", str);
630 fprintf(out, "Read %d frames from %s (time %g to %g %s)\n", nframes, trxfile,
631 output_env_conv_time(oenv, tstart), output_env_conv_time(oenv, tend), output_env_get_time_unit(oenv));
634 fprintf(out, "Read reference structure for fit from %s\n", fitfile);
638 fprintf(out, "Read index groups from %s\n", ndxfile);
642 fprintf(out, "Analysis group is '%s' (%d atoms)\n", ananame, natoms);
645 fprintf(out, "Fit group is '%s' (%d atoms)\n", fitname, nfit);
649 fprintf(out, "No fit was used\n");
651 fprintf(out, "Analysis is %smass weighted\n", bDiffMass2 ? "" : "non-");
654 fprintf(out, "Fit is %smass weighted\n", bDiffMass1 ? "" : "non-");
656 fprintf(out, "Diagonalized the %dx%d covariance matrix\n", (int)ndim, (int)ndim);
657 fprintf(out, "Trace of the covariance matrix before diagonalizing: %g\n",
659 fprintf(out, "Trace of the covariance matrix after diagonalizing: %g\n\n",
662 fprintf(out, "Wrote %d eigenvalues to %s\n", (int)ndim, eigvalfile);
663 if (WriteXref == eWXR_YES)
665 fprintf(out, "Wrote reference structure to %s\n", eigvecfile);
667 fprintf(out, "Wrote average structure to %s and %s\n", averfile, eigvecfile);
668 fprintf(out, "Wrote eigenvectors %d to %d to %s\n", 1, end, eigvecfile);
672 fprintf(stderr, "Wrote the log to %s\n", logfile);