Merge release-4-6 into master
[alexxy/gromacs.git] / src / gromacs / gmxana / gmx_rmsdist.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 #include <ctype.h>
43
44 #include "macros.h"
45 #include "smalloc.h"
46 #include "typedefs.h"
47 #include "names.h"
48 #include "gromacs/commandline/pargs.h"
49 #include "gromacs/fileio/tpxio.h"
50 #include "gromacs/fileio/trxio.h"
51 #include "string2.h"
52 #include "gromacs/fileio/strdb.h"
53 #include "vec.h"
54 #include "macros.h"
55 #include "index.h"
56 #include "pbc.h"
57 #include "xvgr.h"
58 #include "gromacs/fileio/futil.h"
59 #include "gromacs/fileio/matio.h"
60 #include "gmx_ana.h"
61
62
63 static void calc_dist(int nind, atom_id index[], rvec x[], int ePBC, matrix box,
64                       real **d)
65 {
66     int      i, j;
67     real    *xi;
68     rvec     dx;
69     real     temp2;
70     t_pbc    pbc;
71
72     set_pbc(&pbc, ePBC, box);
73     for (i = 0; (i < nind-1); i++)
74     {
75         xi = x[index[i]];
76         for (j = i+1; (j < nind); j++)
77         {
78             pbc_dx(&pbc, xi, x[index[j]], dx);
79             temp2   = norm2(dx);
80             d[i][j] = sqrt(temp2);
81         }
82     }
83 }
84
85 static void calc_dist_tot(int nind, atom_id index[], rvec x[],
86                           int ePBC, matrix box,
87                           real **d, real **dtot, real **dtot2,
88                           gmx_bool bNMR, real **dtot1_3, real **dtot1_6)
89 {
90     int      i, j;
91     real    *xi;
92     real     temp, temp2, temp1_3;
93     rvec     dx;
94     t_pbc    pbc;
95
96     set_pbc(&pbc, ePBC, box);
97     for (i = 0; (i < nind-1); i++)
98     {
99         xi = x[index[i]];
100         for (j = i+1; (j < nind); j++)
101         {
102             pbc_dx(&pbc, xi, x[index[j]], dx);
103             temp2        = norm2(dx);
104             temp         = sqrt(temp2);
105             d[i][j]      = temp;
106             dtot[i][j]  += temp;
107             dtot2[i][j] += temp2;
108             if (bNMR)
109             {
110                 temp1_3        = 1.0/(temp*temp2);
111                 dtot1_3[i][j] += temp1_3;
112                 dtot1_6[i][j] += temp1_3*temp1_3;
113             }
114         }
115     }
116 }
117
118 static void calc_nmr(int nind, int nframes, real **dtot1_3, real **dtot1_6,
119                      real *max1_3, real *max1_6)
120 {
121     int     i, j;
122     real    temp1_3, temp1_6;
123
124     for (i = 0; (i < nind-1); i++)
125     {
126         for (j = i+1; (j < nind); j++)
127         {
128             temp1_3 = pow(dtot1_3[i][j]/nframes, -1.0/3.0);
129             temp1_6 = pow(dtot1_6[i][j]/nframes, -1.0/6.0);
130             if (temp1_3 > *max1_3)
131             {
132                 *max1_3 = temp1_3;
133             }
134             if (temp1_6 > *max1_6)
135             {
136                 *max1_6 = temp1_6;
137             }
138             dtot1_3[i][j] = temp1_3;
139             dtot1_6[i][j] = temp1_6;
140             dtot1_3[j][i] = temp1_3;
141             dtot1_6[j][i] = temp1_6;
142         }
143     }
144 }
145
146 static char Hnum[] = "123";
147
148 typedef struct {
149     int  nr;
150     real r_3;
151     real r_6;
152     real i_3;
153     real i_6;
154 } t_noe;
155
156 typedef struct {
157     int   anr;
158     int   ianr;
159     int   rnr;
160     char *aname;
161     char *rname;
162 } t_noe_gr;
163
164 typedef struct {
165     int   rnr;
166     char *nname;
167     char *rname;
168     char *aname;
169 } t_equiv;
170
171 static int read_equiv(const char *eq_fn, t_equiv ***equivptr)
172 {
173     FILE     *fp;
174     char      line[STRLEN], resname[10], atomname[10], *lp;
175     int       neq, na, n, resnr;
176     t_equiv **equiv;
177
178     fp    = gmx_ffopen(eq_fn, "r");
179     neq   = 0;
180     equiv = NULL;
181     while (get_a_line(fp, line, STRLEN))
182     {
183         lp = line;
184         /* this is not efficient, but I'm lazy */
185         srenew(equiv, neq+1);
186         equiv[neq] = NULL;
187         na         = 0;
188         if (sscanf(lp, "%s %n", atomname, &n) == 1)
189         {
190             lp += n;
191             snew(equiv[neq], 1);
192             equiv[neq][0].nname = strdup(atomname);
193             while (sscanf(lp, "%d %s %s %n", &resnr, resname, atomname, &n) == 3)
194             {
195                 /* this is not efficient, but I'm lazy (again) */
196                 srenew(equiv[neq], na+1);
197                 equiv[neq][na].rnr   = resnr-1;
198                 equiv[neq][na].rname = strdup(resname);
199                 equiv[neq][na].aname = strdup(atomname);
200                 if (na > 0)
201                 {
202                     equiv[neq][na].nname = NULL;
203                 }
204                 na++;
205                 lp += n;
206             }
207         }
208         /* make empty element as flag for end of array */
209         srenew(equiv[neq], na+1);
210         equiv[neq][na].rnr   = NOTSET;
211         equiv[neq][na].rname = NULL;
212         equiv[neq][na].aname = NULL;
213
214         /* next */
215         neq++;
216     }
217     gmx_ffclose(fp);
218
219     *equivptr = equiv;
220
221     return neq;
222 }
223
224 static void dump_equiv(FILE *out, int neq, t_equiv **equiv)
225 {
226     int i, j;
227
228     fprintf(out, "Dumping equivalent list\n");
229     for (i = 0; i < neq; i++)
230     {
231         fprintf(out, "%s", equiv[i][0].nname);
232         for (j = 0; equiv[i][j].rnr != NOTSET; j++)
233         {
234             fprintf(out, " %d %s %s",
235                     equiv[i][j].rnr, equiv[i][j].rname, equiv[i][j].aname);
236         }
237         fprintf(out, "\n");
238     }
239 }
240
241 static gmx_bool is_equiv(int neq, t_equiv **equiv, char **nname,
242                          int rnr1, char *rname1, char *aname1,
243                          int rnr2, char *rname2, char *aname2)
244 {
245     int      i, j;
246     gmx_bool bFound;
247
248     bFound = FALSE;
249     /* we can terminate each loop when bFound is true! */
250     for (i = 0; i < neq && !bFound; i++)
251     {
252         /* find first atom */
253         for (j = 0; equiv[i][j].rnr != NOTSET && !bFound; j++)
254         {
255             bFound = ( equiv[i][j].rnr == rnr1 &&
256                        strcmp(equiv[i][j].rname, rname1) == 0 &&
257                        strcmp(equiv[i][j].aname, aname1) == 0 );
258         }
259         if (bFound)
260         {
261             /* find second atom */
262             bFound = FALSE;
263             for (j = 0; equiv[i][j].rnr != NOTSET && !bFound; j++)
264             {
265                 bFound = ( equiv[i][j].rnr == rnr2 &&
266                            strcmp(equiv[i][j].rname, rname2) == 0 &&
267                            strcmp(equiv[i][j].aname, aname2) == 0 );
268             }
269         }
270     }
271     if (bFound)
272     {
273         *nname = strdup(equiv[i-1][0].nname);
274     }
275
276     return bFound;
277 }
278
279 static int analyze_noe_equivalent(const char *eq_fn,
280                                   t_atoms *atoms, int isize, atom_id *index,
281                                   gmx_bool bSumH,
282                                   atom_id *noe_index, t_noe_gr *noe_gr)
283 {
284     FILE     *fp;
285     int       i, j, anmil, anmjl, rnri, rnrj, gi, groupnr, neq;
286     char     *anmi, *anmj, **nnm;
287     gmx_bool  bMatch, bEquiv;
288     t_equiv **equiv;
289
290     snew(nnm, isize);
291     if (bSumH)
292     {
293         if (eq_fn)
294         {
295             neq = read_equiv(eq_fn, &equiv);
296             if (debug)
297             {
298                 dump_equiv(debug, neq, equiv);
299             }
300         }
301         else
302         {
303             neq   = 0;
304             equiv = NULL;
305         }
306
307         groupnr = 0;
308         for (i = 0; i < isize; i++)
309         {
310             if (equiv && i < isize-1)
311             {
312                 /* check explicit list of equivalent atoms */
313                 do
314                 {
315                     j      = i+1;
316                     rnri   = atoms->atom[index[i]].resind;
317                     rnrj   = atoms->atom[index[j]].resind;
318                     bEquiv =
319                         is_equiv(neq, equiv, &nnm[i],
320                                  rnri, *atoms->resinfo[rnri].name, *atoms->atomname[index[i]],
321                                  rnrj, *atoms->resinfo[rnrj].name, *atoms->atomname[index[j]]);
322                     if (nnm[i] && bEquiv)
323                     {
324                         nnm[j] = strdup(nnm[i]);
325                     }
326                     if (bEquiv)
327                     {
328                         /* set index for matching atom */
329                         noe_index[j] = groupnr;
330                         /* skip matching atom */
331                         i = j;
332                     }
333                 }
334                 while (bEquiv && i < isize-1);
335             }
336             else
337             {
338                 bEquiv = FALSE;
339             }
340             if (!bEquiv)
341             {
342                 /* look for triplets of consecutive atoms with name XX?,
343                    X are any number of letters or digits and ? goes from 1 to 3
344                    This is supposed to cover all CH3 groups and the like */
345                 anmi   = *atoms->atomname[index[i]];
346                 anmil  = strlen(anmi);
347                 bMatch = i < isize-3 && anmi[anmil-1] == '1';
348                 if (bMatch)
349                 {
350                     for (j = 1; j < 3; j++)
351                     {
352                         anmj   = *atoms->atomname[index[i+j]];
353                         anmjl  = strlen(anmj);
354                         bMatch = bMatch && ( anmil == anmjl && anmj[anmjl-1] == Hnum[j] &&
355                                              strncmp(anmi, anmj, anmil-1) == 0 );
356                     }
357                 }
358                 /* set index for this atom */
359                 noe_index[i] = groupnr;
360                 if (bMatch)
361                 {
362                     /* set index for next two matching atoms */
363                     for (j = 1; j < 3; j++)
364                     {
365                         noe_index[i+j] = groupnr;
366                     }
367                     /* skip matching atoms */
368                     i += 2;
369                 }
370             }
371             groupnr++;
372         }
373     }
374     else
375     {
376         /* make index without looking for equivalent atoms */
377         for (i = 0; i < isize; i++)
378         {
379             noe_index[i] = i;
380         }
381         groupnr = isize;
382     }
383     noe_index[isize] = groupnr;
384
385     if (debug)
386     {
387         /* dump new names */
388         for (i = 0; i < isize; i++)
389         {
390             rnri = atoms->atom[index[i]].resind;
391             fprintf(debug, "%s %s %d -> %s\n", *atoms->atomname[index[i]],
392                     *atoms->resinfo[rnri].name, rnri, nnm[i] ? nnm[i] : "");
393         }
394     }
395
396     for (i = 0; i < isize; i++)
397     {
398         gi = noe_index[i];
399         if (!noe_gr[gi].aname)
400         {
401             noe_gr[gi].ianr = i;
402             noe_gr[gi].anr  = index[i];
403             if (nnm[i])
404             {
405                 noe_gr[gi].aname = strdup(nnm[i]);
406             }
407             else
408             {
409                 noe_gr[gi].aname = strdup(*atoms->atomname[index[i]]);
410                 if (noe_index[i] == noe_index[i+1])
411                 {
412                     noe_gr[gi].aname[strlen(noe_gr[gi].aname)-1] = '*';
413                 }
414             }
415             noe_gr[gi].rnr   = atoms->atom[index[i]].resind;
416             noe_gr[gi].rname = strdup(*atoms->resinfo[noe_gr[gi].rnr].name);
417             /* dump group definitions */
418             if (debug)
419             {
420                 fprintf(debug, "%d %d %d %d %s %s %d\n", i, gi,
421                         noe_gr[gi].ianr, noe_gr[gi].anr, noe_gr[gi].aname,
422                         noe_gr[gi].rname, noe_gr[gi].rnr);
423             }
424         }
425     }
426     for (i = 0; i < isize; i++)
427     {
428         sfree(nnm[i]);
429     }
430     sfree(nnm);
431
432     return groupnr;
433 }
434
435 /* #define NSCALE 3 */
436 /*  static char *noe_scale[NSCALE+1] = { */
437 /*    "strong", "medium", "weak", "none" */
438 /*  }; */
439 #define NSCALE 6
440
441 static char *noe2scale(real r3, real r6, real rmax)
442 {
443     int         i, s3, s6;
444     static char buf[NSCALE+1];
445
446     /* r goes from 0 to rmax
447        NSCALE*r/rmax goes from 0 to NSCALE
448        NSCALE - NSCALE*r/rmax goes from NSCALE to 0 */
449     s3 = NSCALE - min(NSCALE, (int)(NSCALE*r3/rmax));
450     s6 = NSCALE - min(NSCALE, (int)(NSCALE*r6/rmax));
451
452     for (i = 0; i < s3; i++)
453     {
454         buf[i] = '=';
455     }
456     for (; i < s6; i++)
457     {
458         buf[i] = '-';
459     }
460     buf[i] = '\0';
461
462     return buf;
463 }
464
465 static void calc_noe(int isize, atom_id *noe_index,
466                      real **dtot1_3, real **dtot1_6, int gnr, t_noe **noe)
467 {
468     int i, j, gi, gj;
469
470     /* make half matrix of noe-group distances from atom distances */
471     for (i = 0; i < isize; i++)
472     {
473         gi = noe_index[i];
474         for (j = i; j < isize; j++)
475         {
476             gj = noe_index[j];
477             noe[gi][gj].nr++;
478             noe[gi][gj].i_3 += pow(dtot1_3[i][j], -3);
479             noe[gi][gj].i_6 += pow(dtot1_6[i][j], -6);
480         }
481     }
482
483     /* make averages */
484     for (i = 0; i < gnr; i++)
485     {
486         for (j = i+1; j < gnr; j++)
487         {
488             noe[i][j].r_3 = pow(noe[i][j].i_3/noe[i][j].nr, -1.0/3.0);
489             noe[i][j].r_6 = pow(noe[i][j].i_6/noe[i][j].nr, -1.0/6.0);
490             noe[j][i]     = noe[i][j];
491         }
492     }
493 }
494
495 static void write_noe(FILE *fp, int gnr, t_noe **noe, t_noe_gr *noe_gr, real rmax)
496 {
497     int      i, j;
498     real     r3, r6, min3, min6;
499     char     buf[10], b3[10], b6[10];
500     t_noe_gr gri, grj;
501
502     min3 = min6 = 1e6;
503     fprintf(fp,
504             ";%4s %3s %4s %4s%3s %4s %4s %4s %4s%3s %5s %5s %8s %2s %2s %s\n",
505             "ianr", "anr", "anm", "rnm", "rnr", "ianr", "anr", "anm", "rnm", "rnr",
506             "1/r^3", "1/r^6", "intnsty", "Dr", "Da", "scale");
507     for (i = 0; i < gnr; i++)
508     {
509         gri = noe_gr[i];
510         for (j = i+1; j < gnr; j++)
511         {
512             grj  = noe_gr[j];
513             r3   = noe[i][j].r_3;
514             r6   = noe[i][j].r_6;
515             min3 = min(r3, min3);
516             min6 = min(r6, min6);
517             if (r3 < rmax || r6 < rmax)
518             {
519                 if (grj.rnr == gri.rnr)
520                 {
521                     sprintf(buf, "%2d", grj.anr-gri.anr);
522                 }
523                 else
524                 {
525                     buf[0] = '\0';
526                 }
527                 if (r3 < rmax)
528                 {
529                     sprintf(b3, "%-5.3f", r3);
530                 }
531                 else
532                 {
533                     strcpy(b3, "-");
534                 }
535                 if (r6 < rmax)
536                 {
537                     sprintf(b6, "%-5.3f", r6);
538                 }
539                 else
540                 {
541                     strcpy(b6, "-");
542                 }
543                 fprintf(fp,
544                         "%4d %4d %4s %4s%3d %4d %4d %4s %4s%3d %5s %5s %8d %2d %2s %s\n",
545                         gri.ianr+1, gri.anr+1, gri.aname, gri.rname, gri.rnr+1,
546                         grj.ianr+1, grj.anr+1, grj.aname, grj.rname, grj.rnr+1,
547                         b3, b6, (int)(noe[i][j].i_6+0.5), grj.rnr-gri.rnr, buf,
548                         noe2scale(r3, r6, rmax));
549             }
550         }
551     }
552 #define MINI ((i == 3) ? min3 : min6)
553     for (i = 3; i <= 6; i += 3)
554     {
555         if (MINI > rmax)
556         {
557             fprintf(stdout, "NOTE: no 1/r^%d averaged distances found below %g, "
558                     "smallest was %g\n", i, rmax, MINI );
559         }
560         else
561         {
562             fprintf(stdout, "Smallest 1/r^%d averaged distance was %g\n", i, MINI );
563         }
564     }
565 #undef MINI
566 }
567
568 static void calc_rms(int nind, int nframes,
569                      real **dtot, real **dtot2,
570                      real **rmsmat,  real *rmsmax,
571                      real **rmscmat, real *rmscmax,
572                      real **meanmat, real *meanmax)
573 {
574     int     i, j;
575     real    mean, mean2, rms, rmsc;
576 /* N.B. dtot and dtot2 contain the total distance and the total squared
577  * distance respectively, BUT they return RMS and the scaled RMS resp.
578  */
579
580     *rmsmax  = -1000;
581     *rmscmax = -1000;
582     *meanmax = -1000;
583
584     for (i = 0; (i < nind-1); i++)
585     {
586         for (j = i+1; (j < nind); j++)
587         {
588             mean  = dtot[i][j]/nframes;
589             mean2 = dtot2[i][j]/nframes;
590             rms   = sqrt(max(0, mean2-mean*mean));
591             rmsc  = rms/mean;
592             if (mean > *meanmax)
593             {
594                 *meanmax = mean;
595             }
596             if (rms  > *rmsmax)
597             {
598                 *rmsmax = rms;
599             }
600             if (rmsc > *rmscmax)
601             {
602                 *rmscmax = rmsc;
603             }
604             meanmat[i][j] = meanmat[j][i] = mean;
605             rmsmat[i][j]  = rmsmat[j][i] = rms;
606             rmscmat[i][j] = rmscmat[j][i] = rmsc;
607         }
608     }
609 }
610
611 real rms_diff(int natom, real **d, real **d_r)
612 {
613     int  i, j;
614     real r, r2;
615
616     r2 = 0.0;
617     for (i = 0; (i < natom-1); i++)
618     {
619         for (j = i+1; (j < natom); j++)
620         {
621             r   = d[i][j]-d_r[i][j];
622             r2 += r*r;
623         }
624     }
625     r2 /= (natom*(natom-1))/2;
626
627     return sqrt(r2);
628 }
629
630 int gmx_rmsdist(int argc, char *argv[])
631 {
632     const char     *desc[] = {
633         "[THISMODULE] computes the root mean square deviation of atom distances,",
634         "which has the advantage that no fit is needed like in standard RMS",
635         "deviation as computed by [gmx-rms].",
636         "The reference structure is taken from the structure file.",
637         "The RMSD at time t is calculated as the RMS",
638         "of the differences in distance between atom-pairs in the reference",
639         "structure and the structure at time t.[PAR]",
640         "[THISMODULE] can also produce matrices of the rms distances, rms distances",
641         "scaled with the mean distance and the mean distances and matrices with",
642         "NMR averaged distances (1/r^3 and 1/r^6 averaging). Finally, lists",
643         "of atom pairs with 1/r^3 and 1/r^6 averaged distance below the",
644         "maximum distance ([TT]-max[tt], which will default to 0.6 in this case)",
645         "can be generated, by default averaging over equivalent hydrogens",
646         "(all triplets of hydrogens named *[123]). Additionally a list of",
647         "equivalent atoms can be supplied ([TT]-equiv[tt]), each line containing",
648         "a set of equivalent atoms specified as residue number and name and",
649         "atom name; e.g.:[PAR]",
650         "[TT]3 SER  HB1 3 SER  HB2[tt][PAR]",
651         "Residue and atom names must exactly match those in the structure",
652         "file, including case. Specifying non-sequential atoms is undefined."
653
654     };
655
656     int             natom, i, j, teller, gi, gj;
657     real            t;
658
659     t_topology      top;
660     int             ePBC;
661     t_atoms        *atoms;
662     matrix          box;
663     rvec           *x;
664     FILE           *fp;
665
666     t_trxstatus    *status;
667     int             isize, gnr = 0;
668     atom_id        *index, *noe_index;
669     char           *grpname;
670     real          **d_r, **d, **dtot, **dtot2, **mean, **rms, **rmsc, *resnr;
671     real          **dtot1_3 = NULL, **dtot1_6 = NULL;
672     real            rmsnow, meanmax, rmsmax, rmscmax;
673     real            max1_3, max1_6;
674     t_noe_gr       *noe_gr = NULL;
675     t_noe         **noe    = NULL;
676     t_rgb           rlo, rhi;
677     char            buf[255];
678     gmx_bool        bRMS, bScale, bMean, bNOE, bNMR3, bNMR6, bNMR;
679
680     static int      nlevels  = 40;
681     static real     scalemax = -1.0;
682     static gmx_bool bSumH    = TRUE;
683     static gmx_bool bPBC     = TRUE;
684     output_env_t    oenv;
685
686     t_pargs         pa[] = {
687         { "-nlevels",   FALSE, etINT,  {&nlevels},
688           "Discretize RMS in this number of levels" },
689         { "-max",   FALSE, etREAL, {&scalemax},
690           "Maximum level in matrices" },
691         { "-sumh",  FALSE, etBOOL, {&bSumH},
692           "Average distance over equivalent hydrogens" },
693         { "-pbc",   FALSE, etBOOL, {&bPBC},
694           "Use periodic boundary conditions when computing distances" }
695     };
696     t_filenm        fnm[] = {
697         { efTRX, "-f",   NULL,       ffREAD },
698         { efTPS, NULL,   NULL,       ffREAD },
699         { efNDX, NULL,   NULL,       ffOPTRD },
700         { efDAT, "-equiv", "equiv",   ffOPTRD },
701         { efXVG, NULL,   "distrmsd", ffWRITE },
702         { efXPM, "-rms", "rmsdist",  ffOPTWR },
703         { efXPM, "-scl", "rmsscale", ffOPTWR },
704         { efXPM, "-mean", "rmsmean",  ffOPTWR },
705         { efXPM, "-nmr3", "nmr3",     ffOPTWR },
706         { efXPM, "-nmr6", "nmr6",     ffOPTWR },
707         { efDAT, "-noe", "noe",     ffOPTWR },
708     };
709 #define NFILE asize(fnm)
710
711     if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
712                            NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
713     {
714         return 0;
715     }
716
717     bRMS   = opt2bSet("-rms", NFILE, fnm);
718     bScale = opt2bSet("-scl", NFILE, fnm);
719     bMean  = opt2bSet("-mean", NFILE, fnm);
720     bNOE   = opt2bSet("-noe", NFILE, fnm);
721     bNMR3  = opt2bSet("-nmr3", NFILE, fnm);
722     bNMR6  = opt2bSet("-nmr6", NFILE, fnm);
723     bNMR   = bNMR3 || bNMR6 || bNOE;
724
725     max1_3 = 0;
726     max1_6 = 0;
727
728     /* check input */
729     if (bNOE && scalemax < 0)
730     {
731         scalemax = 0.6;
732         fprintf(stderr, "WARNING: using -noe without -max "
733                 "makes no sense, setting -max to %g\n\n", scalemax);
734     }
735
736     /* get topology and index */
737     read_tps_conf(ftp2fn(efTPS, NFILE, fnm), buf, &top, &ePBC, &x, NULL, box, FALSE);
738
739     if (!bPBC)
740     {
741         ePBC = epbcNONE;
742     }
743     atoms = &(top.atoms);
744
745     get_index(atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, &grpname);
746
747     /* initialize arrays */
748     snew(d, isize);
749     snew(dtot, isize);
750     snew(dtot2, isize);
751     if (bNMR)
752     {
753         snew(dtot1_3, isize);
754         snew(dtot1_6, isize);
755     }
756     snew(mean, isize);
757     snew(rms, isize);
758     snew(rmsc, isize);
759     snew(d_r, isize);
760     snew(resnr, isize);
761     for (i = 0; (i < isize); i++)
762     {
763         snew(d[i], isize);
764         snew(dtot[i], isize);
765         snew(dtot2[i], isize);
766         if (bNMR)
767         {
768             snew(dtot1_3[i], isize);
769             snew(dtot1_6[i], isize);
770         }
771         snew(mean[i], isize);
772         snew(rms[i], isize);
773         snew(rmsc[i], isize);
774         snew(d_r[i], isize);
775         resnr[i] = i+1;
776     }
777
778     /*set box type*/
779     calc_dist(isize, index, x, ePBC, box, d_r);
780     sfree(x);
781
782     /*open output files*/
783     fp = xvgropen(ftp2fn(efXVG, NFILE, fnm), "RMS Deviation", "Time (ps)", "RMSD (nm)",
784                   oenv);
785     if (output_env_get_print_xvgr_codes(oenv))
786     {
787         fprintf(fp, "@ subtitle \"of distances between %s atoms\"\n", grpname);
788     }
789
790     /*do a first step*/
791     natom = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
792
793     do
794     {
795         calc_dist_tot(isize, index, x, ePBC, box, d, dtot, dtot2, bNMR, dtot1_3, dtot1_6);
796
797         rmsnow = rms_diff(isize, d, d_r);
798         fprintf(fp, "%g  %g\n", t, rmsnow);
799     }
800     while (read_next_x(oenv, status, &t, x, box));
801     fprintf(stderr, "\n");
802
803     gmx_ffclose(fp);
804
805     teller = nframes_read(status);
806
807     close_trj(status);
808
809     calc_rms(isize, teller, dtot, dtot2, mean, &meanmax, rms, &rmsmax, rmsc, &rmscmax);
810     fprintf(stderr, "rmsmax = %g, rmscmax = %g\n", rmsmax, rmscmax);
811
812     if (bNMR)
813     {
814         calc_nmr(isize, teller, dtot1_3, dtot1_6, &max1_3, &max1_6);
815     }
816
817     if (scalemax > -1.0)
818     {
819         rmsmax  = scalemax;
820         rmscmax = scalemax;
821         meanmax = scalemax;
822         max1_3  = scalemax;
823         max1_6  = scalemax;
824     }
825
826     if (bNOE)
827     {
828         /* make list of noe atom groups */
829         snew(noe_index, isize+1);
830         snew(noe_gr, isize);
831         gnr = analyze_noe_equivalent(opt2fn_null("-equiv", NFILE, fnm),
832                                      atoms, isize, index, bSumH, noe_index, noe_gr);
833         fprintf(stdout, "Found %d non-equivalent atom-groups in %d atoms\n",
834                 gnr, isize);
835         /* make half matrix of of noe-group distances from atom distances */
836         snew(noe, gnr);
837         for (i = 0; i < gnr; i++)
838         {
839             snew(noe[i], gnr);
840         }
841         calc_noe(isize, noe_index, dtot1_3, dtot1_6, gnr, noe);
842     }
843
844     rlo.r = 1.0, rlo.g = 1.0, rlo.b = 1.0;
845     rhi.r = 0.0, rhi.g = 0.0, rhi.b = 0.0;
846
847     if (bRMS)
848     {
849         write_xpm(opt2FILE("-rms", NFILE, fnm, "w"), 0,
850                   "RMS of distance", "RMS (nm)", "Atom Index", "Atom Index",
851                   isize, isize, resnr, resnr, rms, 0.0, rmsmax, rlo, rhi, &nlevels);
852     }
853
854     if (bScale)
855     {
856         write_xpm(opt2FILE("-scl", NFILE, fnm, "w"), 0,
857                   "Relative RMS", "RMS", "Atom Index", "Atom Index",
858                   isize, isize, resnr, resnr, rmsc, 0.0, rmscmax, rlo, rhi, &nlevels);
859     }
860
861     if (bMean)
862     {
863         write_xpm(opt2FILE("-mean", NFILE, fnm, "w"), 0,
864                   "Mean Distance", "Distance (nm)", "Atom Index", "Atom Index",
865                   isize, isize, resnr, resnr, mean, 0.0, meanmax, rlo, rhi, &nlevels);
866     }
867
868     if (bNMR3)
869     {
870         write_xpm(opt2FILE("-nmr3", NFILE, fnm, "w"), 0, "1/r^3 averaged distances",
871                   "Distance (nm)", "Atom Index", "Atom Index",
872                   isize, isize, resnr, resnr, dtot1_3, 0.0, max1_3, rlo, rhi, &nlevels);
873     }
874     if (bNMR6)
875     {
876         write_xpm(opt2FILE("-nmr6", NFILE, fnm, "w"), 0, "1/r^6 averaged distances",
877                   "Distance (nm)", "Atom Index", "Atom Index",
878                   isize, isize, resnr, resnr, dtot1_6, 0.0, max1_6, rlo, rhi, &nlevels);
879     }
880
881     if (bNOE)
882     {
883         write_noe(opt2FILE("-noe", NFILE, fnm, "w"), gnr, noe, noe_gr, scalemax);
884     }
885
886     do_view(oenv, ftp2fn(efXVG, NFILE, fnm), NULL);
887
888     return 0;
889 }