c092a7a26499b9dcfee48bc364912958834ccdaf
[alexxy/gromacs.git] / src / gromacs / gmxana / gmx_mindist.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, 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 <stdlib.h>
43
44 #include "sysstuff.h"
45 #include <string.h>
46 #include "typedefs.h"
47 #include "smalloc.h"
48 #include "macros.h"
49 #include "vec.h"
50 #include "xvgr.h"
51 #include "pbc.h"
52 #include "gromacs/fileio/futil.h"
53 #include "gromacs/commandline/pargs.h"
54 #include "index.h"
55 #include "gromacs/fileio/tpxio.h"
56 #include "gromacs/fileio/trxio.h"
57 #include "rmpbc.h"
58 #include "gromacs/fileio/xtcio.h"
59 #include "gmx_ana.h"
60
61
62 static void periodic_dist(matrix box, rvec x[], int n, atom_id index[],
63                           real *rmin, real *rmax, int *min_ind)
64 {
65 #define NSHIFT 26
66     int  sx, sy, sz, i, j, s;
67     real sqr_box, r2min, r2max, r2;
68     rvec shift[NSHIFT], d0, d;
69
70     sqr_box = sqr(min(norm(box[XX]), min(norm(box[YY]), norm(box[ZZ]))));
71
72     s = 0;
73     for (sz = -1; sz <= 1; sz++)
74     {
75         for (sy = -1; sy <= 1; sy++)
76         {
77             for (sx = -1; sx <= 1; sx++)
78             {
79                 if (sx != 0 || sy != 0 || sz != 0)
80                 {
81                     for (i = 0; i < DIM; i++)
82                     {
83                         shift[s][i] = sx*box[XX][i]+sy*box[YY][i]+sz*box[ZZ][i];
84                     }
85                     s++;
86                 }
87             }
88         }
89     }
90
91     r2min = sqr_box;
92     r2max = 0;
93
94     for (i = 0; i < n; i++)
95     {
96         for (j = i+1; j < n; j++)
97         {
98             rvec_sub(x[index[i]], x[index[j]], d0);
99             r2 = norm2(d0);
100             if (r2 > r2max)
101             {
102                 r2max = r2;
103             }
104             for (s = 0; s < NSHIFT; s++)
105             {
106                 rvec_add(d0, shift[s], d);
107                 r2 = norm2(d);
108                 if (r2 < r2min)
109                 {
110                     r2min      = r2;
111                     min_ind[0] = i;
112                     min_ind[1] = j;
113                 }
114             }
115         }
116     }
117
118     *rmin = sqrt(r2min);
119     *rmax = sqrt(r2max);
120 }
121
122 static void periodic_mindist_plot(const char *trxfn, const char *outfn,
123                                   t_topology *top, int ePBC,
124                                   int n, atom_id index[], gmx_bool bSplit,
125                                   const output_env_t oenv)
126 {
127     FILE        *out;
128     const char  *leg[5] = { "min per.", "max int.", "box1", "box2", "box3" };
129     t_trxstatus *status;
130     real         t;
131     rvec        *x;
132     matrix       box;
133     int          natoms, ind_min[2] = {0, 0}, ind_mini = 0, ind_minj = 0;
134     real         r, rmin, rmax, rmint, tmint;
135     gmx_bool     bFirst;
136     gmx_rmpbc_t  gpbc = NULL;
137
138     natoms = read_first_x(oenv, &status, trxfn, &t, &x, box);
139
140     check_index(NULL, n, index, NULL, natoms);
141
142     out = xvgropen(outfn, "Minimum distance to periodic image",
143                    output_env_get_time_label(oenv), "Distance (nm)", oenv);
144     if (output_env_get_print_xvgr_codes(oenv))
145     {
146         fprintf(out, "@ subtitle \"and maximum internal distance\"\n");
147     }
148     xvgr_legend(out, 5, leg, oenv);
149
150     rmint = box[XX][XX];
151     tmint = 0;
152
153     if (NULL != top)
154     {
155         gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
156     }
157
158     bFirst = TRUE;
159     do
160     {
161         if (NULL != top)
162         {
163             gmx_rmpbc(gpbc, natoms, box, x);
164         }
165
166         periodic_dist(box, x, n, index, &rmin, &rmax, ind_min);
167         if (rmin < rmint)
168         {
169             rmint    = rmin;
170             tmint    = t;
171             ind_mini = ind_min[0];
172             ind_minj = ind_min[1];
173         }
174         if (bSplit && !bFirst && abs(t/output_env_get_time_factor(oenv)) < 1e-5)
175         {
176             fprintf(out, "&\n");
177         }
178         fprintf(out, "\t%g\t%6.3f %6.3f %6.3f %6.3f %6.3f\n",
179                 output_env_conv_time(oenv, t), rmin, rmax, norm(box[0]), norm(box[1]), norm(box[2]));
180         bFirst = FALSE;
181     }
182     while (read_next_x(oenv, status, &t, x, box));
183
184     if (NULL != top)
185     {
186         gmx_rmpbc_done(gpbc);
187     }
188
189     ffclose(out);
190
191     fprintf(stdout,
192             "\nThe shortest periodic distance is %g (nm) at time %g (%s),\n"
193             "between atoms %d and %d\n",
194             rmint, output_env_conv_time(oenv, tmint), output_env_get_time_unit(oenv),
195             index[ind_mini]+1, index[ind_minj]+1);
196 }
197
198 static void calc_dist(real rcut, gmx_bool bPBC, int ePBC, matrix box, rvec x[],
199                       int nx1, int nx2, atom_id index1[], atom_id index2[],
200                       gmx_bool bGroup,
201                       real *rmin, real *rmax, int *nmin, int *nmax,
202                       int *ixmin, int *jxmin, int *ixmax, int *jxmax)
203 {
204     int      i, j, i0 = 0, j1;
205     int      ix, jx;
206     atom_id *index3;
207     rvec     dx;
208     real     r2, rmin2, rmax2, rcut2;
209     t_pbc    pbc;
210     int      nmin_j, nmax_j;
211
212     *ixmin = -1;
213     *jxmin = -1;
214     *ixmax = -1;
215     *jxmax = -1;
216     *nmin  = 0;
217     *nmax  = 0;
218
219     rcut2 = sqr(rcut);
220
221     /* Must init pbc every step because of pressure coupling */
222     if (bPBC)
223     {
224         set_pbc(&pbc, ePBC, box);
225     }
226     if (index2)
227     {
228         i0     = 0;
229         j1     = nx2;
230         index3 = index2;
231     }
232     else
233     {
234         j1     = nx1;
235         index3 = index1;
236     }
237
238     rmin2 = 1e12;
239     rmax2 = -1e12;
240
241     for (j = 0; (j < j1); j++)
242     {
243         jx = index3[j];
244         if (index2 == NULL)
245         {
246             i0 = j + 1;
247         }
248         nmin_j = 0;
249         nmax_j = 0;
250         for (i = i0; (i < nx1); i++)
251         {
252             ix = index1[i];
253             if (ix != jx)
254             {
255                 if (bPBC)
256                 {
257                     pbc_dx(&pbc, x[ix], x[jx], dx);
258                 }
259                 else
260                 {
261                     rvec_sub(x[ix], x[jx], dx);
262                 }
263                 r2 = iprod(dx, dx);
264                 if (r2 < rmin2)
265                 {
266                     rmin2  = r2;
267                     *ixmin = ix;
268                     *jxmin = jx;
269                 }
270                 if (r2 > rmax2)
271                 {
272                     rmax2  = r2;
273                     *ixmax = ix;
274                     *jxmax = jx;
275                 }
276                 if (r2 <= rcut2)
277                 {
278                     nmin_j++;
279                 }
280                 else if (r2 > rcut2)
281                 {
282                     nmax_j++;
283                 }
284             }
285         }
286         if (bGroup)
287         {
288             if (nmin_j > 0)
289             {
290                 (*nmin)++;
291             }
292             if (nmax_j > 0)
293             {
294                 (*nmax)++;
295             }
296         }
297         else
298         {
299             *nmin += nmin_j;
300             *nmax += nmax_j;
301         }
302     }
303     *rmin = sqrt(rmin2);
304     *rmax = sqrt(rmax2);
305 }
306
307 void dist_plot(const char *fn, const char *afile, const char *dfile,
308                const char *nfile, const char *rfile, const char *xfile,
309                real rcut, gmx_bool bMat, t_atoms *atoms,
310                int ng, atom_id *index[], int gnx[], char *grpn[], gmx_bool bSplit,
311                gmx_bool bMin, int nres, atom_id *residue, gmx_bool bPBC, int ePBC,
312                gmx_bool bGroup, gmx_bool bEachResEachTime, gmx_bool bPrintResName,
313                const output_env_t oenv)
314 {
315     FILE            *atm, *dist, *num;
316     t_trxstatus     *trxout;
317     char             buf[256];
318     char           **leg;
319     real             t, dmin, dmax, **mindres = NULL, **maxdres = NULL;
320     int              nmin, nmax;
321     t_trxstatus     *status;
322     int              i = -1, j, k, natoms;
323     int              min1, min2, max1, max2, min1r, min2r, max1r, max2r;
324     atom_id          oindex[2];
325     rvec            *x0;
326     matrix           box;
327     t_trxframe       frout;
328     gmx_bool         bFirst;
329     FILE            *respertime = NULL;
330
331     if ((natoms = read_first_x(oenv, &status, fn, &t, &x0, box)) == 0)
332     {
333         gmx_fatal(FARGS, "Could not read coordinates from statusfile\n");
334     }
335
336     sprintf(buf, "%simum Distance", bMin ? "Min" : "Max");
337     dist = xvgropen(dfile, buf, output_env_get_time_label(oenv), "Distance (nm)", oenv);
338     sprintf(buf, "Number of Contacts %s %g nm", bMin ? "<" : ">", rcut);
339     num    = nfile ? xvgropen(nfile, buf, output_env_get_time_label(oenv), "Number", oenv) : NULL;
340     atm    = afile ? ffopen(afile, "w") : NULL;
341     trxout = xfile ? open_trx(xfile, "w") : NULL;
342
343     if (bMat)
344     {
345         if (ng == 1)
346         {
347             snew(leg, 1);
348             sprintf(buf, "Internal in %s", grpn[0]);
349             leg[0] = strdup(buf);
350             xvgr_legend(dist, 0, (const char**)leg, oenv);
351             if (num)
352             {
353                 xvgr_legend(num, 0, (const char**)leg, oenv);
354             }
355         }
356         else
357         {
358             snew(leg, (ng*(ng-1))/2);
359             for (i = j = 0; (i < ng-1); i++)
360             {
361                 for (k = i+1; (k < ng); k++, j++)
362                 {
363                     sprintf(buf, "%s-%s", grpn[i], grpn[k]);
364                     leg[j] = strdup(buf);
365                 }
366             }
367             xvgr_legend(dist, j, (const char**)leg, oenv);
368             if (num)
369             {
370                 xvgr_legend(num, j, (const char**)leg, oenv);
371             }
372         }
373     }
374     else
375     {
376         snew(leg, ng-1);
377         for (i = 0; (i < ng-1); i++)
378         {
379             sprintf(buf, "%s-%s", grpn[0], grpn[i+1]);
380             leg[i] = strdup(buf);
381         }
382         xvgr_legend(dist, ng-1, (const char**)leg, oenv);
383         if (num)
384         {
385             xvgr_legend(num, ng-1, (const char**)leg, oenv);
386         }
387     }
388
389     if (bEachResEachTime)
390     {
391         sprintf(buf, "%simum Distance", bMin ? "Min" : "Max");
392         respertime = xvgropen(rfile, buf, output_env_get_time_label(oenv), "Distance (nm)", oenv);
393         xvgr_legend(respertime, ng-1, (const char**)leg, oenv);
394         if (bPrintResName)
395         {
396             fprintf(respertime, "# ");
397         }
398         for (j = 0; j < nres; j++)
399         {
400             fprintf(respertime, "%s%d ", *(atoms->resinfo[atoms->atom[index[0][residue[j]]].resind].name), atoms->atom[index[0][residue[j]]].resind);
401         }
402         fprintf(respertime, "\n");
403
404     }
405
406     j = 0;
407     if (nres)
408     {
409         snew(mindres, ng-1);
410         snew(maxdres, ng-1);
411         for (i = 1; i < ng; i++)
412         {
413             snew(mindres[i-1], nres);
414             snew(maxdres[i-1], nres);
415             for (j = 0; j < nres; j++)
416             {
417                 mindres[i-1][j] = 1e6;
418             }
419             /* maxdres[*][*] is already 0 */
420         }
421     }
422     bFirst = TRUE;
423     do
424     {
425         if (bSplit && !bFirst && abs(t/output_env_get_time_factor(oenv)) < 1e-5)
426         {
427             fprintf(dist, "&\n");
428             if (num)
429             {
430                 fprintf(num, "&\n");
431             }
432             if (atm)
433             {
434                 fprintf(atm, "&\n");
435             }
436         }
437         fprintf(dist, "%12e", output_env_conv_time(oenv, t));
438         if (num)
439         {
440             fprintf(num, "%12e", output_env_conv_time(oenv, t));
441         }
442
443         if (bMat)
444         {
445             if (ng == 1)
446             {
447                 calc_dist(rcut, bPBC, ePBC, box, x0, gnx[0], gnx[0], index[0], index[0], bGroup,
448                           &dmin, &dmax, &nmin, &nmax, &min1, &min2, &max1, &max2);
449                 fprintf(dist, "  %12e", bMin ? dmin : dmax);
450                 if (num)
451                 {
452                     fprintf(num, "  %8d", bMin ? nmin : nmax);
453                 }
454             }
455             else
456             {
457                 for (i = 0; (i < ng-1); i++)
458                 {
459                     for (k = i+1; (k < ng); k++)
460                     {
461                         calc_dist(rcut, bPBC, ePBC, box, x0, gnx[i], gnx[k], index[i], index[k],
462                                   bGroup, &dmin, &dmax, &nmin, &nmax, &min1, &min2, &max1, &max2);
463                         fprintf(dist, "  %12e", bMin ? dmin : dmax);
464                         if (num)
465                         {
466                             fprintf(num, "  %8d", bMin ? nmin : nmax);
467                         }
468                     }
469                 }
470             }
471         }
472         else
473         {
474             for (i = 1; (i < ng); i++)
475             {
476                 calc_dist(rcut, bPBC, ePBC, box, x0, gnx[0], gnx[i], index[0], index[i], bGroup,
477                           &dmin, &dmax, &nmin, &nmax, &min1, &min2, &max1, &max2);
478                 fprintf(dist, "  %12e", bMin ? dmin : dmax);
479                 if (num)
480                 {
481                     fprintf(num, "  %8d", bMin ? nmin : nmax);
482                 }
483                 if (nres)
484                 {
485                     for (j = 0; j < nres; j++)
486                     {
487                         calc_dist(rcut, bPBC, ePBC, box, x0, residue[j+1]-residue[j], gnx[i],
488                                   &(index[0][residue[j]]), index[i], bGroup,
489                                   &dmin, &dmax, &nmin, &nmax, &min1r, &min2r, &max1r, &max2r);
490                         mindres[i-1][j] = min(mindres[i-1][j], dmin);
491                         maxdres[i-1][j] = max(maxdres[i-1][j], dmax);
492                     }
493                 }
494             }
495         }
496         fprintf(dist, "\n");
497         if (num)
498         {
499             fprintf(num, "\n");
500         }
501         if ( (bMin ? min1 : max1) != -1)
502         {
503             if (atm)
504             {
505                 fprintf(atm, "%12e  %12d  %12d\n",
506                         output_env_conv_time(oenv, t), 1+(bMin ? min1 : max1),
507                         1+(bMin ? min2 : max2));
508             }
509         }
510
511         if (trxout)
512         {
513             oindex[0] = bMin ? min1 : max1;
514             oindex[1] = bMin ? min2 : max2;
515             write_trx(trxout, 2, oindex, atoms, i, t, box, x0, NULL, NULL);
516         }
517         bFirst = FALSE;
518         /*dmin should be minimum distance for residue and group*/
519         if (bEachResEachTime)
520         {
521             fprintf(respertime, "%12e", t);
522             for (i = 1; i < ng; i++)
523             {
524                 for (j = 0; j < nres; j++)
525                 {
526                     fprintf(respertime, " %7g", bMin ? mindres[i-1][j] : maxdres[i-1][j]);
527                     /*reset distances for next time point*/
528                     mindres[i-1][j] = 1e6;
529                     maxdres[i-1][j] = 0;
530                 }
531             }
532             fprintf(respertime, "\n");
533         }
534     }
535     while (read_next_x(oenv, status, &t, x0, box));
536
537     close_trj(status);
538     ffclose(dist);
539     if (num)
540     {
541         ffclose(num);
542     }
543     if (atm)
544     {
545         ffclose(atm);
546     }
547     if (trxout)
548     {
549         close_trx(trxout);
550     }
551
552     if (nres && !bEachResEachTime)
553     {
554         FILE *res;
555
556         sprintf(buf, "%simum Distance", bMin ? "Min" : "Max");
557         res = xvgropen(rfile, buf, "Residue (#)", "Distance (nm)", oenv);
558         xvgr_legend(res, ng-1, (const char**)leg, oenv);
559         for (j = 0; j < nres; j++)
560         {
561             fprintf(res, "%4d", j+1);
562             for (i = 1; i < ng; i++)
563             {
564                 fprintf(res, " %7g", bMin ? mindres[i-1][j] : maxdres[i-1][j]);
565             }
566             fprintf(res, "\n");
567         }
568     }
569
570     sfree(x0);
571 }
572
573 int find_residues(t_atoms *atoms, int n, atom_id index[], atom_id **resindex)
574 {
575     int  i;
576     int  nres = 0, resnr, presnr;
577     int *residx;
578
579     /* build index of first atom numbers for each residue */
580     presnr = NOTSET;
581     snew(residx, atoms->nres+1);
582     for (i = 0; i < n; i++)
583     {
584         resnr = atoms->atom[index[i]].resind;
585         if (resnr != presnr)
586         {
587             residx[nres] = i;
588             nres++;
589             presnr = resnr;
590         }
591     }
592     if (debug)
593     {
594         printf("Found %d residues out of %d (%d/%d atoms)\n",
595                nres, atoms->nres, atoms->nr, n);
596     }
597     srenew(residx, nres+1);
598     /* mark end of last residue */
599     residx[nres] = n;
600     *resindex    = residx;
601     return nres;
602 }
603
604 void dump_res(FILE *out, int nres, atom_id *resindex, atom_id index[])
605 {
606     int i, j;
607
608     for (i = 0; i < nres-1; i++)
609     {
610         fprintf(out, "Res %d (%d):", i, resindex[i+1]-resindex[i]);
611         for (j = resindex[i]; j < resindex[i+1]; j++)
612         {
613             fprintf(out, " %d(%d)", j, index[j]);
614         }
615         fprintf(out, "\n");
616     }
617 }
618
619 int gmx_mindist(int argc, char *argv[])
620 {
621     const char     *desc[] = {
622         "[THISMODULE] computes the distance between one group and a number of",
623         "other groups. Both the minimum distance",
624         "(between any pair of atoms from the respective groups)",
625         "and the number of contacts within a given",
626         "distance are written to two separate output files.",
627         "With the [TT]-group[tt] option a contact of an atom in another group",
628         "with multiple atoms in the first group is counted as one contact",
629         "instead of as multiple contacts.",
630         "With [TT]-or[tt], minimum distances to each residue in the first",
631         "group are determined and plotted as a function of residue number.[PAR]",
632         "With option [TT]-pi[tt] the minimum distance of a group to its",
633         "periodic image is plotted. This is useful for checking if a protein",
634         "has seen its periodic image during a simulation. Only one shift in",
635         "each direction is considered, giving a total of 26 shifts.",
636         "It also plots the maximum distance within the group and the lengths",
637         "of the three box vectors.[PAR]",
638         "Also [gmx-distance] calculates distances."
639     };
640     const char     *bugs[] = {
641         "The [TT]-pi[tt] option is very slow."
642     };
643
644     static gmx_bool bMat             = FALSE, bPI = FALSE, bSplit = FALSE, bMax = FALSE, bPBC = TRUE;
645     static gmx_bool bGroup           = FALSE;
646     static real     rcutoff          = 0.6;
647     static int      ng               = 1;
648     static gmx_bool bEachResEachTime = FALSE, bPrintResName = FALSE;
649     t_pargs         pa[]             = {
650         { "-matrix", FALSE, etBOOL, {&bMat},
651           "Calculate half a matrix of group-group distances" },
652         { "-max",    FALSE, etBOOL, {&bMax},
653           "Calculate *maximum* distance instead of minimum" },
654         { "-d",      FALSE, etREAL, {&rcutoff},
655           "Distance for contacts" },
656         { "-group",      FALSE, etBOOL, {&bGroup},
657           "Count contacts with multiple atoms in the first group as one" },
658         { "-pi",     FALSE, etBOOL, {&bPI},
659           "Calculate minimum distance with periodic images" },
660         { "-split",  FALSE, etBOOL, {&bSplit},
661           "Split graph where time is zero" },
662         { "-ng",       FALSE, etINT, {&ng},
663           "Number of secondary groups to compute distance to a central group" },
664         { "-pbc",    FALSE, etBOOL, {&bPBC},
665           "Take periodic boundary conditions into account" },
666         { "-respertime",  FALSE, etBOOL, {&bEachResEachTime},
667           "When writing per-residue distances, write distance for each time point" },
668         { "-printresname",  FALSE, etBOOL, {&bPrintResName},
669           "Write residue names" }
670     };
671     output_env_t    oenv;
672     t_topology     *top  = NULL;
673     int             ePBC = -1;
674     char            title[256];
675     real            t;
676     rvec           *x;
677     matrix          box;
678     gmx_bool        bTop = FALSE;
679
680     FILE           *atm;
681     int             i, j, nres = 0;
682     const char     *trxfnm, *tpsfnm, *ndxfnm, *distfnm, *numfnm, *atmfnm, *oxfnm, *resfnm;
683     char          **grpname;
684     int            *gnx;
685     atom_id       **index, *residues = NULL;
686     t_filenm        fnm[] = {
687         { efTRX, "-f",  NULL,      ffREAD },
688         { efTPS,  NULL, NULL,      ffOPTRD },
689         { efNDX,  NULL, NULL,      ffOPTRD },
690         { efXVG, "-od", "mindist",  ffWRITE },
691         { efXVG, "-on", "numcont",  ffOPTWR },
692         { efOUT, "-o", "atm-pair", ffOPTWR },
693         { efTRO, "-ox", "mindist",  ffOPTWR },
694         { efXVG, "-or", "mindistres", ffOPTWR }
695     };
696 #define NFILE asize(fnm)
697
698     if (!parse_common_args(&argc, argv,
699                            PCA_CAN_VIEW | PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
700                            NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
701     {
702         return 0;
703     }
704
705     trxfnm  = ftp2fn(efTRX, NFILE, fnm);
706     ndxfnm  = ftp2fn_null(efNDX, NFILE, fnm);
707     distfnm = opt2fn("-od", NFILE, fnm);
708     numfnm  = opt2fn_null("-on", NFILE, fnm);
709     atmfnm  = ftp2fn_null(efOUT, NFILE, fnm);
710     oxfnm   = opt2fn_null("-ox", NFILE, fnm);
711     resfnm  = opt2fn_null("-or", NFILE, fnm);
712     if (bPI || resfnm != NULL)
713     {
714         /* We need a tps file */
715         tpsfnm = ftp2fn(efTPS, NFILE, fnm);
716     }
717     else
718     {
719         tpsfnm = ftp2fn_null(efTPS, NFILE, fnm);
720     }
721
722     if (!tpsfnm && !ndxfnm)
723     {
724         gmx_fatal(FARGS, "You have to specify either the index file or a tpr file");
725     }
726
727     if (bPI)
728     {
729         ng = 1;
730         fprintf(stderr, "Choose a group for distance calculation\n");
731     }
732     else if (!bMat)
733     {
734         ng++;
735     }
736
737     snew(gnx, ng);
738     snew(index, ng);
739     snew(grpname, ng);
740
741     if (tpsfnm || resfnm || !ndxfnm)
742     {
743         snew(top, 1);
744         bTop = read_tps_conf(tpsfnm, title, top, &ePBC, &x, NULL, box, FALSE);
745         if (bPI && !bTop)
746         {
747             printf("\nWARNING: Without a run input file a trajectory with broken molecules will not give the correct periodic image distance\n\n");
748         }
749     }
750     get_index(top ? &(top->atoms) : NULL, ndxfnm, ng, gnx, index, grpname);
751
752     if (bMat && (ng == 1))
753     {
754         ng = gnx[0];
755         printf("Special case: making distance matrix between all atoms in group %s\n",
756                grpname[0]);
757         srenew(gnx, ng);
758         srenew(index, ng);
759         srenew(grpname, ng);
760         for (i = 1; (i < ng); i++)
761         {
762             gnx[i]      = 1;
763             grpname[i]  = grpname[0];
764             snew(index[i], 1);
765             index[i][0] = index[0][i];
766         }
767         gnx[0] = 1;
768     }
769
770     if (resfnm)
771     {
772         nres = find_residues(top ? &(top->atoms) : NULL,
773                              gnx[0], index[0], &residues);
774         if (debug)
775         {
776             dump_res(debug, nres, residues, index[0]);
777         }
778     }
779
780     if (bPI)
781     {
782         periodic_mindist_plot(trxfnm, distfnm, top, ePBC, gnx[0], index[0], bSplit, oenv);
783     }
784     else
785     {
786         dist_plot(trxfnm, atmfnm, distfnm, numfnm, resfnm, oxfnm,
787                   rcutoff, bMat, top ? &(top->atoms) : NULL,
788                   ng, index, gnx, grpname, bSplit, !bMax, nres, residues, bPBC, ePBC,
789                   bGroup, bEachResEachTime, bPrintResName, oenv);
790     }
791
792     do_view(oenv, distfnm, "-nxy");
793     if (!bPI)
794     {
795         do_view(oenv, numfnm, "-nxy");
796     }
797
798     return 0;
799 }