3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
10 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12 * Copyright (c) 2001-2004, The GROMACS development team,
13 * check out http://www.gromacs.org for more information.
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * If you want to redistribute modifications, please consider that
21 * scientific software is very special. Version control is crucial -
22 * bugs must be traceable. We will be happy to consider code for
23 * inclusion in the official distribution, but derived work must not
24 * be called official GROMACS. Details are found in the README & COPYING
25 * files - if they are missing, get the official version at www.gromacs.org.
27 * To help us fund GROMACS development, we humbly ask that you cite
28 * the papers on the package - you can find them in the top README file.
30 * For more info, check our website at http://www.gromacs.org
33 * Green Red Orange Magenta Azure Cyan Skyblue
43 #ifdef HAVE_SYS_TIME_H
54 #include "gromacs/fileio/futil.h"
57 #include "gromacs/fileio/confio.h"
58 #include "gromacs/fileio/trnio.h"
64 #include "gromacs/fileio/matio.h"
69 #include "gromacs/fileio/trxio.h"
71 #include "gromacs/linearalgebra/eigensolver.h"
73 int gmx_covar(int argc, char *argv[])
75 const char *desc[] = {
76 "[TT]g_covar[tt] calculates and diagonalizes the (mass-weighted)",
78 "All structures are fitted to the structure in the structure file.",
79 "When this is not a run input file periodicity will not be taken into",
80 "account. When the fit and analysis groups are identical and the analysis",
81 "is non mass-weighted, the fit will also be non mass-weighted.",
83 "The eigenvectors are written to a trajectory file ([TT]-v[tt]).",
84 "When the same atoms are used for the fit and the covariance analysis,",
85 "the reference structure for the fit is written first with t=-1.",
86 "The average (or reference when [TT]-ref[tt] is used) structure is",
87 "written with t=0, the eigenvectors",
88 "are written as frames with the eigenvector number as timestamp.",
90 "The eigenvectors can be analyzed with [TT]g_anaeig[tt].",
92 "Option [TT]-ascii[tt] writes the whole covariance matrix to",
93 "an ASCII file. The order of the elements is: x1x1, x1y1, x1z1, x1x2, ...",
95 "Option [TT]-xpm[tt] writes the whole covariance matrix to an [TT].xpm[tt] file.",
97 "Option [TT]-xpma[tt] writes the atomic covariance matrix to an [TT].xpm[tt] file,",
98 "i.e. for each atom pair the sum of the xx, yy and zz covariances is",
101 "Note that the diagonalization of a matrix requires memory and time",
102 "that will increase at least as fast as than the square of the number",
103 "of atoms involved. It is easy to run out of memory, in which",
104 "case this tool will probably exit with a 'Segmentation fault'. You",
105 "should consider carefully whether a reduced set of atoms will meet",
106 "your needs for lower costs."
108 static gmx_bool bFit = TRUE, bRef = FALSE, bM = FALSE, bPBC = TRUE;
111 { "-fit", FALSE, etBOOL, {&bFit},
112 "Fit to a reference structure"},
113 { "-ref", FALSE, etBOOL, {&bRef},
114 "Use the deviation from the conformation in the structure file instead of from the average" },
115 { "-mwa", FALSE, etBOOL, {&bM},
116 "Mass-weighted covariance analysis"},
117 { "-last", FALSE, etINT, {&end},
118 "Last eigenvector to write away (-1 is till the last)" },
119 { "-pbc", FALSE, etBOOL, {&bPBC},
120 "Apply corrections for periodic boundary conditions" }
128 rvec *x, *xread, *xref, *xav, *xproj;
130 real *sqrtm, *mat, *eigenvalues, sum, trace, inv_nframes;
131 real t, tstart, tend, **mat2;
132 real xj, *w_rls = NULL;
133 real min, max, *axis;
135 int natoms, nat, count, nframes0, nframes, nlevels;
136 gmx_large_int_t ndim, i, j, k, l;
138 const char *fitfile, *trxfile, *ndxfile;
139 const char *eigvalfile, *eigvecfile, *averfile, *logfile;
140 const char *asciifile, *xpmfile, *xpmafile;
141 char str[STRLEN], *fitname, *ananame, *pcwd;
143 atom_id *index, *ifit;
144 gmx_bool bDiffMass1, bDiffMass2;
146 char timebuf[STRLEN];
150 gmx_rmpbc_t gpbc = NULL;
153 { efTRX, "-f", NULL, ffREAD },
154 { efTPS, NULL, NULL, ffREAD },
155 { efNDX, NULL, NULL, ffOPTRD },
156 { efXVG, NULL, "eigenval", ffWRITE },
157 { efTRN, "-v", "eigenvec", ffWRITE },
158 { efSTO, "-av", "average.pdb", ffWRITE },
159 { efLOG, NULL, "covar", ffWRITE },
160 { efDAT, "-ascii", "covar", ffOPTWR },
161 { efXPM, "-xpm", "covar", ffOPTWR },
162 { efXPM, "-xpma", "covara", ffOPTWR }
164 #define NFILE asize(fnm)
166 if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
167 NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
174 fitfile = ftp2fn(efTPS, NFILE, fnm);
175 trxfile = ftp2fn(efTRX, NFILE, fnm);
176 ndxfile = ftp2fn_null(efNDX, NFILE, fnm);
177 eigvalfile = ftp2fn(efXVG, NFILE, fnm);
178 eigvecfile = ftp2fn(efTRN, NFILE, fnm);
179 averfile = ftp2fn(efSTO, NFILE, fnm);
180 logfile = ftp2fn(efLOG, NFILE, fnm);
181 asciifile = opt2fn_null("-ascii", NFILE, fnm);
182 xpmfile = opt2fn_null("-xpm", NFILE, fnm);
183 xpmafile = opt2fn_null("-xpma", NFILE, fnm);
185 read_tps_conf(fitfile, str, &top, &ePBC, &xref, NULL, box, TRUE);
190 printf("\nChoose a group for the least squares fit\n");
191 get_index(atoms, ndxfile, 1, &nfit, &ifit, &fitname);
194 gmx_fatal(FARGS, "Need >= 3 points to fit!\n");
201 printf("\nChoose a group for the covariance analysis\n");
202 get_index(atoms, ndxfile, 1, &natoms, &index, &ananame);
207 snew(w_rls, atoms->nr);
208 for (i = 0; (i < nfit); i++)
210 w_rls[ifit[i]] = atoms->atom[ifit[i]].m;
213 bDiffMass1 = bDiffMass1 || (w_rls[ifit[i]] != w_rls[ifit[i-1]]);
219 for (i = 0; (i < natoms); i++)
223 sqrtm[i] = sqrt(atoms->atom[index[i]].m);
226 bDiffMass2 = bDiffMass2 || (sqrtm[i] != sqrtm[i-1]);
235 if (bFit && bDiffMass1 && !bDiffMass2)
237 bDiffMass1 = natoms != nfit;
239 for (i = 0; (i < natoms) && !bDiffMass1; i++)
241 bDiffMass1 = index[i] != ifit[i];
246 "Note: the fit and analysis group are identical,\n"
247 " while the fit is mass weighted and the analysis is not.\n"
248 " Making the fit non mass weighted.\n\n");
249 for (i = 0; (i < nfit); i++)
251 w_rls[ifit[i]] = 1.0;
256 /* Prepare reference frame */
259 gpbc = gmx_rmpbc_init(&top.idef, ePBC, atoms->nr);
260 gmx_rmpbc(gpbc, atoms->nr, box, xref);
264 reset_x(nfit, ifit, atoms->nr, NULL, xref, w_rls);
270 if (sqrt(GMX_LARGE_INT_MAX) < ndim)
272 gmx_fatal(FARGS, "Number of degrees of freedoms to large for matrix.\n");
274 snew(mat, ndim*ndim);
276 fprintf(stderr, "Calculating the average structure ...\n");
278 nat = read_first_x(oenv, &status, trxfile, &t, &xread, box);
279 if (nat != atoms->nr)
281 fprintf(stderr, "\nWARNING: number of atoms in tpx (%d) and trajectory (%d) do not match\n", natoms, nat);
286 /* calculate x: a fitted struture of the selected atoms */
289 gmx_rmpbc(gpbc, nat, box, xread);
293 reset_x(nfit, ifit, nat, NULL, xread, w_rls);
294 do_fit(nat, w_rls, xref, xread);
296 for (i = 0; i < natoms; i++)
298 rvec_inc(xav[i], xread[index[i]]);
301 while (read_next_x(oenv, status, &t, xread, box));
304 inv_nframes = 1.0/nframes0;
305 for (i = 0; i < natoms; i++)
307 for (d = 0; d < DIM; d++)
309 xav[i][d] *= inv_nframes;
310 xread[index[i]][d] = xav[i][d];
313 write_sto_conf_indexed(opt2fn("-av", NFILE, fnm), "Average structure",
314 atoms, xread, NULL, epbcNONE, zerobox, natoms, index);
317 fprintf(stderr, "Constructing covariance matrix (%dx%d) ...\n", (int)ndim, (int)ndim);
319 nat = read_first_x(oenv, &status, trxfile, &t, &xread, box);
325 /* calculate x: a (fitted) structure of the selected atoms */
328 gmx_rmpbc(gpbc, nat, box, xread);
332 reset_x(nfit, ifit, nat, NULL, xread, w_rls);
333 do_fit(nat, w_rls, xref, xread);
337 for (i = 0; i < natoms; i++)
339 rvec_sub(xread[index[i]], xref[index[i]], x[i]);
344 for (i = 0; i < natoms; i++)
346 rvec_sub(xread[index[i]], xav[i], x[i]);
350 for (j = 0; j < natoms; j++)
352 for (dj = 0; dj < DIM; dj++)
356 for (i = j; i < natoms; i++)
359 for (d = 0; d < DIM; d++)
361 mat[l+d] += x[i][d]*xj;
367 while (read_next_x(oenv, status, &t, xread, box) &&
368 (bRef || nframes < nframes0));
370 gmx_rmpbc_done(gpbc);
372 fprintf(stderr, "Read %d frames\n", nframes);
376 /* copy the reference structure to the ouput array x */
378 for (i = 0; i < natoms; i++)
380 copy_rvec(xref[index[i]], xproj[i]);
388 /* correct the covariance matrix for the mass */
389 inv_nframes = 1.0/nframes;
390 for (j = 0; j < natoms; j++)
392 for (dj = 0; dj < DIM; dj++)
394 for (i = j; i < natoms; i++)
396 k = ndim*(DIM*j+dj)+DIM*i;
397 for (d = 0; d < DIM; d++)
399 mat[k+d] = mat[k+d]*inv_nframes*sqrtm[i]*sqrtm[j];
405 /* symmetrize the matrix */
406 for (j = 0; j < ndim; j++)
408 for (i = j; i < ndim; i++)
410 mat[ndim*i+j] = mat[ndim*j+i];
415 for (i = 0; i < ndim; i++)
417 trace += mat[i*ndim+i];
419 fprintf(stderr, "\nTrace of the covariance matrix: %g (%snm^2)\n",
420 trace, bM ? "u " : "");
424 out = ffopen(asciifile, "w");
425 for (j = 0; j < ndim; j++)
427 for (i = 0; i < ndim; i += 3)
429 fprintf(out, "%g %g %g\n",
430 mat[ndim*j+i], mat[ndim*j+i+1], mat[ndim*j+i+2]);
441 for (j = 0; j < ndim; j++)
443 mat2[j] = &(mat[ndim*j]);
444 for (i = 0; i <= j; i++)
446 if (mat2[j][i] < min)
450 if (mat2[j][j] > max)
457 for (i = 0; i < ndim; i++)
461 rlo.r = 0; rlo.g = 0; rlo.b = 1;
462 rmi.r = 1; rmi.g = 1; rmi.b = 1;
463 rhi.r = 1; rhi.g = 0; rhi.b = 0;
464 out = ffopen(xpmfile, "w");
466 write_xpm3(out, 0, "Covariance", bM ? "u nm^2" : "nm^2",
467 "dim", "dim", ndim, ndim, axis, axis,
468 mat2, min, 0.0, max, rlo, rmi, rhi, &nlevels);
478 snew(mat2, ndim/DIM);
479 for (i = 0; i < ndim/DIM; i++)
481 snew(mat2[i], ndim/DIM);
483 for (j = 0; j < ndim/DIM; j++)
485 for (i = 0; i <= j; i++)
488 for (d = 0; d < DIM; d++)
490 mat2[j][i] += mat[ndim*(DIM*j+d)+DIM*i+d];
492 if (mat2[j][i] < min)
496 if (mat2[j][j] > max)
500 mat2[i][j] = mat2[j][i];
503 snew(axis, ndim/DIM);
504 for (i = 0; i < ndim/DIM; i++)
508 rlo.r = 0; rlo.g = 0; rlo.b = 1;
509 rmi.r = 1; rmi.g = 1; rmi.b = 1;
510 rhi.r = 1; rhi.g = 0; rhi.b = 0;
511 out = ffopen(xpmafile, "w");
513 write_xpm3(out, 0, "Covariance", bM ? "u nm^2" : "nm^2",
514 "atom", "atom", ndim/DIM, ndim/DIM, axis, axis,
515 mat2, min, 0.0, max, rlo, rmi, rhi, &nlevels);
518 for (i = 0; i < ndim/DIM; i++)
526 /* call diagonalization routine */
528 snew(eigenvalues, ndim);
529 snew(eigenvectors, ndim*ndim);
531 memcpy(eigenvectors, mat, ndim*ndim*sizeof(real));
532 fprintf(stderr, "\nDiagonalizing ...\n");
534 eigensolver(eigenvectors, ndim, 0, ndim, eigenvalues, mat);
537 /* now write the output */
540 for (i = 0; i < ndim; i++)
542 sum += eigenvalues[i];
544 fprintf(stderr, "\nSum of the eigenvalues: %g (%snm^2)\n",
545 sum, bM ? "u " : "");
546 if (fabs(trace-sum) > 0.01*trace)
548 fprintf(stderr, "\nWARNING: eigenvalue sum deviates from the trace of the covariance matrix\n");
551 fprintf(stderr, "\nWriting eigenvalues to %s\n", eigvalfile);
553 sprintf(str, "(%snm\\S2\\N)", bM ? "u " : "");
554 out = xvgropen(eigvalfile,
555 "Eigenvalues of the covariance matrix",
556 "Eigenvector index", str, oenv);
557 for (i = 0; (i < ndim); i++)
559 fprintf (out, "%10d %g\n", (int)i+1, eigenvalues[ndim-1-i]);
565 if (nframes-1 < ndim)
576 /* misuse lambda: 0/1 mass weighted analysis no/yes */
579 WriteXref = eWXR_YES;
580 for (i = 0; i < nfit; i++)
582 copy_rvec(xref[ifit[i]], x[i]);
592 /* misuse lambda: -1 for no fit */
593 WriteXref = eWXR_NOFIT;
596 write_eigenvectors(eigvecfile, natoms, mat, TRUE, 1, end,
597 WriteXref, x, bDiffMass1, xproj, bM, eigenvalues);
599 out = ffopen(logfile, "w");
602 gmx_ctime_r(&now, timebuf, STRLEN);
603 fprintf(out, "Covariance analysis log, written %s\n", timebuf);
605 fprintf(out, "Program: %s\n", argv[0]);
606 gmx_getcwd(str, STRLEN);
608 fprintf(out, "Working directory: %s\n\n", str);
610 fprintf(out, "Read %d frames from %s (time %g to %g %s)\n", nframes, trxfile,
611 output_env_conv_time(oenv, tstart), output_env_conv_time(oenv, tend), output_env_get_time_unit(oenv));
614 fprintf(out, "Read reference structure for fit from %s\n", fitfile);
618 fprintf(out, "Read index groups from %s\n", ndxfile);
622 fprintf(out, "Analysis group is '%s' (%d atoms)\n", ananame, natoms);
625 fprintf(out, "Fit group is '%s' (%d atoms)\n", fitname, nfit);
629 fprintf(out, "No fit was used\n");
631 fprintf(out, "Analysis is %smass weighted\n", bDiffMass2 ? "" : "non-");
634 fprintf(out, "Fit is %smass weighted\n", bDiffMass1 ? "" : "non-");
636 fprintf(out, "Diagonalized the %dx%d covariance matrix\n", (int)ndim, (int)ndim);
637 fprintf(out, "Trace of the covariance matrix before diagonalizing: %g\n",
639 fprintf(out, "Trace of the covariance matrix after diagonalizing: %g\n\n",
642 fprintf(out, "Wrote %d eigenvalues to %s\n", (int)ndim, eigvalfile);
643 if (WriteXref == eWXR_YES)
645 fprintf(out, "Wrote reference structure to %s\n", eigvecfile);
647 fprintf(out, "Wrote average structure to %s and %s\n", averfile, eigvecfile);
648 fprintf(out, "Wrote eigenvectors %d to %d to %s\n", 1, end, eigvecfile);
652 fprintf(stderr, "Wrote the log to %s\n", logfile);