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
42 #ifdef HAVE_SYS_TIME_H
47 #if ((defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64) && !defined __CYGWIN__ && !defined __CYGWIN32__)
72 #include "eigensolver.h"
77 /* Portable version of ctime_r implemented in src/gmxlib/string2.c, but we do not want it declared in public installed headers */
79 gmx_ctime_r(const time_t *clock,char *buf, int n);
82 int gmx_covar(int argc,char *argv[])
84 const char *desc[] = {
85 "g_covar calculates and diagonalizes the (mass-weighted)",
87 "All structures are fitted to the structure in the structure file.",
88 "When this is not a run input file periodicity will not be taken into",
89 "account. When the fit and analysis groups are identical and the analysis",
90 "is non mass-weighted, the fit will also be non mass-weighted.",
92 "The eigenvectors are written to a trajectory file ([TT]-v[tt]).",
93 "When the same atoms are used for the fit and the covariance analysis,",
94 "the reference structure for the fit is written first with t=-1.",
95 "The average (or reference when [TT]-ref[tt] is used) structure is",
96 "written with t=0, the eigenvectors",
97 "are written as frames with the eigenvector number as timestamp.",
99 "The eigenvectors can be analyzed with [TT]g_anaeig[tt].",
101 "Option [TT]-ascii[tt] writes the whole covariance matrix to",
102 "an ASCII file. The order of the elements is: x1x1, x1y1, x1z1, x1x2, ...",
104 "Option [TT]-xpm[tt] writes the whole covariance matrix to an xpm file.",
106 "Option [TT]-xpma[tt] writes the atomic covariance matrix to an xpm file,",
107 "i.e. for each atom pair the sum of the xx, yy and zz covariances is",
110 "Note that the diagonalization of a matrix requires memory and time",
111 "that will increase at least as fast as than the square of the number",
112 "of atoms involved. It is easy to run out of memory, in which",
113 "case this tool will probably exit with a 'Segmentation fault'. You",
114 "should consider carefully whether a reduced set of atoms will meet",
115 "your needs for lower costs."
117 static gmx_bool bFit=TRUE,bRef=FALSE,bM=FALSE,bPBC=TRUE;
120 { "-fit", FALSE, etBOOL, {&bFit},
121 "Fit to a reference structure"},
122 { "-ref", FALSE, etBOOL, {&bRef},
123 "Use the deviation from the conformation in the structure file instead of from the average" },
124 { "-mwa", FALSE, etBOOL, {&bM},
125 "Mass-weighted covariance analysis"},
126 { "-last", FALSE, etINT, {&end},
127 "Last eigenvector to write away (-1 is till the last)" },
128 { "-pbc", FALSE, etBOOL, {&bPBC},
129 "Apply corrections for periodic boundary conditions" }
137 rvec *x,*xread,*xref,*xav,*xproj;
139 real *sqrtm,*mat,*eigval,sum,trace,inv_nframes;
140 real t,tstart,tend,**mat2;
144 int natoms,nat,count,nframes0,nframes,nlevels;
145 gmx_large_int_t ndim,i,j,k,l;
147 const char *fitfile,*trxfile,*ndxfile;
148 const char *eigvalfile,*eigvecfile,*averfile,*logfile;
149 const char *asciifile,*xpmfile,*xpmafile;
150 char str[STRLEN],*fitname,*ananame,*pcwd;
152 atom_id *index,*ifit;
153 gmx_bool bDiffMass1,bDiffMass2;
155 char timebuf[STRLEN];
159 gmx_rmpbc_t gpbc=NULL;
162 { efTRX, "-f", NULL, ffREAD },
163 { efTPS, NULL, NULL, ffREAD },
164 { efNDX, NULL, NULL, ffOPTRD },
165 { efXVG, NULL, "eigenval", ffWRITE },
166 { efTRN, "-v", "eigenvec", ffWRITE },
167 { efSTO, "-av", "average.pdb", ffWRITE },
168 { efLOG, NULL, "covar", ffWRITE },
169 { efDAT, "-ascii","covar", ffOPTWR },
170 { efXPM, "-xpm","covar", ffOPTWR },
171 { efXPM, "-xpma","covara", ffOPTWR }
173 #define NFILE asize(fnm)
175 CopyRight(stderr,argv[0]);
176 parse_common_args(&argc,argv,PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
177 NFILE,fnm,asize(pa),pa,asize(desc),desc,0,NULL,&oenv);
181 fitfile = ftp2fn(efTPS,NFILE,fnm);
182 trxfile = ftp2fn(efTRX,NFILE,fnm);
183 ndxfile = ftp2fn_null(efNDX,NFILE,fnm);
184 eigvalfile = ftp2fn(efXVG,NFILE,fnm);
185 eigvecfile = ftp2fn(efTRN,NFILE,fnm);
186 averfile = ftp2fn(efSTO,NFILE,fnm);
187 logfile = ftp2fn(efLOG,NFILE,fnm);
188 asciifile = opt2fn_null("-ascii",NFILE,fnm);
189 xpmfile = opt2fn_null("-xpm",NFILE,fnm);
190 xpmafile = opt2fn_null("-xpma",NFILE,fnm);
192 read_tps_conf(fitfile,str,&top,&ePBC,&xref,NULL,box,TRUE);
196 printf("\nChoose a group for the least squares fit\n");
197 get_index(atoms,ndxfile,1,&nfit,&ifit,&fitname);
199 gmx_fatal(FARGS,"Need >= 3 points to fit!\n");
202 printf("\nChoose a group for the covariance analysis\n");
203 get_index(atoms,ndxfile,1,&natoms,&index,&ananame);
207 snew(w_rls,atoms->nr);
208 for(i=0; (i<nfit); i++) {
209 w_rls[ifit[i]]=atoms->atom[ifit[i]].m;
211 bDiffMass1 = bDiffMass1 || (w_rls[ifit[i]]!=w_rls[ifit[i-1]]);
216 for(i=0; (i<natoms); i++)
218 sqrtm[i]=sqrt(atoms->atom[index[i]].m);
220 bDiffMass2 = bDiffMass2 || (sqrtm[i]!=sqrtm[i-1]);
225 if (bFit && bDiffMass1 && !bDiffMass2) {
226 bDiffMass1 = natoms != nfit;
228 for (i=0; (i<natoms) && !bDiffMass1; i++)
229 bDiffMass1 = index[i] != ifit[i];
232 "Note: the fit and analysis group are identical,\n"
233 " while the fit is mass weighted and the analysis is not.\n"
234 " Making the fit non mass weighted.\n\n");
235 for(i=0; (i<nfit); i++)
240 /* Prepare reference frame */
242 gpbc = gmx_rmpbc_init(&top.idef,ePBC,atoms->nr,box);
243 gmx_rmpbc(gpbc,atoms->nr,box,xref);
246 reset_x(nfit,ifit,atoms->nr,NULL,xref,w_rls);
251 if (sqrt(GMX_LARGE_INT_MAX)<ndim) {
252 gmx_fatal(FARGS,"Number of degrees of freedoms to large for matrix.\n");
256 fprintf(stderr,"Calculating the average structure ...\n");
258 nat=read_first_x(oenv,&status,trxfile,&t,&xread,box);
259 if (nat != atoms->nr)
260 fprintf(stderr,"\nWARNING: number of atoms in tpx (%d) and trajectory (%d) do not match\n",natoms,nat);
263 /* calculate x: a fitted struture of the selected atoms */
265 gmx_rmpbc(gpbc,nat,box,xread);
267 reset_x(nfit,ifit,nat,NULL,xread,w_rls);
268 do_fit(nat,w_rls,xref,xread);
270 for (i=0; i<natoms; i++)
271 rvec_inc(xav[i],xread[index[i]]);
272 } while (read_next_x(oenv,status,&t,nat,xread,box));
275 inv_nframes = 1.0/nframes0;
276 for(i=0; i<natoms; i++)
277 for(d=0; d<DIM; d++) {
278 xav[i][d] *= inv_nframes;
279 xread[index[i]][d] = xav[i][d];
281 write_sto_conf_indexed(opt2fn("-av",NFILE,fnm),"Average structure",
282 atoms,xread,NULL,epbcNONE,zerobox,natoms,index);
285 fprintf(stderr,"Constructing covariance matrix (%dx%d) ...\n",(int)ndim,(int)ndim);
287 nat=read_first_x(oenv,&status,trxfile,&t,&xread,box);
292 /* calculate x: a (fitted) structure of the selected atoms */
294 gmx_rmpbc(gpbc,nat,box,xread);
296 reset_x(nfit,ifit,nat,NULL,xread,w_rls);
297 do_fit(nat,w_rls,xref,xread);
300 for (i=0; i<natoms; i++)
301 rvec_sub(xread[index[i]],xref[index[i]],x[i]);
303 for (i=0; i<natoms; i++)
304 rvec_sub(xread[index[i]],xav[i],x[i]);
306 for (j=0; j<natoms; j++) {
307 for (dj=0; dj<DIM; dj++) {
310 for (i=j; i<natoms; i++) {
313 mat[l+d] += x[i][d]*xj;
317 } while (read_next_x(oenv,status,&t,nat,xread,box) &&
318 (bRef || nframes < nframes0));
320 gmx_rmpbc_done(gpbc);
322 fprintf(stderr,"Read %d frames\n",nframes);
325 /* copy the reference structure to the ouput array x */
327 for (i=0; i<natoms; i++)
328 copy_rvec(xref[index[i]],xproj[i]);
333 /* correct the covariance matrix for the mass */
334 inv_nframes = 1.0/nframes;
335 for (j=0; j<natoms; j++)
336 for (dj=0; dj<DIM; dj++)
337 for (i=j; i<natoms; i++) {
338 k = ndim*(DIM*j+dj)+DIM*i;
339 for (d=0; d<DIM; d++)
340 mat[k+d] = mat[k+d]*inv_nframes*sqrtm[i]*sqrtm[j];
343 /* symmetrize the matrix */
344 for (j=0; j<ndim; j++)
345 for (i=j; i<ndim; i++)
346 mat[ndim*i+j]=mat[ndim*j+i];
349 for(i=0; i<ndim; i++)
350 trace+=mat[i*ndim+i];
351 fprintf(stderr,"\nTrace of the covariance matrix: %g (%snm^2)\n",
352 trace,bM ? "u " : "");
355 out = ffopen(asciifile,"w");
356 for (j=0; j<ndim; j++) {
357 for (i=0; i<ndim; i+=3)
358 fprintf(out,"%g %g %g\n",
359 mat[ndim*j+i],mat[ndim*j+i+1],mat[ndim*j+i+2]);
368 for (j=0; j<ndim; j++) {
369 mat2[j] = &(mat[ndim*j]);
370 for (i=0; i<=j; i++) {
371 if (mat2[j][i] < min)
373 if (mat2[j][j] > max)
378 for(i=0; i<ndim; i++)
380 rlo.r = 0; rlo.g = 0; rlo.b = 1;
381 rmi.r = 1; rmi.g = 1; rmi.b = 1;
382 rhi.r = 1; rhi.g = 0; rhi.b = 0;
383 out = ffopen(xpmfile,"w");
385 write_xpm3(out,0,"Covariance",bM ? "u nm^2" : "nm^2",
386 "dim","dim",ndim,ndim,axis,axis,
387 mat2,min,0.0,max,rlo,rmi,rhi,&nlevels);
397 for (i=0; i<ndim/DIM; i++)
398 snew(mat2[i],ndim/DIM);
399 for (j=0; j<ndim/DIM; j++) {
400 for (i=0; i<=j; i++) {
403 mat2[j][i] += mat[ndim*(DIM*j+d)+DIM*i+d];
404 if (mat2[j][i] < min)
406 if (mat2[j][j] > max)
408 mat2[i][j] = mat2[j][i];
412 for(i=0; i<ndim/DIM; i++)
414 rlo.r = 0; rlo.g = 0; rlo.b = 1;
415 rmi.r = 1; rmi.g = 1; rmi.b = 1;
416 rhi.r = 1; rhi.g = 0; rhi.b = 0;
417 out = ffopen(xpmafile,"w");
419 write_xpm3(out,0,"Covariance",bM ? "u nm^2" : "nm^2",
420 "atom","atom",ndim/DIM,ndim/DIM,axis,axis,
421 mat2,min,0.0,max,rlo,rmi,rhi,&nlevels);
424 for (i=0; i<ndim/DIM; i++)
430 /* call diagonalization routine */
432 fprintf(stderr,"\nDiagonalizing ...\n");
437 memcpy(tmp,mat,ndim*ndim*sizeof(real));
438 eigensolver(tmp,ndim,0,ndim,eigval,mat);
441 /* now write the output */
444 for(i=0; i<ndim; i++)
446 fprintf(stderr,"\nSum of the eigenvalues: %g (%snm^2)\n",
448 if (fabs(trace-sum)>0.01*trace)
449 fprintf(stderr,"\nWARNING: eigenvalue sum deviates from the trace of the covariance matrix\n");
451 fprintf(stderr,"\nWriting eigenvalues to %s\n",eigvalfile);
453 sprintf(str,"(%snm\\S2\\N)",bM ? "u " : "");
454 out=xvgropen(eigvalfile,
455 "Eigenvalues of the covariance matrix",
456 "Eigenvector index",str,oenv);
457 for (i=0; (i<ndim); i++)
458 fprintf (out,"%10d %g\n",(int)i+1,eigval[ndim-1-i]);
462 if (nframes-1 < ndim)
468 /* misuse lambda: 0/1 mass weighted analysis no/yes */
470 WriteXref = eWXR_YES;
471 for(i=0; i<nfit; i++)
472 copy_rvec(xref[ifit[i]],x[i]);
476 /* misuse lambda: -1 for no fit */
477 WriteXref = eWXR_NOFIT;
480 write_eigenvectors(eigvecfile,natoms,mat,TRUE,1,end,
481 WriteXref,x,bDiffMass1,xproj,bM,eigval);
483 out = ffopen(logfile,"w");
486 gmx_ctime_r(&now,timebuf,STRLEN);
487 fprintf(out,"Covariance analysis log, written %s\n",timebuf);
489 fprintf(out,"Program: %s\n",argv[0]);
490 #if ((defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64) && !defined __CYGWIN__ && !defined __CYGWIN32__)
491 pcwd=_getcwd(str,STRLEN);
493 pcwd=getcwd(str,STRLEN);
497 gmx_fatal(FARGS,"Current working directory is undefined");
500 fprintf(out,"Working directory: %s\n\n",str);
502 fprintf(out,"Read %d frames from %s (time %g to %g %s)\n",nframes,trxfile,
503 output_env_conv_time(oenv,tstart),output_env_conv_time(oenv,tend),output_env_get_time_unit(oenv));
505 fprintf(out,"Read reference structure for fit from %s\n",fitfile);
507 fprintf(out,"Read index groups from %s\n",ndxfile);
510 fprintf(out,"Analysis group is '%s' (%d atoms)\n",ananame,natoms);
512 fprintf(out,"Fit group is '%s' (%d atoms)\n",fitname,nfit);
514 fprintf(out,"No fit was used\n");
515 fprintf(out,"Analysis is %smass weighted\n", bDiffMass2 ? "":"non-");
517 fprintf(out,"Fit is %smass weighted\n", bDiffMass1 ? "":"non-");
518 fprintf(out,"Diagonalized the %dx%d covariance matrix\n",(int)ndim,(int)ndim);
519 fprintf(out,"Trace of the covariance matrix before diagonalizing: %g\n",
521 fprintf(out,"Trace of the covariance matrix after diagonalizing: %g\n\n",
524 fprintf(out,"Wrote %d eigenvalues to %s\n",(int)ndim,eigvalfile);
525 if (WriteXref == eWXR_YES)
526 fprintf(out,"Wrote reference structure to %s\n",eigvecfile);
527 fprintf(out,"Wrote average structure to %s and %s\n",averfile,eigvecfile);
528 fprintf(out,"Wrote eigenvectors %d to %d to %s\n",1,end,eigvecfile);
532 fprintf(stderr,"Wrote the log to %s\n",logfile);