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