Merge release-5-0 into master
[alexxy/gromacs.git] / src / gromacs / gmxana / gmx_rmsf.c
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #include <math.h>
42
43 #include "gromacs/utility/smalloc.h"
44 #include "macros.h"
45 #include "typedefs.h"
46 #include "gromacs/fileio/xvgr.h"
47 #include "viewit.h"
48 #include "gromacs/commandline/pargs.h"
49 #include "vec.h"
50 #include "index.h"
51 #include "gromacs/fileio/pdbio.h"
52 #include "gromacs/fileio/tpxio.h"
53 #include "gromacs/fileio/trxio.h"
54 #include "pbc.h"
55 #include "gromacs/utility/futil.h"
56 #include "princ.h"
57 #include "rmpbc.h"
58 #include "gromacs/fileio/confio.h"
59 #include "gmx_ana.h"
60
61 #include "gromacs/linearalgebra/eigensolver.h"
62 #include "gromacs/math/do_fit.h"
63
64 static real find_pdb_bfac(t_atoms *atoms, t_resinfo *ri, char *atomnm)
65 {
66     char rresnm[8];
67     int  i;
68
69     strcpy(rresnm, *ri->name);
70     rresnm[3] = '\0';
71     for (i = 0; (i < atoms->nr); i++)
72     {
73         if ((ri->nr == atoms->resinfo[atoms->atom[i].resind].nr) &&
74             (ri->ic == atoms->resinfo[atoms->atom[i].resind].ic) &&
75             (strcmp(*atoms->resinfo[atoms->atom[i].resind].name, rresnm) == 0) &&
76             (strstr(*atoms->atomname[i], atomnm) != NULL))
77         {
78             break;
79         }
80     }
81     if (i == atoms->nr)
82     {
83         fprintf(stderr, "\rCan not find %s%d-%s in pdbfile\n",
84                 rresnm, ri->nr, atomnm);
85         return 0.0;
86     }
87
88     return atoms->pdbinfo[i].bfac;
89 }
90
91 void correlate_aniso(const char *fn, t_atoms *ref, t_atoms *calc,
92                      const output_env_t oenv)
93 {
94     FILE *fp;
95     int   i, j;
96
97     fp = xvgropen(fn, "Correlation between X-Ray and Computed Uij", "X-Ray",
98                   "Computed", oenv);
99     for (i = 0; (i < ref->nr); i++)
100     {
101         if (ref->pdbinfo[i].bAnisotropic)
102         {
103             for (j = U11; (j <= U23); j++)
104             {
105                 fprintf(fp, "%10d  %10d\n", ref->pdbinfo[i].uij[j], calc->pdbinfo[i].uij[j]);
106             }
107         }
108     }
109     gmx_ffclose(fp);
110 }
111
112 static void average_residues(double f[], double **U, int uind,
113                              int isize, atom_id index[], real w_rls[],
114                              t_atoms *atoms)
115 {
116     int    i, j, start;
117     double av, m;
118
119     start = 0;
120     av    = 0;
121     m     = 0;
122     for (i = 0; i < isize; i++)
123     {
124         av += w_rls[index[i]]*(f != NULL ? f[i] : U[i][uind]);
125         m  += w_rls[index[i]];
126         if (i+1 == isize ||
127             atoms->atom[index[i]].resind != atoms->atom[index[i+1]].resind)
128         {
129             av /= m;
130             if (f != NULL)
131             {
132                 for (j = start; j <= i; j++)
133                 {
134                     f[i] = av;
135                 }
136             }
137             else
138             {
139                 for (j = start; j <= i; j++)
140                 {
141                     U[j][uind] = av;
142                 }
143             }
144             start = i+1;
145             av    = 0;
146             m     = 0;
147         }
148     }
149 }
150
151 void print_dir(FILE *fp, real *Uaver)
152 {
153     real eigvec[DIM*DIM];
154     real tmp[DIM*DIM];
155     rvec eigval;
156     int  d, m;
157
158     fprintf(fp, "MSF     X         Y         Z\n");
159     for (d = 0; d < DIM; d++)
160     {
161         fprintf(fp, " %c ", 'X'+d-XX);
162         for (m = 0; m < DIM; m++)
163         {
164             fprintf(fp, " %9.2e", Uaver[3*m+d]);
165         }
166         fprintf(fp, "%s\n", m == DIM ? " (nm^2)" : "");
167     }
168
169     for (m = 0; m < DIM*DIM; m++)
170     {
171         tmp[m] = Uaver[m];
172     }
173
174
175     eigensolver(tmp, DIM, 0, DIM, eigval, eigvec);
176
177     fprintf(fp, "\n             Eigenvectors\n\n");
178     fprintf(fp, "Eigv  %-8.2e %-8.2e %-8.2e (nm^2)\n\n",
179             eigval[2], eigval[1], eigval[0]);
180     for (d = 0; d < DIM; d++)
181     {
182         fprintf(fp, "  %c   ", 'X'+d-XX);
183         for (m = DIM-1; m >= 0; m--)
184         {
185             fprintf(fp, "%7.4f  ", eigvec[3*m+d]);
186         }
187         fprintf(fp, "\n");
188     }
189 }
190
191 int gmx_rmsf(int argc, char *argv[])
192 {
193     const char      *desc[] = {
194         "[THISMODULE] computes the root mean square fluctuation (RMSF, i.e. standard ",
195         "deviation) of atomic positions in the trajectory (supplied with [TT]-f[tt])",
196         "after (optionally) fitting to a reference frame (supplied with [TT]-s[tt]).[PAR]",
197         "With option [TT]-oq[tt] the RMSF values are converted to B-factor",
198         "values, which are written to a [TT].pdb[tt] file with the coordinates, of the",
199         "structure file, or of a [TT].pdb[tt] file when [TT]-q[tt] is specified.",
200         "Option [TT]-ox[tt] writes the B-factors to a file with the average",
201         "coordinates.[PAR]",
202         "With the option [TT]-od[tt] the root mean square deviation with",
203         "respect to the reference structure is calculated.[PAR]",
204         "With the option [TT]-aniso[tt], [THISMODULE] will compute anisotropic",
205         "temperature factors and then it will also output average coordinates",
206         "and a [TT].pdb[tt] file with ANISOU records (corresonding to the [TT]-oq[tt]",
207         "or [TT]-ox[tt] option). Please note that the U values",
208         "are orientation-dependent, so before comparison with experimental data",
209         "you should verify that you fit to the experimental coordinates.[PAR]",
210         "When a [TT].pdb[tt] input file is passed to the program and the [TT]-aniso[tt]",
211         "flag is set",
212         "a correlation plot of the Uij will be created, if any anisotropic",
213         "temperature factors are present in the [TT].pdb[tt] file.[PAR]",
214         "With option [TT]-dir[tt] the average MSF (3x3) matrix is diagonalized.",
215         "This shows the directions in which the atoms fluctuate the most and",
216         "the least."
217     };
218     static gmx_bool  bRes    = FALSE, bAniso = FALSE, bdevX = FALSE, bFit = TRUE;
219     t_pargs          pargs[] = {
220         { "-res", FALSE, etBOOL, {&bRes},
221           "Calculate averages for each residue" },
222         { "-aniso", FALSE, etBOOL, {&bAniso},
223           "Compute anisotropic termperature factors" },
224         { "-fit", FALSE, etBOOL, {&bFit},
225           "Do a least squares superposition before computing RMSF. Without this you must make sure that the reference structure and the trajectory match." }
226     };
227     int              natom;
228     int              step, nre, natoms, i, g, m, teller = 0;
229     real             t, lambda, *w_rls, *w_rms;
230
231     t_tpxheader      header;
232     t_inputrec       ir;
233     t_topology       top;
234     int              ePBC;
235     t_atoms         *pdbatoms, *refatoms;
236     gmx_bool         bCont;
237
238     matrix           box, pdbbox;
239     rvec            *x, *pdbx, *xref;
240     t_trxstatus     *status;
241     int              npdbatoms, res0;
242     char             buf[256];
243     const char      *label;
244     char             title[STRLEN];
245
246     FILE            *fp;          /* the graphics file */
247     const char      *devfn, *dirfn;
248     int              resind;
249
250     gmx_bool         bReadPDB;
251     atom_id         *index;
252     int              isize;
253     char            *grpnames;
254
255     real             bfac, pdb_bfac, *Uaver;
256     double         **U, *xav;
257     atom_id          aid;
258     rvec            *rmsd_x = NULL;
259     double          *rmsf, invcount, totmass;
260     int              d;
261     real             count = 0;
262     rvec             xcm;
263     gmx_rmpbc_t      gpbc = NULL;
264
265     output_env_t     oenv;
266
267     const char      *leg[2] = { "MD", "X-Ray" };
268
269     t_filenm         fnm[] = {
270         { efTRX, "-f",  NULL,     ffREAD  },
271         { efTPS, NULL,  NULL,     ffREAD  },
272         { efNDX, NULL,  NULL,     ffOPTRD },
273         { efPDB, "-q",  NULL,     ffOPTRD },
274         { efPDB, "-oq", "bfac",   ffOPTWR },
275         { efPDB, "-ox", "xaver",  ffOPTWR },
276         { efXVG, "-o",  "rmsf",   ffWRITE },
277         { efXVG, "-od", "rmsdev", ffOPTWR },
278         { efXVG, "-oc", "correl", ffOPTWR },
279         { efLOG, "-dir", "rmsf",  ffOPTWR }
280     };
281 #define NFILE asize(fnm)
282
283     if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW | PCA_BE_NICE,
284                            NFILE, fnm, asize(pargs), pargs, asize(desc), desc, 0, NULL,
285                            &oenv))
286     {
287         return 0;
288     }
289
290     bReadPDB = ftp2bSet(efPDB, NFILE, fnm);
291     devfn    = opt2fn_null("-od", NFILE, fnm);
292     dirfn    = opt2fn_null("-dir", NFILE, fnm);
293
294     read_tps_conf(ftp2fn(efTPS, NFILE, fnm), title, &top, &ePBC, &xref, NULL, box, TRUE);
295     snew(w_rls, top.atoms.nr);
296
297     fprintf(stderr, "Select group(s) for root mean square calculation\n");
298     get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, &grpnames);
299
300     /* Set the weight */
301     for (i = 0; i < isize; i++)
302     {
303         w_rls[index[i]] = top.atoms.atom[index[i]].m;
304     }
305
306     /* Malloc the rmsf arrays */
307     snew(xav, isize*DIM);
308     snew(U, isize);
309     for (i = 0; i < isize; i++)
310     {
311         snew(U[i], DIM*DIM);
312     }
313     snew(rmsf, isize);
314     if (devfn)
315     {
316         snew(rmsd_x, isize);
317     }
318
319     if (bReadPDB)
320     {
321         get_stx_coordnum(opt2fn("-q", NFILE, fnm), &npdbatoms);
322         snew(pdbatoms, 1);
323         snew(refatoms, 1);
324         init_t_atoms(pdbatoms, npdbatoms, TRUE);
325         init_t_atoms(refatoms, npdbatoms, TRUE);
326         snew(pdbx, npdbatoms);
327         /* Read coordinates twice */
328         read_stx_conf(opt2fn("-q", NFILE, fnm), title, pdbatoms, pdbx, NULL, NULL, pdbbox);
329         read_stx_conf(opt2fn("-q", NFILE, fnm), title, refatoms, pdbx, NULL, NULL, pdbbox);
330     }
331     else
332     {
333         pdbatoms  = &top.atoms;
334         refatoms  = &top.atoms;
335         pdbx      = xref;
336         npdbatoms = pdbatoms->nr;
337         snew(pdbatoms->pdbinfo, npdbatoms);
338         copy_mat(box, pdbbox);
339     }
340
341     if (bFit)
342     {
343         sub_xcm(xref, isize, index, top.atoms.atom, xcm, FALSE);
344     }
345
346     natom = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
347
348     if (bFit)
349     {
350         gpbc = gmx_rmpbc_init(&top.idef, ePBC, natom);
351     }
352
353     /* Now read the trj again to compute fluctuations */
354     teller = 0;
355     do
356     {
357         if (bFit)
358         {
359             /* Remove periodic boundary */
360             gmx_rmpbc(gpbc, natom, box, x);
361
362             /* Set center of mass to zero */
363             sub_xcm(x, isize, index, top.atoms.atom, xcm, FALSE);
364
365             /* Fit to reference structure */
366             do_fit(natom, w_rls, xref, x);
367         }
368
369         /* Calculate Anisotropic U Tensor */
370         for (i = 0; i < isize; i++)
371         {
372             aid = index[i];
373             for (d = 0; d < DIM; d++)
374             {
375                 xav[i*DIM + d] += x[aid][d];
376                 for (m = 0; m < DIM; m++)
377                 {
378                     U[i][d*DIM + m] += x[aid][d]*x[aid][m];
379                 }
380             }
381         }
382
383         if (devfn)
384         {
385             /* Calculate RMS Deviation */
386             for (i = 0; (i < isize); i++)
387             {
388                 aid = index[i];
389                 for (d = 0; (d < DIM); d++)
390                 {
391                     rmsd_x[i][d] += sqr(x[aid][d]-xref[aid][d]);
392                 }
393             }
394         }
395         count += 1.0;
396         teller++;
397     }
398     while (read_next_x(oenv, status, &t, x, box));
399     close_trj(status);
400
401     if (bFit)
402     {
403         gmx_rmpbc_done(gpbc);
404     }
405
406
407     invcount = 1.0/count;
408     snew(Uaver, DIM*DIM);
409     totmass = 0;
410     for (i = 0; i < isize; i++)
411     {
412         for (d = 0; d < DIM; d++)
413         {
414             xav[i*DIM + d] *= invcount;
415         }
416         for (d = 0; d < DIM; d++)
417         {
418             for (m = 0; m < DIM; m++)
419             {
420                 U[i][d*DIM + m] = U[i][d*DIM + m]*invcount
421                     - xav[i*DIM + d]*xav[i*DIM + m];
422                 Uaver[3*d+m] += top.atoms.atom[index[i]].m*U[i][d*DIM + m];
423             }
424         }
425         totmass += top.atoms.atom[index[i]].m;
426     }
427     for (d = 0; d < DIM*DIM; d++)
428     {
429         Uaver[d] /= totmass;
430     }
431
432     if (bRes)
433     {
434         for (d = 0; d < DIM*DIM; d++)
435         {
436             average_residues(NULL, U, d, isize, index, w_rls, &top.atoms);
437         }
438     }
439
440     if (bAniso)
441     {
442         for (i = 0; i < isize; i++)
443         {
444             aid = index[i];
445             pdbatoms->pdbinfo[aid].bAnisotropic = TRUE;
446             pdbatoms->pdbinfo[aid].uij[U11]     = 1e6*U[i][XX*DIM + XX];
447             pdbatoms->pdbinfo[aid].uij[U22]     = 1e6*U[i][YY*DIM + YY];
448             pdbatoms->pdbinfo[aid].uij[U33]     = 1e6*U[i][ZZ*DIM + ZZ];
449             pdbatoms->pdbinfo[aid].uij[U12]     = 1e6*U[i][XX*DIM + YY];
450             pdbatoms->pdbinfo[aid].uij[U13]     = 1e6*U[i][XX*DIM + ZZ];
451             pdbatoms->pdbinfo[aid].uij[U23]     = 1e6*U[i][YY*DIM + ZZ];
452         }
453     }
454     if (bRes)
455     {
456         label = "Residue";
457     }
458     else
459     {
460         label = "Atom";
461     }
462
463     for (i = 0; i < isize; i++)
464     {
465         rmsf[i] = U[i][XX*DIM + XX] + U[i][YY*DIM + YY] + U[i][ZZ*DIM + ZZ];
466     }
467
468     if (dirfn)
469     {
470         fprintf(stdout, "\n");
471         print_dir(stdout, Uaver);
472         fp = gmx_ffopen(dirfn, "w");
473         print_dir(fp, Uaver);
474         gmx_ffclose(fp);
475     }
476
477     for (i = 0; i < isize; i++)
478     {
479         sfree(U[i]);
480     }
481     sfree(U);
482
483     /* Write RMSF output */
484     if (bReadPDB)
485     {
486         bfac = 8.0*M_PI*M_PI/3.0*100;
487         fp   = xvgropen(ftp2fn(efXVG, NFILE, fnm), "B-Factors",
488                         label, "(A\\b\\S\\So\\N\\S2\\N)", oenv);
489         xvgr_legend(fp, 2, leg, oenv);
490         for (i = 0; (i < isize); i++)
491         {
492             if (!bRes || i+1 == isize ||
493                 top.atoms.atom[index[i]].resind != top.atoms.atom[index[i+1]].resind)
494             {
495                 resind    = top.atoms.atom[index[i]].resind;
496                 pdb_bfac  = find_pdb_bfac(pdbatoms, &top.atoms.resinfo[resind],
497                                           *(top.atoms.atomname[index[i]]));
498
499                 fprintf(fp, "%5d  %10.5f  %10.5f\n",
500                         bRes ? top.atoms.resinfo[top.atoms.atom[index[i]].resind].nr : index[i]+1, rmsf[i]*bfac,
501                         pdb_bfac);
502             }
503         }
504         gmx_ffclose(fp);
505     }
506     else
507     {
508         fp = xvgropen(ftp2fn(efXVG, NFILE, fnm), "RMS fluctuation", label, "(nm)", oenv);
509         for (i = 0; i < isize; i++)
510         {
511             if (!bRes || i+1 == isize ||
512                 top.atoms.atom[index[i]].resind != top.atoms.atom[index[i+1]].resind)
513             {
514                 fprintf(fp, "%5d %8.4f\n",
515                         bRes ? top.atoms.resinfo[top.atoms.atom[index[i]].resind].nr : index[i]+1, sqrt(rmsf[i]));
516             }
517         }
518         gmx_ffclose(fp);
519     }
520
521     for (i = 0; i < isize; i++)
522     {
523         pdbatoms->pdbinfo[index[i]].bfac = 800*M_PI*M_PI/3.0*rmsf[i];
524     }
525
526     if (devfn)
527     {
528         for (i = 0; i < isize; i++)
529         {
530             rmsf[i] = (rmsd_x[i][XX]+rmsd_x[i][YY]+rmsd_x[i][ZZ])/count;
531         }
532         if (bRes)
533         {
534             average_residues(rmsf, NULL, 0, isize, index, w_rls, &top.atoms);
535         }
536         /* Write RMSD output */
537         fp = xvgropen(devfn, "RMS Deviation", label, "(nm)", oenv);
538         for (i = 0; i < isize; i++)
539         {
540             if (!bRes || i+1 == isize ||
541                 top.atoms.atom[index[i]].resind != top.atoms.atom[index[i+1]].resind)
542             {
543                 fprintf(fp, "%5d %8.4f\n",
544                         bRes ? top.atoms.resinfo[top.atoms.atom[index[i]].resind].nr : index[i]+1, sqrt(rmsf[i]));
545             }
546         }
547         gmx_ffclose(fp);
548     }
549
550     if (opt2bSet("-oq", NFILE, fnm))
551     {
552         /* Write a .pdb file with B-factors and optionally anisou records */
553         for (i = 0; i < isize; i++)
554         {
555             rvec_inc(xref[index[i]], xcm);
556         }
557         write_sto_conf_indexed(opt2fn("-oq", NFILE, fnm), title, pdbatoms, pdbx,
558                                NULL, ePBC, pdbbox, isize, index);
559     }
560     if (opt2bSet("-ox", NFILE, fnm))
561     {
562         /* Misuse xref as a temporary array */
563         for (i = 0; i < isize; i++)
564         {
565             for (d = 0; d < DIM; d++)
566             {
567                 xref[index[i]][d] = xcm[d] + xav[i*DIM + d];
568             }
569         }
570         /* Write a .pdb file with B-factors and optionally anisou records */
571         write_sto_conf_indexed(opt2fn("-ox", NFILE, fnm), title, pdbatoms, xref, NULL,
572                                ePBC, pdbbox, isize, index);
573     }
574     if (bAniso)
575     {
576         correlate_aniso(opt2fn("-oc", NFILE, fnm), refatoms, pdbatoms, oenv);
577         do_view(oenv, opt2fn("-oc", NFILE, fnm), "-nxy");
578     }
579     do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy");
580     if (devfn)
581     {
582         do_view(oenv, opt2fn("-od", NFILE, fnm), "-nxy");
583     }
584
585     return 0;
586 }