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