Move gmx_sort.* to src/gromacs/utility/
[alexxy/gromacs.git] / src / gromacs / gmxana / gmx_trjconv.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 <string.h>
42 #include <math.h>
43
44 #include "copyrite.h"
45 #include "macros.h"
46 #include "sysstuff.h"
47 #include "smalloc.h"
48 #include "typedefs.h"
49 #include "gromacs/fileio/gmxfio.h"
50 #include "gromacs/fileio/tpxio.h"
51 #include "gromacs/fileio/trxio.h"
52 #include "gromacs/fileio/trnio.h"
53 #include "gromacs/commandline/pargs.h"
54 #include "gromacs/fileio/futil.h"
55 #include "gromacs/fileio/pdbio.h"
56 #include "gromacs/fileio/confio.h"
57 #include "names.h"
58 #include "index.h"
59 #include "vec.h"
60 #include "gromacs/fileio/xtcio.h"
61 #include "do_fit.h"
62 #include "rmpbc.h"
63 #include "gromacs/fileio/g87io.h"
64 #include "pbc.h"
65 #include "viewit.h"
66 #include "xvgr.h"
67 #include "gmx_ana.h"
68
69 #ifdef HAVE_UNISTD_H
70 #include <unistd.h>
71 #endif
72
73 enum {
74     euSel, euRect, euTric, euCompact, euNR
75 };
76
77
78 static void calc_pbc_cluster(int ecenter, int nrefat, t_topology *top, int ePBC,
79                              rvec x[], atom_id index[], matrix box)
80 {
81     int       m, i, j, j0, j1, jj, ai, aj;
82     int       imin, jmin;
83     real      fac, min_dist2;
84     rvec      dx, xtest, box_center;
85     int       nmol, imol_center;
86     atom_id  *molind;
87     gmx_bool *bMol, *bTmp;
88     rvec     *m_com, *m_shift;
89     t_pbc     pbc;
90     real     *com_dist2;
91     int      *cluster;
92     int      *added;
93     int       ncluster, nadded;
94     real      tmp_r2;
95
96     calc_box_center(ecenter, box, box_center);
97
98     /* Initiate the pbc structure */
99     memset(&pbc, 0, sizeof(pbc));
100     set_pbc(&pbc, ePBC, box);
101
102     /* Convert atom index to molecular */
103     nmol   = top->mols.nr;
104     molind = top->mols.index;
105     snew(bMol, nmol);
106     snew(m_com, nmol);
107     snew(m_shift, nmol);
108     snew(cluster, nmol);
109     snew(added, nmol);
110     snew(bTmp, top->atoms.nr);
111
112     for (i = 0; (i < nrefat); i++)
113     {
114         /* Mark all molecules in the index */
115         ai       = index[i];
116         bTmp[ai] = TRUE;
117         /* Binary search assuming the molecules are sorted */
118         j0 = 0;
119         j1 = nmol-1;
120         while (j0 < j1)
121         {
122             if (ai < molind[j0+1])
123             {
124                 j1 = j0;
125             }
126             else if (ai >= molind[j1])
127             {
128                 j0 = j1;
129             }
130             else
131             {
132                 jj = (j0+j1)/2;
133                 if (ai < molind[jj+1])
134                 {
135                     j1 = jj;
136                 }
137                 else
138                 {
139                     j0 = jj;
140                 }
141             }
142         }
143         bMol[j0] = TRUE;
144     }
145     /* Double check whether all atoms in all molecules that are marked are part
146      * of the cluster. Simultaneously compute the center of geometry.
147      */
148     min_dist2   = 10*sqr(trace(box));
149     imol_center = -1;
150     ncluster    = 0;
151     for (i = 0; i < nmol; i++)
152     {
153         for (j = molind[i]; j < molind[i+1]; j++)
154         {
155             if (bMol[i] && !bTmp[j])
156             {
157                 gmx_fatal(FARGS, "Molecule %d marked for clustering but not atom %d in it - check your index!", i+1, j+1);
158             }
159             else if (!bMol[i] && bTmp[j])
160             {
161                 gmx_fatal(FARGS, "Atom %d marked for clustering but not molecule %d - this is an internal error...", j+1, i+1);
162             }
163             else if (bMol[i])
164             {
165                 /* Make molecule whole, move 2nd and higher atom to same periodicity as 1st atom in molecule */
166                 if (j > molind[i])
167                 {
168                     pbc_dx(&pbc, x[j], x[j-1], dx);
169                     rvec_add(x[j-1], dx, x[j]);
170                 }
171                 /* Compute center of geometry of molecule - m_com[i] was zeroed when we did snew() on it! */
172                 rvec_inc(m_com[i], x[j]);
173             }
174         }
175         if (bMol[i])
176         {
177             /* Normalize center of geometry */
178             fac = 1.0/(molind[i+1]-molind[i]);
179             for (m = 0; (m < DIM); m++)
180             {
181                 m_com[i][m] *= fac;
182             }
183             /* Determine which molecule is closest to the center of the box */
184             pbc_dx(&pbc, box_center, m_com[i], dx);
185             tmp_r2 = iprod(dx, dx);
186
187             if (tmp_r2 < min_dist2)
188             {
189                 min_dist2   = tmp_r2;
190                 imol_center = i;
191             }
192             cluster[ncluster++] = i;
193         }
194     }
195     sfree(bTmp);
196
197     if (ncluster <= 0)
198     {
199         fprintf(stderr, "No molecules selected in the cluster\n");
200         return;
201     }
202     else if (imol_center == -1)
203     {
204         fprintf(stderr, "No central molecules could be found\n");
205         return;
206     }
207
208     nadded            = 0;
209     added[nadded++]   = imol_center;
210     bMol[imol_center] = FALSE;
211
212     while (nadded < ncluster)
213     {
214         /* Find min distance between cluster molecules and those remaining to be added */
215         min_dist2   = 10*sqr(trace(box));
216         imin        = -1;
217         jmin        = -1;
218         /* Loop over added mols */
219         for (i = 0; i < nadded; i++)
220         {
221             ai = added[i];
222             /* Loop over all mols */
223             for (j = 0; j < ncluster; j++)
224             {
225                 aj = cluster[j];
226                 /* check those remaining to be added */
227                 if (bMol[aj])
228                 {
229                     pbc_dx(&pbc, m_com[aj], m_com[ai], dx);
230                     tmp_r2 = iprod(dx, dx);
231                     if (tmp_r2 < min_dist2)
232                     {
233                         min_dist2   = tmp_r2;
234                         imin        = ai;
235                         jmin        = aj;
236                     }
237                 }
238             }
239         }
240
241         /* Add the best molecule */
242         added[nadded++]   = jmin;
243         bMol[jmin]        = FALSE;
244         /* Calculate the shift from the ai molecule */
245         pbc_dx(&pbc, m_com[jmin], m_com[imin], dx);
246         rvec_add(m_com[imin], dx, xtest);
247         rvec_sub(xtest, m_com[jmin], m_shift[jmin]);
248         rvec_inc(m_com[jmin], m_shift[jmin]);
249
250         for (j = molind[jmin]; j < molind[jmin+1]; j++)
251         {
252             rvec_inc(x[j], m_shift[jmin]);
253         }
254         fprintf(stdout, "\rClustering iteration %d of %d...", nadded, ncluster);
255         fflush(stdout);
256     }
257
258     sfree(added);
259     sfree(cluster);
260     sfree(bMol);
261     sfree(m_com);
262     sfree(m_shift);
263
264     fprintf(stdout, "\n");
265 }
266
267 static void put_molecule_com_in_box(int unitcell_enum, int ecenter,
268                                     t_block *mols,
269                                     int natoms, t_atom atom[],
270                                     int ePBC, matrix box, rvec x[])
271 {
272     atom_id i, j;
273     int     d;
274     rvec    com, new_com, shift, dx, box_center;
275     real    m;
276     double  mtot;
277     t_pbc   pbc;
278
279     calc_box_center(ecenter, box, box_center);
280     set_pbc(&pbc, ePBC, box);
281     if (mols->nr <= 0)
282     {
283         gmx_fatal(FARGS, "There are no molecule descriptions. I need a .tpr file for this pbc option.");
284     }
285     for (i = 0; (i < mols->nr); i++)
286     {
287         /* calc COM */
288         clear_rvec(com);
289         mtot = 0;
290         for (j = mols->index[i]; (j < mols->index[i+1] && j < natoms); j++)
291         {
292             m = atom[j].m;
293             for (d = 0; d < DIM; d++)
294             {
295                 com[d] += m*x[j][d];
296             }
297             mtot += m;
298         }
299         /* calculate final COM */
300         svmul(1.0/mtot, com, com);
301
302         /* check if COM is outside box */
303         copy_rvec(com, new_com);
304         switch (unitcell_enum)
305         {
306             case euRect:
307                 put_atoms_in_box(ePBC, box, 1, &new_com);
308                 break;
309             case euTric:
310                 put_atoms_in_triclinic_unitcell(ecenter, box, 1, &new_com);
311                 break;
312             case euCompact:
313                 put_atoms_in_compact_unitcell(ePBC, ecenter, box, 1, &new_com);
314                 break;
315         }
316         rvec_sub(new_com, com, shift);
317         if (norm2(shift) > 0)
318         {
319             if (debug)
320             {
321                 fprintf(debug, "\nShifting position of molecule %d "
322                         "by %8.3f  %8.3f  %8.3f\n", i+1,
323                         shift[XX], shift[YY], shift[ZZ]);
324             }
325             for (j = mols->index[i]; (j < mols->index[i+1] && j < natoms); j++)
326             {
327                 rvec_inc(x[j], shift);
328             }
329         }
330     }
331 }
332
333 static void put_residue_com_in_box(int unitcell_enum, int ecenter,
334                                    int natoms, t_atom atom[],
335                                    int ePBC, matrix box, rvec x[])
336 {
337     atom_id i, j, res_start, res_end, res_nat;
338     int     d, presnr;
339     real    m;
340     double  mtot;
341     rvec    box_center, com, new_com, shift;
342
343     calc_box_center(ecenter, box, box_center);
344
345     presnr    = NOTSET;
346     res_start = 0;
347     clear_rvec(com);
348     mtot = 0;
349     for (i = 0; i < natoms+1; i++)
350     {
351         if (i == natoms || (presnr != atom[i].resind && presnr != NOTSET))
352         {
353             /* calculate final COM */
354             res_end = i;
355             res_nat = res_end - res_start;
356             svmul(1.0/mtot, com, com);
357
358             /* check if COM is outside box */
359             copy_rvec(com, new_com);
360             switch (unitcell_enum)
361             {
362                 case euRect:
363                     put_atoms_in_box(ePBC, box, 1, &new_com);
364                     break;
365                 case euTric:
366                     put_atoms_in_triclinic_unitcell(ecenter, box, 1, &new_com);
367                     break;
368                 case euCompact:
369                     put_atoms_in_compact_unitcell(ePBC, ecenter, box, 1, &new_com);
370                     break;
371             }
372             rvec_sub(new_com, com, shift);
373             if (norm2(shift))
374             {
375                 if (debug)
376                 {
377                     fprintf(debug, "\nShifting position of residue %d (atoms %u-%u) "
378                             "by %g,%g,%g\n", atom[res_start].resind+1,
379                             res_start+1, res_end+1, shift[XX], shift[YY], shift[ZZ]);
380                 }
381                 for (j = res_start; j < res_end; j++)
382                 {
383                     rvec_inc(x[j], shift);
384                 }
385             }
386             clear_rvec(com);
387             mtot = 0;
388
389             /* remember start of new residue */
390             res_start = i;
391         }
392         if (i < natoms)
393         {
394             /* calc COM */
395             m = atom[i].m;
396             for (d = 0; d < DIM; d++)
397             {
398                 com[d] += m*x[i][d];
399             }
400             mtot += m;
401
402             presnr = atom[i].resind;
403         }
404     }
405 }
406
407 static void center_x(int ecenter, rvec x[], matrix box, int n, int nc, atom_id ci[])
408 {
409     int  i, m, ai;
410     rvec cmin, cmax, box_center, dx;
411
412     if (nc > 0)
413     {
414         copy_rvec(x[ci[0]], cmin);
415         copy_rvec(x[ci[0]], cmax);
416         for (i = 0; i < nc; i++)
417         {
418             ai = ci[i];
419             for (m = 0; m < DIM; m++)
420             {
421                 if (x[ai][m] < cmin[m])
422                 {
423                     cmin[m] = x[ai][m];
424                 }
425                 else if (x[ai][m] > cmax[m])
426                 {
427                     cmax[m] = x[ai][m];
428                 }
429             }
430         }
431         calc_box_center(ecenter, box, box_center);
432         for (m = 0; m < DIM; m++)
433         {
434             dx[m] = box_center[m]-(cmin[m]+cmax[m])*0.5;
435         }
436
437         for (i = 0; i < n; i++)
438         {
439             rvec_inc(x[i], dx);
440         }
441     }
442 }
443
444 static void mk_filenm(char *base, const char *ext, int ndigit, int file_nr,
445                       char out_file[])
446 {
447     char nbuf[128];
448     int  nd = 0, fnr;
449
450     strcpy(out_file, base);
451     fnr = file_nr;
452     do
453     {
454         fnr /= 10;
455         nd++;
456     }
457     while (fnr > 0);
458
459     if (nd < ndigit)
460     {
461         strncat(out_file, "00000000000", ndigit-nd);
462     }
463     sprintf(nbuf, "%d.", file_nr);
464     strcat(out_file, nbuf);
465     strcat(out_file, ext);
466 }
467
468 void check_trn(const char *fn)
469 {
470     if ((fn2ftp(fn) != efTRJ)  && (fn2ftp(fn) != efTRR))
471     {
472         gmx_fatal(FARGS, "%s is not a trajectory file, exiting\n", fn);
473     }
474 }
475
476 #ifndef GMX_NATIVE_WINDOWS
477 void do_trunc(const char *fn, real t0)
478 {
479     t_fileio        *in;
480     FILE            *fp;
481     gmx_bool         bStop, bOK;
482     t_trnheader      sh;
483     gmx_off_t        fpos;
484     char             yesno[256];
485     int              j;
486     real             t = 0;
487
488     if (t0 == -1)
489     {
490         gmx_fatal(FARGS, "You forgot to set the truncation time");
491     }
492
493     /* Check whether this is a .trj file */
494     check_trn(fn);
495
496     in   = open_trn(fn, "r");
497     fp   = gmx_fio_getfp(in);
498     if (fp == NULL)
499     {
500         fprintf(stderr, "Sorry, can not trunc %s, truncation of this filetype is not supported\n", fn);
501         close_trn(in);
502     }
503     else
504     {
505         j     = 0;
506         fpos  = gmx_fio_ftell(in);
507         bStop = FALSE;
508         while (!bStop && fread_trnheader(in, &sh, &bOK))
509         {
510             fread_htrn(in, &sh, NULL, NULL, NULL, NULL);
511             fpos = gmx_ftell(fp);
512             t    = sh.t;
513             if (t >= t0)
514             {
515                 gmx_fseek(fp, fpos, SEEK_SET);
516                 bStop = TRUE;
517             }
518         }
519         if (bStop)
520         {
521             fprintf(stderr, "Do you REALLY want to truncate this trajectory (%s) at:\n"
522                     "frame %d, time %g, bytes %ld ??? (type YES if so)\n",
523                     fn, j, t, (long int)fpos);
524             if (1 != scanf("%s", yesno))
525             {
526                 gmx_fatal(FARGS, "Error reading user input");
527             }
528             if (strcmp(yesno, "YES") == 0)
529             {
530                 fprintf(stderr, "Once again, I'm gonna DO this...\n");
531                 close_trn(in);
532                 if (0 != truncate(fn, fpos))
533                 {
534                     gmx_fatal(FARGS, "Error truncating file %s", fn);
535                 }
536             }
537             else
538             {
539                 fprintf(stderr, "Ok, I'll forget about it\n");
540             }
541         }
542         else
543         {
544             fprintf(stderr, "Already at end of file (t=%g)...\n", t);
545             close_trn(in);
546         }
547     }
548 }
549 #endif
550
551 int gmx_trjconv(int argc, char *argv[])
552 {
553     const char *desc[] = {
554         "[THISMODULE] can convert trajectory files in many ways:[BR]",
555         "[BB]1.[bb] from one format to another[BR]",
556         "[BB]2.[bb] select a subset of atoms[BR]",
557         "[BB]3.[bb] change the periodicity representation[BR]",
558         "[BB]4.[bb] keep multimeric molecules together[BR]",
559         "[BB]5.[bb] center atoms in the box[BR]",
560         "[BB]6.[bb] fit atoms to reference structure[BR]",
561         "[BB]7.[bb] reduce the number of frames[BR]",
562         "[BB]8.[bb] change the timestamps of the frames ",
563         "([TT]-t0[tt] and [TT]-timestep[tt])[BR]",
564         "[BB]9.[bb] cut the trajectory in small subtrajectories according",
565         "to information in an index file. This allows subsequent analysis of",
566         "the subtrajectories that could, for example, be the result of a",
567         "cluster analysis. Use option [TT]-sub[tt].",
568         "This assumes that the entries in the index file are frame numbers and",
569         "dumps each group in the index file to a separate trajectory file.[BR]",
570         "[BB]10.[bb] select frames within a certain range of a quantity given",
571         "in an [TT].xvg[tt] file.[PAR]",
572
573         "[gmx-trjcat] is better suited for concatenating multiple trajectory files.",
574         "[PAR]",
575
576         "Currently seven formats are supported for input and output:",
577         "[TT].xtc[tt], [TT].trr[tt], [TT].trj[tt], [TT].gro[tt], [TT].g96[tt],",
578         "[TT].pdb[tt] and [TT].g87[tt].",
579         "The file formats are detected from the file extension.",
580         "The precision of [TT].xtc[tt] and [TT].gro[tt] output is taken from the",
581         "input file for [TT].xtc[tt], [TT].gro[tt] and [TT].pdb[tt],",
582         "and from the [TT]-ndec[tt] option for other input formats. The precision",
583         "is always taken from [TT]-ndec[tt], when this option is set.",
584         "All other formats have fixed precision. [TT].trr[tt] and [TT].trj[tt]",
585         "output can be single or double precision, depending on the precision",
586         "of the [THISMODULE] binary.",
587         "Note that velocities are only supported in",
588         "[TT].trr[tt], [TT].trj[tt], [TT].gro[tt] and [TT].g96[tt] files.[PAR]",
589
590         "Option [TT]-app[tt] can be used to",
591         "append output to an existing trajectory file.",
592         "No checks are performed to ensure integrity",
593         "of the resulting combined trajectory file.[PAR]",
594
595         "Option [TT]-sep[tt] can be used to write every frame to a separate",
596         "[TT].gro, .g96[tt] or [TT].pdb[tt] file. By default, all frames all written to one file.",
597         "[TT].pdb[tt] files with all frames concatenated can be viewed with",
598         "[TT]rasmol -nmrpdb[tt].[PAR]",
599
600         "It is possible to select part of your trajectory and write it out",
601         "to a new trajectory file in order to save disk space, e.g. for leaving",
602         "out the water from a trajectory of a protein in water.",
603         "[BB]ALWAYS[bb] put the original trajectory on tape!",
604         "We recommend to use the portable [TT].xtc[tt] format for your analysis",
605         "to save disk space and to have portable files.[PAR]",
606
607         "There are two options for fitting the trajectory to a reference",
608         "either for essential dynamics analysis, etc.",
609         "The first option is just plain fitting to a reference structure",
610         "in the structure file. The second option is a progressive fit",
611         "in which the first timeframe is fitted to the reference structure ",
612         "in the structure file to obtain and each subsequent timeframe is ",
613         "fitted to the previously fitted structure. This way a continuous",
614         "trajectory is generated, which might not be the case when using the",
615         "regular fit method, e.g. when your protein undergoes large",
616         "conformational transitions.[PAR]",
617
618         "Option [TT]-pbc[tt] sets the type of periodic boundary condition",
619         "treatment:[BR]",
620         "[TT]* mol[tt] puts the center of mass of molecules in the box,",
621         "and requires a run input file to be supplied with [TT]-s[tt].[BR]",
622         "[TT]* res[tt] puts the center of mass of residues in the box.[BR]",
623         "[TT]* atom[tt] puts all the atoms in the box.[BR]",
624         "[TT]* nojump[tt] checks if atoms jump across the box and then puts",
625         "them back. This has the effect that all molecules",
626         "will remain whole (provided they were whole in the initial",
627         "conformation). [BB]Note[bb] that this ensures a continuous trajectory but",
628         "molecules may diffuse out of the box. The starting configuration",
629         "for this procedure is taken from the structure file, if one is",
630         "supplied, otherwise it is the first frame.[BR]",
631         "[TT]* cluster[tt] clusters all the atoms in the selected index",
632         "such that they are all closest to the center of mass of the cluster,",
633         "which is iteratively updated. [BB]Note[bb] that this will only give meaningful",
634         "results if you in fact have a cluster. Luckily that can be checked",
635         "afterwards using a trajectory viewer. Note also that if your molecules",
636         "are broken this will not work either.[BR]",
637         "The separate option [TT]-clustercenter[tt] can be used to specify an",
638         "approximate center for the cluster. This is useful e.g. if you have",
639         "two big vesicles, and you want to maintain their relative positions.[BR]",
640         "[TT]* whole[tt] only makes broken molecules whole.[PAR]",
641
642         "Option [TT]-ur[tt] sets the unit cell representation for options",
643         "[TT]mol[tt], [TT]res[tt] and [TT]atom[tt] of [TT]-pbc[tt].",
644         "All three options give different results for triclinic boxes and",
645         "identical results for rectangular boxes.",
646         "[TT]rect[tt] is the ordinary brick shape.",
647         "[TT]tric[tt] is the triclinic unit cell.",
648         "[TT]compact[tt] puts all atoms at the closest distance from the center",
649         "of the box. This can be useful for visualizing e.g. truncated octahedra",
650         "or rhombic dodecahedra. The center for options [TT]tric[tt] and [TT]compact[tt]",
651         "is [TT]tric[tt] (see below), unless the option [TT]-boxcenter[tt]",
652         "is set differently.[PAR]",
653
654         "Option [TT]-center[tt] centers the system in the box. The user can",
655         "select the group which is used to determine the geometrical center.",
656         "Option [TT]-boxcenter[tt] sets the location of the center of the box",
657         "for options [TT]-pbc[tt] and [TT]-center[tt]. The center options are:",
658         "[TT]tric[tt]: half of the sum of the box vectors,",
659         "[TT]rect[tt]: half of the box diagonal,",
660         "[TT]zero[tt]: zero.",
661         "Use option [TT]-pbc mol[tt] in addition to [TT]-center[tt] when you",
662         "want all molecules in the box after the centering.[PAR]",
663
664         "It is not always possible to use combinations of [TT]-pbc[tt],",
665         "[TT]-fit[tt], [TT]-ur[tt] and [TT]-center[tt] to do exactly what",
666         "you want in one call to [THISMODULE]. Consider using multiple",
667         "calls, and check out the GROMACS website for suggestions.[PAR]",
668
669         "With [TT]-dt[tt], it is possible to reduce the number of ",
670         "frames in the output. This option relies on the accuracy of the times",
671         "in your input trajectory, so if these are inaccurate use the",
672         "[TT]-timestep[tt] option to modify the time (this can be done",
673         "simultaneously). For making smooth movies, the program [gmx-filter]",
674         "can reduce the number of frames while using low-pass frequency",
675         "filtering, this reduces aliasing of high frequency motions.[PAR]",
676
677         "Using [TT]-trunc[tt] [THISMODULE] can truncate [TT].trj[tt] in place, i.e.",
678         "without copying the file. This is useful when a run has crashed",
679         "during disk I/O (i.e. full disk), or when two contiguous",
680         "trajectories must be concatenated without having double frames.[PAR]",
681
682         "Option [TT]-dump[tt] can be used to extract a frame at or near",
683         "one specific time from your trajectory.[PAR]",
684
685         "Option [TT]-drop[tt] reads an [TT].xvg[tt] file with times and values.",
686         "When options [TT]-dropunder[tt] and/or [TT]-dropover[tt] are set,",
687         "frames with a value below and above the value of the respective options",
688         "will not be written."
689     };
690
691     int         pbc_enum;
692     enum
693     {
694         epSel,
695         epNone,
696         epComMol,
697         epComRes,
698         epComAtom,
699         epNojump,
700         epCluster,
701         epWhole,
702         epNR
703     };
704     const char *pbc_opt[epNR + 1] =
705     {
706         NULL, "none", "mol", "res", "atom", "nojump", "cluster", "whole",
707         NULL
708     };
709
710     int         unitcell_enum;
711     const char *unitcell_opt[euNR+1] =
712     { NULL, "rect", "tric", "compact", NULL };
713
714     enum
715     {
716         ecSel, ecTric, ecRect, ecZero, ecNR
717     };
718     const char *center_opt[ecNR+1] =
719     { NULL, "tric", "rect", "zero", NULL };
720     int         ecenter;
721
722     int         fit_enum;
723     enum
724     {
725         efSel, efNone, efFit, efFitXY, efReset, efResetXY, efPFit, efNR
726     };
727     const char *fit[efNR + 1] =
728     {
729         NULL, "none", "rot+trans", "rotxy+transxy", "translation", "transxy",
730         "progressive", NULL
731     };
732
733     static gmx_bool  bAppend       = FALSE, bSeparate = FALSE, bVels = TRUE, bForce = FALSE, bCONECT = FALSE;
734     static gmx_bool  bCenter       = FALSE;
735     static int       skip_nr       = 1, ndec = 3, nzero = 0;
736     static real      tzero         = 0, delta_t = 0, timestep = 0, ttrunc = -1, tdump = -1, split_t = 0;
737     static rvec      newbox        = {0, 0, 0}, shift = {0, 0, 0}, trans = {0, 0, 0};
738     static char     *exec_command  = NULL;
739     static real      dropunder     = 0, dropover = 0;
740     static gmx_bool  bRound        = FALSE;
741
742     t_pargs
743         pa[] =
744     {
745         { "-skip", FALSE, etINT,
746           { &skip_nr }, "Only write every nr-th frame" },
747         { "-dt", FALSE, etTIME,
748           { &delta_t },
749           "Only write frame when t MOD dt = first time (%t)" },
750         { "-round", FALSE, etBOOL,
751           { &bRound }, "Round measurements to nearest picosecond"},
752         { "-dump", FALSE, etTIME,
753           { &tdump }, "Dump frame nearest specified time (%t)" },
754         { "-t0", FALSE, etTIME,
755           { &tzero },
756           "Starting time (%t) (default: don't change)" },
757         { "-timestep", FALSE, etTIME,
758           { &timestep },
759           "Change time step between input frames (%t)" },
760         { "-pbc", FALSE, etENUM,
761           { pbc_opt },
762           "PBC treatment (see help text for full description)" },
763         { "-ur", FALSE, etENUM,
764           { unitcell_opt }, "Unit-cell representation" },
765         { "-center", FALSE, etBOOL,
766           { &bCenter }, "Center atoms in box" },
767         { "-boxcenter", FALSE, etENUM,
768           { center_opt }, "Center for -pbc and -center" },
769         { "-box", FALSE, etRVEC,
770           { newbox },
771           "Size for new cubic box (default: read from input)" },
772         { "-trans", FALSE, etRVEC,
773           { trans },
774           "All coordinates will be translated by trans. This "
775           "can advantageously be combined with -pbc mol -ur "
776           "compact." },
777         { "-shift", FALSE, etRVEC,
778           { shift },
779           "All coordinates will be shifted by framenr*shift" },
780         { "-fit", FALSE, etENUM,
781           { fit },
782           "Fit molecule to ref structure in the structure file" },
783         { "-ndec", FALSE, etINT,
784           { &ndec },
785           "Precision for .xtc and .gro writing in number of "
786           "decimal places" },
787         { "-vel", FALSE, etBOOL,
788           { &bVels }, "Read and write velocities if possible" },
789         { "-force", FALSE, etBOOL,
790           { &bForce }, "Read and write forces if possible" },
791 #ifndef GMX_NATIVE_WINDOWS
792         { "-trunc", FALSE, etTIME,
793           { &ttrunc },
794           "Truncate input trajectory file after this time (%t)" },
795 #endif
796         { "-exec", FALSE, etSTR,
797           { &exec_command },
798           "Execute command for every output frame with the "
799           "frame number as argument" },
800         { "-app", FALSE, etBOOL,
801           { &bAppend }, "Append output" },
802         { "-split", FALSE, etTIME,
803           { &split_t },
804           "Start writing new file when t MOD split = first "
805           "time (%t)" },
806         { "-sep", FALSE, etBOOL,
807           { &bSeparate },
808           "Write each frame to a separate .gro, .g96 or .pdb "
809           "file" },
810         { "-nzero", FALSE, etINT,
811           { &nzero },
812           "If the -sep flag is set, use these many digits "
813           "for the file numbers and prepend zeros as needed" },
814         { "-dropunder", FALSE, etREAL,
815           { &dropunder }, "Drop all frames below this value" },
816         { "-dropover", FALSE, etREAL,
817           { &dropover }, "Drop all frames above this value" },
818         { "-conect", FALSE, etBOOL,
819           { &bCONECT },
820           "Add conect records when writing [TT].pdb[tt] files. Useful "
821           "for visualization of non-standard molecules, e.g. "
822           "coarse grained ones" }
823     };
824 #define NPA asize(pa)
825
826     FILE            *out    = NULL;
827     t_trxstatus     *trxout = NULL;
828     t_trxstatus     *status;
829     int              ftp, ftpin = 0, file_nr;
830     t_trxframe       fr, frout;
831     int              flags;
832     rvec            *xmem = NULL, *vmem = NULL, *fmem = NULL;
833     rvec            *xp   = NULL, x_shift, hbox, box_center, dx;
834     real             xtcpr, lambda, *w_rls = NULL;
835     int              m, i, d, frame, outframe, natoms, nout, ncent, nre, newstep = 0, model_nr;
836 #define SKIP 10
837     t_topology       top;
838     gmx_conect       gc    = NULL;
839     int              ePBC  = -1;
840     t_atoms         *atoms = NULL, useatoms;
841     matrix           top_box;
842     atom_id         *index, *cindex;
843     char            *grpnm;
844     int             *frindex, nrfri;
845     char            *frname;
846     int              ifit, irms, my_clust = -1;
847     atom_id         *ind_fit, *ind_rms;
848     char            *gn_fit, *gn_rms;
849     t_cluster_ndx   *clust           = NULL;
850     t_trxstatus    **clust_status    = NULL;
851     int             *clust_status_id = NULL;
852     int              ntrxopen        = 0;
853     int             *nfwritten       = NULL;
854     int              ndrop           = 0, ncol, drop0 = 0, drop1 = 0, dropuse = 0;
855     double         **dropval;
856     real             tshift = 0, t0 = -1, dt = 0.001, prec;
857     gmx_bool         bFit, bFitXY, bPFit, bReset;
858     int              nfitdim;
859     gmx_rmpbc_t      gpbc = NULL;
860     gmx_bool         bRmPBC, bPBCWhole, bPBCcomRes, bPBCcomMol, bPBCcomAtom, bPBC, bNoJump, bCluster;
861     gmx_bool         bCopy, bDoIt, bIndex, bTDump, bSetTime, bTPS = FALSE, bDTset = FALSE;
862     gmx_bool         bExec, bTimeStep = FALSE, bDumpFrame = FALSE, bSetPrec, bNeedPrec;
863     gmx_bool         bHaveFirstFrame, bHaveNextFrame, bSetBox, bSetUR, bSplit = FALSE;
864     gmx_bool         bSubTraj = FALSE, bDropUnder = FALSE, bDropOver = FALSE, bTrans = FALSE;
865     gmx_bool         bWriteFrame, bSplitHere;
866     const char      *top_file, *in_file, *out_file = NULL;
867     char             out_file2[256], *charpt;
868     char            *outf_base = NULL;
869     const char      *outf_ext  = NULL;
870     char             top_title[256], title[256], command[256], filemode[5];
871     int              xdr          = 0;
872     gmx_bool         bWarnCompact = FALSE;
873     const char      *warn;
874     output_env_t     oenv;
875
876     t_filenm         fnm[] = {
877         { efTRX, "-f",   NULL,      ffREAD  },
878         { efTRO, "-o",   NULL,      ffWRITE },
879         { efTPS, NULL,   NULL,      ffOPTRD },
880         { efNDX, NULL,   NULL,      ffOPTRD },
881         { efNDX, "-fr",  "frames",  ffOPTRD },
882         { efNDX, "-sub", "cluster", ffOPTRD },
883         { efXVG, "-drop", "drop",    ffOPTRD }
884     };
885 #define NFILE asize(fnm)
886
887     if (!parse_common_args(&argc, argv,
888                            PCA_CAN_BEGIN | PCA_CAN_END | PCA_CAN_VIEW |
889                            PCA_TIME_UNIT | PCA_BE_NICE,
890                            NFILE, fnm, NPA, pa, asize(desc), desc,
891                            0, NULL, &oenv))
892     {
893         return 0;
894     }
895
896     top_file = ftp2fn(efTPS, NFILE, fnm);
897     init_top(&top);
898
899     /* Check command line */
900     in_file = opt2fn("-f", NFILE, fnm);
901
902     if (ttrunc != -1)
903     {
904 #ifndef GMX_NATIVE_WINDOWS
905         do_trunc(in_file, ttrunc);
906 #endif
907     }
908     else
909     {
910         /* mark active cmdline options */
911         bSetBox    = opt2parg_bSet("-box", NPA, pa);
912         bSetTime   = opt2parg_bSet("-t0", NPA, pa);
913         bSetPrec   = opt2parg_bSet("-ndec", NPA, pa);
914         bSetUR     = opt2parg_bSet("-ur", NPA, pa);
915         bExec      = opt2parg_bSet("-exec", NPA, pa);
916         bTimeStep  = opt2parg_bSet("-timestep", NPA, pa);
917         bTDump     = opt2parg_bSet("-dump", NPA, pa);
918         bDropUnder = opt2parg_bSet("-dropunder", NPA, pa);
919         bDropOver  = opt2parg_bSet("-dropover", NPA, pa);
920         bTrans     = opt2parg_bSet("-trans", NPA, pa);
921         bSplit     = (split_t != 0);
922
923         /* parse enum options */
924         fit_enum      = nenum(fit);
925         bFit          = (fit_enum == efFit || fit_enum == efFitXY);
926         bFitXY        = fit_enum == efFitXY;
927         bReset        = (fit_enum == efReset || fit_enum == efResetXY);
928         bPFit         = fit_enum == efPFit;
929         pbc_enum      = nenum(pbc_opt);
930         bPBCWhole     = pbc_enum == epWhole;
931         bPBCcomRes    = pbc_enum == epComRes;
932         bPBCcomMol    = pbc_enum == epComMol;
933         bPBCcomAtom   = pbc_enum == epComAtom;
934         bNoJump       = pbc_enum == epNojump;
935         bCluster      = pbc_enum == epCluster;
936         bPBC          = pbc_enum != epNone;
937         unitcell_enum = nenum(unitcell_opt);
938         ecenter       = nenum(center_opt) - ecTric;
939
940         /* set and check option dependencies */
941         if (bPFit)
942         {
943             bFit = TRUE;        /* for pfit, fit *must* be set */
944         }
945         if (bFit)
946         {
947             bReset = TRUE;       /* for fit, reset *must* be set */
948         }
949         nfitdim = 0;
950         if (bFit || bReset)
951         {
952             nfitdim = (fit_enum == efFitXY || fit_enum == efResetXY) ? 2 : 3;
953         }
954         bRmPBC = bFit || bPBCWhole || bPBCcomRes || bPBCcomMol;
955
956         if (bSetUR)
957         {
958             if (!(bPBCcomRes || bPBCcomMol ||  bPBCcomAtom))
959             {
960                 fprintf(stderr,
961                         "WARNING: Option for unitcell representation (-ur %s)\n"
962                         "         only has effect in combination with -pbc %s, %s or %s.\n"
963                         "         Ingoring unitcell representation.\n\n",
964                         unitcell_opt[0], pbc_opt[2], pbc_opt[3], pbc_opt[4]);
965                 bSetUR = FALSE;
966             }
967         }
968         if (bFit && bPBC)
969         {
970             gmx_fatal(FARGS, "PBC condition treatment does not work together with rotational fit.\n"
971                       "Please do the PBC condition treatment first and then run trjconv in a second step\n"
972                       "for the rotational fit.\n"
973                       "First doing the rotational fit and then doing the PBC treatment gives incorrect\n"
974                       "results!");
975         }
976
977         /* ndec is in nr of decimal places, prec is a multiplication factor: */
978         prec = 1;
979         for (i = 0; i < ndec; i++)
980         {
981             prec *= 10;
982         }
983
984         bIndex = ftp2bSet(efNDX, NFILE, fnm);
985
986
987         /* Determine output type */
988         out_file = opt2fn("-o", NFILE, fnm);
989         ftp      = fn2ftp(out_file);
990         fprintf(stderr, "Will write %s: %s\n", ftp2ext(ftp), ftp2desc(ftp));
991         bNeedPrec = (ftp == efXTC || ftp == efGRO);
992         if (bVels)
993         {
994             /* check if velocities are possible in input and output files */
995             ftpin = fn2ftp(in_file);
996             bVels = (ftp == efTRR || ftp == efTRJ || ftp == efGRO || ftp == efG96)
997                 && (ftpin == efTRR || ftpin == efTRJ || ftpin == efGRO || ftpin == efG96 ||
998                     ftpin == efCPT);
999         }
1000         if (bSeparate || bSplit)
1001         {
1002             outf_ext = strrchr(out_file, '.');
1003             if (outf_ext == NULL)
1004             {
1005                 gmx_fatal(FARGS, "Output file name '%s' does not contain a '.'", out_file);
1006             }
1007             outf_base = strdup(out_file);
1008             outf_base[outf_ext - out_file] = '\0';
1009         }
1010
1011         bSubTraj = opt2bSet("-sub", NFILE, fnm);
1012         if (bSubTraj)
1013         {
1014             if ((ftp != efXTC) && (ftp != efTRR))
1015             {
1016                 /* It seems likely that other trajectory file types
1017                  * could work here. */
1018                 gmx_fatal(FARGS, "Can only use the sub option with output file types "
1019                           "xtc and trr");
1020             }
1021             clust = cluster_index(NULL, opt2fn("-sub", NFILE, fnm));
1022
1023             /* Check for number of files disabled, as FOPEN_MAX is not the correct
1024              * number to check for. In my linux box it is only 16.
1025              */
1026             if (0 && (clust->clust->nr > FOPEN_MAX-4))
1027             {
1028                 gmx_fatal(FARGS, "Can not open enough (%d) files to write all the"
1029                           " trajectories.\ntry splitting the index file in %d parts.\n"
1030                           "FOPEN_MAX = %d",
1031                           clust->clust->nr, 1+clust->clust->nr/FOPEN_MAX, FOPEN_MAX);
1032             }
1033             gmx_warning("The -sub option could require as many open output files as there are\n"
1034                         "index groups in the file (%d). If you get I/O errors opening new files,\n"
1035                         "try reducing the number of index groups in the file, and perhaps\n"
1036                         "using trjconv -sub several times on different chunks of your index file.\n",
1037                         clust->clust->nr);
1038
1039             snew(clust_status, clust->clust->nr);
1040             snew(clust_status_id, clust->clust->nr);
1041             snew(nfwritten, clust->clust->nr);
1042             for (i = 0; (i < clust->clust->nr); i++)
1043             {
1044                 clust_status[i]    = NULL;
1045                 clust_status_id[i] = -1;
1046             }
1047             bSeparate = bSplit = FALSE;
1048         }
1049         /* skipping */
1050         if (skip_nr <= 0)
1051         {
1052         }
1053
1054         /* Determine whether to read a topology */
1055         bTPS = (ftp2bSet(efTPS, NFILE, fnm) ||
1056                 bRmPBC || bReset || bPBCcomMol || bCluster ||
1057                 (ftp == efGRO) || (ftp == efPDB) || bCONECT);
1058
1059         /* Determine if when can read index groups */
1060         bIndex = (bIndex || bTPS);
1061
1062         if (bTPS)
1063         {
1064             read_tps_conf(top_file, top_title, &top, &ePBC, &xp, NULL, top_box,
1065                           bReset || bPBCcomRes);
1066             atoms = &top.atoms;
1067
1068             if (0 == top.mols.nr && (bCluster || bPBCcomMol))
1069             {
1070                 gmx_fatal(FARGS, "Option -pbc %s requires a .tpr file for the -s option", pbc_opt[pbc_enum]);
1071             }
1072
1073             /* top_title is only used for gro and pdb,
1074              * the header in such a file is top_title t= ...
1075              * to prevent a double t=, remove it from top_title
1076              */
1077             if ((charpt = strstr(top_title, " t= ")))
1078             {
1079                 charpt[0] = '\0';
1080             }
1081
1082             if (bCONECT)
1083             {
1084                 gc = gmx_conect_generate(&top);
1085             }
1086             if (bRmPBC)
1087             {
1088                 gpbc = gmx_rmpbc_init(&top.idef, ePBC, top.atoms.nr);
1089             }
1090         }
1091
1092         /* get frame number index */
1093         frindex = NULL;
1094         if (opt2bSet("-fr", NFILE, fnm))
1095         {
1096             printf("Select groups of frame number indices:\n");
1097             rd_index(opt2fn("-fr", NFILE, fnm), 1, &nrfri, (atom_id **)&frindex, &frname);
1098             if (debug)
1099             {
1100                 for (i = 0; i < nrfri; i++)
1101                 {
1102                     fprintf(debug, "frindex[%4d]=%4d\n", i, frindex[i]);
1103                 }
1104             }
1105         }
1106
1107         /* get index groups etc. */
1108         if (bReset)
1109         {
1110             printf("Select group for %s fit\n",
1111                    bFit ? "least squares" : "translational");
1112             get_index(atoms, ftp2fn_null(efNDX, NFILE, fnm),
1113                       1, &ifit, &ind_fit, &gn_fit);
1114
1115             if (bFit)
1116             {
1117                 if (ifit < 2)
1118                 {
1119                     gmx_fatal(FARGS, "Need at least 2 atoms to fit!\n");
1120                 }
1121                 else if (ifit == 3)
1122                 {
1123                     fprintf(stderr, "WARNING: fitting with only 2 atoms is not unique\n");
1124                 }
1125             }
1126         }
1127         else if (bCluster)
1128         {
1129             printf("Select group for clustering\n");
1130             get_index(atoms, ftp2fn_null(efNDX, NFILE, fnm),
1131                       1, &ifit, &ind_fit, &gn_fit);
1132         }
1133
1134         if (bIndex)
1135         {
1136             if (bCenter)
1137             {
1138                 printf("Select group for centering\n");
1139                 get_index(atoms, ftp2fn_null(efNDX, NFILE, fnm),
1140                           1, &ncent, &cindex, &grpnm);
1141             }
1142             printf("Select group for output\n");
1143             get_index(atoms, ftp2fn_null(efNDX, NFILE, fnm),
1144                       1, &nout, &index, &grpnm);
1145         }
1146         else
1147         {
1148             /* no index file, so read natoms from TRX */
1149             if (!read_first_frame(oenv, &status, in_file, &fr, TRX_DONT_SKIP))
1150             {
1151                 gmx_fatal(FARGS, "Could not read a frame from %s", in_file);
1152             }
1153             natoms = fr.natoms;
1154             close_trj(status);
1155             sfree(fr.x);
1156             snew(index, natoms);
1157             for (i = 0; i < natoms; i++)
1158             {
1159                 index[i] = i;
1160             }
1161             nout = natoms;
1162             if (bCenter)
1163             {
1164                 ncent  = nout;
1165                 cindex = index;
1166             }
1167         }
1168
1169         if (bReset)
1170         {
1171             snew(w_rls, atoms->nr);
1172             for (i = 0; (i < ifit); i++)
1173             {
1174                 w_rls[ind_fit[i]] = atoms->atom[ind_fit[i]].m;
1175             }
1176
1177             /* Restore reference structure and set to origin,
1178                store original location (to put structure back) */
1179             if (bRmPBC)
1180             {
1181                 gmx_rmpbc(gpbc, top.atoms.nr, top_box, xp);
1182             }
1183             copy_rvec(xp[index[0]], x_shift);
1184             reset_x_ndim(nfitdim, ifit, ind_fit, atoms->nr, NULL, xp, w_rls);
1185             rvec_dec(x_shift, xp[index[0]]);
1186         }
1187         else
1188         {
1189             clear_rvec(x_shift);
1190         }
1191
1192         if (bDropUnder || bDropOver)
1193         {
1194             /* Read the .xvg file with the drop values */
1195             fprintf(stderr, "\nReading drop file ...");
1196             ndrop = read_xvg(opt2fn("-drop", NFILE, fnm), &dropval, &ncol);
1197             fprintf(stderr, " %d time points\n", ndrop);
1198             if (ndrop == 0 || ncol < 2)
1199             {
1200                 gmx_fatal(FARGS, "Found no data points in %s",
1201                           opt2fn("-drop", NFILE, fnm));
1202             }
1203             drop0 = 0;
1204             drop1 = 0;
1205         }
1206
1207         /* Make atoms struct for output in GRO or PDB files */
1208         if ((ftp == efGRO) || ((ftp == efG96) && bTPS) || (ftp == efPDB))
1209         {
1210             /* get memory for stuff to go in .pdb file */
1211             init_t_atoms(&useatoms, atoms->nr, FALSE);
1212             sfree(useatoms.resinfo);
1213             useatoms.resinfo = atoms->resinfo;
1214             for (i = 0; (i < nout); i++)
1215             {
1216                 useatoms.atomname[i] = atoms->atomname[index[i]];
1217                 useatoms.atom[i]     = atoms->atom[index[i]];
1218                 useatoms.nres        = max(useatoms.nres, useatoms.atom[i].resind+1);
1219             }
1220             useatoms.nr = nout;
1221         }
1222         /* select what to read */
1223         if (ftp == efTRR || ftp == efTRJ)
1224         {
1225             flags = TRX_READ_X;
1226         }
1227         else
1228         {
1229             flags = TRX_NEED_X;
1230         }
1231         if (bVels)
1232         {
1233             flags = flags | TRX_READ_V;
1234         }
1235         if (bForce)
1236         {
1237             flags = flags | TRX_READ_F;
1238         }
1239
1240         /* open trx file for reading */
1241         bHaveFirstFrame = read_first_frame(oenv, &status, in_file, &fr, flags);
1242         if (fr.bPrec)
1243         {
1244             fprintf(stderr, "\nPrecision of %s is %g (nm)\n", in_file, 1/fr.prec);
1245         }
1246         if (bNeedPrec)
1247         {
1248             if (bSetPrec || !fr.bPrec)
1249             {
1250                 fprintf(stderr, "\nSetting output precision to %g (nm)\n", 1/prec);
1251             }
1252             else
1253             {
1254                 fprintf(stderr, "Using output precision of %g (nm)\n", 1/prec);
1255             }
1256         }
1257
1258         if (bHaveFirstFrame)
1259         {
1260             set_trxframe_ePBC(&fr, ePBC);
1261
1262             natoms = fr.natoms;
1263
1264             if (bSetTime)
1265             {
1266                 tshift = tzero-fr.time;
1267             }
1268             else
1269             {
1270                 tzero = fr.time;
1271             }
1272
1273             /* open output for writing */
1274             if ((bAppend) && (gmx_fexist(out_file)))
1275             {
1276                 strcpy(filemode, "a");
1277                 fprintf(stderr, "APPENDING to existing file %s\n", out_file);
1278             }
1279             else
1280             {
1281                 strcpy(filemode, "w");
1282             }
1283             switch (ftp)
1284             {
1285                 case efXTC:
1286                 case efG87:
1287                 case efTRR:
1288                 case efTRJ:
1289                     out = NULL;
1290                     if (!bSplit && !bSubTraj)
1291                     {
1292                         trxout = open_trx(out_file, filemode);
1293                     }
1294                     break;
1295                 case efGRO:
1296                 case efG96:
1297                 case efPDB:
1298                     if (( !bSeparate && !bSplit ) && !bSubTraj)
1299                     {
1300                         out = ffopen(out_file, filemode);
1301                     }
1302                     break;
1303             }
1304
1305             bCopy = FALSE;
1306             if (bIndex)
1307             {
1308                 /* check if index is meaningful */
1309                 for (i = 0; i < nout; i++)
1310                 {
1311                     if (index[i] >= natoms)
1312                     {
1313                         gmx_fatal(FARGS,
1314                                   "Index[%d] %d is larger than the number of atoms in the\n"
1315                                   "trajectory file (%d). There is a mismatch in the contents\n"
1316                                   "of your -f, -s and/or -n files.", i, index[i]+1, natoms);
1317                     }
1318                     bCopy = bCopy || (i != index[i]);
1319                 }
1320             }
1321             if (bCopy)
1322             {
1323                 snew(xmem, nout);
1324                 if (bVels)
1325                 {
1326                     snew(vmem, nout);
1327                 }
1328                 if (bForce)
1329                 {
1330                     snew(fmem, nout);
1331                 }
1332             }
1333
1334             if (ftp == efG87)
1335             {
1336                 fprintf(gmx_fio_getfp(trx_get_fileio(trxout)),
1337                         "Generated by %s. #atoms=%d, a BOX is"
1338                         " stored in this file.\n", ShortProgram(), nout);
1339             }
1340
1341             /* Start the big loop over frames */
1342             file_nr  =  0;
1343             frame    =  0;
1344             outframe =  0;
1345             model_nr =  0;
1346             bDTset   = FALSE;
1347
1348             /* Main loop over frames */
1349             do
1350             {
1351                 if (!fr.bStep)
1352                 {
1353                     /* set the step */
1354                     fr.step = newstep;
1355                     newstep++;
1356                 }
1357                 if (bSubTraj)
1358                 {
1359                     /*if (frame >= clust->clust->nra)
1360                        gmx_fatal(FARGS,"There are more frames in the trajectory than in the cluster index file\n");*/
1361                     if (frame > clust->maxframe)
1362                     {
1363                         my_clust = -1;
1364                     }
1365                     else
1366                     {
1367                         my_clust = clust->inv_clust[frame];
1368                     }
1369                     if ((my_clust < 0) || (my_clust >= clust->clust->nr) ||
1370                         (my_clust == NO_ATID))
1371                     {
1372                         my_clust = -1;
1373                     }
1374                 }
1375
1376                 if (bSetBox)
1377                 {
1378                     /* generate new box */
1379                     clear_mat(fr.box);
1380                     for (m = 0; m < DIM; m++)
1381                     {
1382                         fr.box[m][m] = newbox[m];
1383                     }
1384                 }
1385
1386                 if (bTrans)
1387                 {
1388                     for (i = 0; i < natoms; i++)
1389                     {
1390                         rvec_inc(fr.x[i], trans);
1391                     }
1392                 }
1393
1394                 if (bTDump)
1395                 {
1396                     /* determine timestep */
1397                     if (t0 == -1)
1398                     {
1399                         t0 = fr.time;
1400                     }
1401                     else
1402                     {
1403                         if (!bDTset)
1404                         {
1405                             dt     = fr.time-t0;
1406                             bDTset = TRUE;
1407                         }
1408                     }
1409                     /* This is not very elegant, as one can not dump a frame after
1410                      * a timestep with is more than twice as small as the first one. */
1411                     bDumpFrame = (fr.time > tdump-0.5*dt) && (fr.time <= tdump+0.5*dt);
1412                 }
1413                 else
1414                 {
1415                     bDumpFrame = FALSE;
1416                 }
1417
1418                 /* determine if an atom jumped across the box and reset it if so */
1419                 if (bNoJump && (bTPS || frame != 0))
1420                 {
1421                     for (d = 0; d < DIM; d++)
1422                     {
1423                         hbox[d] = 0.5*fr.box[d][d];
1424                     }
1425                     for (i = 0; i < natoms; i++)
1426                     {
1427                         if (bReset)
1428                         {
1429                             rvec_dec(fr.x[i], x_shift);
1430                         }
1431                         for (m = DIM-1; m >= 0; m--)
1432                         {
1433                             if (hbox[m] > 0)
1434                             {
1435                                 while (fr.x[i][m]-xp[i][m] <= -hbox[m])
1436                                 {
1437                                     for (d = 0; d <= m; d++)
1438                                     {
1439                                         fr.x[i][d] += fr.box[m][d];
1440                                     }
1441                                 }
1442                                 while (fr.x[i][m]-xp[i][m] > hbox[m])
1443                                 {
1444                                     for (d = 0; d <= m; d++)
1445                                     {
1446                                         fr.x[i][d] -= fr.box[m][d];
1447                                     }
1448                                 }
1449                             }
1450                         }
1451                     }
1452                 }
1453                 else if (bCluster)
1454                 {
1455                     calc_pbc_cluster(ecenter, ifit, &top, ePBC, fr.x, ind_fit, fr.box);
1456                 }
1457
1458                 if (bPFit)
1459                 {
1460                     /* Now modify the coords according to the flags,
1461                        for normal fit, this is only done for output frames */
1462                     if (bRmPBC)
1463                     {
1464                         gmx_rmpbc_trxfr(gpbc, &fr);
1465                     }
1466
1467                     reset_x_ndim(nfitdim, ifit, ind_fit, natoms, NULL, fr.x, w_rls);
1468                     do_fit(natoms, w_rls, xp, fr.x);
1469                 }
1470
1471                 /* store this set of coordinates for future use */
1472                 if (bPFit || bNoJump)
1473                 {
1474                     if (xp == NULL)
1475                     {
1476                         snew(xp, natoms);
1477                     }
1478                     for (i = 0; (i < natoms); i++)
1479                     {
1480                         copy_rvec(fr.x[i], xp[i]);
1481                         rvec_inc(fr.x[i], x_shift);
1482                     }
1483                 }
1484
1485                 if (frindex)
1486                 {
1487                     /* see if we have a frame from the frame index group */
1488                     for (i = 0; i < nrfri && !bDumpFrame; i++)
1489                     {
1490                         bDumpFrame = frame == frindex[i];
1491                     }
1492                 }
1493                 if (debug && bDumpFrame)
1494                 {
1495                     fprintf(debug, "dumping %d\n", frame);
1496                 }
1497
1498                 bWriteFrame =
1499                     ( ( !bTDump && !frindex && frame % skip_nr == 0 ) || bDumpFrame );
1500
1501                 if (bWriteFrame && (bDropUnder || bDropOver))
1502                 {
1503                     while (dropval[0][drop1] < fr.time && drop1+1 < ndrop)
1504                     {
1505                         drop0 = drop1;
1506                         drop1++;
1507                     }
1508                     if (fabs(dropval[0][drop0] - fr.time)
1509                         < fabs(dropval[0][drop1] - fr.time))
1510                     {
1511                         dropuse = drop0;
1512                     }
1513                     else
1514                     {
1515                         dropuse = drop1;
1516                     }
1517                     if ((bDropUnder && dropval[1][dropuse] < dropunder) ||
1518                         (bDropOver && dropval[1][dropuse] > dropover))
1519                     {
1520                         bWriteFrame = FALSE;
1521                     }
1522                 }
1523
1524                 if (bWriteFrame)
1525                 {
1526
1527                     /* calc new time */
1528                     if (bTimeStep)
1529                     {
1530                         fr.time = tzero+frame*timestep;
1531                     }
1532                     else
1533                     if (bSetTime)
1534                     {
1535                         fr.time += tshift;
1536                     }
1537
1538                     if (bTDump)
1539                     {
1540                         fprintf(stderr, "\nDumping frame at t= %g %s\n",
1541                                 output_env_conv_time(oenv, fr.time), output_env_get_time_unit(oenv));
1542                     }
1543
1544                     /* check for writing at each delta_t */
1545                     bDoIt = (delta_t == 0);
1546                     if (!bDoIt)
1547                     {
1548                         if (!bRound)
1549                         {
1550                             bDoIt = bRmod(fr.time, tzero, delta_t);
1551                         }
1552                         else
1553                         {
1554                             /* round() is not C89 compatible, so we do this:  */
1555                             bDoIt = bRmod(floor(fr.time+0.5), floor(tzero+0.5),
1556                                           floor(delta_t+0.5));
1557                         }
1558                     }
1559
1560                     if (bDoIt || bTDump)
1561                     {
1562                         /* print sometimes */
1563                         if ( ((outframe % SKIP) == 0) || (outframe < SKIP) )
1564                         {
1565                             fprintf(stderr, " ->  frame %6d time %8.3f      \r",
1566                                     outframe, output_env_conv_time(oenv, fr.time));
1567                         }
1568
1569                         if (!bPFit)
1570                         {
1571                             /* Now modify the coords according to the flags,
1572                                for PFit we did this already! */
1573
1574                             if (bRmPBC)
1575                             {
1576                                 gmx_rmpbc_trxfr(gpbc, &fr);
1577                             }
1578
1579                             if (bReset)
1580                             {
1581                                 reset_x_ndim(nfitdim, ifit, ind_fit, natoms, NULL, fr.x, w_rls);
1582                                 if (bFit)
1583                                 {
1584                                     do_fit_ndim(nfitdim, natoms, w_rls, xp, fr.x);
1585                                 }
1586                                 if (!bCenter)
1587                                 {
1588                                     for (i = 0; i < natoms; i++)
1589                                     {
1590                                         rvec_inc(fr.x[i], x_shift);
1591                                     }
1592                                 }
1593                             }
1594
1595                             if (bCenter)
1596                             {
1597                                 center_x(ecenter, fr.x, fr.box, natoms, ncent, cindex);
1598                             }
1599                         }
1600
1601                         if (bPBCcomAtom)
1602                         {
1603                             switch (unitcell_enum)
1604                             {
1605                                 case euRect:
1606                                     put_atoms_in_box(ePBC, fr.box, natoms, fr.x);
1607                                     break;
1608                                 case euTric:
1609                                     put_atoms_in_triclinic_unitcell(ecenter, fr.box, natoms, fr.x);
1610                                     break;
1611                                 case euCompact:
1612                                     warn = put_atoms_in_compact_unitcell(ePBC, ecenter, fr.box,
1613                                                                          natoms, fr.x);
1614                                     if (warn && !bWarnCompact)
1615                                     {
1616                                         fprintf(stderr, "\n%s\n", warn);
1617                                         bWarnCompact = TRUE;
1618                                     }
1619                                     break;
1620                             }
1621                         }
1622                         if (bPBCcomRes)
1623                         {
1624                             put_residue_com_in_box(unitcell_enum, ecenter,
1625                                                    natoms, atoms->atom, ePBC, fr.box, fr.x);
1626                         }
1627                         if (bPBCcomMol)
1628                         {
1629                             put_molecule_com_in_box(unitcell_enum, ecenter,
1630                                                     &top.mols,
1631                                                     natoms, atoms->atom, ePBC, fr.box, fr.x);
1632                         }
1633                         /* Copy the input trxframe struct to the output trxframe struct */
1634                         frout        = fr;
1635                         frout.bV     = (frout.bV && bVels);
1636                         frout.bF     = (frout.bF && bForce);
1637                         frout.natoms = nout;
1638                         if (bNeedPrec && (bSetPrec || !fr.bPrec))
1639                         {
1640                             frout.bPrec = TRUE;
1641                             frout.prec  = prec;
1642                         }
1643                         if (bCopy)
1644                         {
1645                             frout.x = xmem;
1646                             if (frout.bV)
1647                             {
1648                                 frout.v = vmem;
1649                             }
1650                             if (frout.bF)
1651                             {
1652                                 frout.f = fmem;
1653                             }
1654                             for (i = 0; i < nout; i++)
1655                             {
1656                                 copy_rvec(fr.x[index[i]], frout.x[i]);
1657                                 if (frout.bV)
1658                                 {
1659                                     copy_rvec(fr.v[index[i]], frout.v[i]);
1660                                 }
1661                                 if (frout.bF)
1662                                 {
1663                                     copy_rvec(fr.f[index[i]], frout.f[i]);
1664                                 }
1665                             }
1666                         }
1667
1668                         if (opt2parg_bSet("-shift", NPA, pa))
1669                         {
1670                             for (i = 0; i < nout; i++)
1671                             {
1672                                 for (d = 0; d < DIM; d++)
1673                                 {
1674                                     frout.x[i][d] += outframe*shift[d];
1675                                 }
1676                             }
1677                         }
1678
1679                         if (!bRound)
1680                         {
1681                             bSplitHere = bSplit && bRmod(fr.time, tzero, split_t);
1682                         }
1683                         else
1684                         {
1685                             /* round() is not C89 compatible, so we do this: */
1686                             bSplitHere = bSplit && bRmod(floor(fr.time+0.5),
1687                                                          floor(tzero+0.5),
1688                                                          floor(split_t+0.5));
1689                         }
1690                         if (bSeparate || bSplitHere)
1691                         {
1692                             mk_filenm(outf_base, ftp2ext(ftp), nzero, file_nr, out_file2);
1693                         }
1694
1695                         switch (ftp)
1696                         {
1697                             case efTRJ:
1698                             case efTRR:
1699                             case efG87:
1700                             case efXTC:
1701                                 if (bSplitHere)
1702                                 {
1703                                     if (trxout)
1704                                     {
1705                                         close_trx(trxout);
1706                                     }
1707                                     trxout = open_trx(out_file2, filemode);
1708                                 }
1709                                 if (bSubTraj)
1710                                 {
1711                                     if (my_clust != -1)
1712                                     {
1713                                         char buf[STRLEN];
1714                                         if (clust_status_id[my_clust] == -1)
1715                                         {
1716                                             sprintf(buf, "%s.%s", clust->grpname[my_clust], ftp2ext(ftp));
1717                                             clust_status[my_clust]    = open_trx(buf, "w");
1718                                             clust_status_id[my_clust] = 1;
1719                                             ntrxopen++;
1720                                         }
1721                                         else if (clust_status_id[my_clust] == -2)
1722                                         {
1723                                             gmx_fatal(FARGS, "File %s.xtc should still be open (%d open .xtc files)\n" "in order to write frame %d. my_clust = %d",
1724                                                       clust->grpname[my_clust], ntrxopen, frame,
1725                                                       my_clust);
1726                                         }
1727                                         write_trxframe(clust_status[my_clust], &frout, gc);
1728                                         nfwritten[my_clust]++;
1729                                         if (nfwritten[my_clust] ==
1730                                             (clust->clust->index[my_clust+1]-
1731                                              clust->clust->index[my_clust]))
1732                                         {
1733                                             close_trx(clust_status[my_clust]);
1734                                             clust_status[my_clust]    = NULL;
1735                                             clust_status_id[my_clust] = -2;
1736                                             ntrxopen--;
1737                                             if (ntrxopen < 0)
1738                                             {
1739                                                 gmx_fatal(FARGS, "Less than zero open .xtc files!");
1740                                             }
1741                                         }
1742                                     }
1743                                 }
1744                                 else
1745                                 {
1746                                     write_trxframe(trxout, &frout, gc);
1747                                 }
1748                                 break;
1749                             case efGRO:
1750                             case efG96:
1751                             case efPDB:
1752                                 sprintf(title, "Generated by trjconv : %s t= %9.5f",
1753                                         top_title, fr.time);
1754                                 if (bSeparate || bSplitHere)
1755                                 {
1756                                     out = ffopen(out_file2, "w");
1757                                 }
1758                                 switch (ftp)
1759                                 {
1760                                     case efGRO:
1761                                         write_hconf_p(out, title, &useatoms, prec2ndec(frout.prec),
1762                                                       frout.x, frout.bV ? frout.v : NULL, frout.box);
1763                                         break;
1764                                     case efPDB:
1765                                         fprintf(out, "REMARK    GENERATED BY TRJCONV\n");
1766                                         sprintf(title, "%s t= %9.5f", top_title, frout.time);
1767                                         /* if reading from pdb, we want to keep the original
1768                                            model numbering else we write the output frame
1769                                            number plus one, because model 0 is not allowed in pdb */
1770                                         if (ftpin == efPDB && fr.bStep && fr.step > model_nr)
1771                                         {
1772                                             model_nr = fr.step;
1773                                         }
1774                                         else
1775                                         {
1776                                             model_nr++;
1777                                         }
1778                                         write_pdbfile(out, title, &useatoms, frout.x,
1779                                                       frout.ePBC, frout.box, ' ', model_nr, gc, TRUE);
1780                                         break;
1781                                     case efG96:
1782                                         frout.title = title;
1783                                         if (bSeparate || bTDump)
1784                                         {
1785                                             frout.bTitle = TRUE;
1786                                             if (bTPS)
1787                                             {
1788                                                 frout.bAtoms = TRUE;
1789                                             }
1790                                             frout.atoms  = &useatoms;
1791                                             frout.bStep  = FALSE;
1792                                             frout.bTime  = FALSE;
1793                                         }
1794                                         else
1795                                         {
1796                                             frout.bTitle = (outframe == 0);
1797                                             frout.bAtoms = FALSE;
1798                                             frout.bStep  = TRUE;
1799                                             frout.bTime  = TRUE;
1800                                         }
1801                                         write_g96_conf(out, &frout, -1, NULL);
1802                                 }
1803                                 if (bSeparate)
1804                                 {
1805                                     ffclose(out);
1806                                     out = NULL;
1807                                 }
1808                                 break;
1809                             default:
1810                                 gmx_fatal(FARGS, "DHE, ftp=%d\n", ftp);
1811                         }
1812                         if (bSeparate || bSplitHere)
1813                         {
1814                             file_nr++;
1815                         }
1816
1817                         /* execute command */
1818                         if (bExec)
1819                         {
1820                             char c[255];
1821                             sprintf(c, "%s  %d", exec_command, file_nr-1);
1822                             /*fprintf(stderr,"Executing '%s'\n",c);*/
1823 #ifdef GMX_NO_SYSTEM
1824                             printf("Warning-- No calls to system(3) supported on this platform.");
1825                             printf("Warning-- Skipping execution of 'system(\"%s\")'.", c);
1826 #else
1827                             if (0 != system(c))
1828                             {
1829                                 gmx_fatal(FARGS, "Error executing command: %s", c);
1830                             }
1831 #endif
1832                         }
1833                         outframe++;
1834                     }
1835                 }
1836                 frame++;
1837                 bHaveNextFrame = read_next_frame(oenv, status, &fr);
1838             }
1839             while (!(bTDump && bDumpFrame) && bHaveNextFrame);
1840         }
1841
1842         if (!bHaveFirstFrame || (bTDump && !bDumpFrame))
1843         {
1844             fprintf(stderr, "\nWARNING no output, "
1845                     "last frame read at t=%g\n", fr.time);
1846         }
1847         fprintf(stderr, "\n");
1848
1849         close_trj(status);
1850         sfree(outf_base);
1851
1852         if (bRmPBC)
1853         {
1854             gmx_rmpbc_done(gpbc);
1855         }
1856
1857         if (trxout)
1858         {
1859             close_trx(trxout);
1860         }
1861         else if (out != NULL)
1862         {
1863             ffclose(out);
1864         }
1865         if (bSubTraj)
1866         {
1867             for (i = 0; (i < clust->clust->nr); i++)
1868             {
1869                 if (clust_status_id[i] >= 0)
1870                 {
1871                     close_trx(clust_status[i]);
1872                 }
1873             }
1874         }
1875     }
1876
1877     do_view(oenv, out_file, NULL);
1878
1879     return 0;
1880 }