3/3 of old-style casting
[alexxy/gromacs.git] / src / gromacs / fileio / tngio.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2013,2014,2015,2018, 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 "gmxpre.h"
36
37 #include "tngio.h"
38
39 #include "config.h"
40
41 #include <cmath>
42
43 #include <algorithm>
44 #include <iterator>
45 #include <memory>
46 #include <vector>
47
48 #if GMX_USE_TNG
49 #include "tng/tng_io.h"
50 #endif
51
52 #include "gromacs/math/units.h"
53 #include "gromacs/math/utilities.h"
54 #include "gromacs/mdtypes/inputrec.h"
55 #include "gromacs/topology/ifunc.h"
56 #include "gromacs/topology/topology.h"
57 #include "gromacs/trajectory/trajectoryframe.h"
58 #include "gromacs/utility/basedefinitions.h"
59 #include "gromacs/utility/baseversion.h"
60 #include "gromacs/utility/fatalerror.h"
61 #include "gromacs/utility/futil.h"
62 #include "gromacs/utility/gmxassert.h"
63 #include "gromacs/utility/programcontext.h"
64 #include "gromacs/utility/smalloc.h"
65 #include "gromacs/utility/sysinfo.h"
66 #include "gromacs/utility/unique_cptr.h"
67
68 /*! \brief Gromacs Wrapper around tng datatype
69  *
70  * This could in principle hold any GROMACS-specific requirements not yet
71  * implemented in or not relevant to the TNG library itself. However, for now
72  * we only use it to handle some shortcomings we have discovered, where the TNG
73  * API itself is a bit fragile and can end up overwriting data if called several
74  * times with the same frame number.
75  * The logic to determine the time per step was also a bit fragile. This is not
76  * critical, but since we anyway need a wrapper for ensuring unique frame
77  * numbers, we can also use it to store the time of the first step and use that
78  * to derive a slightly better/safer estimate of the time per step.
79  *
80  * At some future point where we have a second-generation TNG API we should
81  * consider removing this again.
82  */
83 struct gmx_tng_trajectory
84 {
85     tng_trajectory_t tng;                  //!< Actual TNG handle (pointer)
86     bool             lastStepDataIsValid;  //!< True if lastStep has been set
87     std::int64_t     lastStep;             //!< Index/step used for last frame
88     bool             lastTimeDataIsValid;  //!< True if lastTime has been set
89     double           lastTime;             //!< Time of last frame (TNG unit is seconds)
90     bool             timePerFrameIsSet;    //!< True if we have set the time per frame
91     int              boxOutputInterval;    //!< Number of steps between the output of box size
92     int              lambdaOutputInterval; //!< Number of steps between the output of lambdas
93 };
94
95 static const char *modeToVerb(char mode)
96 {
97     const char *p;
98     switch (mode)
99     {
100         case 'r':
101             p = "reading";
102             break;
103         case 'w':
104             p = "writing";
105             break;
106         case 'a':
107             p = "appending";
108             break;
109         default:
110             gmx_fatal(FARGS, "Invalid file opening mode %c", mode);
111     }
112     return p;
113 }
114
115 void gmx_tng_open(const char           *filename,
116                   char                  mode,
117                   gmx_tng_trajectory_t *gmx_tng)
118 {
119 #if GMX_USE_TNG
120     /* First check whether we have to make a backup,
121      * only for writing, not for read or append.
122      */
123     if (mode == 'w')
124     {
125         make_backup(filename);
126     }
127
128     *gmx_tng                        = new gmx_tng_trajectory;
129     (*gmx_tng)->lastStepDataIsValid = false;
130     (*gmx_tng)->lastTimeDataIsValid = false;
131     (*gmx_tng)->timePerFrameIsSet   = false;
132     tng_trajectory_t * tng = &(*gmx_tng)->tng;
133
134     /* tng must not be pointing at already allocated memory.
135      * Memory will be allocated by tng_util_trajectory_open() and must
136      * later on be freed by tng_util_trajectory_close(). */
137     if (TNG_SUCCESS != tng_util_trajectory_open(filename, mode, tng))
138     {
139         /* TNG does return more than one degree of error, but there is
140            no use case for GROMACS handling the non-fatal errors
141            gracefully. */
142         gmx_fatal(FARGS,
143                   "File I/O error while opening %s for %s",
144                   filename,
145                   modeToVerb(mode));
146     }
147
148     if (mode == 'w' || mode == 'a')
149     {
150         char hostname[256];
151         gmx_gethostname(hostname, 256);
152         if (mode == 'w')
153         {
154             tng_first_computer_name_set(*tng, hostname);
155         }
156         else
157         {
158             tng_last_computer_name_set(*tng, hostname);
159         }
160
161         char        programInfo[256];
162         const char *precisionString = "";
163 #if GMX_DOUBLE
164         precisionString = " (double precision)";
165 #endif
166         sprintf(programInfo, "%.100s %.128s%.24s",
167                 gmx::getProgramContext().displayName(),
168                 gmx_version(), precisionString);
169         if (mode == 'w')
170         {
171             tng_first_program_name_set(*tng, programInfo);
172         }
173         else
174         {
175             tng_last_program_name_set(*tng, programInfo);
176         }
177
178         char username[256];
179         if (!gmx_getusername(username, 256))
180         {
181             if (mode == 'w')
182             {
183                 tng_first_user_name_set(*tng, username);
184             }
185             else
186             {
187                 tng_last_user_name_set(*tng, username);
188                 tng_file_headers_write(*tng, TNG_USE_HASH);
189             }
190         }
191     }
192 #else
193     gmx_file("GROMACS was compiled without TNG support, cannot handle this file type");
194     GMX_UNUSED_VALUE(filename);
195     GMX_UNUSED_VALUE(mode);
196     GMX_UNUSED_VALUE(gmx_tng);
197 #endif
198 }
199
200 void gmx_tng_close(gmx_tng_trajectory_t *gmx_tng)
201 {
202     /* We have to check that tng is set because
203      * tng_util_trajectory_close wants to return a NULL in it, and
204      * gives a fatal error if it is NULL. */
205 #if GMX_USE_TNG
206     if (gmx_tng == nullptr || *gmx_tng == nullptr)
207     {
208         return;
209     }
210     tng_trajectory_t * tng = &(*gmx_tng)->tng;
211
212     if (tng)
213     {
214         tng_util_trajectory_close(tng);
215     }
216     delete *gmx_tng;
217     *gmx_tng = nullptr;
218
219 #else
220     GMX_UNUSED_VALUE(gmx_tng);
221 #endif
222 }
223
224 #if GMX_USE_TNG
225 static void addTngMoleculeFromTopology(gmx_tng_trajectory_t gmx_tng,
226                                        const char          *moleculeName,
227                                        const t_atoms       *atoms,
228                                        int64_t              numMolecules,
229                                        tng_molecule_t      *tngMol)
230 {
231     tng_trajectory_t tng      = gmx_tng->tng;
232     tng_chain_t      tngChain = nullptr;
233     tng_residue_t    tngRes   = nullptr;
234
235     if (tng_molecule_add(tng, moleculeName, tngMol) != TNG_SUCCESS)
236     {
237         gmx_file("Cannot add molecule to TNG molecular system.");
238     }
239
240     for (int atomIndex = 0; atomIndex < atoms->nr; atomIndex++)
241     {
242         const t_atom *at = &atoms->atom[atomIndex];
243         /* FIXME: Currently the TNG API can only add atoms belonging to a
244          * residue and chain. Wait for TNG 2.0*/
245         if (atoms->nres > 0)
246         {
247             const t_resinfo *resInfo        = &atoms->resinfo[at->resind];
248             char             chainName[2]   = {resInfo->chainid, 0};
249             tng_atom_t       tngAtom        = nullptr;
250             t_atom          *prevAtom;
251
252             if (atomIndex > 0)
253             {
254                 prevAtom = &atoms->atom[atomIndex - 1];
255             }
256             else
257             {
258                 prevAtom = nullptr;
259             }
260
261             /* If this is the first atom or if the residue changed add the
262              * residue to the TNG molecular system. */
263             if (!prevAtom || resInfo != &atoms->resinfo[prevAtom->resind])
264             {
265                 /* If this is the first atom or if the chain changed add
266                  * the chain to the TNG molecular system. */
267                 if (!prevAtom || resInfo->chainid !=
268                     atoms->resinfo[prevAtom->resind].chainid)
269                 {
270                     tng_molecule_chain_add(tng, *tngMol, chainName,
271                                            &tngChain);
272                 }
273                 /* FIXME: When TNG supports both residue index and residue
274                  * number the latter should be used. Wait for TNG 2.0*/
275                 tng_chain_residue_add(tng, tngChain, *resInfo->name, &tngRes);
276             }
277             tng_residue_atom_add(tng, tngRes, *(atoms->atomname[atomIndex]), *(atoms->atomtype[atomIndex]), &tngAtom);
278         }
279     }
280     tng_molecule_cnt_set(tng, *tngMol, numMolecules);
281 }
282
283 void gmx_tng_add_mtop(gmx_tng_trajectory_t  gmx_tng,
284                       const gmx_mtop_t     *mtop)
285 {
286     int                i;
287     int                j;
288     std::vector<real>  atomCharges;
289     std::vector<real>  atomMasses;
290     const t_ilist     *ilist;
291     tng_bond_t         tngBond;
292     char               datatype;
293
294     tng_trajectory_t   tng = gmx_tng->tng;
295
296     if (!mtop)
297     {
298         /* No topology information available to add. */
299         return;
300     }
301
302 #if GMX_DOUBLE
303     datatype = TNG_DOUBLE_DATA;
304 #else
305     datatype = TNG_FLOAT_DATA;
306 #endif
307
308     atomCharges.reserve(mtop->natoms);
309     atomMasses.reserve(mtop->natoms);
310
311     for (const gmx_molblock_t &molBlock :  mtop->molblock)
312     {
313         tng_molecule_t       tngMol  = nullptr;
314         const gmx_moltype_t *molType = &mtop->moltype[molBlock.type];
315
316         /* Add a molecule to the TNG trajectory with the same name as the
317          * current molecule. */
318         addTngMoleculeFromTopology(gmx_tng,
319                                    *(molType->name),
320                                    &molType->atoms,
321                                    molBlock.nmol,
322                                    &tngMol);
323
324         /* Bonds have to be deduced from interactions (constraints etc). Different
325          * interactions have different sets of parameters. */
326         /* Constraints are specified using two atoms */
327         for (i = 0; i < F_NRE; i++)
328         {
329             if (IS_CHEMBOND(i))
330             {
331                 ilist = &molType->ilist[i];
332                 if (ilist)
333                 {
334                     j = 1;
335                     while (j < ilist->nr)
336                     {
337                         tng_molecule_bond_add(tng, tngMol, ilist->iatoms[j], ilist->iatoms[j+1], &tngBond);
338                         j += 3;
339                     }
340                 }
341             }
342         }
343         /* Settle is described using three atoms */
344         ilist = &molType->ilist[F_SETTLE];
345         if (ilist)
346         {
347             j = 1;
348             while (j < ilist->nr)
349             {
350                 tng_molecule_bond_add(tng, tngMol, ilist->iatoms[j], ilist->iatoms[j+1], &tngBond);
351                 tng_molecule_bond_add(tng, tngMol, ilist->iatoms[j], ilist->iatoms[j+2], &tngBond);
352                 j += 4;
353             }
354         }
355         /* First copy atom charges and masses, first atom by atom and then copy the memory for the molecule instances.
356          * FIXME: Atom B state data should also be written to TNG (v 2.0?) */
357         for (int atomCounter = 0; atomCounter < molType->atoms.nr; atomCounter++)
358         {
359             atomCharges.push_back(molType->atoms.atom[atomCounter].q);
360             atomMasses.push_back(molType->atoms.atom[atomCounter].m);
361         }
362         for (int molCounter = 1; molCounter < molBlock.nmol; molCounter++)
363         {
364             std::copy_n(atomCharges.end() - molType->atoms.nr, molType->atoms.nr, std::back_inserter(atomCharges));
365             std::copy_n(atomMasses.end()  - molType->atoms.nr, molType->atoms.nr, std::back_inserter(atomMasses));
366         }
367     }
368     /* Write the TNG data blocks. */
369     tng_particle_data_block_add(tng, TNG_TRAJ_PARTIAL_CHARGES, "PARTIAL CHARGES",
370                                 datatype, TNG_NON_TRAJECTORY_BLOCK,
371                                 1, 1, 1, 0, mtop->natoms,
372                                 TNG_GZIP_COMPRESSION, atomCharges.data());
373     tng_particle_data_block_add(tng, TNG_TRAJ_MASSES, "ATOM MASSES",
374                                 datatype, TNG_NON_TRAJECTORY_BLOCK,
375                                 1, 1, 1, 0, mtop->natoms,
376                                 TNG_GZIP_COMPRESSION, atomMasses.data());
377 }
378
379 /*! \libinternal \brief Compute greatest common divisor of n1 and n2
380  * if they are positive.
381  *
382  * If only one of n1 and n2 is positive, then return it.
383  * If neither n1 or n2 is positive, then return -1. */
384 static int
385 greatest_common_divisor_if_positive(int n1, int n2)
386 {
387     if (0 >= n1)
388     {
389         return (0 >= n2) ? -1 : n2;
390     }
391     if (0 >= n2)
392     {
393         return n1;
394     }
395
396     /* We have a non-trivial greatest common divisor to compute. */
397     return gmx_greatest_common_divisor(n1, n2);
398 }
399
400 /* By default try to write 100 frames (of actual output) in each frame set.
401  * This number is the number of outputs of the most frequently written data
402  * type per frame set.
403  * TODO for 5.1: Verify that 100 frames per frame set is efficient for most
404  * setups regarding compression efficiency and compression time. Make this
405  * a hidden command-line option? */
406 const int defaultFramesPerFrameSet = 100;
407
408 /*! \libinternal \brief  Set the number of frames per frame
409  * set according to output intervals.
410  * The default is that 100 frames are written of the data
411  * that is written most often. */
412 static void tng_set_frames_per_frame_set(gmx_tng_trajectory_t  gmx_tng,
413                                          const gmx_bool        bUseLossyCompression,
414                                          const t_inputrec     *ir)
415 {
416     int              gcd = -1;
417     tng_trajectory_t tng = gmx_tng->tng;
418
419     /* Set the number of frames per frame set to contain at least
420      * defaultFramesPerFrameSet of the lowest common denominator of
421      * the writing interval of positions and velocities. */
422     /* FIXME after 5.0: consider nstenergy also? */
423     if (bUseLossyCompression)
424     {
425         gcd = ir->nstxout_compressed;
426     }
427     else
428     {
429         gcd = greatest_common_divisor_if_positive(ir->nstxout, ir->nstvout);
430         gcd = greatest_common_divisor_if_positive(gcd, ir->nstfout);
431     }
432     if (0 >= gcd)
433     {
434         return;
435     }
436
437     tng_num_frames_per_frame_set_set(tng, gcd * defaultFramesPerFrameSet);
438 }
439
440 /*! \libinternal \brief Set the data-writing intervals, and number of
441  * frames per frame set */
442 static void set_writing_intervals(gmx_tng_trajectory_t  gmx_tng,
443                                   const gmx_bool        bUseLossyCompression,
444                                   const t_inputrec     *ir)
445 {
446     tng_trajectory_t tng = gmx_tng->tng;
447
448     /* Define pointers to specific writing functions depending on if we
449      * write float or double data */
450     typedef tng_function_status (*set_writing_interval_func_pointer)(tng_trajectory_t,
451                                                                      const int64_t,
452                                                                      const int64_t,
453                                                                      const int64_t,
454                                                                      const char*,
455                                                                      const char,
456                                                                      const char);
457 #if GMX_DOUBLE
458     set_writing_interval_func_pointer set_writing_interval = tng_util_generic_write_interval_double_set;
459 #else
460     set_writing_interval_func_pointer set_writing_interval = tng_util_generic_write_interval_set;
461 #endif
462     int  xout, vout, fout;
463     int  gcd = -1, lowest = -1;
464     char compression;
465
466     tng_set_frames_per_frame_set(gmx_tng, bUseLossyCompression, ir);
467
468     if (bUseLossyCompression)
469     {
470         xout        = ir->nstxout_compressed;
471
472         /* If there is no uncompressed coordinate output write forces
473            and velocities to the compressed tng file. */
474         if (ir->nstxout)
475         {
476             vout        = 0;
477             fout        = 0;
478         }
479         else
480         {
481             vout        = ir->nstvout;
482             fout        = ir->nstfout;
483         }
484         compression = TNG_TNG_COMPRESSION;
485     }
486     else
487     {
488         xout        = ir->nstxout;
489         vout        = ir->nstvout;
490         fout        = ir->nstfout;
491         compression = TNG_GZIP_COMPRESSION;
492     }
493     if (xout)
494     {
495         set_writing_interval(tng, xout, 3, TNG_TRAJ_POSITIONS,
496                              "POSITIONS", TNG_PARTICLE_BLOCK_DATA,
497                              compression);
498         /* TODO: if/when we write energies to TNG also, reconsider how
499          * and when box information is written, because GROMACS
500          * behaviour pre-5.0 was to write the box with every
501          * trajectory frame and every energy frame, and probably
502          * people depend on this. */
503
504         gcd = greatest_common_divisor_if_positive(gcd, xout);
505         if (lowest < 0 || xout < lowest)
506         {
507             lowest = xout;
508         }
509     }
510     if (vout)
511     {
512         set_writing_interval(tng, vout, 3, TNG_TRAJ_VELOCITIES,
513                              "VELOCITIES", TNG_PARTICLE_BLOCK_DATA,
514                              compression);
515
516         gcd = greatest_common_divisor_if_positive(gcd, vout);
517         if (lowest < 0 || vout < lowest)
518         {
519             lowest = vout;
520         }
521     }
522     if (fout)
523     {
524         set_writing_interval(tng, fout, 3, TNG_TRAJ_FORCES,
525                              "FORCES", TNG_PARTICLE_BLOCK_DATA,
526                              TNG_GZIP_COMPRESSION);
527
528         gcd = greatest_common_divisor_if_positive(gcd, fout);
529         if (lowest < 0 || fout < lowest)
530         {
531             lowest = fout;
532         }
533     }
534     if (gcd > 0)
535     {
536         /* Lambdas and box shape written at an interval of the lowest common
537            denominator of other output */
538         set_writing_interval(tng, gcd, 1, TNG_GMX_LAMBDA,
539                              "LAMBDAS", TNG_NON_PARTICLE_BLOCK_DATA,
540                              TNG_GZIP_COMPRESSION);
541
542         set_writing_interval(tng, gcd, 9, TNG_TRAJ_BOX_SHAPE,
543                              "BOX SHAPE", TNG_NON_PARTICLE_BLOCK_DATA,
544                              TNG_GZIP_COMPRESSION);
545         gmx_tng->lambdaOutputInterval = gcd;
546         gmx_tng->boxOutputInterval    = gcd;
547         if (gcd < lowest / 10)
548         {
549             gmx_warning("The lowest common denominator of trajectory output is "
550                         "every %d step(s), whereas the shortest output interval "
551                         "is every %d steps.", gcd, lowest);
552         }
553     }
554 }
555 #endif
556
557 void gmx_tng_prepare_md_writing(gmx_tng_trajectory_t  gmx_tng,
558                                 const gmx_mtop_t     *mtop,
559                                 const t_inputrec     *ir)
560 {
561 #if GMX_USE_TNG
562     gmx_tng_add_mtop(gmx_tng, mtop);
563     set_writing_intervals(gmx_tng, FALSE, ir);
564     tng_time_per_frame_set(gmx_tng->tng, ir->delta_t * PICO);
565     gmx_tng->timePerFrameIsSet = true;
566 #else
567     GMX_UNUSED_VALUE(gmx_tng);
568     GMX_UNUSED_VALUE(mtop);
569     GMX_UNUSED_VALUE(ir);
570 #endif
571 }
572
573 #if GMX_USE_TNG
574 /* Check if all atoms in the molecule system are selected
575  * by a selection group of type specified by gtype. */
576 static gmx_bool all_atoms_selected(const gmx_mtop_t *mtop,
577                                    const int         gtype)
578 {
579     /* Iterate through all atoms in the molecule system and
580      * check if they belong to a selection group of the
581      * requested type. */
582     int i = 0;
583     for (const gmx_molblock_t &molBlock : mtop->molblock)
584     {
585         const gmx_moltype_t &molType = mtop->moltype[molBlock.type];
586         const t_atoms       &atoms   = molType.atoms;
587
588         for (int j = 0; j < molBlock.nmol; j++)
589         {
590             for (int atomIndex = 0; atomIndex < atoms.nr; atomIndex++, i++)
591             {
592                 if (ggrpnr(&mtop->groups, gtype, i) != 0)
593                 {
594                     return FALSE;
595                 }
596             }
597         }
598     }
599     return TRUE;
600 }
601
602 /* Create TNG molecules which will represent each of the selection
603  * groups for writing. But do that only if there is actually a
604  * specified selection group and it is not the whole system.
605  * TODO: Currently the only selection that is taken into account
606  * is egcCompressedX, but other selections should be added when
607  * e.g. writing energies is implemented.
608  */
609 static void add_selection_groups(gmx_tng_trajectory_t  gmx_tng,
610                                  const gmx_mtop_t     *mtop)
611 {
612     const t_atoms           *atoms;
613     const t_atom            *at;
614     const t_resinfo         *resInfo;
615     const t_ilist           *ilist;
616     int                      nameIndex;
617     int                      atom_offset = 0;
618     tng_molecule_t           mol, iterMol;
619     tng_chain_t              chain;
620     tng_residue_t            res;
621     tng_atom_t               atom;
622     tng_bond_t               tngBond;
623     int64_t                  nMols;
624     char                    *groupName;
625     tng_trajectory_t         tng = gmx_tng->tng;
626
627     /* TODO: When the TNG molecules block is more flexible TNG selection
628      * groups should not need all atoms specified. It should be possible
629      * just to specify what molecules are selected (and/or which atoms in
630      * the molecule) and how many (if applicable). */
631
632     /* If no atoms are selected we do not need to create a
633      * TNG selection group molecule. */
634     if (mtop->groups.ngrpnr[egcCompressedX] == 0)
635     {
636         return;
637     }
638
639     /* If all atoms are selected we do not have to create a selection
640      * group molecule in the TNG molecule system. */
641     if (all_atoms_selected(mtop, egcCompressedX))
642     {
643         return;
644     }
645
646     /* The name of the TNG molecule containing the selection group is the
647      * same as the name of the selection group. */
648     nameIndex = *mtop->groups.grps[egcCompressedX].nm_ind;
649     groupName = *mtop->groups.grpname[nameIndex];
650
651     tng_molecule_alloc(tng, &mol);
652     tng_molecule_name_set(tng, mol, groupName);
653     tng_molecule_chain_add(tng, mol, "", &chain);
654     int i = 0;
655     for (const gmx_molblock_t &molBlock :  mtop->molblock)
656     {
657         const gmx_moltype_t &molType = mtop->moltype[molBlock.type];
658
659         atoms = &molType.atoms;
660
661         for (int j = 0; j < molBlock.nmol; j++)
662         {
663             bool bAtomsAdded = FALSE;
664             for (int atomIndex = 0; atomIndex < atoms->nr; atomIndex++, i++)
665             {
666                 char *res_name;
667                 int   res_id;
668
669                 if (ggrpnr(&mtop->groups, egcCompressedX, i) != 0)
670                 {
671                     continue;
672                 }
673                 at = &atoms->atom[atomIndex];
674                 if (atoms->nres > 0)
675                 {
676                     resInfo = &atoms->resinfo[at->resind];
677                     /* FIXME: When TNG supports both residue index and residue
678                      * number the latter should be used. */
679                     res_name = *resInfo->name;
680                     res_id   = at->resind + 1;
681                 }
682                 else
683                 {
684                     res_name = const_cast<char*>("");
685                     res_id   = 0;
686                 }
687                 if (tng_chain_residue_find(tng, chain, res_name, res_id, &res)
688                     != TNG_SUCCESS)
689                 {
690                     /* Since there is ONE chain for selection groups do not keep the
691                      * original residue IDs - otherwise there might be conflicts. */
692                     tng_chain_residue_add(tng, chain, res_name, &res);
693                 }
694                 tng_residue_atom_w_id_add(tng, res, *(atoms->atomname[atomIndex]),
695                                           *(atoms->atomtype[atomIndex]),
696                                           atom_offset + atomIndex, &atom);
697                 bAtomsAdded = TRUE;
698             }
699             /* Add bonds. */
700             if (bAtomsAdded)
701             {
702                 for (int k = 0; k < F_NRE; k++)
703                 {
704                     if (IS_CHEMBOND(k))
705                     {
706                         ilist = &molType.ilist[k];
707                         if (ilist)
708                         {
709                             int l = 1;
710                             while (l < ilist->nr)
711                             {
712                                 int atom1, atom2;
713                                 atom1 = ilist->iatoms[l] + atom_offset;
714                                 atom2 = ilist->iatoms[l+1] + atom_offset;
715                                 if (ggrpnr(&mtop->groups, egcCompressedX, atom1) == 0 &&
716                                     ggrpnr(&mtop->groups, egcCompressedX, atom2) == 0)
717                                 {
718                                     tng_molecule_bond_add(tng, mol, ilist->iatoms[l],
719                                                           ilist->iatoms[l+1], &tngBond);
720                                 }
721                                 l += 3;
722                             }
723                         }
724                     }
725                 }
726                 /* Settle is described using three atoms */
727                 ilist = &molType.ilist[F_SETTLE];
728                 if (ilist)
729                 {
730                     int l = 1;
731                     while (l < ilist->nr)
732                     {
733                         int atom1, atom2, atom3;
734                         atom1 = ilist->iatoms[l] + atom_offset;
735                         atom2 = ilist->iatoms[l+1] + atom_offset;
736                         atom3 = ilist->iatoms[l+2] + atom_offset;
737                         if (ggrpnr(&mtop->groups, egcCompressedX, atom1) == 0)
738                         {
739                             if (ggrpnr(&mtop->groups, egcCompressedX, atom2) == 0)
740                             {
741                                 tng_molecule_bond_add(tng, mol, atom1,
742                                                       atom2, &tngBond);
743                             }
744                             if (ggrpnr(&mtop->groups, egcCompressedX, atom3) == 0)
745                             {
746                                 tng_molecule_bond_add(tng, mol, atom1,
747                                                       atom3, &tngBond);
748                             }
749                         }
750                         l += 4;
751                     }
752                 }
753             }
754             atom_offset += atoms->nr;
755         }
756     }
757     tng_molecule_existing_add(tng, &mol);
758     tng_molecule_cnt_set(tng, mol, 1);
759     tng_num_molecule_types_get(tng, &nMols);
760     for (int64_t k = 0; k < nMols; k++)
761     {
762         tng_molecule_of_index_get(tng, k, &iterMol);
763         if (iterMol == mol)
764         {
765             continue;
766         }
767         tng_molecule_cnt_set(tng, iterMol, 0);
768     }
769 }
770 #endif
771
772 void gmx_tng_set_compression_precision(gmx_tng_trajectory_t gmx_tng,
773                                        real                 prec)
774 {
775 #if GMX_USE_TNG
776     tng_compression_precision_set(gmx_tng->tng, prec);
777 #else
778     GMX_UNUSED_VALUE(gmx_tng);
779     GMX_UNUSED_VALUE(prec);
780 #endif
781 }
782
783 void gmx_tng_prepare_low_prec_writing(gmx_tng_trajectory_t  gmx_tng,
784                                       const gmx_mtop_t     *mtop,
785                                       const t_inputrec     *ir)
786 {
787 #if GMX_USE_TNG
788     gmx_tng_add_mtop(gmx_tng, mtop);
789     add_selection_groups(gmx_tng, mtop);
790     set_writing_intervals(gmx_tng, TRUE, ir);
791     tng_time_per_frame_set(gmx_tng->tng, ir->delta_t * PICO);
792     gmx_tng->timePerFrameIsSet = true;
793     gmx_tng_set_compression_precision(gmx_tng, ir->x_compression_precision);
794 #else
795     GMX_UNUSED_VALUE(gmx_tng);
796     GMX_UNUSED_VALUE(mtop);
797     GMX_UNUSED_VALUE(ir);
798 #endif
799 }
800
801 void gmx_fwrite_tng(gmx_tng_trajectory_t gmx_tng,
802                     const gmx_bool       bUseLossyCompression,
803                     int64_t              step,
804                     real                 elapsedPicoSeconds,
805                     real                 lambda,
806                     const rvec          *box,
807                     int                  nAtoms,
808                     const rvec          *x,
809                     const rvec          *v,
810                     const rvec          *f)
811 {
812 #if GMX_USE_TNG
813     typedef tng_function_status (*write_data_func_pointer)(tng_trajectory_t,
814                                                            const int64_t,
815                                                            const double,
816                                                            const real*,
817                                                            const int64_t,
818                                                            const int64_t,
819                                                            const char*,
820                                                            const char,
821                                                            const char);
822 #if GMX_DOUBLE
823     static write_data_func_pointer           write_data           = tng_util_generic_with_time_double_write;
824 #else
825     static write_data_func_pointer           write_data           = tng_util_generic_with_time_write;
826 #endif
827     double                                   elapsedSeconds = elapsedPicoSeconds * PICO;
828     int64_t                                  nParticles;
829     char                                     compression;
830
831
832     if (!gmx_tng)
833     {
834         /* This function might get called when the type of the
835            compressed trajectory is actually XTC. So we exit and move
836            on. */
837         return;
838     }
839     tng_trajectory_t tng = gmx_tng->tng;
840
841     // While the GROMACS interface to this routine specifies 'step', TNG itself
842     // only uses 'frame index' internally, although it suggests that it's a good
843     // idea to let this represent the step rather than just frame numbers.
844     //
845     // However, the way the GROMACS interface works, there are cases where
846     // the step is simply not set, so all frames rather have step=0.
847     // If we call the lower-level TNG routines with the same frame index
848     // (which is set from the step) multiple times, things will go very
849     // wrong (overwritten data).
850     //
851     // To avoid this, we require the step value to always be larger than the
852     // previous value, and if this is not true we simply set it to a value
853     // one unit larger than the previous step.
854     //
855     // Note: We do allow the user to specify any crazy value the want for the
856     // first step. If it is "not set", GROMACS will have used the value 0.
857     if (gmx_tng->lastStepDataIsValid && step <= gmx_tng->lastStep)
858     {
859         step = gmx_tng->lastStep + 1;
860     }
861
862     // Now that we have fixed the potentially incorrect step, we can also set
863     // the time per frame if it was not already done, and we have
864     // step/time information from the last frame so it is possible to calculate it.
865     // Note that we do allow the time to be the same for all steps, or even
866     // decreasing. In that case we will simply say the time per step is 0
867     // or negative. A bit strange, but a correct representation of the data :-)
868     if (!gmx_tng->timePerFrameIsSet && gmx_tng->lastTimeDataIsValid && gmx_tng->lastStepDataIsValid)
869     {
870         double       deltaTime = elapsedSeconds - gmx_tng->lastTime;
871         std::int64_t deltaStep = step - gmx_tng->lastStep;
872         tng_time_per_frame_set(tng, deltaTime / deltaStep );
873         gmx_tng->timePerFrameIsSet = true;
874     }
875
876     tng_num_particles_get(tng, &nParticles);
877     if (nAtoms != static_cast<int>(nParticles))
878     {
879         tng_implicit_num_particles_set(tng, nAtoms);
880     }
881
882     if (bUseLossyCompression)
883     {
884         compression = TNG_TNG_COMPRESSION;
885     }
886     else
887     {
888         compression = TNG_GZIP_COMPRESSION;
889     }
890
891     /* The writing is done using write_data, which writes float or double
892      * depending on the GROMACS compilation. */
893     if (x)
894     {
895         GMX_ASSERT(box, "Need a non-NULL box if positions are written");
896
897         if (write_data(tng, step, elapsedSeconds,
898                        reinterpret_cast<const real *>(x),
899                        3, TNG_TRAJ_POSITIONS, "POSITIONS",
900                        TNG_PARTICLE_BLOCK_DATA,
901                        compression) != TNG_SUCCESS)
902         {
903             gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
904         }
905     }
906
907     if (v)
908     {
909         if (write_data(tng, step, elapsedSeconds,
910                        reinterpret_cast<const real *>(v),
911                        3, TNG_TRAJ_VELOCITIES, "VELOCITIES",
912                        TNG_PARTICLE_BLOCK_DATA,
913                        compression) != TNG_SUCCESS)
914         {
915             gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
916         }
917     }
918
919     if (f)
920     {
921         /* TNG-MF1 compression only compresses positions and velocities. Use lossless
922          * compression for forces regardless of output mode */
923         if (write_data(tng, step, elapsedSeconds,
924                        reinterpret_cast<const real *>(f),
925                        3, TNG_TRAJ_FORCES, "FORCES",
926                        TNG_PARTICLE_BLOCK_DATA,
927                        TNG_GZIP_COMPRESSION) != TNG_SUCCESS)
928         {
929             gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
930         }
931     }
932
933     if (box)
934     {
935         /* TNG-MF1 compression only compresses positions and velocities. Use lossless
936          * compression for box shape regardless of output mode */
937         if (write_data(tng, step, elapsedSeconds,
938                        reinterpret_cast<const real *>(box),
939                        9, TNG_TRAJ_BOX_SHAPE, "BOX SHAPE",
940                        TNG_NON_PARTICLE_BLOCK_DATA,
941                        TNG_GZIP_COMPRESSION) != TNG_SUCCESS)
942         {
943             gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
944         }
945     }
946
947     if (lambda >= 0)
948     {
949         /* TNG-MF1 compression only compresses positions and velocities. Use lossless
950          * compression for lambda regardless of output mode */
951         if (write_data(tng, step, elapsedSeconds,
952                        reinterpret_cast<const real *>(&lambda),
953                        1, TNG_GMX_LAMBDA, "LAMBDAS",
954                        TNG_NON_PARTICLE_BLOCK_DATA,
955                        TNG_GZIP_COMPRESSION) != TNG_SUCCESS)
956         {
957             gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
958         }
959     }
960
961     // Update the data in the wrapper
962
963     gmx_tng->lastStepDataIsValid = true;
964     gmx_tng->lastStep            = step;
965     gmx_tng->lastTimeDataIsValid = true;
966     gmx_tng->lastTime            = elapsedSeconds;
967 #else
968     GMX_UNUSED_VALUE(gmx_tng);
969     GMX_UNUSED_VALUE(bUseLossyCompression);
970     GMX_UNUSED_VALUE(step);
971     GMX_UNUSED_VALUE(elapsedPicoSeconds);
972     GMX_UNUSED_VALUE(lambda);
973     GMX_UNUSED_VALUE(box);
974     GMX_UNUSED_VALUE(nAtoms);
975     GMX_UNUSED_VALUE(x);
976     GMX_UNUSED_VALUE(v);
977     GMX_UNUSED_VALUE(f);
978 #endif
979 }
980
981 void fflush_tng(gmx_tng_trajectory_t gmx_tng)
982 {
983 #if GMX_USE_TNG
984     if (!gmx_tng)
985     {
986         return;
987     }
988     tng_frame_set_premature_write(gmx_tng->tng, TNG_USE_HASH);
989 #else
990     GMX_UNUSED_VALUE(gmx_tng);
991 #endif
992 }
993
994 float gmx_tng_get_time_of_final_frame(gmx_tng_trajectory_t gmx_tng)
995 {
996 #if GMX_USE_TNG
997     int64_t          nFrames;
998     double           time;
999     float            fTime;
1000     tng_trajectory_t tng = gmx_tng->tng;
1001
1002     tng_num_frames_get(tng, &nFrames);
1003     tng_util_time_of_frame_get(tng, nFrames - 1, &time);
1004
1005     fTime = time / PICO;
1006     return fTime;
1007 #else
1008     GMX_UNUSED_VALUE(gmx_tng);
1009     return -1.0;
1010 #endif
1011 }
1012
1013 void gmx_prepare_tng_writing(const char              *filename,
1014                              char                     mode,
1015                              gmx_tng_trajectory_t    *gmx_tng_input,
1016                              gmx_tng_trajectory_t    *gmx_tng_output,
1017                              int                      nAtoms,
1018                              const gmx_mtop_t        *mtop,
1019                              const int               *index,
1020                              const char              *indexGroupName)
1021 {
1022 #if GMX_USE_TNG
1023     tng_trajectory_t   *input  = (gmx_tng_input && *gmx_tng_input) ? &(*gmx_tng_input)->tng : nullptr;
1024     /* FIXME after 5.0: Currently only standard block types are read */
1025     const int           defaultNumIds              = 5;
1026     static int64_t      fallbackIds[defaultNumIds] =
1027     {
1028         TNG_TRAJ_BOX_SHAPE, TNG_TRAJ_POSITIONS,
1029         TNG_TRAJ_VELOCITIES, TNG_TRAJ_FORCES,
1030         TNG_GMX_LAMBDA
1031     };
1032     static char         fallbackNames[defaultNumIds][32] =
1033     {
1034         "BOX SHAPE", "POSITIONS", "VELOCITIES",
1035         "FORCES", "LAMBDAS"
1036     };
1037
1038     typedef tng_function_status (*set_writing_interval_func_pointer)(tng_trajectory_t,
1039                                                                      const int64_t,
1040                                                                      const int64_t,
1041                                                                      const int64_t,
1042                                                                      const char*,
1043                                                                      const char,
1044                                                                      const char);
1045 #if GMX_DOUBLE
1046     set_writing_interval_func_pointer set_writing_interval = tng_util_generic_write_interval_double_set;
1047 #else
1048     set_writing_interval_func_pointer set_writing_interval = tng_util_generic_write_interval_set;
1049 #endif
1050
1051     gmx_tng_open(filename, mode, gmx_tng_output);
1052     tng_trajectory_t *output = &(*gmx_tng_output)->tng;
1053
1054     /* Do we have an input file in TNG format? If so, then there's
1055        more data we can copy over, rather than having to improvise. */
1056     if (gmx_tng_input && *gmx_tng_input)
1057     {
1058         /* Set parameters (compression, time per frame, molecule
1059          * information, number of frames per frame set and writing
1060          * intervals of positions, box shape and lambdas) of the
1061          * output tng container based on their respective values int
1062          * the input tng container */
1063         double      time, compression_precision;
1064         int64_t     n_frames_per_frame_set, interval = -1;
1065
1066         tng_compression_precision_get(*input, &compression_precision);
1067         tng_compression_precision_set(*output, compression_precision);
1068         // TODO make this configurable in a future version
1069         char compression_type = TNG_TNG_COMPRESSION;
1070
1071         tng_molecule_system_copy(*input, *output);
1072
1073         tng_time_per_frame_get(*input, &time);
1074         tng_time_per_frame_set(*output, time);
1075         // Since we have copied the value from the input TNG we should not change it again
1076         (*gmx_tng_output)->timePerFrameIsSet = true;
1077
1078         tng_num_frames_per_frame_set_get(*input, &n_frames_per_frame_set);
1079         tng_num_frames_per_frame_set_set(*output, n_frames_per_frame_set);
1080
1081         for (int i = 0; i < defaultNumIds; i++)
1082         {
1083             if (tng_data_get_stride_length(*input, fallbackIds[i], -1, &interval)
1084                 == TNG_SUCCESS)
1085             {
1086                 switch (fallbackIds[i])
1087                 {
1088                     case TNG_TRAJ_POSITIONS:
1089                     case TNG_TRAJ_VELOCITIES:
1090                         set_writing_interval(*output, interval, 3, fallbackIds[i],
1091                                              fallbackNames[i], TNG_PARTICLE_BLOCK_DATA,
1092                                              compression_type);
1093                         break;
1094                     case TNG_TRAJ_FORCES:
1095                         set_writing_interval(*output, interval, 3, fallbackIds[i],
1096                                              fallbackNames[i], TNG_PARTICLE_BLOCK_DATA,
1097                                              TNG_GZIP_COMPRESSION);
1098                         break;
1099                     case TNG_TRAJ_BOX_SHAPE:
1100                         set_writing_interval(*output, interval, 9, fallbackIds[i],
1101                                              fallbackNames[i], TNG_NON_PARTICLE_BLOCK_DATA,
1102                                              TNG_GZIP_COMPRESSION);
1103                         (*gmx_tng_output)->boxOutputInterval = interval;
1104                         break;
1105                     case TNG_GMX_LAMBDA:
1106                         set_writing_interval(*output, interval, 1, fallbackIds[i],
1107                                              fallbackNames[i], TNG_NON_PARTICLE_BLOCK_DATA,
1108                                              TNG_GZIP_COMPRESSION);
1109                         (*gmx_tng_output)->lambdaOutputInterval = interval;
1110                         break;
1111                     default:
1112                         continue;
1113                 }
1114             }
1115         }
1116
1117     }
1118     else
1119     {
1120         /* TODO after trjconv is modularized: fix this so the user can
1121            change precision when they are doing an operation where
1122            this makes sense, and not otherwise.
1123
1124            char compression = bUseLossyCompression ? TNG_TNG_COMPRESSION : TNG_GZIP_COMPRESSION;
1125            gmx_tng_set_compression_precision(*output, ndec2prec(nDecimalsOfPrecision));
1126          */
1127         gmx_tng_add_mtop(*gmx_tng_output, mtop);
1128         tng_num_frames_per_frame_set_set(*output, 1);
1129     }
1130
1131     if (index && nAtoms > 0)
1132     {
1133         gmx_tng_setup_atom_subgroup(*gmx_tng_output, nAtoms, index, indexGroupName);
1134     }
1135
1136     /* If for some reason there are more requested atoms than there are atoms in the
1137      * molecular system create a number of implicit atoms (without atom data) to
1138      * compensate for that. */
1139     if (nAtoms >= 0)
1140     {
1141         tng_implicit_num_particles_set(*output, nAtoms);
1142     }
1143 #else
1144     GMX_UNUSED_VALUE(filename);
1145     GMX_UNUSED_VALUE(mode);
1146     GMX_UNUSED_VALUE(gmx_tng_input);
1147     GMX_UNUSED_VALUE(gmx_tng_output);
1148     GMX_UNUSED_VALUE(nAtoms);
1149     GMX_UNUSED_VALUE(mtop);
1150     GMX_UNUSED_VALUE(index);
1151     GMX_UNUSED_VALUE(indexGroupName);
1152 #endif
1153 }
1154
1155 void gmx_write_tng_from_trxframe(gmx_tng_trajectory_t    gmx_tng_output,
1156                                  const t_trxframe       *frame,
1157                                  int                     natoms)
1158 {
1159 #if GMX_USE_TNG
1160     if (natoms < 0)
1161     {
1162         natoms = frame->natoms;
1163     }
1164     gmx_fwrite_tng(gmx_tng_output,
1165                    TRUE,
1166                    frame->step,
1167                    frame->time,
1168                    0,
1169                    frame->box,
1170                    natoms,
1171                    frame->x,
1172                    frame->v,
1173                    frame->f);
1174 #else
1175     GMX_UNUSED_VALUE(gmx_tng_output);
1176     GMX_UNUSED_VALUE(frame);
1177     GMX_UNUSED_VALUE(natoms);
1178 #endif
1179 }
1180
1181 namespace
1182 {
1183
1184 #if GMX_USE_TNG
1185 void
1186 convert_array_to_real_array(void       *from,
1187                             real       *to,
1188                             const float fact,
1189                             const int   nAtoms,
1190                             const int   nValues,
1191                             const char  datatype)
1192 {
1193     int        i, j;
1194
1195     const bool useDouble = GMX_DOUBLE;
1196     switch (datatype)
1197     {
1198         case TNG_FLOAT_DATA:
1199             if (!useDouble)
1200             {
1201                 if (fact == 1)
1202                 {
1203                     memcpy(to, from, nValues * sizeof(real) * nAtoms);
1204                 }
1205                 else
1206                 {
1207                     for (i = 0; i < nAtoms; i++)
1208                     {
1209                         for (j = 0; j < nValues; j++)
1210                         {
1211                             to[i*nValues+j] = reinterpret_cast<float *>(from)[i*nValues+j] * fact;
1212                         }
1213                     }
1214                 }
1215             }
1216             else
1217             {
1218                 for (i = 0; i < nAtoms; i++)
1219                 {
1220                     for (j = 0; j < nValues; j++)
1221                     {
1222                         to[i*nValues+j] = reinterpret_cast<float *>(from)[i*nValues+j] * fact;
1223                     }
1224                 }
1225             }
1226             break;
1227         case TNG_INT_DATA:
1228             for (i = 0; i < nAtoms; i++)
1229             {
1230                 for (j = 0; j < nValues; j++)
1231                 {
1232                     to[i*nValues+j] = reinterpret_cast<int64_t *>(from)[i*nValues+j] * fact;
1233                 }
1234             }
1235             break;
1236         case TNG_DOUBLE_DATA:
1237             if (sizeof(real) == sizeof(double))
1238             {
1239                 if (fact == 1)
1240                 {
1241                     memcpy(to, from, nValues * sizeof(real) * nAtoms);
1242                 }
1243                 else
1244                 {
1245                     for (i = 0; i < nAtoms; i++)
1246                     {
1247                         for (j = 0; j < nValues; j++)
1248                         {
1249                             to[i*nValues+j] = reinterpret_cast<double *>(from)[i*nValues+j] * fact;
1250                         }
1251                     }
1252                 }
1253             }
1254             else
1255             {
1256                 for (i = 0; i < nAtoms; i++)
1257                 {
1258                     for (j = 0; j < nValues; j++)
1259                     {
1260                         to[i*nValues+j] = reinterpret_cast<double *>(from)[i*nValues+j] * fact;
1261                     }
1262                 }
1263             }
1264             break;
1265         default:
1266             gmx_incons("Illegal datatype when converting values to a real array!");
1267     }
1268 }
1269
1270 real getDistanceScaleFactor(gmx_tng_trajectory_t in)
1271 {
1272     int64_t     exp = -1;
1273     real        distanceScaleFactor;
1274
1275     // TODO Hopefully, TNG 2.0 will do this kind of thing for us
1276     tng_distance_unit_exponential_get(in->tng, &exp);
1277
1278     // GROMACS expects distances in nm
1279     switch (exp)
1280     {
1281         case 9:
1282             distanceScaleFactor = NANO/NANO;
1283             break;
1284         case 10:
1285             distanceScaleFactor = NANO/ANGSTROM;
1286             break;
1287         default:
1288             distanceScaleFactor = pow(10.0, exp + 9.0);
1289     }
1290
1291     return distanceScaleFactor;
1292 }
1293 #endif
1294
1295 } // namespace
1296
1297 void gmx_tng_setup_atom_subgroup(gmx_tng_trajectory_t gmx_tng,
1298                                  const int            nind,
1299                                  const int           *ind,
1300                                  const char          *name)
1301 {
1302 #if GMX_USE_TNG
1303     int64_t                  nAtoms, cnt, nMols;
1304     tng_molecule_t           mol, iterMol;
1305     tng_chain_t              chain;
1306     tng_residue_t            res;
1307     tng_atom_t               atom;
1308     tng_function_status      stat;
1309     tng_trajectory_t         tng = gmx_tng->tng;
1310
1311     tng_num_particles_get(tng, &nAtoms);
1312
1313     if (nAtoms == nind)
1314     {
1315         return;
1316     }
1317
1318     stat = tng_molecule_find(tng, name, -1, &mol);
1319     if (stat == TNG_SUCCESS)
1320     {
1321         tng_molecule_num_atoms_get(tng, mol, &nAtoms);
1322         tng_molecule_cnt_get(tng, mol, &cnt);
1323         if (nAtoms == nind)
1324         {
1325             stat = TNG_SUCCESS;
1326         }
1327         else
1328         {
1329             stat = TNG_FAILURE;
1330         }
1331     }
1332     if (stat == TNG_FAILURE)
1333     {
1334         /* The indexed atoms are added to one separate molecule. */
1335         tng_molecule_alloc(tng, &mol);
1336         tng_molecule_name_set(tng, mol, name);
1337         tng_molecule_chain_add(tng, mol, "", &chain);
1338
1339         for (int i = 0; i < nind; i++)
1340         {
1341             char        temp_name[256], temp_type[256];
1342
1343             /* Try to retrieve the residue name of the atom */
1344             stat = tng_residue_name_of_particle_nr_get(tng, ind[i], temp_name, 256);
1345             if (stat != TNG_SUCCESS)
1346             {
1347                 temp_name[0] = '\0';
1348             }
1349             /* Check if the molecule of the selection already contains this residue */
1350             if (tng_chain_residue_find(tng, chain, temp_name, -1, &res)
1351                 != TNG_SUCCESS)
1352             {
1353                 tng_chain_residue_add(tng, chain, temp_name, &res);
1354             }
1355             /* Try to find the original name and type of the atom */
1356             stat = tng_atom_name_of_particle_nr_get(tng, ind[i], temp_name, 256);
1357             if (stat != TNG_SUCCESS)
1358             {
1359                 temp_name[0] = '\0';
1360             }
1361             stat = tng_atom_type_of_particle_nr_get(tng, ind[i], temp_type, 256);
1362             if (stat != TNG_SUCCESS)
1363             {
1364                 temp_type[0] = '\0';
1365             }
1366             tng_residue_atom_w_id_add(tng, res, temp_name, temp_type, ind[i], &atom);
1367         }
1368         tng_molecule_existing_add(tng, &mol);
1369     }
1370     /* Set the count of the molecule containing the selected atoms to 1 and all
1371      * other molecules to 0 */
1372     tng_molecule_cnt_set(tng, mol, 1);
1373     tng_num_molecule_types_get(tng, &nMols);
1374     for (int64_t k = 0; k < nMols; k++)
1375     {
1376         tng_molecule_of_index_get(tng, k, &iterMol);
1377         if (iterMol == mol)
1378         {
1379             continue;
1380         }
1381         tng_molecule_cnt_set(tng, iterMol, 0);
1382     }
1383 #else
1384     GMX_UNUSED_VALUE(gmx_tng);
1385     GMX_UNUSED_VALUE(nind);
1386     GMX_UNUSED_VALUE(ind);
1387     GMX_UNUSED_VALUE(name);
1388 #endif
1389 }
1390
1391 /* TODO: If/when TNG acquires the ability to copy data blocks without
1392  * uncompressing them, then this implemenation should be reconsidered.
1393  * Ideally, gmx trjconv -f a.tng -o b.tng -b 10 -e 20 would be fast
1394  * and lose no information. */
1395 gmx_bool gmx_read_next_tng_frame(gmx_tng_trajectory_t        gmx_tng_input,
1396                                  t_trxframe                 *fr,
1397                                  int64_t                    *requestedIds,
1398                                  int                         numRequestedIds)
1399 {
1400 #if GMX_USE_TNG
1401     tng_trajectory_t        input = gmx_tng_input->tng;
1402     gmx_bool                bOK   = TRUE;
1403     tng_function_status     stat;
1404     int64_t                 numberOfAtoms = -1, frameNumber = -1;
1405     int64_t                 nBlocks, blockId, *blockIds = nullptr, codecId;
1406     char                    datatype      = -1;
1407     void                   *values        = nullptr;
1408     double                  frameTime     = -1.0;
1409     int                     size, blockDependency;
1410     double                  prec;
1411     const int               defaultNumIds = 5;
1412     static int64_t          fallbackRequestedIds[defaultNumIds] =
1413     {
1414         TNG_TRAJ_BOX_SHAPE, TNG_TRAJ_POSITIONS,
1415         TNG_TRAJ_VELOCITIES, TNG_TRAJ_FORCES,
1416         TNG_GMX_LAMBDA
1417     };
1418
1419
1420     fr->bStep     = FALSE;
1421     fr->bTime     = FALSE;
1422     fr->bLambda   = FALSE;
1423     fr->bAtoms    = FALSE;
1424     fr->bPrec     = FALSE;
1425     fr->bX        = FALSE;
1426     fr->bV        = FALSE;
1427     fr->bF        = FALSE;
1428     fr->bBox      = FALSE;
1429
1430     /* If no specific IDs were requested read all block types that can
1431      * currently be interpreted */
1432     if (!requestedIds || numRequestedIds == 0)
1433     {
1434         numRequestedIds = defaultNumIds;
1435         requestedIds    = fallbackRequestedIds;
1436     }
1437
1438     stat = tng_num_particles_get(input, &numberOfAtoms);
1439     if (stat != TNG_SUCCESS)
1440     {
1441         gmx_file("Cannot determine number of atoms from TNG file.");
1442     }
1443     fr->natoms = numberOfAtoms;
1444
1445     bool nextFrameExists = gmx_get_tng_data_block_types_of_next_frame(gmx_tng_input,
1446                                                                       fr->step,
1447                                                                       numRequestedIds,
1448                                                                       requestedIds,
1449                                                                       &frameNumber,
1450                                                                       &nBlocks,
1451                                                                       &blockIds);
1452     gmx::unique_cptr<int64_t, gmx::free_wrapper> blockIdsGuard(blockIds);
1453     if (!nextFrameExists)
1454     {
1455         return FALSE;
1456     }
1457
1458     if (nBlocks == 0)
1459     {
1460         return FALSE;
1461     }
1462
1463     for (int64_t i = 0; i < nBlocks; i++)
1464     {
1465         blockId = blockIds[i];
1466         tng_data_block_dependency_get(input, blockId, &blockDependency);
1467         if (blockDependency & TNG_PARTICLE_DEPENDENT)
1468         {
1469             stat = tng_util_particle_data_next_frame_read(input,
1470                                                           blockId,
1471                                                           &values,
1472                                                           &datatype,
1473                                                           &frameNumber,
1474                                                           &frameTime);
1475         }
1476         else
1477         {
1478             stat = tng_util_non_particle_data_next_frame_read(input,
1479                                                               blockId,
1480                                                               &values,
1481                                                               &datatype,
1482                                                               &frameNumber,
1483                                                               &frameTime);
1484         }
1485         if (stat == TNG_CRITICAL)
1486         {
1487             gmx_file("Cannot read positions from TNG file.");
1488             return FALSE;
1489         }
1490         else if (stat == TNG_FAILURE)
1491         {
1492             continue;
1493         }
1494         switch (blockId)
1495         {
1496             case TNG_TRAJ_BOX_SHAPE:
1497                 switch (datatype)
1498                 {
1499                     case TNG_INT_DATA:
1500                         size = sizeof(int64_t);
1501                         break;
1502                     case TNG_FLOAT_DATA:
1503                         size = sizeof(float);
1504                         break;
1505                     case TNG_DOUBLE_DATA:
1506                         size = sizeof(double);
1507                         break;
1508                     default:
1509                         gmx_incons("Illegal datatype of box shape values!");
1510                 }
1511                 for (int i = 0; i < DIM; i++)
1512                 {
1513                     convert_array_to_real_array(reinterpret_cast<char *>(values) + size * i * DIM,
1514                                                 reinterpret_cast<real *>(fr->box[i]),
1515                                                 getDistanceScaleFactor(gmx_tng_input),
1516                                                 1,
1517                                                 DIM,
1518                                                 datatype);
1519                 }
1520                 fr->bBox = TRUE;
1521                 break;
1522             case TNG_TRAJ_POSITIONS:
1523                 srenew(fr->x, fr->natoms);
1524                 convert_array_to_real_array(values,
1525                                             reinterpret_cast<real *>(fr->x),
1526                                             getDistanceScaleFactor(gmx_tng_input),
1527                                             fr->natoms,
1528                                             DIM,
1529                                             datatype);
1530                 fr->bX = TRUE;
1531                 tng_util_frame_current_compression_get(input, blockId, &codecId, &prec);
1532                 /* This must be updated if/when more lossy compression methods are added */
1533                 if (codecId == TNG_TNG_COMPRESSION)
1534                 {
1535                     fr->prec  = prec;
1536                     fr->bPrec = TRUE;
1537                 }
1538                 break;
1539             case TNG_TRAJ_VELOCITIES:
1540                 srenew(fr->v, fr->natoms);
1541                 convert_array_to_real_array(values,
1542                                             reinterpret_cast<real *>(fr->v),
1543                                             getDistanceScaleFactor(gmx_tng_input),
1544                                             fr->natoms,
1545                                             DIM,
1546                                             datatype);
1547                 fr->bV = TRUE;
1548                 tng_util_frame_current_compression_get(input, blockId, &codecId, &prec);
1549                 /* This must be updated if/when more lossy compression methods are added */
1550                 if (codecId == TNG_TNG_COMPRESSION)
1551                 {
1552                     fr->prec  = prec;
1553                     fr->bPrec = TRUE;
1554                 }
1555                 break;
1556             case TNG_TRAJ_FORCES:
1557                 srenew(fr->f, fr->natoms);
1558                 convert_array_to_real_array(values,
1559                                             reinterpret_cast<real *>(fr->f),
1560                                             getDistanceScaleFactor(gmx_tng_input),
1561                                             fr->natoms,
1562                                             DIM,
1563                                             datatype);
1564                 fr->bF = TRUE;
1565                 break;
1566             case TNG_GMX_LAMBDA:
1567                 switch (datatype)
1568                 {
1569                     case TNG_FLOAT_DATA:
1570                         fr->lambda = *(reinterpret_cast<float *>(values));
1571                         break;
1572                     case TNG_DOUBLE_DATA:
1573                         fr->lambda = *(reinterpret_cast<double *>(values));
1574                         break;
1575                     default:
1576                         gmx_incons("Illegal datatype lambda value!");
1577                 }
1578                 fr->bLambda = TRUE;
1579                 break;
1580             default:
1581                 gmx_warning("Illegal block type! Currently GROMACS tools can only handle certain data types. Skipping block.");
1582         }
1583         /* values does not have to be freed before reading next frame. It will
1584          * be reallocated if it is not NULL. */
1585     }
1586
1587     fr->step  = frameNumber;
1588     fr->bStep = TRUE;
1589
1590     // Convert the time to ps
1591     fr->time  = frameTime / PICO;
1592     fr->bTime = (frameTime > 0);
1593
1594     // TODO This does not leak, but is not exception safe.
1595     /* values must be freed before leaving this function */
1596     sfree(values);
1597
1598     return bOK;
1599 #else
1600     GMX_UNUSED_VALUE(gmx_tng_input);
1601     GMX_UNUSED_VALUE(fr);
1602     GMX_UNUSED_VALUE(requestedIds);
1603     GMX_UNUSED_VALUE(numRequestedIds);
1604     return FALSE;
1605 #endif
1606 }
1607
1608 void gmx_print_tng_molecule_system(gmx_tng_trajectory_t gmx_tng_input,
1609                                    FILE                *stream)
1610 {
1611 #if GMX_USE_TNG
1612     int64_t             nMolecules, nChains, nResidues, nAtoms, nFramesRead;
1613     int64_t             strideLength, nParticlesRead, nValuesPerFrameRead, *molCntList;
1614     tng_molecule_t      molecule;
1615     tng_chain_t         chain;
1616     tng_residue_t       residue;
1617     tng_atom_t          atom;
1618     tng_function_status stat;
1619     char                str[256];
1620     char                varNAtoms;
1621     char                datatype;
1622     void               *data = nullptr;
1623     std::vector<real>   atomCharges;
1624     std::vector<real>   atomMasses;
1625     tng_trajectory_t    input = gmx_tng_input->tng;
1626
1627     tng_num_molecule_types_get(input, &nMolecules);
1628     tng_molecule_cnt_list_get(input, &molCntList);
1629     /* Can the number of particles change in the trajectory or is it constant? */
1630     tng_num_particles_variable_get(input, &varNAtoms);
1631
1632     for (int64_t i = 0; i < nMolecules; i++)
1633     {
1634         tng_molecule_of_index_get(input, i, &molecule);
1635         tng_molecule_name_get(input, molecule, str, 256);
1636         if (varNAtoms == TNG_CONSTANT_N_ATOMS)
1637         {
1638             if (static_cast<int>(molCntList[i]) == 0)
1639             {
1640                 continue;
1641             }
1642             fprintf(stream, "Molecule: %s, count: %d\n", str, static_cast<int>(molCntList[i]));
1643         }
1644         else
1645         {
1646             fprintf(stream, "Molecule: %s\n", str);
1647         }
1648         tng_molecule_num_chains_get(input, molecule, &nChains);
1649         if (nChains > 0)
1650         {
1651             for (int64_t j = 0; j < nChains; j++)
1652             {
1653                 tng_molecule_chain_of_index_get(input, molecule, j, &chain);
1654                 tng_chain_name_get(input, chain, str, 256);
1655                 fprintf(stream, "\tChain: %s\n", str);
1656                 tng_chain_num_residues_get(input, chain, &nResidues);
1657                 for (int64_t k = 0; k < nResidues; k++)
1658                 {
1659                     tng_chain_residue_of_index_get(input, chain, k, &residue);
1660                     tng_residue_name_get(input, residue, str, 256);
1661                     fprintf(stream, "\t\tResidue: %s\n", str);
1662                     tng_residue_num_atoms_get(input, residue, &nAtoms);
1663                     for (int64_t l = 0; l < nAtoms; l++)
1664                     {
1665                         tng_residue_atom_of_index_get(input, residue, l, &atom);
1666                         tng_atom_name_get(input, atom, str, 256);
1667                         fprintf(stream, "\t\t\tAtom: %s", str);
1668                         tng_atom_type_get(input, atom, str, 256);
1669                         fprintf(stream, " (%s)\n", str);
1670                     }
1671                 }
1672             }
1673         }
1674         /* It is possible to have a molecule without chains, in which case
1675          * residues in the molecule can be iterated through without going
1676          * through chains. */
1677         else
1678         {
1679             tng_molecule_num_residues_get(input, molecule, &nResidues);
1680             if (nResidues > 0)
1681             {
1682                 for (int64_t k = 0; k < nResidues; k++)
1683                 {
1684                     tng_molecule_residue_of_index_get(input, molecule, k, &residue);
1685                     tng_residue_name_get(input, residue, str, 256);
1686                     fprintf(stream, "\t\tResidue: %s\n", str);
1687                     tng_residue_num_atoms_get(input, residue, &nAtoms);
1688                     for (int64_t l = 0; l < nAtoms; l++)
1689                     {
1690                         tng_residue_atom_of_index_get(input, residue, l, &atom);
1691                         tng_atom_name_get(input, atom, str, 256);
1692                         fprintf(stream, "\t\t\tAtom: %s", str);
1693                         tng_atom_type_get(input, atom, str, 256);
1694                         fprintf(stream, " (%s)\n", str);
1695                     }
1696                 }
1697             }
1698             else
1699             {
1700                 tng_molecule_num_atoms_get(input, molecule, &nAtoms);
1701                 for (int64_t l = 0; l < nAtoms; l++)
1702                 {
1703                     tng_molecule_atom_of_index_get(input, molecule, l, &atom);
1704                     tng_atom_name_get(input, atom, str, 256);
1705                     fprintf(stream, "\t\t\tAtom: %s", str);
1706                     tng_atom_type_get(input, atom, str, 256);
1707                     fprintf(stream, " (%s)\n", str);
1708                 }
1709             }
1710         }
1711     }
1712
1713     tng_num_particles_get(input, &nAtoms);
1714     stat = tng_particle_data_vector_get(input, TNG_TRAJ_PARTIAL_CHARGES, &data, &nFramesRead,
1715                                         &strideLength, &nParticlesRead,
1716                                         &nValuesPerFrameRead, &datatype);
1717     if (stat == TNG_SUCCESS)
1718     {
1719         atomCharges.resize(nAtoms);
1720         convert_array_to_real_array(data,
1721                                     atomCharges.data(),
1722                                     1,
1723                                     nAtoms,
1724                                     1,
1725                                     datatype);
1726
1727         fprintf(stream, "Atom Charges (%d):\n", int(nAtoms));
1728         for (int64_t i = 0; i < nAtoms; i += 10)
1729         {
1730             fprintf(stream, "Atom Charges [%8d-]=[", int(i));
1731             for (int64_t j = 0; (j < 10 && i + j < nAtoms); j++)
1732             {
1733                 fprintf(stream, " %12.5e", atomCharges[i + j]);
1734             }
1735             fprintf(stream, "]\n");
1736         }
1737     }
1738
1739     stat = tng_particle_data_vector_get(input, TNG_TRAJ_MASSES, &data, &nFramesRead,
1740                                         &strideLength, &nParticlesRead,
1741                                         &nValuesPerFrameRead, &datatype);
1742     if (stat == TNG_SUCCESS)
1743     {
1744         atomMasses.resize(nAtoms);
1745         convert_array_to_real_array(data,
1746                                     atomMasses.data(),
1747                                     1,
1748                                     nAtoms,
1749                                     1,
1750                                     datatype);
1751
1752         fprintf(stream, "Atom Masses (%d):\n", int(nAtoms));
1753         for (int64_t i = 0; i < nAtoms; i += 10)
1754         {
1755             fprintf(stream, "Atom Masses [%8d-]=[", int(i));
1756             for (int64_t j = 0; (j < 10 && i + j < nAtoms); j++)
1757             {
1758                 fprintf(stream, " %12.5e", atomMasses[i + j]);
1759             }
1760             fprintf(stream, "]\n");
1761         }
1762     }
1763
1764     sfree(data);
1765 #else
1766     GMX_UNUSED_VALUE(gmx_tng_input);
1767     GMX_UNUSED_VALUE(stream);
1768 #endif
1769 }
1770
1771 gmx_bool gmx_get_tng_data_block_types_of_next_frame(gmx_tng_trajectory_t gmx_tng_input,
1772                                                     int                  frame,
1773                                                     int                  nRequestedIds,
1774                                                     int64_t             *requestedIds,
1775                                                     int64_t             *nextFrame,
1776                                                     int64_t             *nBlocks,
1777                                                     int64_t            **blockIds)
1778 {
1779 #if GMX_USE_TNG
1780     tng_function_status stat;
1781     tng_trajectory_t    input = gmx_tng_input->tng;
1782
1783     stat = tng_util_trajectory_next_frame_present_data_blocks_find(input, frame,
1784                                                                    nRequestedIds, requestedIds,
1785                                                                    nextFrame,
1786                                                                    nBlocks, blockIds);
1787
1788     if (stat == TNG_CRITICAL)
1789     {
1790         gmx_file("Cannot read TNG file. Cannot find data blocks of next frame.");
1791     }
1792     else if (stat == TNG_FAILURE)
1793     {
1794         return FALSE;
1795     }
1796     return TRUE;
1797 #else
1798     GMX_UNUSED_VALUE(gmx_tng_input);
1799     GMX_UNUSED_VALUE(frame);
1800     GMX_UNUSED_VALUE(nRequestedIds);
1801     GMX_UNUSED_VALUE(requestedIds);
1802     GMX_UNUSED_VALUE(nextFrame);
1803     GMX_UNUSED_VALUE(nBlocks);
1804     GMX_UNUSED_VALUE(blockIds);
1805     return FALSE;
1806 #endif
1807 }
1808
1809 gmx_bool gmx_get_tng_data_next_frame_of_block_type(gmx_tng_trajectory_t gmx_tng_input,
1810                                                    int64_t              blockId,
1811                                                    real               **values,
1812                                                    int64_t             *frameNumber,
1813                                                    double              *frameTime,
1814                                                    int64_t             *nValuesPerFrame,
1815                                                    int64_t             *nAtoms,
1816                                                    real                *prec,
1817                                                    char                *name,
1818                                                    int                  maxLen,
1819                                                    gmx_bool            *bOK)
1820 {
1821 #if GMX_USE_TNG
1822     tng_function_status stat;
1823     char                datatype = -1;
1824     int64_t             codecId;
1825     int                 blockDependency;
1826     void               *data = nullptr;
1827     double              localPrec;
1828     tng_trajectory_t    input = gmx_tng_input->tng;
1829
1830     stat = tng_data_block_name_get(input, blockId, name, maxLen);
1831     if (stat != TNG_SUCCESS)
1832     {
1833         gmx_file("Cannot read next frame of TNG file");
1834     }
1835     stat = tng_data_block_dependency_get(input, blockId, &blockDependency);
1836     if (stat != TNG_SUCCESS)
1837     {
1838         gmx_file("Cannot read next frame of TNG file");
1839     }
1840     if (blockDependency & TNG_PARTICLE_DEPENDENT)
1841     {
1842         tng_num_particles_get(input, nAtoms);
1843         stat = tng_util_particle_data_next_frame_read(input,
1844                                                       blockId,
1845                                                       &data,
1846                                                       &datatype,
1847                                                       frameNumber,
1848                                                       frameTime);
1849     }
1850     else
1851     {
1852         *nAtoms = 1; /* There are not actually any atoms, but it is used for
1853                         allocating memory */
1854         stat    = tng_util_non_particle_data_next_frame_read(input,
1855                                                              blockId,
1856                                                              &data,
1857                                                              &datatype,
1858                                                              frameNumber,
1859                                                              frameTime);
1860     }
1861     if (stat == TNG_CRITICAL)
1862     {
1863         gmx_file("Cannot read next frame of TNG file");
1864     }
1865     if (stat == TNG_FAILURE)
1866     {
1867         *bOK = TRUE;
1868         return FALSE;
1869     }
1870
1871     stat = tng_data_block_num_values_per_frame_get(input, blockId, nValuesPerFrame);
1872     if (stat != TNG_SUCCESS)
1873     {
1874         gmx_file("Cannot read next frame of TNG file");
1875     }
1876     srenew(*values, sizeof(real) * *nValuesPerFrame * *nAtoms);
1877     convert_array_to_real_array(data,
1878                                 *values,
1879                                 getDistanceScaleFactor(gmx_tng_input),
1880                                 *nAtoms,
1881                                 *nValuesPerFrame,
1882                                 datatype);
1883
1884     tng_util_frame_current_compression_get(input, blockId, &codecId, &localPrec);
1885
1886     /* This must be updated if/when more lossy compression methods are added */
1887     if (codecId != TNG_TNG_COMPRESSION)
1888     {
1889         *prec = -1.0;
1890     }
1891     else
1892     {
1893         *prec = localPrec;
1894     }
1895
1896     sfree(data);
1897
1898     *bOK = TRUE;
1899     return TRUE;
1900 #else
1901     GMX_UNUSED_VALUE(gmx_tng_input);
1902     GMX_UNUSED_VALUE(blockId);
1903     GMX_UNUSED_VALUE(values);
1904     GMX_UNUSED_VALUE(frameNumber);
1905     GMX_UNUSED_VALUE(frameTime);
1906     GMX_UNUSED_VALUE(nValuesPerFrame);
1907     GMX_UNUSED_VALUE(nAtoms);
1908     GMX_UNUSED_VALUE(prec);
1909     GMX_UNUSED_VALUE(name);
1910     GMX_UNUSED_VALUE(maxLen);
1911     GMX_UNUSED_VALUE(bOK);
1912     return FALSE;
1913 #endif
1914 }
1915
1916 int gmx_tng_get_box_output_interval(gmx_tng_trajectory_t gmx_tng)
1917 {
1918 #if GMX_USE_TNG
1919     return gmx_tng->boxOutputInterval;
1920 #else
1921     GMX_UNUSED_VALUE(gmx_tng);
1922 #endif
1923 }
1924
1925 int gmx_tng_get_lambda_output_interval(gmx_tng_trajectory_t gmx_tng)
1926 {
1927 #if GMX_USE_TNG
1928     return gmx_tng->lambdaOutputInterval;
1929 #else
1930     GMX_UNUSED_VALUE(gmx_tng);
1931 #endif
1932 }