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
38 #include "gmx_header_config.h"
43 #ifdef HAVE_SYS_TIME_H
48 #ifdef GMX_NATIVE_WINDOWS
53 #include "visibility.h"
74 #include "eigensolver.h"
79 /* Portable version of ctime_r implemented in src/gmxlib/string2.c, but we do not want it declared in public installed headers */
82 gmx_ctime_r(const time_t *clock,char *buf, int n);
85 int gmx_covar(int argc,char *argv[])
87 const char *desc[] = {
88 "[TT]g_covar[tt] calculates and diagonalizes the (mass-weighted)",
90 "All structures are fitted to the structure in the structure file.",
91 "When this is not a run input file periodicity will not be taken into",
92 "account. When the fit and analysis groups are identical and the analysis",
93 "is non mass-weighted, the fit will also be non mass-weighted.",
95 "The eigenvectors are written to a trajectory file ([TT]-v[tt]).",
96 "When the same atoms are used for the fit and the covariance analysis,",
97 "the reference structure for the fit is written first with t=-1.",
98 "The average (or reference when [TT]-ref[tt] is used) structure is",
99 "written with t=0, the eigenvectors",
100 "are written as frames with the eigenvector number as timestamp.",
102 "The eigenvectors can be analyzed with [TT]g_anaeig[tt].",
104 "Option [TT]-ascii[tt] writes the whole covariance matrix to",
105 "an ASCII file. The order of the elements is: x1x1, x1y1, x1z1, x1x2, ...",
107 "Option [TT]-xpm[tt] writes the whole covariance matrix to an [TT].xpm[tt] file.",
109 "Option [TT]-xpma[tt] writes the atomic covariance matrix to an [TT].xpm[tt] file,",
110 "i.e. for each atom pair the sum of the xx, yy and zz covariances is",
113 "Note that the diagonalization of a matrix requires memory and time",
114 "that will increase at least as fast as than the square of the number",
115 "of atoms involved. It is easy to run out of memory, in which",
116 "case this tool will probably exit with a 'Segmentation fault'. You",
117 "should consider carefully whether a reduced set of atoms will meet",
118 "your needs for lower costs."
120 static gmx_bool bFit=TRUE,bRef=FALSE,bM=FALSE,bPBC=TRUE;
123 { "-fit", FALSE, etBOOL, {&bFit},
124 "Fit to a reference structure"},
125 { "-ref", FALSE, etBOOL, {&bRef},
126 "Use the deviation from the conformation in the structure file instead of from the average" },
127 { "-mwa", FALSE, etBOOL, {&bM},
128 "Mass-weighted covariance analysis"},
129 { "-last", FALSE, etINT, {&end},
130 "Last eigenvector to write away (-1 is till the last)" },
131 { "-pbc", FALSE, etBOOL, {&bPBC},
132 "Apply corrections for periodic boundary conditions" }
140 rvec *x,*xread,*xref,*xav,*xproj;
142 real *sqrtm,*mat,*eigenvalues,sum,trace,inv_nframes;
143 real t,tstart,tend,**mat2;
147 int natoms,nat,count,nframes0,nframes,nlevels;
148 gmx_large_int_t ndim,i,j,k,l;
150 const char *fitfile,*trxfile,*ndxfile;
151 const char *eigvalfile,*eigvecfile,*averfile,*logfile;
152 const char *asciifile,*xpmfile,*xpmafile;
153 char str[STRLEN],*fitname,*ananame,*pcwd;
155 atom_id *index,*ifit;
156 gmx_bool bDiffMass1,bDiffMass2;
158 char timebuf[STRLEN];
162 gmx_rmpbc_t gpbc=NULL;
165 { efTRX, "-f", NULL, ffREAD },
166 { efTPS, NULL, NULL, ffREAD },
167 { efNDX, NULL, NULL, ffOPTRD },
168 { efXVG, NULL, "eigenval", ffWRITE },
169 { efTRN, "-v", "eigenvec", ffWRITE },
170 { efSTO, "-av", "average.pdb", ffWRITE },
171 { efLOG, NULL, "covar", ffWRITE },
172 { efDAT, "-ascii","covar", ffOPTWR },
173 { efXPM, "-xpm","covar", ffOPTWR },
174 { efXPM, "-xpma","covara", ffOPTWR }
176 #define NFILE asize(fnm)
178 CopyRight(stderr,argv[0]);
179 parse_common_args(&argc,argv,PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
180 NFILE,fnm,asize(pa),pa,asize(desc),desc,0,NULL,&oenv);
184 fitfile = ftp2fn(efTPS,NFILE,fnm);
185 trxfile = ftp2fn(efTRX,NFILE,fnm);
186 ndxfile = ftp2fn_null(efNDX,NFILE,fnm);
187 eigvalfile = ftp2fn(efXVG,NFILE,fnm);
188 eigvecfile = ftp2fn(efTRN,NFILE,fnm);
189 averfile = ftp2fn(efSTO,NFILE,fnm);
190 logfile = ftp2fn(efLOG,NFILE,fnm);
191 asciifile = opt2fn_null("-ascii",NFILE,fnm);
192 xpmfile = opt2fn_null("-xpm",NFILE,fnm);
193 xpmafile = opt2fn_null("-xpma",NFILE,fnm);
195 read_tps_conf(fitfile,str,&top,&ePBC,&xref,NULL,box,TRUE);
199 printf("\nChoose a group for the least squares fit\n");
200 get_index(atoms,ndxfile,1,&nfit,&ifit,&fitname);
202 gmx_fatal(FARGS,"Need >= 3 points to fit!\n");
205 printf("\nChoose a group for the covariance analysis\n");
206 get_index(atoms,ndxfile,1,&natoms,&index,&ananame);
210 snew(w_rls,atoms->nr);
211 for(i=0; (i<nfit); i++) {
212 w_rls[ifit[i]]=atoms->atom[ifit[i]].m;
214 bDiffMass1 = bDiffMass1 || (w_rls[ifit[i]]!=w_rls[ifit[i-1]]);
219 for(i=0; (i<natoms); i++)
221 sqrtm[i]=sqrt(atoms->atom[index[i]].m);
223 bDiffMass2 = bDiffMass2 || (sqrtm[i]!=sqrtm[i-1]);
228 if (bFit && bDiffMass1 && !bDiffMass2) {
229 bDiffMass1 = natoms != nfit;
231 for (i=0; (i<natoms) && !bDiffMass1; i++)
232 bDiffMass1 = index[i] != ifit[i];
235 "Note: the fit and analysis group are identical,\n"
236 " while the fit is mass weighted and the analysis is not.\n"
237 " Making the fit non mass weighted.\n\n");
238 for(i=0; (i<nfit); i++)
243 /* Prepare reference frame */
245 gpbc = gmx_rmpbc_init(&top.idef,ePBC,atoms->nr,box);
246 gmx_rmpbc(gpbc,atoms->nr,box,xref);
249 reset_x(nfit,ifit,atoms->nr,NULL,xref,w_rls);
254 if (sqrt(GMX_LARGE_INT_MAX)<ndim) {
255 gmx_fatal(FARGS,"Number of degrees of freedoms to large for matrix.\n");
259 fprintf(stderr,"Calculating the average structure ...\n");
261 nat=read_first_x(oenv,&status,trxfile,&t,&xread,box);
262 if (nat != atoms->nr)
263 fprintf(stderr,"\nWARNING: number of atoms in tpx (%d) and trajectory (%d) do not match\n",natoms,nat);
266 /* calculate x: a fitted struture of the selected atoms */
268 gmx_rmpbc(gpbc,nat,box,xread);
270 reset_x(nfit,ifit,nat,NULL,xread,w_rls);
271 do_fit(nat,w_rls,xref,xread);
273 for (i=0; i<natoms; i++)
274 rvec_inc(xav[i],xread[index[i]]);
275 } while (read_next_x(oenv,status,&t,nat,xread,box));
278 inv_nframes = 1.0/nframes0;
279 for(i=0; i<natoms; i++)
280 for(d=0; d<DIM; d++) {
281 xav[i][d] *= inv_nframes;
282 xread[index[i]][d] = xav[i][d];
284 write_sto_conf_indexed(opt2fn("-av",NFILE,fnm),"Average structure",
285 atoms,xread,NULL,epbcNONE,zerobox,natoms,index);
288 fprintf(stderr,"Constructing covariance matrix (%dx%d) ...\n",(int)ndim,(int)ndim);
290 nat=read_first_x(oenv,&status,trxfile,&t,&xread,box);
295 /* calculate x: a (fitted) structure of the selected atoms */
297 gmx_rmpbc(gpbc,nat,box,xread);
299 reset_x(nfit,ifit,nat,NULL,xread,w_rls);
300 do_fit(nat,w_rls,xref,xread);
303 for (i=0; i<natoms; i++)
304 rvec_sub(xread[index[i]],xref[index[i]],x[i]);
306 for (i=0; i<natoms; i++)
307 rvec_sub(xread[index[i]],xav[i],x[i]);
309 for (j=0; j<natoms; j++) {
310 for (dj=0; dj<DIM; dj++) {
313 for (i=j; i<natoms; i++) {
316 mat[l+d] += x[i][d]*xj;
320 } while (read_next_x(oenv,status,&t,nat,xread,box) &&
321 (bRef || nframes < nframes0));
323 gmx_rmpbc_done(gpbc);
325 fprintf(stderr,"Read %d frames\n",nframes);
328 /* copy the reference structure to the ouput array x */
330 for (i=0; i<natoms; i++)
331 copy_rvec(xref[index[i]],xproj[i]);
336 /* correct the covariance matrix for the mass */
337 inv_nframes = 1.0/nframes;
338 for (j=0; j<natoms; j++)
339 for (dj=0; dj<DIM; dj++)
340 for (i=j; i<natoms; i++) {
341 k = ndim*(DIM*j+dj)+DIM*i;
342 for (d=0; d<DIM; d++)
343 mat[k+d] = mat[k+d]*inv_nframes*sqrtm[i]*sqrtm[j];
346 /* symmetrize the matrix */
347 for (j=0; j<ndim; j++)
348 for (i=j; i<ndim; i++)
349 mat[ndim*i+j]=mat[ndim*j+i];
352 for(i=0; i<ndim; i++)
353 trace+=mat[i*ndim+i];
354 fprintf(stderr,"\nTrace of the covariance matrix: %g (%snm^2)\n",
355 trace,bM ? "u " : "");
358 out = ffopen(asciifile,"w");
359 for (j=0; j<ndim; j++) {
360 for (i=0; i<ndim; i+=3)
361 fprintf(out,"%g %g %g\n",
362 mat[ndim*j+i],mat[ndim*j+i+1],mat[ndim*j+i+2]);
371 for (j=0; j<ndim; j++) {
372 mat2[j] = &(mat[ndim*j]);
373 for (i=0; i<=j; i++) {
374 if (mat2[j][i] < min)
376 if (mat2[j][j] > max)
381 for(i=0; i<ndim; i++)
383 rlo.r = 0; rlo.g = 0; rlo.b = 1;
384 rmi.r = 1; rmi.g = 1; rmi.b = 1;
385 rhi.r = 1; rhi.g = 0; rhi.b = 0;
386 out = ffopen(xpmfile,"w");
388 write_xpm3(out,0,"Covariance",bM ? "u nm^2" : "nm^2",
389 "dim","dim",ndim,ndim,axis,axis,
390 mat2,min,0.0,max,rlo,rmi,rhi,&nlevels);
400 for (i=0; i<ndim/DIM; i++)
401 snew(mat2[i],ndim/DIM);
402 for (j=0; j<ndim/DIM; j++) {
403 for (i=0; i<=j; i++) {
406 mat2[j][i] += mat[ndim*(DIM*j+d)+DIM*i+d];
407 if (mat2[j][i] < min)
409 if (mat2[j][j] > max)
411 mat2[i][j] = mat2[j][i];
415 for(i=0; i<ndim/DIM; i++)
417 rlo.r = 0; rlo.g = 0; rlo.b = 1;
418 rmi.r = 1; rmi.g = 1; rmi.b = 1;
419 rhi.r = 1; rhi.g = 0; rhi.b = 0;
420 out = ffopen(xpmafile,"w");
422 write_xpm3(out,0,"Covariance",bM ? "u nm^2" : "nm^2",
423 "atom","atom",ndim/DIM,ndim/DIM,axis,axis,
424 mat2,min,0.0,max,rlo,rmi,rhi,&nlevels);
427 for (i=0; i<ndim/DIM; i++)
433 /* call diagonalization routine */
435 snew(eigenvalues,ndim);
436 snew(eigenvectors,ndim*ndim);
438 memcpy(eigenvectors,mat,ndim*ndim*sizeof(real));
439 fprintf(stderr,"\nDiagonalizing ...\n");
441 eigensolver(eigenvectors,ndim,0,ndim,eigenvalues,mat);
444 /* now write the output */
447 for(i=0; i<ndim; i++)
449 fprintf(stderr,"\nSum of the eigenvalues: %g (%snm^2)\n",
451 if (fabs(trace-sum)>0.01*trace)
452 fprintf(stderr,"\nWARNING: eigenvalue sum deviates from the trace of the covariance matrix\n");
454 fprintf(stderr,"\nWriting eigenvalues to %s\n",eigvalfile);
456 sprintf(str,"(%snm\\S2\\N)",bM ? "u " : "");
457 out=xvgropen(eigvalfile,
458 "Eigenvalues of the covariance matrix",
459 "Eigenvector index",str,oenv);
460 for (i=0; (i<ndim); i++)
461 fprintf (out,"%10d %g\n",(int)i+1,eigenvalues[ndim-1-i]);
465 if (nframes-1 < ndim)
471 /* misuse lambda: 0/1 mass weighted analysis no/yes */
473 WriteXref = eWXR_YES;
474 for(i=0; i<nfit; i++)
475 copy_rvec(xref[ifit[i]],x[i]);
479 /* misuse lambda: -1 for no fit */
480 WriteXref = eWXR_NOFIT;
483 write_eigenvectors(eigvecfile,natoms,mat,TRUE,1,end,
484 WriteXref,x,bDiffMass1,xproj,bM,eigenvalues);
486 out = ffopen(logfile,"w");
489 gmx_ctime_r(&now,timebuf,STRLEN);
490 fprintf(out,"Covariance analysis log, written %s\n",timebuf);
492 fprintf(out,"Program: %s\n",argv[0]);
493 #ifdef GMX_NATIVE_WINDOWS
494 pcwd=_getcwd(str,STRLEN);
496 pcwd=getcwd(str,STRLEN);
500 gmx_fatal(FARGS,"Current working directory is undefined");
503 fprintf(out,"Working directory: %s\n\n",str);
505 fprintf(out,"Read %d frames from %s (time %g to %g %s)\n",nframes,trxfile,
506 output_env_conv_time(oenv,tstart),output_env_conv_time(oenv,tend),output_env_get_time_unit(oenv));
508 fprintf(out,"Read reference structure for fit from %s\n",fitfile);
510 fprintf(out,"Read index groups from %s\n",ndxfile);
513 fprintf(out,"Analysis group is '%s' (%d atoms)\n",ananame,natoms);
515 fprintf(out,"Fit group is '%s' (%d atoms)\n",fitname,nfit);
517 fprintf(out,"No fit was used\n");
518 fprintf(out,"Analysis is %smass weighted\n", bDiffMass2 ? "":"non-");
520 fprintf(out,"Fit is %smass weighted\n", bDiffMass1 ? "":"non-");
521 fprintf(out,"Diagonalized the %dx%d covariance matrix\n",(int)ndim,(int)ndim);
522 fprintf(out,"Trace of the covariance matrix before diagonalizing: %g\n",
524 fprintf(out,"Trace of the covariance matrix after diagonalizing: %g\n\n",
527 fprintf(out,"Wrote %d eigenvalues to %s\n",(int)ndim,eigvalfile);
528 if (WriteXref == eWXR_YES)
529 fprintf(out,"Wrote reference structure to %s\n",eigvecfile);
530 fprintf(out,"Wrote average structure to %s and %s\n",averfile,eigvecfile);
531 fprintf(out,"Wrote eigenvectors %d to %d to %s\n",1,end,eigvecfile);
535 fprintf(stderr,"Wrote the log to %s\n",logfile);