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