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
70 #include "gromacs/linearalgebra/eigensolver.h"
72 int gmx_covar(int argc, char *argv[])
74 const char *desc[] = {
75 "[TT]g_covar[tt] calculates and diagonalizes the (mass-weighted)",
77 "All structures are fitted to the structure in the structure file.",
78 "When this is not a run input file periodicity will not be taken into",
79 "account. When the fit and analysis groups are identical and the analysis",
80 "is non mass-weighted, the fit will also be non mass-weighted.",
82 "The eigenvectors are written to a trajectory file ([TT]-v[tt]).",
83 "When the same atoms are used for the fit and the covariance analysis,",
84 "the reference structure for the fit is written first with t=-1.",
85 "The average (or reference when [TT]-ref[tt] is used) structure is",
86 "written with t=0, the eigenvectors",
87 "are written as frames with the eigenvector number as timestamp.",
89 "The eigenvectors can be analyzed with [TT]g_anaeig[tt].",
91 "Option [TT]-ascii[tt] writes the whole covariance matrix to",
92 "an ASCII file. The order of the elements is: x1x1, x1y1, x1z1, x1x2, ...",
94 "Option [TT]-xpm[tt] writes the whole covariance matrix to an [TT].xpm[tt] file.",
96 "Option [TT]-xpma[tt] writes the atomic covariance matrix to an [TT].xpm[tt] file,",
97 "i.e. for each atom pair the sum of the xx, yy and zz covariances is",
100 "Note that the diagonalization of a matrix requires memory and time",
101 "that will increase at least as fast as than the square of the number",
102 "of atoms involved. It is easy to run out of memory, in which",
103 "case this tool will probably exit with a 'Segmentation fault'. You",
104 "should consider carefully whether a reduced set of atoms will meet",
105 "your needs for lower costs."
107 static gmx_bool bFit = TRUE, bRef = FALSE, bM = FALSE, bPBC = TRUE;
110 { "-fit", FALSE, etBOOL, {&bFit},
111 "Fit to a reference structure"},
112 { "-ref", FALSE, etBOOL, {&bRef},
113 "Use the deviation from the conformation in the structure file instead of from the average" },
114 { "-mwa", FALSE, etBOOL, {&bM},
115 "Mass-weighted covariance analysis"},
116 { "-last", FALSE, etINT, {&end},
117 "Last eigenvector to write away (-1 is till the last)" },
118 { "-pbc", FALSE, etBOOL, {&bPBC},
119 "Apply corrections for periodic boundary conditions" }
127 rvec *x, *xread, *xref, *xav, *xproj;
129 real *sqrtm, *mat, *eigenvalues, sum, trace, inv_nframes;
130 real t, tstart, tend, **mat2;
131 real xj, *w_rls = NULL;
132 real min, max, *axis;
134 int natoms, nat, count, nframes0, nframes, nlevels;
135 gmx_large_int_t ndim, i, j, k, l;
137 const char *fitfile, *trxfile, *ndxfile;
138 const char *eigvalfile, *eigvecfile, *averfile, *logfile;
139 const char *asciifile, *xpmfile, *xpmafile;
140 char str[STRLEN], *fitname, *ananame, *pcwd;
142 atom_id *index, *ifit;
143 gmx_bool bDiffMass1, bDiffMass2;
145 char timebuf[STRLEN];
149 gmx_rmpbc_t gpbc = NULL;
152 { efTRX, "-f", NULL, ffREAD },
153 { efTPS, NULL, NULL, ffREAD },
154 { efNDX, NULL, NULL, ffOPTRD },
155 { efXVG, NULL, "eigenval", ffWRITE },
156 { efTRN, "-v", "eigenvec", ffWRITE },
157 { efSTO, "-av", "average.pdb", ffWRITE },
158 { efLOG, NULL, "covar", ffWRITE },
159 { efDAT, "-ascii", "covar", ffOPTWR },
160 { efXPM, "-xpm", "covar", ffOPTWR },
161 { efXPM, "-xpma", "covara", ffOPTWR }
163 #define NFILE asize(fnm)
165 if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
166 NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
173 fitfile = ftp2fn(efTPS, NFILE, fnm);
174 trxfile = ftp2fn(efTRX, NFILE, fnm);
175 ndxfile = ftp2fn_null(efNDX, NFILE, fnm);
176 eigvalfile = ftp2fn(efXVG, NFILE, fnm);
177 eigvecfile = ftp2fn(efTRN, NFILE, fnm);
178 averfile = ftp2fn(efSTO, NFILE, fnm);
179 logfile = ftp2fn(efLOG, NFILE, fnm);
180 asciifile = opt2fn_null("-ascii", NFILE, fnm);
181 xpmfile = opt2fn_null("-xpm", NFILE, fnm);
182 xpmafile = opt2fn_null("-xpma", NFILE, fnm);
184 read_tps_conf(fitfile, str, &top, &ePBC, &xref, NULL, box, TRUE);
189 printf("\nChoose a group for the least squares fit\n");
190 get_index(atoms, ndxfile, 1, &nfit, &ifit, &fitname);
193 gmx_fatal(FARGS, "Need >= 3 points to fit!\n");
200 printf("\nChoose a group for the covariance analysis\n");
201 get_index(atoms, ndxfile, 1, &natoms, &index, &ananame);
206 snew(w_rls, atoms->nr);
207 for (i = 0; (i < nfit); i++)
209 w_rls[ifit[i]] = atoms->atom[ifit[i]].m;
212 bDiffMass1 = bDiffMass1 || (w_rls[ifit[i]] != w_rls[ifit[i-1]]);
218 for (i = 0; (i < natoms); i++)
222 sqrtm[i] = sqrt(atoms->atom[index[i]].m);
225 bDiffMass2 = bDiffMass2 || (sqrtm[i] != sqrtm[i-1]);
234 if (bFit && bDiffMass1 && !bDiffMass2)
236 bDiffMass1 = natoms != nfit;
238 for (i = 0; (i < natoms) && !bDiffMass1; i++)
240 bDiffMass1 = index[i] != ifit[i];
245 "Note: the fit and analysis group are identical,\n"
246 " while the fit is mass weighted and the analysis is not.\n"
247 " Making the fit non mass weighted.\n\n");
248 for (i = 0; (i < nfit); i++)
250 w_rls[ifit[i]] = 1.0;
255 /* Prepare reference frame */
258 gpbc = gmx_rmpbc_init(&top.idef, ePBC, atoms->nr);
259 gmx_rmpbc(gpbc, atoms->nr, box, xref);
263 reset_x(nfit, ifit, atoms->nr, NULL, xref, w_rls);
269 if (sqrt(GMX_LARGE_INT_MAX) < ndim)
271 gmx_fatal(FARGS, "Number of degrees of freedoms to large for matrix.\n");
273 snew(mat, ndim*ndim);
275 fprintf(stderr, "Calculating the average structure ...\n");
277 nat = read_first_x(oenv, &status, trxfile, &t, &xread, box);
278 if (nat != atoms->nr)
280 fprintf(stderr, "\nWARNING: number of atoms in tpx (%d) and trajectory (%d) do not match\n", natoms, nat);
285 /* calculate x: a fitted struture of the selected atoms */
288 gmx_rmpbc(gpbc, nat, box, xread);
292 reset_x(nfit, ifit, nat, NULL, xread, w_rls);
293 do_fit(nat, w_rls, xref, xread);
295 for (i = 0; i < natoms; i++)
297 rvec_inc(xav[i], xread[index[i]]);
300 while (read_next_x(oenv, status, &t, xread, box));
303 inv_nframes = 1.0/nframes0;
304 for (i = 0; i < natoms; i++)
306 for (d = 0; d < DIM; d++)
308 xav[i][d] *= inv_nframes;
309 xread[index[i]][d] = xav[i][d];
312 write_sto_conf_indexed(opt2fn("-av", NFILE, fnm), "Average structure",
313 atoms, xread, NULL, epbcNONE, zerobox, natoms, index);
316 fprintf(stderr, "Constructing covariance matrix (%dx%d) ...\n", (int)ndim, (int)ndim);
318 nat = read_first_x(oenv, &status, trxfile, &t, &xread, box);
324 /* calculate x: a (fitted) structure of the selected atoms */
327 gmx_rmpbc(gpbc, nat, box, xread);
331 reset_x(nfit, ifit, nat, NULL, xread, w_rls);
332 do_fit(nat, w_rls, xref, xread);
336 for (i = 0; i < natoms; i++)
338 rvec_sub(xread[index[i]], xref[index[i]], x[i]);
343 for (i = 0; i < natoms; i++)
345 rvec_sub(xread[index[i]], xav[i], x[i]);
349 for (j = 0; j < natoms; j++)
351 for (dj = 0; dj < DIM; dj++)
355 for (i = j; i < natoms; i++)
358 for (d = 0; d < DIM; d++)
360 mat[l+d] += x[i][d]*xj;
366 while (read_next_x(oenv, status, &t, xread, box) &&
367 (bRef || nframes < nframes0));
369 gmx_rmpbc_done(gpbc);
371 fprintf(stderr, "Read %d frames\n", nframes);
375 /* copy the reference structure to the ouput array x */
377 for (i = 0; i < natoms; i++)
379 copy_rvec(xref[index[i]], xproj[i]);
387 /* correct the covariance matrix for the mass */
388 inv_nframes = 1.0/nframes;
389 for (j = 0; j < natoms; j++)
391 for (dj = 0; dj < DIM; dj++)
393 for (i = j; i < natoms; i++)
395 k = ndim*(DIM*j+dj)+DIM*i;
396 for (d = 0; d < DIM; d++)
398 mat[k+d] = mat[k+d]*inv_nframes*sqrtm[i]*sqrtm[j];
404 /* symmetrize the matrix */
405 for (j = 0; j < ndim; j++)
407 for (i = j; i < ndim; i++)
409 mat[ndim*i+j] = mat[ndim*j+i];
414 for (i = 0; i < ndim; i++)
416 trace += mat[i*ndim+i];
418 fprintf(stderr, "\nTrace of the covariance matrix: %g (%snm^2)\n",
419 trace, bM ? "u " : "");
423 out = ffopen(asciifile, "w");
424 for (j = 0; j < ndim; j++)
426 for (i = 0; i < ndim; i += 3)
428 fprintf(out, "%g %g %g\n",
429 mat[ndim*j+i], mat[ndim*j+i+1], mat[ndim*j+i+2]);
440 for (j = 0; j < ndim; j++)
442 mat2[j] = &(mat[ndim*j]);
443 for (i = 0; i <= j; i++)
445 if (mat2[j][i] < min)
449 if (mat2[j][j] > max)
456 for (i = 0; i < ndim; i++)
460 rlo.r = 0; rlo.g = 0; rlo.b = 1;
461 rmi.r = 1; rmi.g = 1; rmi.b = 1;
462 rhi.r = 1; rhi.g = 0; rhi.b = 0;
463 out = ffopen(xpmfile, "w");
465 write_xpm3(out, 0, "Covariance", bM ? "u nm^2" : "nm^2",
466 "dim", "dim", ndim, ndim, axis, axis,
467 mat2, min, 0.0, max, rlo, rmi, rhi, &nlevels);
477 snew(mat2, ndim/DIM);
478 for (i = 0; i < ndim/DIM; i++)
480 snew(mat2[i], ndim/DIM);
482 for (j = 0; j < ndim/DIM; j++)
484 for (i = 0; i <= j; i++)
487 for (d = 0; d < DIM; d++)
489 mat2[j][i] += mat[ndim*(DIM*j+d)+DIM*i+d];
491 if (mat2[j][i] < min)
495 if (mat2[j][j] > max)
499 mat2[i][j] = mat2[j][i];
502 snew(axis, ndim/DIM);
503 for (i = 0; i < ndim/DIM; i++)
507 rlo.r = 0; rlo.g = 0; rlo.b = 1;
508 rmi.r = 1; rmi.g = 1; rmi.b = 1;
509 rhi.r = 1; rhi.g = 0; rhi.b = 0;
510 out = ffopen(xpmafile, "w");
512 write_xpm3(out, 0, "Covariance", bM ? "u nm^2" : "nm^2",
513 "atom", "atom", ndim/DIM, ndim/DIM, axis, axis,
514 mat2, min, 0.0, max, rlo, rmi, rhi, &nlevels);
517 for (i = 0; i < ndim/DIM; i++)
525 /* call diagonalization routine */
527 snew(eigenvalues, ndim);
528 snew(eigenvectors, ndim*ndim);
530 memcpy(eigenvectors, mat, ndim*ndim*sizeof(real));
531 fprintf(stderr, "\nDiagonalizing ...\n");
533 eigensolver(eigenvectors, ndim, 0, ndim, eigenvalues, mat);
536 /* now write the output */
539 for (i = 0; i < ndim; i++)
541 sum += eigenvalues[i];
543 fprintf(stderr, "\nSum of the eigenvalues: %g (%snm^2)\n",
544 sum, bM ? "u " : "");
545 if (fabs(trace-sum) > 0.01*trace)
547 fprintf(stderr, "\nWARNING: eigenvalue sum deviates from the trace of the covariance matrix\n");
550 fprintf(stderr, "\nWriting eigenvalues to %s\n", eigvalfile);
552 sprintf(str, "(%snm\\S2\\N)", bM ? "u " : "");
553 out = xvgropen(eigvalfile,
554 "Eigenvalues of the covariance matrix",
555 "Eigenvector index", str, oenv);
556 for (i = 0; (i < ndim); i++)
558 fprintf (out, "%10d %g\n", (int)i+1, eigenvalues[ndim-1-i]);
564 if (nframes-1 < ndim)
575 /* misuse lambda: 0/1 mass weighted analysis no/yes */
578 WriteXref = eWXR_YES;
579 for (i = 0; i < nfit; i++)
581 copy_rvec(xref[ifit[i]], x[i]);
591 /* misuse lambda: -1 for no fit */
592 WriteXref = eWXR_NOFIT;
595 write_eigenvectors(eigvecfile, natoms, mat, TRUE, 1, end,
596 WriteXref, x, bDiffMass1, xproj, bM, eigenvalues);
598 out = ffopen(logfile, "w");
601 gmx_ctime_r(&now, timebuf, STRLEN);
602 fprintf(out, "Covariance analysis log, written %s\n", timebuf);
604 fprintf(out, "Program: %s\n", argv[0]);
605 gmx_getcwd(str, STRLEN);
607 fprintf(out, "Working directory: %s\n\n", str);
609 fprintf(out, "Read %d frames from %s (time %g to %g %s)\n", nframes, trxfile,
610 output_env_conv_time(oenv, tstart), output_env_conv_time(oenv, tend), output_env_get_time_unit(oenv));
613 fprintf(out, "Read reference structure for fit from %s\n", fitfile);
617 fprintf(out, "Read index groups from %s\n", ndxfile);
621 fprintf(out, "Analysis group is '%s' (%d atoms)\n", ananame, natoms);
624 fprintf(out, "Fit group is '%s' (%d atoms)\n", fitname, nfit);
628 fprintf(out, "No fit was used\n");
630 fprintf(out, "Analysis is %smass weighted\n", bDiffMass2 ? "" : "non-");
633 fprintf(out, "Fit is %smass weighted\n", bDiffMass1 ? "" : "non-");
635 fprintf(out, "Diagonalized the %dx%d covariance matrix\n", (int)ndim, (int)ndim);
636 fprintf(out, "Trace of the covariance matrix before diagonalizing: %g\n",
638 fprintf(out, "Trace of the covariance matrix after diagonalizing: %g\n\n",
641 fprintf(out, "Wrote %d eigenvalues to %s\n", (int)ndim, eigvalfile);
642 if (WriteXref == eWXR_YES)
644 fprintf(out, "Wrote reference structure to %s\n", eigvecfile);
646 fprintf(out, "Wrote average structure to %s and %s\n", averfile, eigvecfile);
647 fprintf(out, "Wrote eigenvectors %d to %d to %s\n", 1, end, eigvecfile);
651 fprintf(stderr, "Wrote the log to %s\n", logfile);