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
71 #include "gromacs/linearalgebra/eigensolver.h"
73 /* Portable version of ctime_r implemented in src/gmxlib/string2.c, but we do not want it declared in public installed headers */
75 gmx_ctime_r(const time_t *clock,char *buf, int n);
78 int gmx_covar(int argc,char *argv[])
80 const char *desc[] = {
81 "[TT]g_covar[tt] calculates and diagonalizes the (mass-weighted)",
83 "All structures are fitted to the structure in the structure file.",
84 "When this is not a run input file periodicity will not be taken into",
85 "account. When the fit and analysis groups are identical and the analysis",
86 "is non mass-weighted, the fit will also be non mass-weighted.",
88 "The eigenvectors are written to a trajectory file ([TT]-v[tt]).",
89 "When the same atoms are used for the fit and the covariance analysis,",
90 "the reference structure for the fit is written first with t=-1.",
91 "The average (or reference when [TT]-ref[tt] is used) structure is",
92 "written with t=0, the eigenvectors",
93 "are written as frames with the eigenvector number as timestamp.",
95 "The eigenvectors can be analyzed with [TT]g_anaeig[tt].",
97 "Option [TT]-ascii[tt] writes the whole covariance matrix to",
98 "an ASCII file. The order of the elements is: x1x1, x1y1, x1z1, x1x2, ...",
100 "Option [TT]-xpm[tt] writes the whole covariance matrix to an [TT].xpm[tt] file.",
102 "Option [TT]-xpma[tt] writes the atomic covariance matrix to an [TT].xpm[tt] file,",
103 "i.e. for each atom pair the sum of the xx, yy and zz covariances is",
106 "Note that the diagonalization of a matrix requires memory and time",
107 "that will increase at least as fast as than the square of the number",
108 "of atoms involved. It is easy to run out of memory, in which",
109 "case this tool will probably exit with a 'Segmentation fault'. You",
110 "should consider carefully whether a reduced set of atoms will meet",
111 "your needs for lower costs."
113 static gmx_bool bFit=TRUE,bRef=FALSE,bM=FALSE,bPBC=TRUE;
116 { "-fit", FALSE, etBOOL, {&bFit},
117 "Fit to a reference structure"},
118 { "-ref", FALSE, etBOOL, {&bRef},
119 "Use the deviation from the conformation in the structure file instead of from the average" },
120 { "-mwa", FALSE, etBOOL, {&bM},
121 "Mass-weighted covariance analysis"},
122 { "-last", FALSE, etINT, {&end},
123 "Last eigenvector to write away (-1 is till the last)" },
124 { "-pbc", FALSE, etBOOL, {&bPBC},
125 "Apply corrections for periodic boundary conditions" }
133 rvec *x,*xread,*xref,*xav,*xproj;
135 real *sqrtm,*mat,*eigenvalues,sum,trace,inv_nframes;
136 real t,tstart,tend,**mat2;
140 int natoms,nat,count,nframes0,nframes,nlevels;
141 gmx_large_int_t ndim,i,j,k,l;
143 const char *fitfile,*trxfile,*ndxfile;
144 const char *eigvalfile,*eigvecfile,*averfile,*logfile;
145 const char *asciifile,*xpmfile,*xpmafile;
146 char str[STRLEN],*fitname,*ananame,*pcwd;
148 atom_id *index,*ifit;
149 gmx_bool bDiffMass1,bDiffMass2;
151 char timebuf[STRLEN];
155 gmx_rmpbc_t gpbc=NULL;
158 { efTRX, "-f", NULL, ffREAD },
159 { efTPS, NULL, NULL, ffREAD },
160 { efNDX, NULL, NULL, ffOPTRD },
161 { efXVG, NULL, "eigenval", ffWRITE },
162 { efTRN, "-v", "eigenvec", ffWRITE },
163 { efSTO, "-av", "average.pdb", ffWRITE },
164 { efLOG, NULL, "covar", ffWRITE },
165 { efDAT, "-ascii","covar", ffOPTWR },
166 { efXPM, "-xpm","covar", ffOPTWR },
167 { efXPM, "-xpma","covara", ffOPTWR }
169 #define NFILE asize(fnm)
171 CopyRight(stderr,argv[0]);
172 parse_common_args(&argc,argv,PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
173 NFILE,fnm,asize(pa),pa,asize(desc),desc,0,NULL,&oenv);
177 fitfile = ftp2fn(efTPS,NFILE,fnm);
178 trxfile = ftp2fn(efTRX,NFILE,fnm);
179 ndxfile = ftp2fn_null(efNDX,NFILE,fnm);
180 eigvalfile = ftp2fn(efXVG,NFILE,fnm);
181 eigvecfile = ftp2fn(efTRN,NFILE,fnm);
182 averfile = ftp2fn(efSTO,NFILE,fnm);
183 logfile = ftp2fn(efLOG,NFILE,fnm);
184 asciifile = opt2fn_null("-ascii",NFILE,fnm);
185 xpmfile = opt2fn_null("-xpm",NFILE,fnm);
186 xpmafile = opt2fn_null("-xpma",NFILE,fnm);
188 read_tps_conf(fitfile,str,&top,&ePBC,&xref,NULL,box,TRUE);
192 printf("\nChoose a group for the least squares fit\n");
193 get_index(atoms,ndxfile,1,&nfit,&ifit,&fitname);
195 gmx_fatal(FARGS,"Need >= 3 points to fit!\n");
198 printf("\nChoose a group for the covariance analysis\n");
199 get_index(atoms,ndxfile,1,&natoms,&index,&ananame);
203 snew(w_rls,atoms->nr);
204 for(i=0; (i<nfit); i++) {
205 w_rls[ifit[i]]=atoms->atom[ifit[i]].m;
207 bDiffMass1 = bDiffMass1 || (w_rls[ifit[i]]!=w_rls[ifit[i-1]]);
212 for(i=0; (i<natoms); i++)
214 sqrtm[i]=sqrt(atoms->atom[index[i]].m);
216 bDiffMass2 = bDiffMass2 || (sqrtm[i]!=sqrtm[i-1]);
221 if (bFit && bDiffMass1 && !bDiffMass2) {
222 bDiffMass1 = natoms != nfit;
224 for (i=0; (i<natoms) && !bDiffMass1; i++)
225 bDiffMass1 = index[i] != ifit[i];
228 "Note: the fit and analysis group are identical,\n"
229 " while the fit is mass weighted and the analysis is not.\n"
230 " Making the fit non mass weighted.\n\n");
231 for(i=0; (i<nfit); i++)
236 /* Prepare reference frame */
238 gpbc = gmx_rmpbc_init(&top.idef,ePBC,atoms->nr,box);
239 gmx_rmpbc(gpbc,atoms->nr,box,xref);
242 reset_x(nfit,ifit,atoms->nr,NULL,xref,w_rls);
247 if (sqrt(GMX_LARGE_INT_MAX)<ndim) {
248 gmx_fatal(FARGS,"Number of degrees of freedoms to large for matrix.\n");
252 fprintf(stderr,"Calculating the average structure ...\n");
254 nat=read_first_x(oenv,&status,trxfile,&t,&xread,box);
255 if (nat != atoms->nr)
256 fprintf(stderr,"\nWARNING: number of atoms in tpx (%d) and trajectory (%d) do not match\n",natoms,nat);
259 /* calculate x: a fitted struture of the selected atoms */
261 gmx_rmpbc(gpbc,nat,box,xread);
263 reset_x(nfit,ifit,nat,NULL,xread,w_rls);
264 do_fit(nat,w_rls,xref,xread);
266 for (i=0; i<natoms; i++)
267 rvec_inc(xav[i],xread[index[i]]);
268 } while (read_next_x(oenv,status,&t,nat,xread,box));
271 inv_nframes = 1.0/nframes0;
272 for(i=0; i<natoms; i++)
273 for(d=0; d<DIM; d++) {
274 xav[i][d] *= inv_nframes;
275 xread[index[i]][d] = xav[i][d];
277 write_sto_conf_indexed(opt2fn("-av",NFILE,fnm),"Average structure",
278 atoms,xread,NULL,epbcNONE,zerobox,natoms,index);
281 fprintf(stderr,"Constructing covariance matrix (%dx%d) ...\n",(int)ndim,(int)ndim);
283 nat=read_first_x(oenv,&status,trxfile,&t,&xread,box);
288 /* calculate x: a (fitted) structure of the selected atoms */
290 gmx_rmpbc(gpbc,nat,box,xread);
292 reset_x(nfit,ifit,nat,NULL,xread,w_rls);
293 do_fit(nat,w_rls,xref,xread);
296 for (i=0; i<natoms; i++)
297 rvec_sub(xread[index[i]],xref[index[i]],x[i]);
299 for (i=0; i<natoms; i++)
300 rvec_sub(xread[index[i]],xav[i],x[i]);
302 for (j=0; j<natoms; j++) {
303 for (dj=0; dj<DIM; dj++) {
306 for (i=j; i<natoms; i++) {
309 mat[l+d] += x[i][d]*xj;
313 } while (read_next_x(oenv,status,&t,nat,xread,box) &&
314 (bRef || nframes < nframes0));
316 gmx_rmpbc_done(gpbc);
318 fprintf(stderr,"Read %d frames\n",nframes);
321 /* copy the reference structure to the ouput array x */
323 for (i=0; i<natoms; i++)
324 copy_rvec(xref[index[i]],xproj[i]);
329 /* correct the covariance matrix for the mass */
330 inv_nframes = 1.0/nframes;
331 for (j=0; j<natoms; j++)
332 for (dj=0; dj<DIM; dj++)
333 for (i=j; i<natoms; i++) {
334 k = ndim*(DIM*j+dj)+DIM*i;
335 for (d=0; d<DIM; d++)
336 mat[k+d] = mat[k+d]*inv_nframes*sqrtm[i]*sqrtm[j];
339 /* symmetrize the matrix */
340 for (j=0; j<ndim; j++)
341 for (i=j; i<ndim; i++)
342 mat[ndim*i+j]=mat[ndim*j+i];
345 for(i=0; i<ndim; i++)
346 trace+=mat[i*ndim+i];
347 fprintf(stderr,"\nTrace of the covariance matrix: %g (%snm^2)\n",
348 trace,bM ? "u " : "");
351 out = ffopen(asciifile,"w");
352 for (j=0; j<ndim; j++) {
353 for (i=0; i<ndim; i+=3)
354 fprintf(out,"%g %g %g\n",
355 mat[ndim*j+i],mat[ndim*j+i+1],mat[ndim*j+i+2]);
364 for (j=0; j<ndim; j++) {
365 mat2[j] = &(mat[ndim*j]);
366 for (i=0; i<=j; i++) {
367 if (mat2[j][i] < min)
369 if (mat2[j][j] > max)
374 for(i=0; i<ndim; i++)
376 rlo.r = 0; rlo.g = 0; rlo.b = 1;
377 rmi.r = 1; rmi.g = 1; rmi.b = 1;
378 rhi.r = 1; rhi.g = 0; rhi.b = 0;
379 out = ffopen(xpmfile,"w");
381 write_xpm3(out,0,"Covariance",bM ? "u nm^2" : "nm^2",
382 "dim","dim",ndim,ndim,axis,axis,
383 mat2,min,0.0,max,rlo,rmi,rhi,&nlevels);
393 for (i=0; i<ndim/DIM; i++)
394 snew(mat2[i],ndim/DIM);
395 for (j=0; j<ndim/DIM; j++) {
396 for (i=0; i<=j; i++) {
399 mat2[j][i] += mat[ndim*(DIM*j+d)+DIM*i+d];
400 if (mat2[j][i] < min)
402 if (mat2[j][j] > max)
404 mat2[i][j] = mat2[j][i];
408 for(i=0; i<ndim/DIM; i++)
410 rlo.r = 0; rlo.g = 0; rlo.b = 1;
411 rmi.r = 1; rmi.g = 1; rmi.b = 1;
412 rhi.r = 1; rhi.g = 0; rhi.b = 0;
413 out = ffopen(xpmafile,"w");
415 write_xpm3(out,0,"Covariance",bM ? "u nm^2" : "nm^2",
416 "atom","atom",ndim/DIM,ndim/DIM,axis,axis,
417 mat2,min,0.0,max,rlo,rmi,rhi,&nlevels);
420 for (i=0; i<ndim/DIM; i++)
426 /* call diagonalization routine */
428 snew(eigenvalues,ndim);
429 snew(eigenvectors,ndim*ndim);
431 memcpy(eigenvectors,mat,ndim*ndim*sizeof(real));
432 fprintf(stderr,"\nDiagonalizing ...\n");
434 eigensolver(eigenvectors,ndim,0,ndim,eigenvalues,mat);
437 /* now write the output */
440 for(i=0; i<ndim; i++)
442 fprintf(stderr,"\nSum of the eigenvalues: %g (%snm^2)\n",
444 if (fabs(trace-sum)>0.01*trace)
445 fprintf(stderr,"\nWARNING: eigenvalue sum deviates from the trace of the covariance matrix\n");
447 fprintf(stderr,"\nWriting eigenvalues to %s\n",eigvalfile);
449 sprintf(str,"(%snm\\S2\\N)",bM ? "u " : "");
450 out=xvgropen(eigvalfile,
451 "Eigenvalues of the covariance matrix",
452 "Eigenvector index",str,oenv);
453 for (i=0; (i<ndim); i++)
454 fprintf (out,"%10d %g\n",(int)i+1,eigenvalues[ndim-1-i]);
458 if (nframes-1 < ndim)
464 /* misuse lambda: 0/1 mass weighted analysis no/yes */
466 WriteXref = eWXR_YES;
467 for(i=0; i<nfit; i++)
468 copy_rvec(xref[ifit[i]],x[i]);
472 /* misuse lambda: -1 for no fit */
473 WriteXref = eWXR_NOFIT;
476 write_eigenvectors(eigvecfile,natoms,mat,TRUE,1,end,
477 WriteXref,x,bDiffMass1,xproj,bM,eigenvalues);
479 out = ffopen(logfile,"w");
482 gmx_ctime_r(&now,timebuf,STRLEN);
483 fprintf(out,"Covariance analysis log, written %s\n",timebuf);
485 fprintf(out,"Program: %s\n",argv[0]);
486 gmx_getcwd(str, STRLEN);
488 fprintf(out,"Working directory: %s\n\n",str);
490 fprintf(out,"Read %d frames from %s (time %g to %g %s)\n",nframes,trxfile,
491 output_env_conv_time(oenv,tstart),output_env_conv_time(oenv,tend),output_env_get_time_unit(oenv));
493 fprintf(out,"Read reference structure for fit from %s\n",fitfile);
495 fprintf(out,"Read index groups from %s\n",ndxfile);
498 fprintf(out,"Analysis group is '%s' (%d atoms)\n",ananame,natoms);
500 fprintf(out,"Fit group is '%s' (%d atoms)\n",fitname,nfit);
502 fprintf(out,"No fit was used\n");
503 fprintf(out,"Analysis is %smass weighted\n", bDiffMass2 ? "":"non-");
505 fprintf(out,"Fit is %smass weighted\n", bDiffMass1 ? "":"non-");
506 fprintf(out,"Diagonalized the %dx%d covariance matrix\n",(int)ndim,(int)ndim);
507 fprintf(out,"Trace of the covariance matrix before diagonalizing: %g\n",
509 fprintf(out,"Trace of the covariance matrix after diagonalizing: %g\n\n",
512 fprintf(out,"Wrote %d eigenvalues to %s\n",(int)ndim,eigvalfile);
513 if (WriteXref == eWXR_YES)
514 fprintf(out,"Wrote reference structure to %s\n",eigvecfile);
515 fprintf(out,"Wrote average structure to %s and %s\n",averfile,eigvecfile);
516 fprintf(out,"Wrote eigenvectors %d to %d to %s\n",1,end,eigvecfile);
520 fprintf(stderr,"Wrote the log to %s\n",logfile);