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,2015,2016,2017 by the GROMACS development team.
7  * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
8  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9  * and including many others, as listed in the AUTHORS file in the
10  * top-level source directory and at http://www.gromacs.org.
11  *
12  * GROMACS is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * as published by the Free Software Foundation; either version 2.1
15  * of the License, or (at your option) any later version.
16  *
17  * GROMACS is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with GROMACS; if not, see
24  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
26  *
27  * If you want to redistribute modifications to GROMACS, please
28  * consider that scientific software is very special. Version
29  * control is crucial - bugs must be traceable. We will be happy to
30  * consider code for inclusion in the official distribution, but
31  * derived work must not be called official GROMACS. Details are found
32  * in the README & COPYING files - if they are missing, get the
33  * official version at http://www.gromacs.org.
34  *
35  * To help us fund GROMACS development, we humbly ask that you cite
36  * the research papers on the package. Check out http://www.gromacs.org.
37  */
38 #include "gmxpre.h"
39 
40 #include "trxio.h"
41 
42 #include "config.h"
43 
44 #include <cassert>
45 #include <cmath>
46 #include <cstring>
47 
48 #include "gromacs/fileio/checkpoint.h"
49 #include "gromacs/fileio/confio.h"
50 #include "gromacs/fileio/filetypes.h"
51 #include "gromacs/fileio/g96io.h"
52 #include "gromacs/fileio/gmxfio.h"
53 #include "gromacs/fileio/gmxfio_xdr.h"
54 #include "gromacs/fileio/groio.h"
55 #include "gromacs/fileio/oenv.h"
56 #include "gromacs/fileio/pdbio.h"
57 #include "gromacs/fileio/timecontrol.h"
58 #include "gromacs/fileio/tngio.h"
59 #include "gromacs/fileio/tpxio.h"
60 #include "gromacs/fileio/trrio.h"
61 #include "gromacs/fileio/xdrf.h"
62 #include "gromacs/fileio/xtcio.h"
63 #include "gromacs/math/vec.h"
64 #include "gromacs/mdtypes/md_enums.h"
65 #include "gromacs/topology/atoms.h"
66 #include "gromacs/topology/symtab.h"
67 #include "gromacs/topology/topology.h"
68 #include "gromacs/trajectory/trajectoryframe.h"
69 #include "gromacs/utility/fatalerror.h"
70 #include "gromacs/utility/futil.h"
71 #include "gromacs/utility/gmxassert.h"
72 #include "gromacs/utility/smalloc.h"
73 
74 #if GMX_USE_PLUGINS
75 #    include "gromacs/fileio/vmdio.h"
76 #endif
77 
78 /* defines for frame counter output */
79 #define SKIP1 10
80 #define SKIP2 100
81 #define SKIP3 1000
82 
83 struct t_trxstatus
84 {
85     int  flags; /* flags for read_first/next_frame  */
86     int  __frame;
87     real t0;                 /* time of the first frame, needed  *
88                               * for skipping frames with -dt     */
89     real                 tf; /* internal frame time              */
90     t_trxframe*          xframe;
91     t_fileio*            fio;
92     gmx_tng_trajectory_t tng;
93     int                  natoms;
94     double               DT, BOX[3];
95     gmx_bool             bReadBox;
96     char*                persistent_line; /* Persistent line for reading g96 trajectories */
97 #if GMX_USE_PLUGINS
98     gmx_vmdplugin_t* vmdplugin;
99 #endif
100 };
101 
102 /* utility functions */
103 
bRmod_fd(double a,double b,double c,gmx_bool bDouble)104 gmx_bool bRmod_fd(double a, double b, double c, gmx_bool bDouble)
105 {
106     int    iq;
107     double tol;
108 
109     tol = 2 * (bDouble ? GMX_DOUBLE_EPS : GMX_FLOAT_EPS);
110 
111     iq = static_cast<int>((a - b + tol * a) / c);
112 
113     return fabs(a - b - c * iq) <= tol * fabs(a);
114 }
115 
116 
check_times2(real t,real t0,gmx_bool bDouble)117 int check_times2(real t, real t0, gmx_bool bDouble)
118 {
119     int r;
120 
121 #if !GMX_DOUBLE
122     /* since t is float, we can not use double precision for bRmod */
123     bDouble = FALSE;
124 #endif
125 
126     r = -1;
127     if ((!bTimeSet(TBEGIN) || (t >= rTimeValue(TBEGIN))) && (!bTimeSet(TEND) || (t <= rTimeValue(TEND))))
128     {
129         if (bTimeSet(TDELTA) && !bRmod_fd(t, t0, rTimeValue(TDELTA), bDouble))
130         {
131             r = -1;
132         }
133         else
134         {
135             r = 0;
136         }
137     }
138     else if (bTimeSet(TEND) && (t >= rTimeValue(TEND)))
139     {
140         r = 1;
141     }
142     if (debug)
143     {
144         fprintf(debug, "t=%g, t0=%g, b=%g, e=%g, dt=%g: r=%d\n", t, t0, rTimeValue(TBEGIN),
145                 rTimeValue(TEND), rTimeValue(TDELTA), r);
146     }
147     return r;
148 }
149 
check_times(real t)150 int check_times(real t)
151 {
152     return check_times2(t, t, FALSE);
153 }
154 
initcount(t_trxstatus * status)155 static void initcount(t_trxstatus* status)
156 {
157     status->__frame = -1;
158 }
159 
status_init(t_trxstatus * status)160 static void status_init(t_trxstatus* status)
161 {
162     status->flags           = 0;
163     status->xframe          = nullptr;
164     status->fio             = nullptr;
165     status->__frame         = -1;
166     status->t0              = 0;
167     status->tf              = 0;
168     status->persistent_line = nullptr;
169     status->tng             = nullptr;
170 }
171 
172 
nframes_read(t_trxstatus * status)173 int nframes_read(t_trxstatus* status)
174 {
175     return status->__frame;
176 }
177 
printcount_(t_trxstatus * status,const gmx_output_env_t * oenv,const char * l,real t)178 static void printcount_(t_trxstatus* status, const gmx_output_env_t* oenv, const char* l, real t)
179 {
180     if ((status->__frame < 2 * SKIP1 || status->__frame % SKIP1 == 0)
181         && (status->__frame < 2 * SKIP2 || status->__frame % SKIP2 == 0)
182         && (status->__frame < 2 * SKIP3 || status->__frame % SKIP3 == 0)
183         && output_env_get_trajectory_io_verbosity(oenv) != 0)
184     {
185         fprintf(stderr, "\r%-14s %6d time %8.3f   ", l, status->__frame, output_env_conv_time(oenv, t));
186         fflush(stderr);
187     }
188 }
189 
printcount(t_trxstatus * status,const gmx_output_env_t * oenv,real t,gmx_bool bSkip)190 static void printcount(t_trxstatus* status, const gmx_output_env_t* oenv, real t, gmx_bool bSkip)
191 {
192     status->__frame++;
193     printcount_(status, oenv, bSkip ? "Skipping frame" : "Reading frame", t);
194 }
195 
printlast(t_trxstatus * status,const gmx_output_env_t * oenv,real t)196 static void printlast(t_trxstatus* status, const gmx_output_env_t* oenv, real t)
197 {
198     printcount_(status, oenv, "Last frame", t);
199     fprintf(stderr, "\n");
200     fflush(stderr);
201 }
202 
printincomp(t_trxstatus * status,t_trxframe * fr)203 static void printincomp(t_trxstatus* status, t_trxframe* fr)
204 {
205     if (fr->not_ok & HEADER_NOT_OK)
206     {
207         fprintf(stderr, "WARNING: Incomplete header: nr %d time %g\n", status->__frame + 1, fr->time);
208     }
209     else if (fr->not_ok)
210     {
211         fprintf(stderr, "WARNING: Incomplete frame: nr %d time %g\n", status->__frame + 1, fr->time);
212     }
213     fflush(stderr);
214 }
215 
prec2ndec(real prec)216 int prec2ndec(real prec)
217 {
218     if (prec <= 0)
219     {
220         gmx_fatal(FARGS, "DEATH HORROR prec (%g) <= 0 in prec2ndec", prec);
221     }
222 
223     return gmx::roundToInt(log(prec) / log(10.0));
224 }
225 
ndec2prec(int ndec)226 real ndec2prec(int ndec)
227 {
228     return pow(10.0, ndec);
229 }
230 
trx_get_fileio(t_trxstatus * status)231 t_fileio* trx_get_fileio(t_trxstatus* status)
232 {
233     return status->fio;
234 }
235 
trx_get_time_of_final_frame(t_trxstatus * status)236 float trx_get_time_of_final_frame(t_trxstatus* status)
237 {
238     t_fileio* stfio    = trx_get_fileio(status);
239     int       filetype = gmx_fio_getftp(stfio);
240     gmx_bool  bOK;
241     float     lasttime = -1;
242 
243     if (filetype == efXTC)
244     {
245         lasttime = xdr_xtc_get_last_frame_time(gmx_fio_getfp(stfio), gmx_fio_getxdr(stfio),
246                                                status->natoms, &bOK);
247         if (!bOK)
248         {
249             gmx_fatal(FARGS, "Error reading last frame. Maybe seek not supported.");
250         }
251     }
252     else if (filetype == efTNG)
253     {
254         gmx_tng_trajectory_t tng = status->tng;
255         if (!tng)
256         {
257             gmx_fatal(FARGS, "Error opening TNG file.");
258         }
259         lasttime = gmx_tng_get_time_of_final_frame(tng);
260     }
261     else
262     {
263         gmx_incons("Only supported for TNG and XTC");
264     }
265     return lasttime;
266 }
267 
clear_trxframe(t_trxframe * fr,gmx_bool bFirst)268 void clear_trxframe(t_trxframe* fr, gmx_bool bFirst)
269 {
270     fr->not_ok    = 0;
271     fr->bStep     = FALSE;
272     fr->bTime     = FALSE;
273     fr->bLambda   = FALSE;
274     fr->bFepState = FALSE;
275     fr->bAtoms    = FALSE;
276     fr->bPrec     = FALSE;
277     fr->bX        = FALSE;
278     fr->bV        = FALSE;
279     fr->bF        = FALSE;
280     fr->bBox      = FALSE;
281     if (bFirst)
282     {
283         fr->bDouble   = FALSE;
284         fr->natoms    = -1;
285         fr->step      = 0;
286         fr->time      = 0;
287         fr->lambda    = 0;
288         fr->fep_state = 0;
289         fr->atoms     = nullptr;
290         fr->prec      = 0;
291         fr->x         = nullptr;
292         fr->v         = nullptr;
293         fr->f         = nullptr;
294         clear_mat(fr->box);
295         fr->bPBC    = FALSE;
296         fr->pbcType = PbcType::Unset;
297     }
298 }
299 
setTrxFramePbcType(t_trxframe * fr,PbcType pbcType)300 void setTrxFramePbcType(t_trxframe* fr, PbcType pbcType)
301 {
302     fr->bPBC    = (pbcType == PbcType::Unset);
303     fr->pbcType = pbcType;
304 }
305 
write_trxframe_indexed(t_trxstatus * status,const t_trxframe * fr,int nind,const int * ind,gmx_conect gc)306 int write_trxframe_indexed(t_trxstatus* status, const t_trxframe* fr, int nind, const int* ind, gmx_conect gc)
307 {
308     char  title[STRLEN];
309     rvec *xout = nullptr, *vout = nullptr, *fout = nullptr;
310     int   i, ftp = -1;
311     real  prec;
312 
313     if (fr->bPrec)
314     {
315         prec = fr->prec;
316     }
317     else
318     {
319         prec = 1000.0;
320     }
321 
322     if (status->tng)
323     {
324         ftp = efTNG;
325     }
326     else if (status->fio)
327     {
328         ftp = gmx_fio_getftp(status->fio);
329     }
330     else
331     {
332         gmx_incons("No input file available");
333     }
334 
335     switch (ftp)
336     {
337         case efTRR:
338         case efTNG: break;
339         default:
340             if (!fr->bX)
341             {
342                 gmx_fatal(FARGS, "Need coordinates to write a %s trajectory", ftp2ext(ftp));
343             }
344             break;
345     }
346 
347     switch (ftp)
348     {
349         case efTRR:
350         case efTNG:
351             if (fr->bV)
352             {
353                 snew(vout, nind);
354                 for (i = 0; i < nind; i++)
355                 {
356                     copy_rvec(fr->v[ind[i]], vout[i]);
357                 }
358             }
359             if (fr->bF)
360             {
361                 snew(fout, nind);
362                 for (i = 0; i < nind; i++)
363                 {
364                     copy_rvec(fr->f[ind[i]], fout[i]);
365                 }
366             }
367             if (fr->bX)
368             {
369                 snew(xout, nind);
370                 for (i = 0; i < nind; i++)
371                 {
372                     copy_rvec(fr->x[ind[i]], xout[i]);
373                 }
374             }
375             break;
376         case efXTC:
377             if (fr->bX)
378             {
379                 snew(xout, nind);
380                 for (i = 0; i < nind; i++)
381                 {
382                     copy_rvec(fr->x[ind[i]], xout[i]);
383                 }
384             }
385             break;
386         default: break;
387     }
388 
389     switch (ftp)
390     {
391         case efTNG: gmx_write_tng_from_trxframe(status->tng, fr, nind); break;
392         case efXTC: write_xtc(status->fio, nind, fr->step, fr->time, fr->box, xout, prec); break;
393         case efTRR:
394             gmx_trr_write_frame(status->fio, nframes_read(status), fr->time, fr->step, fr->box,
395                                 nind, xout, vout, fout);
396             break;
397         case efGRO:
398         case efPDB:
399         case efBRK:
400         case efENT:
401             if (!fr->bAtoms)
402             {
403                 gmx_fatal(FARGS, "Can not write a %s file without atom names", ftp2ext(ftp));
404             }
405             sprintf(title, "frame t= %.3f", fr->time);
406             if (ftp == efGRO)
407             {
408                 write_hconf_indexed_p(gmx_fio_getfp(status->fio), title, fr->atoms, nind, ind,
409                                       fr->x, fr->bV ? fr->v : nullptr, fr->box);
410             }
411             else
412             {
413                 write_pdbfile_indexed(gmx_fio_getfp(status->fio), title, fr->atoms, fr->x,
414                                       PbcType::Unset, fr->box, ' ', fr->step, nind, ind, gc, FALSE);
415             }
416             break;
417         case efG96:
418             sprintf(title, "frame t= %.3f", fr->time);
419             write_g96_conf(gmx_fio_getfp(status->fio), title, fr, nind, ind);
420             break;
421         default: gmx_fatal(FARGS, "Sorry, write_trxframe_indexed can not write %s", ftp2ext(ftp));
422     }
423 
424     switch (ftp)
425     {
426         case efTRR:
427         case efTNG:
428             if (vout)
429             {
430                 sfree(vout);
431             }
432             if (fout)
433             {
434                 sfree(fout);
435             }
436             sfree(xout);
437             break;
438         case efXTC: sfree(xout); break;
439         default: break;
440     }
441 
442     return 0;
443 }
444 
trjtools_gmx_prepare_tng_writing(const char * filename,char filemode,t_trxstatus * in,const char * infile,const int natoms,const gmx_mtop_t * mtop,gmx::ArrayRef<const int> index,const char * index_group_name)445 t_trxstatus* trjtools_gmx_prepare_tng_writing(const char*              filename,
446                                               char                     filemode,
447                                               t_trxstatus*             in,
448                                               const char*              infile,
449                                               const int                natoms,
450                                               const gmx_mtop_t*        mtop,
451                                               gmx::ArrayRef<const int> index,
452                                               const char*              index_group_name)
453 {
454     if (filemode != 'w' && filemode != 'a')
455     {
456         gmx_incons("Sorry, can only prepare for TNG output.");
457     }
458     t_trxstatus* out;
459     snew(out, 1);
460     status_init(out);
461 
462     if (in != nullptr)
463     {
464         gmx_prepare_tng_writing(filename, filemode, &in->tng, &out->tng, natoms, mtop, index,
465                                 index_group_name);
466     }
467     else if ((infile) && (efTNG == fn2ftp(infile)))
468     {
469         gmx_tng_trajectory_t tng_in;
470         gmx_tng_open(infile, 'r', &tng_in);
471 
472         gmx_prepare_tng_writing(filename, filemode, &tng_in, &out->tng, natoms, mtop, index,
473                                 index_group_name);
474     }
475     else
476     {
477         // we start from a file that is not a tng file or have been unable to load the
478         // input file, so we need to populate the fields independently of it
479         gmx_prepare_tng_writing(filename, filemode, nullptr, &out->tng, natoms, mtop, index,
480                                 index_group_name);
481     }
482     return out;
483 }
484 
write_tng_frame(t_trxstatus * status,t_trxframe * frame)485 void write_tng_frame(t_trxstatus* status, t_trxframe* frame)
486 {
487     gmx_write_tng_from_trxframe(status->tng, frame, -1);
488 }
489 
write_trxframe(t_trxstatus * status,t_trxframe * fr,gmx_conect gc)490 int write_trxframe(t_trxstatus* status, t_trxframe* fr, gmx_conect gc)
491 {
492     char title[STRLEN];
493     title[0] = '\0';
494     real prec;
495 
496     if (fr->bPrec)
497     {
498         prec = fr->prec;
499     }
500     else
501     {
502         prec = 1000.0;
503     }
504 
505     if (status->tng)
506     {
507         gmx_tng_set_compression_precision(status->tng, prec);
508         write_tng_frame(status, fr);
509 
510         return 0;
511     }
512 
513     switch (gmx_fio_getftp(status->fio))
514     {
515         case efTRR: break;
516         default:
517             if (!fr->bX)
518             {
519                 gmx_fatal(FARGS, "Need coordinates to write a %s trajectory",
520                           ftp2ext(gmx_fio_getftp(status->fio)));
521             }
522             break;
523     }
524 
525     switch (gmx_fio_getftp(status->fio))
526     {
527         case efXTC:
528             write_xtc(status->fio, fr->natoms, fr->step, fr->time, fr->box, fr->x, prec);
529             break;
530         case efTRR:
531             gmx_trr_write_frame(status->fio, fr->step, fr->time, fr->lambda, fr->box, fr->natoms,
532                                 fr->bX ? fr->x : nullptr, fr->bV ? fr->v : nullptr,
533                                 fr->bF ? fr->f : nullptr);
534             break;
535         case efGRO:
536         case efPDB:
537         case efBRK:
538         case efENT:
539             if (!fr->bAtoms)
540             {
541                 gmx_fatal(FARGS, "Can not write a %s file without atom names",
542                           ftp2ext(gmx_fio_getftp(status->fio)));
543             }
544             sprintf(title, "frame t= %.3f", fr->time);
545             if (gmx_fio_getftp(status->fio) == efGRO)
546             {
547                 write_hconf_p(gmx_fio_getfp(status->fio), title, fr->atoms, fr->x,
548                               fr->bV ? fr->v : nullptr, fr->box);
549             }
550             else
551             {
552                 write_pdbfile(gmx_fio_getfp(status->fio), title, fr->atoms, fr->x,
553                               fr->bPBC ? fr->pbcType : PbcType::Unset, fr->box, ' ', fr->step, gc);
554             }
555             break;
556         case efG96: write_g96_conf(gmx_fio_getfp(status->fio), title, fr, -1, nullptr); break;
557         default:
558             gmx_fatal(FARGS, "Sorry, write_trxframe can not write %s",
559                       ftp2ext(gmx_fio_getftp(status->fio)));
560     }
561 
562     return 0;
563 }
564 
write_trx(t_trxstatus * status,int nind,const int * ind,const t_atoms * atoms,int step,real time,matrix box,rvec x[],rvec * v,gmx_conect gc)565 int write_trx(t_trxstatus*   status,
566               int            nind,
567               const int*     ind,
568               const t_atoms* atoms,
569               int            step,
570               real           time,
571               matrix         box,
572               rvec           x[],
573               rvec*          v,
574               gmx_conect     gc)
575 {
576     t_trxframe fr;
577 
578     clear_trxframe(&fr, TRUE);
579     fr.bStep  = TRUE;
580     fr.step   = step;
581     fr.bTime  = TRUE;
582     fr.time   = time;
583     fr.bAtoms = atoms != nullptr;
584     fr.atoms  = const_cast<t_atoms*>(atoms);
585     fr.bX     = TRUE;
586     fr.x      = x;
587     fr.bV     = v != nullptr;
588     fr.v      = v;
589     fr.bBox   = TRUE;
590     copy_mat(box, fr.box);
591 
592     return write_trxframe_indexed(status, &fr, nind, ind, gc);
593 }
594 
close_trx(t_trxstatus * status)595 void close_trx(t_trxstatus* status)
596 {
597     if (status == nullptr)
598     {
599         return;
600     }
601     gmx_tng_close(&status->tng);
602     if (status->fio)
603     {
604         gmx_fio_close(status->fio);
605     }
606     sfree(status->persistent_line);
607 #if GMX_USE_PLUGINS
608     sfree(status->vmdplugin);
609 #endif
610     /* The memory in status->xframe is lost here,
611      * but the read_first_x/read_next_x functions are deprecated anyhow.
612      * read_first_frame/read_next_frame and close_trx should be used.
613      */
614     sfree(status);
615 }
616 
open_trx(const char * outfile,const char * filemode)617 t_trxstatus* open_trx(const char* outfile, const char* filemode)
618 {
619     t_trxstatus* stat;
620     if (filemode[0] != 'w' && filemode[0] != 'a' && filemode[1] != '+')
621     {
622         gmx_fatal(FARGS, "Sorry, write_trx can only write");
623     }
624 
625     snew(stat, 1);
626     status_init(stat);
627 
628     stat->fio = gmx_fio_open(outfile, filemode);
629     return stat;
630 }
631 
gmx_next_frame(t_trxstatus * status,t_trxframe * fr)632 static gmx_bool gmx_next_frame(t_trxstatus* status, t_trxframe* fr)
633 {
634     gmx_trr_header_t sh;
635     gmx_bool         bOK, bRet;
636 
637     bRet = FALSE;
638 
639     if (gmx_trr_read_frame_header(status->fio, &sh, &bOK))
640     {
641         fr->bDouble   = sh.bDouble;
642         fr->natoms    = sh.natoms;
643         fr->bStep     = TRUE;
644         fr->step      = sh.step;
645         fr->bTime     = TRUE;
646         fr->time      = sh.t;
647         fr->bLambda   = TRUE;
648         fr->bFepState = TRUE;
649         fr->lambda    = sh.lambda;
650         fr->bBox      = sh.box_size > 0;
651         if (status->flags & (TRX_READ_X | TRX_NEED_X))
652         {
653             if (fr->x == nullptr)
654             {
655                 snew(fr->x, sh.natoms);
656             }
657             fr->bX = sh.x_size > 0;
658         }
659         if (status->flags & (TRX_READ_V | TRX_NEED_V))
660         {
661             if (fr->v == nullptr)
662             {
663                 snew(fr->v, sh.natoms);
664             }
665             fr->bV = sh.v_size > 0;
666         }
667         if (status->flags & (TRX_READ_F | TRX_NEED_F))
668         {
669             if (fr->f == nullptr)
670             {
671                 snew(fr->f, sh.natoms);
672             }
673             fr->bF = sh.f_size > 0;
674         }
675         if (gmx_trr_read_frame_data(status->fio, &sh, fr->box, fr->x, fr->v, fr->f))
676         {
677             bRet = TRUE;
678         }
679         else
680         {
681             fr->not_ok = DATA_NOT_OK;
682         }
683     }
684     else if (!bOK)
685     {
686         fr->not_ok = HEADER_NOT_OK;
687     }
688 
689     return bRet;
690 }
691 
pdb_next_x(t_trxstatus * status,FILE * fp,t_trxframe * fr)692 static gmx_bool pdb_next_x(t_trxstatus* status, FILE* fp, t_trxframe* fr)
693 {
694     t_atoms   atoms;
695     t_symtab* symtab;
696     matrix    boxpdb;
697     // Initiate model_nr to -1 rather than NOTSET.
698     // It is not worthwhile introducing extra variables in the
699     // read_pdbfile call to verify that a model_nr was read.
700     PbcType pbcType;
701     int     model_nr = -1, na;
702     char    title[STRLEN], *time, *step;
703     double  dbl;
704 
705     atoms.nr      = fr->natoms;
706     atoms.atom    = nullptr;
707     atoms.pdbinfo = nullptr;
708     /* the other pointers in atoms should not be accessed if these are NULL */
709     snew(symtab, 1);
710     open_symtab(symtab);
711     na = read_pdbfile(fp, title, &model_nr, &atoms, symtab, fr->x, &pbcType, boxpdb, nullptr);
712     free_symtab(symtab);
713     sfree(symtab);
714     setTrxFramePbcType(fr, pbcType);
715     if (nframes_read(status) == 0)
716     {
717         fprintf(stderr, " '%s', %d atoms\n", title, fr->natoms);
718     }
719     fr->bPrec = TRUE;
720     fr->prec  = 10000;
721     fr->bX    = TRUE;
722     fr->bBox  = (boxpdb[XX][XX] != 0.0);
723     if (fr->bBox)
724     {
725         copy_mat(boxpdb, fr->box);
726     }
727 
728     fr->step  = 0;
729     step      = std::strstr(title, " step= ");
730     fr->bStep = ((step != nullptr) && sscanf(step + 7, "%" SCNd64, &fr->step) == 1);
731 
732     dbl       = 0.0;
733     time      = std::strstr(title, " t= ");
734     fr->bTime = ((time != nullptr) && sscanf(time + 4, "%lf", &dbl) == 1);
735     fr->time  = dbl;
736 
737     if (na == 0)
738     {
739         return FALSE;
740     }
741     else
742     {
743         if (na != fr->natoms)
744         {
745             gmx_fatal(FARGS, "Number of atoms in pdb frame %d is %d instead of %d",
746                       nframes_read(status), na, fr->natoms);
747         }
748         return TRUE;
749     }
750 }
751 
pdb_first_x(t_trxstatus * status,FILE * fp,t_trxframe * fr)752 static int pdb_first_x(t_trxstatus* status, FILE* fp, t_trxframe* fr)
753 {
754     initcount(status);
755 
756     fprintf(stderr, "Reading frames from pdb file");
757     frewind(fp);
758     get_pdb_coordnum(fp, &fr->natoms);
759     if (fr->natoms == 0)
760     {
761         gmx_fatal(FARGS, "\nNo coordinates in pdb file\n");
762     }
763     frewind(fp);
764     snew(fr->x, fr->natoms);
765     pdb_next_x(status, fp, fr);
766 
767     return fr->natoms;
768 }
769 
read_next_frame(const gmx_output_env_t * oenv,t_trxstatus * status,t_trxframe * fr)770 bool read_next_frame(const gmx_output_env_t* oenv, t_trxstatus* status, t_trxframe* fr)
771 {
772     real     pt;
773     int      ct;
774     gmx_bool bOK, bMissingData = FALSE, bSkip = FALSE;
775     bool     bRet = false;
776     int      ftp;
777 
778     pt = status->tf;
779 
780     do
781     {
782         clear_trxframe(fr, FALSE);
783 
784         if (status->tng)
785         {
786             /* Special treatment for TNG files */
787             ftp = efTNG;
788         }
789         else
790         {
791             ftp = gmx_fio_getftp(status->fio);
792         }
793         switch (ftp)
794         {
795             case efTRR: bRet = gmx_next_frame(status, fr); break;
796             case efCPT:
797                 /* Checkpoint files can not contain mulitple frames */
798                 break;
799             case efG96:
800             {
801                 t_symtab* symtab = nullptr;
802                 read_g96_conf(gmx_fio_getfp(status->fio), nullptr, nullptr, fr, symtab,
803                               status->persistent_line);
804                 bRet = (fr->natoms > 0);
805                 break;
806             }
807             case efXTC:
808                 if (bTimeSet(TBEGIN) && (status->tf < rTimeValue(TBEGIN)))
809                 {
810                     if (xtc_seek_time(status->fio, rTimeValue(TBEGIN), fr->natoms, TRUE))
811                     {
812                         gmx_fatal(FARGS,
813                                   "Specified frame (time %f) doesn't exist or file "
814                                   "corrupt/inconsistent.",
815                                   rTimeValue(TBEGIN));
816                     }
817                     initcount(status);
818                 }
819                 bRet = (read_next_xtc(status->fio, fr->natoms, &fr->step, &fr->time, fr->box, fr->x,
820                                       &fr->prec, &bOK)
821                         != 0);
822                 fr->bPrec = (bRet && fr->prec > 0);
823                 fr->bStep = bRet;
824                 fr->bTime = bRet;
825                 fr->bX    = bRet;
826                 fr->bBox  = bRet;
827                 if (!bOK)
828                 {
829                     /* Actually the header could also be not ok,
830                        but from bOK from read_next_xtc this can't be distinguished */
831                     fr->not_ok = DATA_NOT_OK;
832                 }
833                 break;
834             case efTNG: bRet = gmx_read_next_tng_frame(status->tng, fr, nullptr, 0); break;
835             case efPDB: bRet = pdb_next_x(status, gmx_fio_getfp(status->fio), fr); break;
836             case efGRO: bRet = gro_next_x_or_v(gmx_fio_getfp(status->fio), fr); break;
837             default:
838 #if GMX_USE_PLUGINS
839                 bRet = read_next_vmd_frame(status->vmdplugin, fr);
840 #else
841                 gmx_fatal(FARGS, "DEATH HORROR in read_next_frame ftp=%s,status=%s",
842                           ftp2ext(gmx_fio_getftp(status->fio)), gmx_fio_getname(status->fio));
843 #endif
844         }
845         status->tf = fr->time;
846 
847         if (bRet)
848         {
849             bMissingData = ((((status->flags & TRX_NEED_X) != 0) && !fr->bX)
850                             || (((status->flags & TRX_NEED_V) != 0) && !fr->bV)
851                             || (((status->flags & TRX_NEED_F) != 0) && !fr->bF));
852             bSkip        = FALSE;
853             if (!bMissingData)
854             {
855                 ct = check_times2(fr->time, status->t0, fr->bDouble);
856                 if (ct == 0 || ((status->flags & TRX_DONT_SKIP) && ct < 0))
857                 {
858                     printcount(status, oenv, fr->time, FALSE);
859                 }
860                 else if (ct > 0)
861                 {
862                     bRet = false;
863                 }
864                 else
865                 {
866                     printcount(status, oenv, fr->time, TRUE);
867                     bSkip = TRUE;
868                 }
869             }
870         }
871 
872     } while (bRet && (bMissingData || bSkip));
873 
874     if (!bRet)
875     {
876         printlast(status, oenv, pt);
877         if (fr->not_ok)
878         {
879             printincomp(status, fr);
880         }
881     }
882 
883     return bRet;
884 }
885 
read_first_frame(const gmx_output_env_t * oenv,t_trxstatus ** status,const char * fn,t_trxframe * fr,int flags)886 bool read_first_frame(const gmx_output_env_t* oenv, t_trxstatus** status, const char* fn, t_trxframe* fr, int flags)
887 {
888     t_fileio* fio = nullptr;
889     gmx_bool  bFirst, bOK;
890     int       ftp = fn2ftp(fn);
891 
892     clear_trxframe(fr, TRUE);
893 
894     bFirst = TRUE;
895 
896     snew((*status), 1);
897 
898     status_init(*status);
899     initcount(*status);
900     (*status)->flags = flags;
901 
902     if (efTNG == ftp)
903     {
904         /* Special treatment for TNG files */
905         gmx_tng_open(fn, 'r', &(*status)->tng);
906     }
907     else
908     {
909         fio = (*status)->fio = gmx_fio_open(fn, "r");
910     }
911     switch (ftp)
912     {
913         case efTRR: break;
914         case efCPT:
915             read_checkpoint_trxframe(fio, fr);
916             bFirst = FALSE;
917             break;
918         case efG96:
919         {
920             /* Can not rewind a compressed file, so open it twice */
921             if (!(*status)->persistent_line)
922             {
923                 /* allocate the persistent line */
924                 snew((*status)->persistent_line, STRLEN + 1);
925             }
926             t_symtab* symtab = nullptr;
927             read_g96_conf(gmx_fio_getfp(fio), fn, nullptr, fr, symtab, (*status)->persistent_line);
928             gmx_fio_close(fio);
929             clear_trxframe(fr, FALSE);
930             if (flags & (TRX_READ_X | TRX_NEED_X))
931             {
932                 snew(fr->x, fr->natoms);
933             }
934             if (flags & (TRX_READ_V | TRX_NEED_V))
935             {
936                 snew(fr->v, fr->natoms);
937             }
938             (*status)->fio = gmx_fio_open(fn, "r");
939             break;
940         }
941         case efXTC:
942             if (read_first_xtc(fio, &fr->natoms, &fr->step, &fr->time, fr->box, &fr->x, &fr->prec, &bOK) == 0)
943             {
944                 GMX_RELEASE_ASSERT(!bOK,
945                                    "Inconsistent results - OK status from read_first_xtc, but 0 "
946                                    "atom coords read");
947                 fr->not_ok = DATA_NOT_OK;
948             }
949             if (fr->not_ok)
950             {
951                 fr->natoms = 0;
952                 printincomp(*status, fr);
953             }
954             else
955             {
956                 fr->bPrec = (fr->prec > 0);
957                 fr->bStep = TRUE;
958                 fr->bTime = TRUE;
959                 fr->bX    = TRUE;
960                 fr->bBox  = TRUE;
961                 printcount(*status, oenv, fr->time, FALSE);
962             }
963             bFirst = FALSE;
964             break;
965         case efTNG:
966             fr->step = -1;
967             if (!gmx_read_next_tng_frame((*status)->tng, fr, nullptr, 0))
968             {
969                 fr->not_ok = DATA_NOT_OK;
970                 fr->natoms = 0;
971                 printincomp(*status, fr);
972             }
973             else
974             {
975                 printcount(*status, oenv, fr->time, FALSE);
976             }
977             bFirst = FALSE;
978             break;
979         case efPDB:
980             pdb_first_x(*status, gmx_fio_getfp(fio), fr);
981             if (fr->natoms)
982             {
983                 printcount(*status, oenv, fr->time, FALSE);
984             }
985             bFirst = FALSE;
986             break;
987         case efGRO:
988             if (gro_first_x_or_v(gmx_fio_getfp(fio), fr))
989             {
990                 printcount(*status, oenv, fr->time, FALSE);
991             }
992             bFirst = FALSE;
993             break;
994         default:
995 #if GMX_USE_PLUGINS
996             fprintf(stderr,
997                     "The file format of %s is not a known trajectory format to GROMACS.\n"
998                     "Please make sure that the file is a trajectory!\n"
999                     "GROMACS will now assume it to be a trajectory and will try to open it using "
1000                     "the VMD plug-ins.\n"
1001                     "This will only work in case the VMD plugins are found and it is a trajectory "
1002                     "format supported by VMD.\n",
1003                     fn);
1004             gmx_fio_fp_close(fio); /*only close the file without removing FIO entry*/
1005             if (!read_first_vmd_frame(fn, &(*status)->vmdplugin, fr))
1006             {
1007                 gmx_fatal(FARGS, "Not supported in read_first_frame: %s", fn);
1008             }
1009 #else
1010             gmx_fatal(FARGS,
1011                       "Not supported in read_first_frame: %s. Please make sure that the file is a "
1012                       "trajectory.\n"
1013                       "GROMACS is not compiled with plug-in support. Thus it cannot read "
1014                       "non-GROMACS trajectory formats using the VMD plug-ins.\n"
1015                       "Please compile with plug-in support if you want to read non-GROMACS "
1016                       "trajectory formats.\n",
1017                       fn);
1018 #endif
1019     }
1020     (*status)->tf = fr->time;
1021 
1022     /* Return FALSE if we read a frame that's past the set ending time. */
1023     if (!bFirst && (!(flags & TRX_DONT_SKIP) && check_times(fr->time) > 0))
1024     {
1025         (*status)->t0 = fr->time;
1026         return FALSE;
1027     }
1028 
1029     if (bFirst || (!(flags & TRX_DONT_SKIP) && check_times(fr->time) < 0))
1030     {
1031         /* Read a frame when no frame was read or the first was skipped */
1032         if (!read_next_frame(oenv, *status, fr))
1033         {
1034             return FALSE;
1035         }
1036     }
1037     (*status)->t0 = fr->time;
1038 
1039     /* We need the number of atoms for random-access XTC searching, even when
1040      * we don't have access to the actual frame data.
1041      */
1042     (*status)->natoms = fr->natoms;
1043 
1044     return (fr->natoms > 0);
1045 }
1046 
1047 /***** C O O R D I N A T E   S T U F F *****/
1048 
read_first_x(const gmx_output_env_t * oenv,t_trxstatus ** status,const char * fn,real * t,rvec ** x,matrix box)1049 int read_first_x(const gmx_output_env_t* oenv, t_trxstatus** status, const char* fn, real* t, rvec** x, matrix box)
1050 {
1051     t_trxframe fr;
1052 
1053     read_first_frame(oenv, status, fn, &fr, TRX_NEED_X);
1054 
1055     snew((*status)->xframe, 1);
1056     (*(*status)->xframe) = fr;
1057     *t                   = (*status)->xframe->time;
1058     *x                   = (*status)->xframe->x;
1059     copy_mat((*status)->xframe->box, box);
1060 
1061     return (*status)->xframe->natoms;
1062 }
1063 
read_next_x(const gmx_output_env_t * oenv,t_trxstatus * status,real * t,rvec x[],matrix box)1064 gmx_bool read_next_x(const gmx_output_env_t* oenv, t_trxstatus* status, real* t, rvec x[], matrix box)
1065 {
1066     gmx_bool bRet;
1067 
1068     status->xframe->x = x;
1069     /*xframe[status].x = x;*/
1070     bRet = read_next_frame(oenv, status, status->xframe);
1071     *t   = status->xframe->time;
1072     copy_mat(status->xframe->box, box);
1073 
1074     return bRet;
1075 }
1076 
rewind_trj(t_trxstatus * status)1077 void rewind_trj(t_trxstatus* status)
1078 {
1079     initcount(status);
1080 
1081     gmx_fio_rewind(status->fio);
1082 }
1083 
1084 /***** T O P O L O G Y   S T U F F ******/
1085 
read_top(const char * fn,PbcType * pbcType)1086 t_topology* read_top(const char* fn, PbcType* pbcType)
1087 {
1088     int         natoms;
1089     PbcType     pbcTypeFile;
1090     t_topology* top;
1091 
1092     snew(top, 1);
1093     pbcTypeFile = read_tpx_top(fn, nullptr, nullptr, &natoms, nullptr, nullptr, top);
1094     if (pbcType)
1095     {
1096         *pbcType = pbcTypeFile;
1097     }
1098 
1099     return top;
1100 }
1101