TNG Fix for GMX_DOUBLE builds and where ZLIB is not available.
[alexxy/gromacs.git] / src / gromacs / fileio / tngio_for_tools.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2013,2014, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 #include "tngio_for_tools.h"
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #include <math.h>
42
43 #include "tngio.h"
44 #include "trx.h"
45
46 #ifdef GMX_USE_TNG
47 #include "tng/tng_io.h"
48 #endif
49
50 #include "gromacs/legacyheaders/types/atoms.h"
51 #include "gromacs/legacyheaders/physics.h"
52 #include "gromacs/legacyheaders/gmx_fatal.h"
53
54 #include "gromacs/utility/common.h"
55 #include "gromacs/utility/smalloc.h"
56
57 void gmx_prepare_tng_writing(const char              *filename,
58                              char                     mode,
59                              tng_trajectory_t        *input,
60                              tng_trajectory_t        *output,
61                              int                      nAtoms,
62                              const gmx_mtop_t        *mtop,
63                              const atom_id           *index,
64                              const char              *indexGroupName)
65 {
66 #ifdef GMX_USE_TNG
67     /* FIXME after 5.0: Currently only standard block types are read */
68     const int           defaultNumIds              = 5;
69     static gmx_int64_t  fallbackIds[defaultNumIds] =
70     {
71         TNG_TRAJ_BOX_SHAPE, TNG_TRAJ_POSITIONS,
72         TNG_TRAJ_VELOCITIES, TNG_TRAJ_FORCES,
73         TNG_GMX_LAMBDA
74     };
75     static char         fallbackNames[defaultNumIds][32] =
76     {
77         "BOX SHAPE", "POSITIONS", "VELOCITIES",
78         "FORCES", "LAMBDAS"
79     };
80
81     typedef tng_function_status (*set_writing_interval_func_pointer)(tng_trajectory_t,
82                                                                      const gmx_int64_t,
83                                                                      const gmx_int64_t,
84                                                                      const gmx_int64_t,
85                                                                      const char*,
86                                                                      const char,
87                                                                      const char);
88 #ifdef GMX_DOUBLE
89     set_writing_interval_func_pointer set_writing_interval = tng_util_generic_write_interval_double_set;
90 #else
91     set_writing_interval_func_pointer set_writing_interval = tng_util_generic_write_interval_set;
92 #endif
93
94     gmx_tng_open(filename, mode, output);
95
96     /* Do we have an input file in TNG format? If so, then there's
97        more data we can copy over, rather than having to improvise. */
98     if (*input)
99     {
100         /* Set parameters (compression, time per frame, molecule
101          * information, number of frames per frame set and writing
102          * intervals of positions, box shape and lambdas) of the
103          * output tng container based on their respective values int
104          * the input tng container */
105         double      time, compression_precision;
106         gmx_int64_t n_frames_per_frame_set, interval = -1;
107
108         tng_compression_precision_get(*input, &compression_precision);
109         tng_compression_precision_set(*output, compression_precision);
110         // TODO make this configurable in a future version
111         char compression_type = TNG_TNG_COMPRESSION;
112
113         tng_molecule_system_copy(*input, *output);
114
115         tng_time_per_frame_get(*input, &time);
116         tng_time_per_frame_set(*output, time);
117
118         tng_num_frames_per_frame_set_get(*input, &n_frames_per_frame_set);
119         tng_num_frames_per_frame_set_set(*output, n_frames_per_frame_set);
120
121         for (int i = 0; i < defaultNumIds; i++)
122         {
123             if (tng_data_get_stride_length(*input, fallbackIds[i], -1, &interval)
124                 == TNG_SUCCESS)
125             {
126                 switch (fallbackIds[i])
127                 {
128                     case TNG_TRAJ_POSITIONS:
129                     case TNG_TRAJ_VELOCITIES:
130                         set_writing_interval(*output, interval, 3, fallbackIds[i],
131                                              fallbackNames[i], TNG_PARTICLE_BLOCK_DATA,
132                                              compression_type);
133                         break;
134                     case TNG_TRAJ_FORCES:
135                         set_writing_interval(*output, interval, 3, fallbackIds[i],
136                                              fallbackNames[i], TNG_PARTICLE_BLOCK_DATA,
137                                              TNG_GZIP_COMPRESSION);
138                         break;
139                     case TNG_TRAJ_BOX_SHAPE:
140                         set_writing_interval(*output, interval, 9, fallbackIds[i],
141                                              fallbackNames[i], TNG_NON_PARTICLE_BLOCK_DATA,
142                                              TNG_GZIP_COMPRESSION);
143                         break;
144                     case TNG_GMX_LAMBDA:
145                         set_writing_interval(*output, interval, 1, fallbackIds[i],
146                                              fallbackNames[i], TNG_NON_PARTICLE_BLOCK_DATA,
147                                              TNG_GZIP_COMPRESSION);
148                     default:
149                         continue;
150                 }
151             }
152         }
153
154     }
155     else
156     {
157         /* TODO after trjconv is modularized: fix this so the user can
158            change precision when they are doing an operation where
159            this makes sense, and not otherwise.
160
161            char compression = bUseLossyCompression ? TNG_TNG_COMPRESSION : TNG_GZIP_COMPRESSION;
162            gmx_tng_set_compression_precision(*output, ndec2prec(nDecimalsOfPrecision));
163          */
164         gmx_tng_add_mtop(*output, mtop);
165         tng_num_frames_per_frame_set_set(*output, 1);
166     }
167
168     if (index && nAtoms > 0)
169     {
170         gmx_tng_setup_atom_subgroup(*output, nAtoms, index, indexGroupName);
171     }
172
173     /* If for some reason there are more requested atoms than there are atoms in the
174      * molecular system create a number of implicit atoms (without atom data) to
175      * compensate for that. */
176     if (nAtoms >= 0)
177     {
178         tng_implicit_num_particles_set(*output, nAtoms);
179     }
180 #else
181     GMX_UNUSED_VALUE(filename);
182     GMX_UNUSED_VALUE(mode);
183     GMX_UNUSED_VALUE(input);
184     GMX_UNUSED_VALUE(output);
185     GMX_UNUSED_VALUE(nAtoms);
186     GMX_UNUSED_VALUE(mtop);
187     GMX_UNUSED_VALUE(index);
188     GMX_UNUSED_VALUE(indexGroupName);
189 #endif
190 }
191
192 void gmx_write_tng_from_trxframe(tng_trajectory_t        output,
193                                  t_trxframe             *frame,
194                                  int                     natoms)
195 {
196 #ifdef GMX_USE_TNG
197     if (frame->step > 0)
198     {
199         double timePerFrame = frame->time * PICO / frame->step;
200         tng_time_per_frame_set(output, timePerFrame);
201     }
202     if (natoms < 0)
203     {
204         natoms = frame->natoms;
205     }
206     gmx_fwrite_tng(output,
207                    TRUE,
208                    frame->step,
209                    frame->time,
210                    0,
211                    (const rvec *) frame->box,
212                    natoms,
213                    (const rvec *) frame->x,
214                    (const rvec *) frame->v,
215                    (const rvec *) frame->f);
216 #else
217     GMX_UNUSED_VALUE(output);
218     GMX_UNUSED_VALUE(frame);
219     GMX_UNUSED_VALUE(natoms);
220 #endif
221 }
222
223 #ifdef GMX_USE_TNG
224 static void
225 convert_array_to_real_array(void       *from,
226                             real       *to,
227                             const float fact,
228                             const int   nAtoms,
229                             const int   nValues,
230                             const char  datatype)
231 {
232     int i, j;
233
234     switch (datatype)
235     {
236         case TNG_FLOAT_DATA:
237             if (sizeof(real) == sizeof(float))
238             {
239                 if (fact == 1)
240                 {
241                     memcpy(to, from, nValues * sizeof(real) * nAtoms);
242                 }
243                 else
244                 {
245                     for (i = 0; i < nAtoms; i++)
246                     {
247                         for (j = 0; j < nValues; j++)
248                         {
249                             to[i*nValues+j] = (real)((float *)from)[i*nValues+j] * fact;
250                         }
251                     }
252                 }
253             }
254             else
255             {
256                 for (i = 0; i < nAtoms; i++)
257                 {
258                     for (j = 0; j < nValues; j++)
259                     {
260                         to[i*nValues+j] = (real)((float *)from)[i*nValues+j] * fact;
261                     }
262                 }
263             }
264             break;
265         case TNG_INT_DATA:
266             for (i = 0; i < nAtoms; i++)
267             {
268                 for (j = 0; j < nValues; j++)
269                 {
270                     to[i*nValues+j] = (real)((gmx_int64_t *)from)[i*nValues+j] * fact;
271                 }
272             }
273             break;
274         case TNG_DOUBLE_DATA:
275             if (sizeof(real) == sizeof(double))
276             {
277                 if (fact == 1)
278                 {
279                     memcpy(to, from, nValues * sizeof(real) * nAtoms);
280                 }
281                 else
282                 {
283                     for (i = 0; i < nAtoms; i++)
284                     {
285                         for (j = 0; j < nValues; j++)
286                         {
287                             to[i*nValues+j] = (real)((double *)from)[i*nValues+j] * fact;
288                         }
289                     }
290                 }
291             }
292             else
293             {
294                 for (i = 0; i < nAtoms; i++)
295                 {
296                     for (j = 0; j < nValues; j++)
297                     {
298                         to[i*nValues+j] = (real)((double *)from)[i*nValues+j] * fact;
299                     }
300                 }
301             }
302             break;
303         default:
304             gmx_incons("Illegal datatype when converting values to a real array!");
305             return;
306     }
307     return;
308 }
309
310 static real getDistanceScaleFactor(tng_trajectory_t in)
311 {
312     gmx_int64_t exp = -1;
313     real        distanceScaleFactor;
314
315     // TODO Hopefully, TNG 2.0 will do this kind of thing for us
316     tng_distance_unit_exponential_get(in, &exp);
317
318     // GROMACS expects distances in nm
319     switch (exp)
320     {
321         case 9:
322             distanceScaleFactor = NANO/NANO;
323             break;
324         case 10:
325             distanceScaleFactor = NANO/ANGSTROM;
326             break;
327         default:
328             distanceScaleFactor = pow(10.0, exp + 9.0);
329     }
330
331     return distanceScaleFactor;
332 }
333 #endif
334
335 void gmx_tng_setup_atom_subgroup(tng_trajectory_t tng,
336                                  const int        nind,
337                                  const atom_id   *ind,
338                                  const char      *name)
339 {
340 #ifdef GMX_USE_TNG
341     gmx_int64_t              nAtoms, cnt, nMols;
342     tng_molecule_t           mol, iterMol;
343     tng_chain_t              chain;
344     tng_residue_t            res;
345     tng_atom_t               atom;
346     tng_function_status      stat;
347
348     tng_num_particles_get(tng, &nAtoms);
349
350     if (nAtoms == nind)
351     {
352         return;
353     }
354
355     stat = tng_molecule_find(tng, name, -1, &mol);
356     if (stat == TNG_SUCCESS)
357     {
358         tng_molecule_num_atoms_get(tng, mol, &nAtoms);
359         tng_molecule_cnt_get(tng, mol, &cnt);
360         if (nAtoms == nind)
361         {
362             stat = TNG_SUCCESS;
363         }
364         else
365         {
366             stat = TNG_FAILURE;
367         }
368     }
369     if (stat == TNG_FAILURE)
370     {
371         /* The indexed atoms are added to one separate molecule. */
372         tng_molecule_alloc(tng, &mol);
373         tng_molecule_name_set(tng, mol, name);
374         tng_molecule_chain_add(tng, mol, "", &chain);
375
376         for (int i = 0; i < nind; i++)
377         {
378             char        temp_name[256], temp_type[256];
379
380             /* Try to retrieve the residue name of the atom */
381             stat = tng_residue_name_of_particle_nr_get(tng, ind[i], temp_name, 256);
382             if (stat != TNG_SUCCESS)
383             {
384                 temp_name[0] = '\0';
385             }
386             /* Check if the molecule of the selection already contains this residue */
387             if (tng_chain_residue_find(tng, chain, temp_name, -1, &res)
388                 != TNG_SUCCESS)
389             {
390                 tng_chain_residue_add(tng, chain, temp_name, &res);
391             }
392             /* Try to find the original name and type of the atom */
393             stat = tng_atom_name_of_particle_nr_get(tng, ind[i], temp_name, 256);
394             if (stat != TNG_SUCCESS)
395             {
396                 temp_name[0] = '\0';
397             }
398             stat = tng_atom_type_of_particle_nr_get(tng, ind[i], temp_type, 256);
399             if (stat != TNG_SUCCESS)
400             {
401                 temp_type[0] = '\0';
402             }
403             tng_residue_atom_w_id_add(tng, res, temp_name, temp_type, ind[i], &atom);
404         }
405         tng_molecule_existing_add(tng, &mol);
406     }
407     /* Set the count of the molecule containing the selected atoms to 1 and all
408      * other molecules to 0 */
409     tng_molecule_cnt_set(tng, mol, 1);
410     tng_num_molecule_types_get(tng, &nMols);
411     for (gmx_int64_t k = 0; k < nMols; k++)
412     {
413         tng_molecule_of_index_get(tng, k, &iterMol);
414         if (iterMol == mol)
415         {
416             continue;
417         }
418         tng_molecule_cnt_set(tng, iterMol, 0);
419     }
420 #else
421     GMX_UNUSED_VALUE(tng);
422     GMX_UNUSED_VALUE(nind);
423     GMX_UNUSED_VALUE(ind);
424     GMX_UNUSED_VALUE(name);
425 #endif
426 }
427
428 /* TODO: If/when TNG acquires the ability to copy data blocks without
429  * uncompressing them, then this implemenation should be reconsidered.
430  * Ideally, gmx trjconv -f a.tng -o b.tng -b 10 -e 20 would be fast
431  * and lose no information. */
432 gmx_bool gmx_read_next_tng_frame(tng_trajectory_t            input,
433                                  t_trxframe                 *fr,
434                                  gmx_int64_t                *requestedIds,
435                                  int                         numRequestedIds)
436 {
437 #ifdef GMX_USE_TNG
438     gmx_bool                bOK = TRUE;
439     tng_function_status     stat;
440     gmx_int64_t             numberOfAtoms = -1, frameNumber = -1;
441     gmx_int64_t             nBlocks, blockId, *blockIds = NULL, codecId;
442     char                    datatype      = -1;
443     void                   *values        = NULL;
444     double                  frameTime     = -1.0;
445     int                     size, blockDependency;
446     double                  prec;
447     const int               defaultNumIds = 5;
448     static gmx_int64_t      fallbackRequestedIds[defaultNumIds] =
449     {
450         TNG_TRAJ_BOX_SHAPE, TNG_TRAJ_POSITIONS,
451         TNG_TRAJ_VELOCITIES, TNG_TRAJ_FORCES,
452         TNG_GMX_LAMBDA
453     };
454
455
456     fr->bStep     = FALSE;
457     fr->bTime     = FALSE;
458     fr->bLambda   = FALSE;
459     fr->bAtoms    = FALSE;
460     fr->bPrec     = FALSE;
461     fr->bX        = FALSE;
462     fr->bV        = FALSE;
463     fr->bF        = FALSE;
464     fr->bBox      = FALSE;
465
466     /* If no specific IDs were requested read all block types that can
467      * currently be interpreted */
468     if (!requestedIds || numRequestedIds == 0)
469     {
470         numRequestedIds = defaultNumIds;
471         requestedIds    = fallbackRequestedIds;
472     }
473
474     stat = tng_num_particles_get(input, &numberOfAtoms);
475     if (stat != TNG_SUCCESS)
476     {
477         gmx_file("Cannot determine number of atoms from TNG file.");
478     }
479     fr->natoms = numberOfAtoms;
480
481     if (!gmx_get_tng_data_block_types_of_next_frame(input,
482                                                     fr->step,
483                                                     numRequestedIds,
484                                                     requestedIds,
485                                                     &frameNumber,
486                                                     &nBlocks,
487                                                     &blockIds))
488     {
489         return FALSE;
490     }
491
492     if (nBlocks == 0)
493     {
494         return FALSE;
495     }
496
497     for (gmx_int64_t i = 0; i < nBlocks; i++)
498     {
499         blockId = blockIds[i];
500         tng_data_block_dependency_get(input, blockId, &blockDependency);
501         if (blockDependency & TNG_PARTICLE_DEPENDENT)
502         {
503             stat = tng_util_particle_data_next_frame_read(input,
504                                                           blockId,
505                                                           &values,
506                                                           &datatype,
507                                                           &frameNumber,
508                                                           &frameTime);
509         }
510         else
511         {
512             stat = tng_util_non_particle_data_next_frame_read(input,
513                                                               blockId,
514                                                               &values,
515                                                               &datatype,
516                                                               &frameNumber,
517                                                               &frameTime);
518         }
519         if (stat == TNG_CRITICAL)
520         {
521             gmx_file("Cannot read positions from TNG file.");
522             return FALSE;
523         }
524         else if (stat == TNG_FAILURE)
525         {
526             continue;
527         }
528         switch (blockId)
529         {
530             case TNG_TRAJ_BOX_SHAPE:
531                 switch (datatype)
532                 {
533                     case TNG_INT_DATA:
534                         size = sizeof(gmx_int64_t);
535                         break;
536                     case TNG_FLOAT_DATA:
537                         size = sizeof(float);
538                         break;
539                     case TNG_DOUBLE_DATA:
540                         size = sizeof(double);
541                         break;
542                     default:
543                         size = 0; /* Just to make the compiler happy. */
544                         gmx_incons("Illegal datatype of box shape values!");
545                 }
546                 for (int i = 0; i < DIM; i++)
547                 {
548                     convert_array_to_real_array((char *)(values) + size * i * DIM,
549                                                 (real *) fr->box[i],
550                                                 getDistanceScaleFactor(input),
551                                                 1,
552                                                 DIM,
553                                                 datatype);
554                 }
555                 fr->bBox = TRUE;
556                 break;
557             case TNG_TRAJ_POSITIONS:
558                 srenew(fr->x, fr->natoms);
559                 convert_array_to_real_array(values,
560                                             (real *) fr->x,
561                                             getDistanceScaleFactor(input),
562                                             fr->natoms,
563                                             DIM,
564                                             datatype);
565                 fr->bX = TRUE;
566                 tng_util_frame_current_compression_get(input, blockId, &codecId, &prec);
567                 /* This must be updated if/when more lossy compression methods are added */
568                 if (codecId == TNG_TNG_COMPRESSION)
569                 {
570                     fr->prec  = prec;
571                     fr->bPrec = TRUE;
572                 }
573                 break;
574             case TNG_TRAJ_VELOCITIES:
575                 srenew(fr->v, fr->natoms);
576                 convert_array_to_real_array(values,
577                                             (real *) fr->v,
578                                             getDistanceScaleFactor(input),
579                                             fr->natoms,
580                                             DIM,
581                                             datatype);
582                 fr->bV = TRUE;
583                 tng_util_frame_current_compression_get(input, blockId, &codecId, &prec);
584                 /* This must be updated if/when more lossy compression methods are added */
585                 if (codecId == TNG_TNG_COMPRESSION)
586                 {
587                     fr->prec  = prec;
588                     fr->bPrec = TRUE;
589                 }
590                 break;
591             case TNG_TRAJ_FORCES:
592                 srenew(fr->f, fr->natoms);
593                 convert_array_to_real_array(values,
594                                             (real *) fr->f,
595                                             getDistanceScaleFactor(input),
596                                             fr->natoms,
597                                             DIM,
598                                             datatype);
599                 fr->bF = TRUE;
600                 break;
601             case TNG_GMX_LAMBDA:
602                 switch (datatype)
603                 {
604                     case TNG_FLOAT_DATA:
605                         fr->lambda = (*(float *)values);
606                         break;
607                     case TNG_DOUBLE_DATA:
608                         fr->lambda = (*(double *)values);
609                         break;
610                     default:
611                         gmx_incons("Illegal datatype lambda value!");
612                 }
613                 fr->bLambda = TRUE;
614                 break;
615             default:
616                 gmx_warning("Illegal block type! Currently GROMACS tools can only handle certain data types. Skipping block.");
617         }
618         /* values does not have to be freed before reading next frame. It will
619          * be reallocated if it is not NULL. */
620     }
621
622     fr->step  = (int) frameNumber;
623     fr->bStep = TRUE;
624     // Convert the time to ps
625     fr->time  = frameTime / PICO;
626     fr->bTime = TRUE;
627
628     /* values must be freed before leaving this function */
629     sfree(values);
630
631     return bOK;
632 #else
633     GMX_UNUSED_VALUE(input);
634     GMX_UNUSED_VALUE(fr);
635     GMX_UNUSED_VALUE(requestedIds);
636     GMX_UNUSED_VALUE(numRequestedIds);
637     return FALSE;
638 #endif
639 }
640
641 void gmx_print_tng_molecule_system(tng_trajectory_t input,
642                                    FILE            *stream)
643 {
644 #ifdef GMX_USE_TNG
645     gmx_int64_t        nMolecules, nChains, nResidues, nAtoms, *molCntList;
646     tng_molecule_t     molecule;
647     tng_chain_t        chain;
648     tng_residue_t      residue;
649     tng_atom_t         atom;
650     char               str[256], varNAtoms;
651
652     tng_num_molecule_types_get(input, &nMolecules);
653     tng_molecule_cnt_list_get(input, &molCntList);
654     /* Can the number of particles change in the trajectory or is it constant? */
655     tng_num_particles_variable_get(input, &varNAtoms);
656
657     for (gmx_int64_t i = 0; i < nMolecules; i++)
658     {
659         tng_molecule_of_index_get(input, i, &molecule);
660         tng_molecule_name_get(input, molecule, str, 256);
661         if (varNAtoms == TNG_CONSTANT_N_ATOMS)
662         {
663             if ((int)molCntList[i] == 0)
664             {
665                 continue;
666             }
667             fprintf(stream, "Molecule: %s, count: %d\n", str, (int)molCntList[i]);
668         }
669         else
670         {
671             fprintf(stream, "Molecule: %s\n", str);
672         }
673         tng_molecule_num_chains_get(input, molecule, &nChains);
674         if (nChains > 0)
675         {
676             for (gmx_int64_t j = 0; j < nChains; j++)
677             {
678                 tng_molecule_chain_of_index_get(input, molecule, j, &chain);
679                 tng_chain_name_get(input, chain, str, 256);
680                 fprintf(stream, "\tChain: %s\n", str);
681                 tng_chain_num_residues_get(input, chain, &nResidues);
682                 for (gmx_int64_t k = 0; k < nResidues; k++)
683                 {
684                     tng_chain_residue_of_index_get(input, chain, k, &residue);
685                     tng_residue_name_get(input, residue, str, 256);
686                     fprintf(stream, "\t\tResidue: %s\n", str);
687                     tng_residue_num_atoms_get(input, residue, &nAtoms);
688                     for (gmx_int64_t l = 0; l < nAtoms; l++)
689                     {
690                         tng_residue_atom_of_index_get(input, residue, l, &atom);
691                         tng_atom_name_get(input, atom, str, 256);
692                         fprintf(stream, "\t\t\tAtom: %s", str);
693                         tng_atom_type_get(input, atom, str, 256);
694                         fprintf(stream, " (%s)\n", str);
695                     }
696                 }
697             }
698         }
699         /* It is possible to have a molecule without chains, in which case
700          * residues in the molecule can be iterated through without going
701          * through chains. */
702         else
703         {
704             tng_molecule_num_residues_get(input, molecule, &nResidues);
705             if (nResidues > 0)
706             {
707                 for (gmx_int64_t k = 0; k < nResidues; k++)
708                 {
709                     tng_molecule_residue_of_index_get(input, molecule, k, &residue);
710                     tng_residue_name_get(input, residue, str, 256);
711                     fprintf(stream, "\t\tResidue: %s\n", str);
712                     tng_residue_num_atoms_get(input, residue, &nAtoms);
713                     for (gmx_int64_t l = 0; l < nAtoms; l++)
714                     {
715                         tng_residue_atom_of_index_get(input, residue, l, &atom);
716                         tng_atom_name_get(input, atom, str, 256);
717                         fprintf(stream, "\t\t\tAtom: %s", str);
718                         tng_atom_type_get(input, atom, str, 256);
719                         fprintf(stream, " (%s)\n", str);
720                     }
721                 }
722             }
723             else
724             {
725                 tng_molecule_num_atoms_get(input, molecule, &nAtoms);
726                 for (gmx_int64_t l = 0; l < nAtoms; l++)
727                 {
728                     tng_molecule_atom_of_index_get(input, molecule, l, &atom);
729                     tng_atom_name_get(input, atom, str, 256);
730                     fprintf(stream, "\t\t\tAtom: %s", str);
731                     tng_atom_type_get(input, atom, str, 256);
732                     fprintf(stream, " (%s)\n", str);
733                 }
734             }
735         }
736     }
737 #else
738     GMX_UNUSED_VALUE(input);
739     GMX_UNUSED_VALUE(stream);
740 #endif
741 }
742
743 gmx_bool gmx_get_tng_data_block_types_of_next_frame(tng_trajectory_t     input,
744                                                     int                  frame,
745                                                     int                  nRequestedIds,
746                                                     gmx_int64_t         *requestedIds,
747                                                     gmx_int64_t         *nextFrame,
748                                                     gmx_int64_t         *nBlocks,
749                                                     gmx_int64_t        **blockIds)
750 {
751 #ifdef GMX_USE_TNG
752     tng_function_status stat;
753
754     stat = tng_util_trajectory_next_frame_present_data_blocks_find(input, frame,
755                                                                    nRequestedIds, requestedIds,
756                                                                    nextFrame,
757                                                                    nBlocks, blockIds);
758
759     if (stat == TNG_CRITICAL)
760     {
761         gmx_file("Cannot read TNG file. Cannot find data blocks of next frame.");
762     }
763     else if (stat == TNG_FAILURE)
764     {
765         return FALSE;
766     }
767     return TRUE;
768 #else
769     GMX_UNUSED_VALUE(input);
770     GMX_UNUSED_VALUE(frame);
771     GMX_UNUSED_VALUE(nRequestedIds);
772     GMX_UNUSED_VALUE(requestedIds);
773     GMX_UNUSED_VALUE(nextFrame);
774     GMX_UNUSED_VALUE(nBlocks);
775     GMX_UNUSED_VALUE(blockIds);
776     return FALSE;
777 #endif
778 }
779
780 gmx_bool gmx_get_tng_data_next_frame_of_block_type(tng_trajectory_t     input,
781                                                    gmx_int64_t          blockId,
782                                                    real               **values,
783                                                    gmx_int64_t         *frameNumber,
784                                                    double              *frameTime,
785                                                    gmx_int64_t         *nValuesPerFrame,
786                                                    gmx_int64_t         *nAtoms,
787                                                    real                *prec,
788                                                    char                *name,
789                                                    int                  maxLen,
790                                                    gmx_bool            *bOK)
791 {
792 #ifdef GMX_USE_TNG
793     tng_function_status stat;
794     char                datatype = -1;
795     gmx_int64_t         codecId;
796     int                 blockDependency;
797     void               *data = 0;
798     double              localPrec;
799
800     stat = tng_data_block_name_get(input, blockId, name, maxLen);
801     if (stat != TNG_SUCCESS)
802     {
803         gmx_file("Cannot read next frame of TNG file");
804     }
805     stat = tng_data_block_dependency_get(input, blockId, &blockDependency);
806     if (stat != TNG_SUCCESS)
807     {
808         gmx_file("Cannot read next frame of TNG file");
809     }
810     if (blockDependency & TNG_PARTICLE_DEPENDENT)
811     {
812         tng_num_particles_get(input, nAtoms);
813         stat = tng_util_particle_data_next_frame_read(input,
814                                                       blockId,
815                                                       &data,
816                                                       &datatype,
817                                                       frameNumber,
818                                                       frameTime);
819     }
820     else
821     {
822         *nAtoms = 1; /* There are not actually any atoms, but it is used for
823                         allocating memory */
824         stat    = tng_util_non_particle_data_next_frame_read(input,
825                                                              blockId,
826                                                              &data,
827                                                              &datatype,
828                                                              frameNumber,
829                                                              frameTime);
830     }
831     if (stat == TNG_CRITICAL)
832     {
833         gmx_file("Cannot read next frame of TNG file");
834     }
835     if (stat == TNG_FAILURE)
836     {
837         *bOK = TRUE;
838         return FALSE;
839     }
840
841     stat = tng_data_block_num_values_per_frame_get(input, blockId, nValuesPerFrame);
842     if (stat != TNG_SUCCESS)
843     {
844         gmx_file("Cannot read next frame of TNG file");
845     }
846     snew(*values, sizeof(real) * *nValuesPerFrame * *nAtoms);
847     convert_array_to_real_array(data,
848                                 *values,
849                                 getDistanceScaleFactor(input),
850                                 *nAtoms,
851                                 *nValuesPerFrame,
852                                 datatype);
853
854     tng_util_frame_current_compression_get(input, blockId, &codecId, &localPrec);
855
856     /* This must be updated if/when more lossy compression methods are added */
857     if (codecId != TNG_TNG_COMPRESSION)
858     {
859         *prec = -1.0;
860     }
861     else
862     {
863         *prec = localPrec;
864     }
865
866     *bOK = TRUE;
867     return TRUE;
868 #else
869     GMX_UNUSED_VALUE(input);
870     GMX_UNUSED_VALUE(blockId);
871     GMX_UNUSED_VALUE(values);
872     GMX_UNUSED_VALUE(frameNumber);
873     GMX_UNUSED_VALUE(frameTime);
874     GMX_UNUSED_VALUE(nValuesPerFrame);
875     GMX_UNUSED_VALUE(nAtoms);
876     GMX_UNUSED_VALUE(prec);
877     GMX_UNUSED_VALUE(name);
878     GMX_UNUSED_VALUE(maxLen);
879     GMX_UNUSED_VALUE(bOK);
880     return FALSE;
881 #endif
882 }