d9783b4dba71dad09e0eb29ceb81fb23a0858b41
[alexxy/gromacs.git] / src / tools / gmx_rmsf.c
1 /*
2  * 
3  *                This source code is part of
4  * 
5  *                 G   R   O   M   A   C   S
6  * 
7  *          GROningen MAchine for Chemical Simulations
8  * 
9  *                        VERSION 3.2.0
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.
14
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.
19  * 
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.
26  * 
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.
29  * 
30  * For more info, check our website at http://www.gromacs.org
31  * 
32  * And Hey:
33  * Green Red Orange Magenta Azure Cyan Skyblue
34  */
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #include "smalloc.h"
40 #include "math.h"
41 #include "macros.h"
42 #include "typedefs.h"
43 #include "xvgr.h"
44 #include "copyrite.h"
45 #include "statutil.h"
46 #include "string2.h"
47 #include "vec.h"
48 #include "index.h"
49 #include "pdbio.h"
50 #include "tpxio.h"
51 #include "pbc.h"
52 #include "gmx_fatal.h"
53 #include "futil.h"
54 #include "do_fit.h"
55 #include "princ.h"
56 #include "rmpbc.h"
57 #include "confio.h"
58 #include "eigensolver.h"
59 #include "gmx_ana.h"
60
61
62 static real find_pdb_bfac(t_atoms *atoms,t_resinfo *ri,char *atomnm)
63 {
64   char rresnm[8];
65   int i;
66   
67   strcpy(rresnm,*ri->name);
68   rresnm[3]='\0';
69   for(i=0; (i<atoms->nr); i++) {
70     if ((ri->nr == atoms->resinfo[atoms->atom[i].resind].nr) &&
71         (ri->ic == atoms->resinfo[atoms->atom[i].resind].ic) &&
72         (strcmp(*atoms->resinfo[atoms->atom[i].resind].name,rresnm) == 0) &&
73         (strstr(*atoms->atomname[i],atomnm) != NULL))
74       break;
75   }
76   if (i == atoms->nr) {
77     fprintf(stderr,"\rCan not find %s%d-%s in pdbfile\n",
78             rresnm,ri->nr,atomnm);
79     return 0.0;
80   }
81     
82   return atoms->pdbinfo[i].bfac;
83 }
84
85 void correlate_aniso(const char *fn,t_atoms *ref,t_atoms *calc,
86                      const output_env_t oenv)
87 {
88   FILE *fp;
89   int  i,j;
90   
91   fp = xvgropen(fn,"Correlation between X-Ray and Computed Uij","X-Ray",
92                 "Computed",oenv);
93   for(i=0; (i<ref->nr); i++) {
94     if (ref->pdbinfo[i].bAnisotropic) {
95       for(j=U11; (j<=U23); j++)
96         fprintf(fp,"%10d  %10d\n",ref->pdbinfo[i].uij[j],calc->pdbinfo[i].uij[j]);
97     }
98   }
99   ffclose(fp);
100 }
101
102 static void average_residues(double f[],double **U,int uind,
103                              int isize,atom_id index[],real w_rls[],
104                              t_atoms *atoms)
105 {
106   int i,j,start;
107   double av,m;
108   
109   start = 0;
110   av = 0;
111   m = 0;
112   for(i=0; i<isize; i++) {
113     av += w_rls[index[i]]*(f != NULL ? f[i] : U[i][uind]);
114     m += w_rls[index[i]];
115     if (i+1==isize || 
116         atoms->atom[index[i]].resind!=atoms->atom[index[i+1]].resind) {
117       av /= m;
118       if (f != NULL) {
119         for(j=start; j<=i; j++)
120           f[i] = av;
121       } else {
122         for(j=start; j<=i; j++)
123           U[j][uind] = av;
124       }
125       start = i+1;
126       av = 0;
127       m = 0;
128     }
129   }
130 }
131
132 void print_dir(FILE *fp,real *Uaver)
133 {
134     real eigvec[DIM*DIM];
135     real tmp[DIM*DIM];  
136     rvec eigval;
137     int d,m;
138
139     fprintf(fp,"MSF     X         Y         Z\n");
140     for(d=0; d<DIM; d++)
141     {
142         fprintf(fp," %c ",'X'+d-XX);
143         for(m=0; m<DIM; m++)
144             fprintf(fp," %9.2e",Uaver[3*m+d]);
145         fprintf(fp,"%s\n",m==DIM ? " (nm^2)" : "");
146     }
147   
148     for(m=0; m<DIM*DIM; m++)
149         tmp[m] = Uaver[m];
150
151   
152     eigensolver(tmp,DIM,0,DIM,eigval,eigvec);
153     
154     fprintf(fp,"\n             Eigenvectors\n\n");
155     fprintf(fp,"Eigv  %-8.2e %-8.2e %-8.2e (nm^2)\n\n",
156             eigval[2],eigval[1],eigval[0]);
157     for(d=0; d<DIM; d++)
158     {
159         fprintf(fp,"  %c   ",'X'+d-XX);
160         for(m=DIM-1; m>=0; m--)
161             fprintf(fp,"%7.4f  ",eigvec[3*m+d]);
162         fprintf(fp,"\n");
163     }
164 }
165
166 int gmx_rmsf(int argc,char *argv[])
167 {
168   const char *desc[] = {
169     "g_rmsf computes the root mean square fluctuation (RMSF, i.e. standard ",
170     "deviation) of atomic positions ",
171     "after (optionally) fitting to a reference frame.[PAR]",
172     "With option [TT]-oq[tt] the RMSF values are converted to B-factor",
173     "values, which are written to a pdb file with the coordinates, of the",
174     "structure file, or of a pdb file when [TT]-q[tt] is specified.",
175     "Option [TT]-ox[tt] writes the B-factors to a file with the average",
176     "coordinates.[PAR]",
177     "With the option [TT]-od[tt] the root mean square deviation with",
178     "respect to the reference structure is calculated.[PAR]",
179     "With the option [TT]aniso[tt] g_rmsf will compute anisotropic",
180     "temperature factors and then it will also output average coordinates",
181     "and a pdb file with ANISOU records (corresonding to the [TT]-oq[tt]",
182     "or [TT]-ox[tt] option). Please note that the U values",
183     "are orientation dependent, so before comparison with experimental data",
184     "you should verify that you fit to the experimental coordinates.[PAR]",
185     "When a pdb input file is passed to the program and the [TT]-aniso[tt]",
186     "flag is set",
187     "a correlation plot of the Uij will be created, if any anisotropic",
188     "temperature factors are present in the pdb file.[PAR]",
189     "With option [TT]-dir[tt] the average MSF (3x3) matrix is diagonalized.",
190     "This shows the directions in which the atoms fluctuate the most and",
191     "the least."
192   };
193   static gmx_bool bRes=FALSE,bAniso=FALSE,bdevX=FALSE,bFit=TRUE;
194   t_pargs pargs[] = { 
195     { "-res", FALSE, etBOOL, {&bRes},
196       "Calculate averages for each residue" },
197     { "-aniso",FALSE, etBOOL, {&bAniso},
198       "Compute anisotropic termperature factors" },
199     { "-fit", FALSE, etBOOL, {&bFit},
200       "Do a least squares superposition before computing RMSF. Without this you must make sure that the reference structure and the trajectory match." }
201   };
202   int          natom;
203   int          step,nre,natoms,i,g,m,teller=0;
204   real         t,lambda,*w_rls,*w_rms;
205   
206   t_tpxheader  header;
207   t_inputrec   ir;
208   t_topology   top;
209   int          ePBC;
210   t_atoms      *pdbatoms,*refatoms;
211   gmx_bool         bCont;
212
213   matrix       box,pdbbox;
214   rvec         *x,*pdbx,*xref;
215   t_trxstatus   *status;
216   int          npdbatoms,res0;
217   char         buf[256];
218   const char   *label;
219   char         title[STRLEN];
220   
221   FILE         *fp;               /* the graphics file */
222   const char   *devfn,*dirfn;
223   int          resind;
224
225   gmx_bool         bReadPDB;  
226   atom_id      *index;
227   int          isize;
228   char         *grpnames;
229
230   real         bfac,pdb_bfac,*Uaver;
231   double       **U,*xav;
232   atom_id      aid;
233   rvec         *rmsd_x=NULL;
234   double       *rmsf,invcount,totmass;
235   int          d;
236   real         count=0;
237   rvec         xcm;
238   gmx_rmpbc_t  gpbc=NULL;
239
240   output_env_t oenv;
241
242   const char  *leg[2] = { "MD", "X-Ray" };
243
244   t_filenm fnm[] = {
245     { efTRX, "-f",  NULL,     ffREAD  },
246     { efTPS, NULL,  NULL,     ffREAD  },
247     { efNDX, NULL,  NULL,     ffOPTRD },
248     { efPDB, "-q",  NULL,     ffOPTRD },
249     { efPDB, "-oq", "bfac",   ffOPTWR },
250     { efPDB, "-ox", "xaver",  ffOPTWR },
251     { efXVG, "-o",  "rmsf",   ffWRITE },
252     { efXVG, "-od", "rmsdev", ffOPTWR },
253     { efXVG, "-oc", "correl", ffOPTWR },
254     { efLOG, "-dir", "rmsf",  ffOPTWR }
255   };
256 #define NFILE asize(fnm)
257  
258   CopyRight(stderr,argv[0]);
259   parse_common_args(&argc,argv,PCA_CAN_TIME | PCA_CAN_VIEW | PCA_BE_NICE ,
260                     NFILE,fnm,asize(pargs),pargs,asize(desc),desc,0,NULL,
261                     &oenv);
262
263   bReadPDB = ftp2bSet(efPDB,NFILE,fnm);
264   devfn    = opt2fn_null("-od",NFILE,fnm);
265   dirfn    = opt2fn_null("-dir",NFILE,fnm);
266
267   read_tps_conf(ftp2fn(efTPS,NFILE,fnm),title,&top,&ePBC,&xref,NULL,box,TRUE);
268   snew(w_rls,top.atoms.nr);
269
270   fprintf(stderr,"Select group(s) for root mean square calculation\n");
271   get_index(&top.atoms,ftp2fn_null(efNDX,NFILE,fnm),1,&isize,&index,&grpnames);
272
273   /* Set the weight */
274   for(i=0; i<isize; i++) 
275     w_rls[index[i]]=top.atoms.atom[index[i]].m;
276
277   /* Malloc the rmsf arrays */
278   snew(xav,isize*DIM);
279   snew(U,isize);
280   for(i=0; i<isize; i++)
281     snew(U[i],DIM*DIM);
282   snew(rmsf,isize);
283   if (devfn)
284     snew(rmsd_x, isize);
285   
286   if (bReadPDB) {
287     get_stx_coordnum(opt2fn("-q",NFILE,fnm),&npdbatoms);
288     snew(pdbatoms,1);
289     snew(refatoms,1);
290     init_t_atoms(pdbatoms,npdbatoms,TRUE);
291     init_t_atoms(refatoms,npdbatoms,TRUE);
292     snew(pdbx,npdbatoms);
293     /* Read coordinates twice */
294     read_stx_conf(opt2fn("-q",NFILE,fnm),title,pdbatoms,pdbx,NULL,NULL,pdbbox);
295     read_stx_conf(opt2fn("-q",NFILE,fnm),title,refatoms,pdbx,NULL,NULL,pdbbox);
296   } else {
297     pdbatoms  = &top.atoms;
298     refatoms  = &top.atoms;
299     pdbx      = xref;
300     npdbatoms = pdbatoms->nr;
301     snew(pdbatoms->pdbinfo,npdbatoms);
302     copy_mat(box,pdbbox);
303   }
304   
305   if (bFit) {
306     sub_xcm(xref,isize,index,top.atoms.atom,xcm,FALSE);
307   }
308     
309   natom = read_first_x(oenv,&status,ftp2fn(efTRX,NFILE,fnm),&t,&x,box);
310
311   if (bFit) {
312     gpbc = gmx_rmpbc_init(&top.idef,ePBC,natom,box);
313   }
314     
315   /* Now read the trj again to compute fluctuations */
316   teller = 0;
317   do {
318     if (bFit) {
319       /* Remove periodic boundary */
320       gmx_rmpbc(gpbc,natom,box,x);
321
322       /* Set center of mass to zero */
323       sub_xcm(x,isize,index,top.atoms.atom,xcm,FALSE);
324       
325       /* Fit to reference structure */
326       do_fit(natom,w_rls,xref,x);
327     }
328      
329     /* Calculate Anisotropic U Tensor */  
330     for(i=0; i<isize; i++) {
331       aid = index[i];
332       for(d=0; d<DIM; d++) {
333         xav[i*DIM + d] += x[aid][d];
334         for(m=0; m<DIM; m++)
335           U[i][d*DIM + m] += x[aid][d]*x[aid][m];
336       }
337     }
338     
339     if (devfn) {
340       /* Calculate RMS Deviation */
341       for(i=0;(i<isize);i++) {
342         aid = index[i];
343         for(d=0;(d<DIM);d++) {
344           rmsd_x[i][d] += sqr(x[aid][d]-xref[aid][d]);
345         }
346       }
347     } 
348     count += 1.0;
349     teller++;
350   } while(read_next_x(oenv,status,&t,natom,x,box));
351   close_trj(status);
352   
353   if (bFit)
354     gmx_rmpbc_done(gpbc);
355
356
357   invcount = 1.0/count;
358   snew(Uaver,DIM*DIM);
359   totmass = 0;
360   for(i=0; i<isize; i++) {
361     for(d=0; d<DIM; d++)
362       xav[i*DIM + d] *= invcount;
363     for(d=0; d<DIM; d++)
364       for(m=0; m<DIM; m++) {
365         U[i][d*DIM + m] = U[i][d*DIM + m]*invcount 
366           - xav[i*DIM + d]*xav[i*DIM + m];
367         Uaver[3*d+m] += top.atoms.atom[index[i]].m*U[i][d*DIM + m];
368       }
369     totmass += top.atoms.atom[index[i]].m;
370   }
371   for(d=0; d<DIM*DIM; d++)
372     Uaver[d] /= totmass;
373
374   if (bRes) {
375     for(d=0; d<DIM*DIM; d++) {
376       average_residues(NULL,U,d,isize,index,w_rls,&top.atoms);
377     }
378   }
379
380   if (bAniso) {
381     for(i=0; i<isize; i++) {
382       aid = index[i];
383       pdbatoms->pdbinfo[aid].bAnisotropic = TRUE;
384       pdbatoms->pdbinfo[aid].uij[U11] = 1e6*U[i][XX*DIM + XX];
385       pdbatoms->pdbinfo[aid].uij[U22] = 1e6*U[i][YY*DIM + YY];
386       pdbatoms->pdbinfo[aid].uij[U33] = 1e6*U[i][ZZ*DIM + ZZ];
387       pdbatoms->pdbinfo[aid].uij[U12] = 1e6*U[i][XX*DIM + YY];
388       pdbatoms->pdbinfo[aid].uij[U13] = 1e6*U[i][XX*DIM + ZZ];
389       pdbatoms->pdbinfo[aid].uij[U23] = 1e6*U[i][YY*DIM + ZZ];
390     }
391   }
392   if (bRes) {
393     label = "Residue";
394   } else
395     label = "Atom";
396
397   for(i=0; i<isize; i++)
398     rmsf[i] = U[i][XX*DIM + XX] + U[i][YY*DIM + YY] + U[i][ZZ*DIM + ZZ];
399   
400   if (dirfn) {
401     fprintf(stdout,"\n");
402     print_dir(stdout,Uaver);
403     fp = ffopen(dirfn,"w");
404     print_dir(fp,Uaver);
405     ffclose(fp);
406   }
407
408   for(i=0; i<isize; i++)
409     sfree(U[i]);
410   sfree(U);
411
412   /* Write RMSF output */
413   if (bReadPDB) {
414     bfac = 8.0*M_PI*M_PI/3.0*100;
415     fp   = xvgropen(ftp2fn(efXVG,NFILE,fnm),"B-Factors",
416                     label,"(A\\b\\S\\So\\N\\S2\\N)",oenv);
417     xvgr_legend(fp,2,leg,oenv);
418     for(i=0;(i<isize);i++) {
419       if (!bRes || i+1==isize ||
420           top.atoms.atom[index[i]].resind!=top.atoms.atom[index[i+1]].resind) {
421         resind    = top.atoms.atom[index[i]].resind;
422         pdb_bfac = find_pdb_bfac(pdbatoms,&top.atoms.resinfo[resind],
423                                  *(top.atoms.atomname[index[i]]));
424         
425         fprintf(fp,"%5d  %10.5f  %10.5f\n",
426                 bRes ? top.atoms.resinfo[top.atoms.atom[index[i]].resind].nr : i+1,rmsf[i]*bfac,
427                 pdb_bfac);
428       }
429     }
430     ffclose(fp);
431   } else {
432     fp = xvgropen(ftp2fn(efXVG,NFILE,fnm),"RMS fluctuation",label,"(nm)",oenv);
433     for(i=0; i<isize; i++)
434       if (!bRes || i+1==isize ||
435           top.atoms.atom[index[i]].resind!=top.atoms.atom[index[i+1]].resind)
436         fprintf(fp,"%5d %8.4f\n",
437                 bRes ? top.atoms.resinfo[top.atoms.atom[index[i]].resind].nr : i+1,sqrt(rmsf[i]));
438     ffclose(fp);
439   }
440   
441   for(i=0; i<isize; i++)
442     pdbatoms->pdbinfo[index[i]].bfac = 800*M_PI*M_PI/3.0*rmsf[i];
443
444   if (devfn) {
445     for(i=0; i<isize; i++)
446       rmsf[i] = (rmsd_x[i][XX]+rmsd_x[i][YY]+rmsd_x[i][ZZ])/count;
447     if (bRes)
448       average_residues(rmsf,NULL,0,isize,index,w_rls,&top.atoms); 
449     /* Write RMSD output */
450     fp = xvgropen(devfn,"RMS Deviation",label,"(nm)",oenv);
451     for(i=0; i<isize; i++)
452       if (!bRes || i+1==isize ||
453           top.atoms.atom[index[i]].resind!=top.atoms.atom[index[i+1]].resind)
454         fprintf(fp,"%5d %8.4f\n",
455                 bRes ? top.atoms.resinfo[top.atoms.atom[index[i]].resind].nr : i+1,sqrt(rmsf[i]));
456     ffclose(fp);
457   }
458
459   if (opt2bSet("-oq",NFILE,fnm)) {
460     /* Write a pdb file with B-factors and optionally anisou records */
461     for(i=0; i<isize; i++)
462       rvec_inc(xref[index[i]],xcm);
463     write_sto_conf_indexed(opt2fn("-oq",NFILE,fnm),title,pdbatoms,pdbx,
464                            NULL,ePBC,pdbbox,isize,index);
465   }
466   if (opt2bSet("-ox",NFILE,fnm)) {
467     /* Misuse xref as a temporary array */
468     for(i=0; i<isize; i++)
469       for(d=0; d<DIM; d++)
470         xref[index[i]][d] = xcm[d] + xav[i*DIM + d];
471     /* Write a pdb file with B-factors and optionally anisou records */
472     write_sto_conf_indexed(opt2fn("-ox",NFILE,fnm),title,pdbatoms,xref,NULL,
473                            ePBC,pdbbox,isize,index);
474   }
475   if (bAniso) { 
476     correlate_aniso(opt2fn("-oc",NFILE,fnm),refatoms,pdbatoms,oenv);
477     do_view(oenv,opt2fn("-oc",NFILE,fnm),"-nxy");
478   }
479   do_view(oenv,opt2fn("-o",NFILE,fnm),"-nxy");
480   if (devfn)
481     do_view(oenv,opt2fn("-od",NFILE,fnm),"-nxy");
482     
483   thanx(stderr);
484   
485   return 0;
486 }