2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2013,2014, 2015 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.
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.
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.
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.
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.
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.
46 #include "tng/tng_io.h"
49 #include "gromacs/math/units.h"
50 #include "gromacs/math/utilities.h"
51 #include "gromacs/mdtypes/inputrec.h"
52 #include "gromacs/topology/ifunc.h"
53 #include "gromacs/topology/topology.h"
54 #include "gromacs/trajectory/trajectoryframe.h"
55 #include "gromacs/utility/basedefinitions.h"
56 #include "gromacs/utility/baseversion.h"
57 #include "gromacs/utility/fatalerror.h"
58 #include "gromacs/utility/futil.h"
59 #include "gromacs/utility/gmxassert.h"
60 #include "gromacs/utility/programcontext.h"
61 #include "gromacs/utility/smalloc.h"
62 #include "gromacs/utility/sysinfo.h"
63 #include "gromacs/utility/unique_cptr.h"
65 static const char *modeToVerb(char mode)
80 gmx_fatal(FARGS, "Invalid file opening mode %c", mode);
87 void gmx_tng_open(const char *filename,
89 tng_trajectory_t *tng)
92 /* First check whether we have to make a backup,
93 * only for writing, not for read or append.
97 make_backup(filename);
100 /* tng must not be pointing at already allocated memory.
101 * Memory will be allocated by tng_util_trajectory_open() and must
102 * later on be freed by tng_util_trajectory_close(). */
103 if (TNG_SUCCESS != tng_util_trajectory_open(filename, mode, tng))
105 /* TNG does return more than one degree of error, but there is
106 no use case for GROMACS handling the non-fatal errors
109 "File I/O error while opening %s for %s",
114 if (mode == 'w' || mode == 'a')
117 gmx_gethostname(hostname, 256);
120 tng_first_computer_name_set(*tng, hostname);
124 tng_last_computer_name_set(*tng, hostname);
127 char programInfo[256];
128 const char *precisionString = "";
130 precisionString = " (double precision)";
132 sprintf(programInfo, "%.100s %.128s%.24s",
133 gmx::getProgramContext().displayName(),
134 gmx_version(), precisionString);
137 tng_first_program_name_set(*tng, programInfo);
141 tng_last_program_name_set(*tng, programInfo);
145 if (!gmx_getusername(username, 256))
149 tng_first_user_name_set(*tng, username);
153 tng_last_user_name_set(*tng, username);
154 tng_file_headers_write(*tng, TNG_USE_HASH);
159 gmx_file("GROMACS was compiled without TNG support, cannot handle this file type");
160 GMX_UNUSED_VALUE(filename);
161 GMX_UNUSED_VALUE(mode);
162 GMX_UNUSED_VALUE(tng);
166 void gmx_tng_close(tng_trajectory_t *tng)
168 /* We have to check that tng is set because
169 * tng_util_trajectory_close wants to return a NULL in it, and
170 * gives a fatal error if it is NULL. */
174 tng_util_trajectory_close(tng);
177 GMX_UNUSED_VALUE(tng);
182 static void addTngMoleculeFromTopology(tng_trajectory_t tng,
183 const char *moleculeName,
184 const t_atoms *atoms,
185 gmx_int64_t numMolecules,
186 tng_molecule_t *tngMol)
188 tng_chain_t tngChain = nullptr;
189 tng_residue_t tngRes = nullptr;
191 if (tng_molecule_add(tng, moleculeName, tngMol) != TNG_SUCCESS)
193 gmx_file("Cannot add molecule to TNG molecular system.");
196 /* FIXME: The TNG atoms should contain mass and atomB info (for free
197 * energy calculations), i.e. in when it's available in TNG (2.0). */
198 for (int atomIndex = 0; atomIndex < atoms->nr; atomIndex++)
200 const t_atom *at = &atoms->atom[atomIndex];
201 /* FIXME: Currently the TNG API can only add atoms belonging to a
202 * residue and chain. Wait for TNG 2.0*/
205 const t_resinfo *resInfo = &atoms->resinfo[at->resind];
206 char chainName[2] = {resInfo->chainid, 0};
207 tng_atom_t tngAtom = nullptr;
212 prevAtom = &atoms->atom[atomIndex - 1];
219 /* If this is the first atom or if the residue changed add the
220 * residue to the TNG molecular system. */
221 if (!prevAtom || resInfo != &atoms->resinfo[prevAtom->resind])
223 /* If this is the first atom or if the chain changed add
224 * the chain to the TNG molecular system. */
225 if (!prevAtom || resInfo->chainid !=
226 atoms->resinfo[prevAtom->resind].chainid)
228 tng_molecule_chain_add(tng, *tngMol, chainName,
231 /* FIXME: When TNG supports both residue index and residue
232 * number the latter should be used. Wait for TNG 2.0*/
233 tng_chain_residue_add(tng, tngChain, *resInfo->name, &tngRes);
235 tng_residue_atom_add(tng, tngRes, *(atoms->atomname[atomIndex]), *(atoms->atomtype[atomIndex]), &tngAtom);
238 tng_molecule_cnt_set(tng, *tngMol, numMolecules);
241 void gmx_tng_add_mtop(tng_trajectory_t tng,
242 const gmx_mtop_t *mtop)
245 const t_ilist *ilist;
250 /* No topology information available to add. */
254 for (int molIndex = 0; molIndex < mtop->nmolblock; molIndex++)
256 tng_molecule_t tngMol = nullptr;
257 const gmx_moltype_t *molType =
258 &mtop->moltype[mtop->molblock[molIndex].type];
260 /* Add a molecule to the TNG trajectory with the same name as the
261 * current molecule. */
262 addTngMoleculeFromTopology(tng,
265 mtop->molblock[molIndex].nmol,
268 /* Bonds have to be deduced from interactions (constraints etc). Different
269 * interactions have different sets of parameters. */
270 /* Constraints are specified using two atoms */
271 for (i = 0; i < F_NRE; i++)
275 ilist = &molType->ilist[i];
279 while (j < ilist->nr)
281 tng_molecule_bond_add(tng, tngMol, ilist->iatoms[j], ilist->iatoms[j+1], &tngBond);
287 /* Settle is described using three atoms */
288 ilist = &molType->ilist[F_SETTLE];
292 while (j < ilist->nr)
294 tng_molecule_bond_add(tng, tngMol, ilist->iatoms[j], ilist->iatoms[j+1], &tngBond);
295 tng_molecule_bond_add(tng, tngMol, ilist->iatoms[j], ilist->iatoms[j+2], &tngBond);
302 /*! \libinternal \brief Compute greatest common divisor of n1 and n2
303 * if they are positive.
305 * If only one of n1 and n2 is positive, then return it.
306 * If neither n1 or n2 is positive, then return -1. */
308 greatest_common_divisor_if_positive(int n1, int n2)
312 return (0 >= n2) ? -1 : n2;
319 /* We have a non-trivial greatest common divisor to compute. */
320 return gmx_greatest_common_divisor(n1, n2);
323 /* By default try to write 100 frames (of actual output) in each frame set.
324 * This number is the number of outputs of the most frequently written data
325 * type per frame set.
326 * TODO for 5.1: Verify that 100 frames per frame set is efficient for most
327 * setups regarding compression efficiency and compression time. Make this
328 * a hidden command-line option? */
329 const int defaultFramesPerFrameSet = 100;
331 /*! \libinternal \brief Set the number of frames per frame
332 * set according to output intervals.
333 * The default is that 100 frames are written of the data
334 * that is written most often. */
335 static void tng_set_frames_per_frame_set(tng_trajectory_t tng,
336 const gmx_bool bUseLossyCompression,
337 const t_inputrec *ir)
341 /* Set the number of frames per frame set to contain at least
342 * defaultFramesPerFrameSet of the lowest common denominator of
343 * the writing interval of positions and velocities. */
344 /* FIXME after 5.0: consider nstenergy also? */
345 if (bUseLossyCompression)
347 gcd = ir->nstxout_compressed;
351 gcd = greatest_common_divisor_if_positive(ir->nstxout, ir->nstvout);
352 gcd = greatest_common_divisor_if_positive(gcd, ir->nstfout);
359 tng_num_frames_per_frame_set_set(tng, gcd * defaultFramesPerFrameSet);
362 /*! \libinternal \brief Set the data-writing intervals, and number of
363 * frames per frame set */
364 static void set_writing_intervals(tng_trajectory_t tng,
365 const gmx_bool bUseLossyCompression,
366 const t_inputrec *ir)
368 /* Define pointers to specific writing functions depending on if we
369 * write float or double data */
370 typedef tng_function_status (*set_writing_interval_func_pointer)(tng_trajectory_t,
378 set_writing_interval_func_pointer set_writing_interval = tng_util_generic_write_interval_double_set;
380 set_writing_interval_func_pointer set_writing_interval = tng_util_generic_write_interval_set;
382 int xout, vout, fout;
383 int gcd = -1, lowest = -1;
386 tng_set_frames_per_frame_set(tng, bUseLossyCompression, ir);
388 if (bUseLossyCompression)
390 xout = ir->nstxout_compressed;
392 /* If there is no uncompressed coordinate output write forces
393 and velocities to the compressed tng file. */
404 compression = TNG_TNG_COMPRESSION;
411 compression = TNG_GZIP_COMPRESSION;
415 set_writing_interval(tng, xout, 3, TNG_TRAJ_POSITIONS,
416 "POSITIONS", TNG_PARTICLE_BLOCK_DATA,
418 /* TODO: if/when we write energies to TNG also, reconsider how
419 * and when box information is written, because GROMACS
420 * behaviour pre-5.0 was to write the box with every
421 * trajectory frame and every energy frame, and probably
422 * people depend on this. */
424 gcd = greatest_common_divisor_if_positive(gcd, xout);
425 if (lowest < 0 || xout < lowest)
432 set_writing_interval(tng, vout, 3, TNG_TRAJ_VELOCITIES,
433 "VELOCITIES", TNG_PARTICLE_BLOCK_DATA,
436 gcd = greatest_common_divisor_if_positive(gcd, vout);
437 if (lowest < 0 || vout < lowest)
444 set_writing_interval(tng, fout, 3, TNG_TRAJ_FORCES,
445 "FORCES", TNG_PARTICLE_BLOCK_DATA,
446 TNG_GZIP_COMPRESSION);
448 gcd = greatest_common_divisor_if_positive(gcd, fout);
449 if (lowest < 0 || fout < lowest)
456 /* Lambdas and box shape written at an interval of the lowest common
457 denominator of other output */
458 set_writing_interval(tng, gcd, 1, TNG_GMX_LAMBDA,
459 "LAMBDAS", TNG_NON_PARTICLE_BLOCK_DATA,
460 TNG_GZIP_COMPRESSION);
462 set_writing_interval(tng, gcd, 9, TNG_TRAJ_BOX_SHAPE,
463 "BOX SHAPE", TNG_NON_PARTICLE_BLOCK_DATA,
464 TNG_GZIP_COMPRESSION);
465 if (gcd < lowest / 10)
467 gmx_warning("The lowest common denominator of trajectory output is "
468 "every %d step(s), whereas the shortest output interval "
469 "is every %d steps.", gcd, lowest);
475 void gmx_tng_prepare_md_writing(tng_trajectory_t tng,
476 const gmx_mtop_t *mtop,
477 const t_inputrec *ir)
480 gmx_tng_add_mtop(tng, mtop);
481 set_writing_intervals(tng, FALSE, ir);
482 tng_time_per_frame_set(tng, ir->delta_t * PICO);
484 GMX_UNUSED_VALUE(tng);
485 GMX_UNUSED_VALUE(mtop);
486 GMX_UNUSED_VALUE(ir);
491 /* Check if all atoms in the molecule system are selected
492 * by a selection group of type specified by gtype. */
493 static gmx_bool all_atoms_selected(const gmx_mtop_t *mtop,
496 const gmx_moltype_t *molType;
497 const t_atoms *atoms;
499 /* Iterate through all atoms in the molecule system and
500 * check if they belong to a selection group of the
502 for (int molIndex = 0, i = 0; molIndex < mtop->nmoltype; molIndex++)
504 molType = &mtop->moltype[mtop->molblock[molIndex].type];
506 atoms = &molType->atoms;
508 for (int j = 0; j < mtop->molblock[molIndex].nmol; j++)
510 for (int atomIndex = 0; atomIndex < atoms->nr; atomIndex++, i++)
512 if (ggrpnr(&mtop->groups, gtype, i) != 0)
522 /* Create TNG molecules which will represent each of the selection
523 * groups for writing. But do that only if there is actually a
524 * specified selection group and it is not the whole system.
525 * TODO: Currently the only selection that is taken into account
526 * is egcCompressedX, but other selections should be added when
527 * e.g. writing energies is implemented.
529 static void add_selection_groups(tng_trajectory_t tng,
530 const gmx_mtop_t *mtop)
532 const gmx_moltype_t *molType;
533 const t_atoms *atoms;
535 const t_resinfo *resInfo;
536 const t_ilist *ilist;
539 tng_molecule_t mol, iterMol;
547 /* TODO: When the TNG molecules block is more flexible TNG selection
548 * groups should not need all atoms specified. It should be possible
549 * just to specify what molecules are selected (and/or which atoms in
550 * the molecule) and how many (if applicable). */
552 /* If no atoms are selected we do not need to create a
553 * TNG selection group molecule. */
554 if (mtop->groups.ngrpnr[egcCompressedX] == 0)
559 /* If all atoms are selected we do not have to create a selection
560 * group molecule in the TNG molecule system. */
561 if (all_atoms_selected(mtop, egcCompressedX))
566 /* The name of the TNG molecule containing the selection group is the
567 * same as the name of the selection group. */
568 nameIndex = *mtop->groups.grps[egcCompressedX].nm_ind;
569 groupName = *mtop->groups.grpname[nameIndex];
571 tng_molecule_alloc(tng, &mol);
572 tng_molecule_name_set(tng, mol, groupName);
573 tng_molecule_chain_add(tng, mol, "", &chain);
574 for (int molIndex = 0, i = 0; molIndex < mtop->nmoltype; molIndex++)
576 molType = &mtop->moltype[mtop->molblock[molIndex].type];
578 atoms = &molType->atoms;
580 for (int j = 0; j < mtop->molblock[molIndex].nmol; j++)
582 bool bAtomsAdded = FALSE;
583 for (int atomIndex = 0; atomIndex < atoms->nr; atomIndex++, i++)
588 if (ggrpnr(&mtop->groups, egcCompressedX, i) != 0)
592 at = &atoms->atom[atomIndex];
595 resInfo = &atoms->resinfo[at->resind];
596 /* FIXME: When TNG supports both residue index and residue
597 * number the latter should be used. */
598 res_name = *resInfo->name;
599 res_id = at->resind + 1;
603 res_name = (char *)"";
606 if (tng_chain_residue_find(tng, chain, res_name, res_id, &res)
609 /* Since there is ONE chain for selection groups do not keep the
610 * original residue IDs - otherwise there might be conflicts. */
611 tng_chain_residue_add(tng, chain, res_name, &res);
613 tng_residue_atom_w_id_add(tng, res, *(atoms->atomname[atomIndex]),
614 *(atoms->atomtype[atomIndex]),
615 atom_offset + atomIndex, &atom);
621 for (int k = 0; k < F_NRE; k++)
625 ilist = &molType->ilist[k];
629 while (l < ilist->nr)
632 atom1 = ilist->iatoms[l] + atom_offset;
633 atom2 = ilist->iatoms[l+1] + atom_offset;
634 if (ggrpnr(&mtop->groups, egcCompressedX, atom1) == 0 &&
635 ggrpnr(&mtop->groups, egcCompressedX, atom2) == 0)
637 tng_molecule_bond_add(tng, mol, ilist->iatoms[l],
638 ilist->iatoms[l+1], &tngBond);
645 /* Settle is described using three atoms */
646 ilist = &molType->ilist[F_SETTLE];
650 while (l < ilist->nr)
652 int atom1, atom2, atom3;
653 atom1 = ilist->iatoms[l] + atom_offset;
654 atom2 = ilist->iatoms[l+1] + atom_offset;
655 atom3 = ilist->iatoms[l+2] + atom_offset;
656 if (ggrpnr(&mtop->groups, egcCompressedX, atom1) == 0)
658 if (ggrpnr(&mtop->groups, egcCompressedX, atom2) == 0)
660 tng_molecule_bond_add(tng, mol, atom1,
663 if (ggrpnr(&mtop->groups, egcCompressedX, atom3) == 0)
665 tng_molecule_bond_add(tng, mol, atom1,
673 atom_offset += atoms->nr;
676 tng_molecule_existing_add(tng, &mol);
677 tng_molecule_cnt_set(tng, mol, 1);
678 tng_num_molecule_types_get(tng, &nMols);
679 for (gmx_int64_t k = 0; k < nMols; k++)
681 tng_molecule_of_index_get(tng, k, &iterMol);
686 tng_molecule_cnt_set(tng, iterMol, 0);
691 void gmx_tng_set_compression_precision(tng_trajectory_t tng,
695 tng_compression_precision_set(tng, prec);
697 GMX_UNUSED_VALUE(tng);
698 GMX_UNUSED_VALUE(prec);
702 void gmx_tng_prepare_low_prec_writing(tng_trajectory_t tng,
703 const gmx_mtop_t *mtop,
704 const t_inputrec *ir)
707 gmx_tng_add_mtop(tng, mtop);
708 add_selection_groups(tng, mtop);
709 set_writing_intervals(tng, TRUE, ir);
710 tng_time_per_frame_set(tng, ir->delta_t * PICO);
711 gmx_tng_set_compression_precision(tng, ir->x_compression_precision);
713 GMX_UNUSED_VALUE(tng);
714 GMX_UNUSED_VALUE(mtop);
715 GMX_UNUSED_VALUE(ir);
719 void gmx_fwrite_tng(tng_trajectory_t tng,
720 const gmx_bool bUseLossyCompression,
722 real elapsedPicoSeconds,
731 typedef tng_function_status (*write_data_func_pointer)(tng_trajectory_t,
741 static write_data_func_pointer write_data = tng_util_generic_with_time_double_write;
743 static write_data_func_pointer write_data = tng_util_generic_with_time_write;
745 double elapsedSeconds = elapsedPicoSeconds * PICO;
746 gmx_int64_t nParticles;
752 /* This function might get called when the type of the
753 compressed trajectory is actually XTC. So we exit and move
758 tng_num_particles_get(tng, &nParticles);
759 if (nAtoms != (int)nParticles)
761 tng_implicit_num_particles_set(tng, nAtoms);
764 if (bUseLossyCompression)
766 compression = TNG_TNG_COMPRESSION;
770 compression = TNG_GZIP_COMPRESSION;
773 /* The writing is done using write_data, which writes float or double
774 * depending on the GROMACS compilation. */
777 GMX_ASSERT(box, "Need a non-NULL box if positions are written");
779 if (write_data(tng, step, elapsedSeconds,
780 reinterpret_cast<const real *>(x),
781 3, TNG_TRAJ_POSITIONS, "POSITIONS",
782 TNG_PARTICLE_BLOCK_DATA,
783 compression) != TNG_SUCCESS)
785 gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
791 if (write_data(tng, step, elapsedSeconds,
792 reinterpret_cast<const real *>(v),
793 3, TNG_TRAJ_VELOCITIES, "VELOCITIES",
794 TNG_PARTICLE_BLOCK_DATA,
795 compression) != TNG_SUCCESS)
797 gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
803 /* TNG-MF1 compression only compresses positions and velocities. Use lossless
804 * compression for forces regardless of output mode */
805 if (write_data(tng, step, elapsedSeconds,
806 reinterpret_cast<const real *>(f),
807 3, TNG_TRAJ_FORCES, "FORCES",
808 TNG_PARTICLE_BLOCK_DATA,
809 TNG_GZIP_COMPRESSION) != TNG_SUCCESS)
811 gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
815 /* TNG-MF1 compression only compresses positions and velocities. Use lossless
816 * compression for lambdas and box shape regardless of output mode */
817 if (write_data(tng, step, elapsedSeconds,
818 reinterpret_cast<const real *>(box),
819 9, TNG_TRAJ_BOX_SHAPE, "BOX SHAPE",
820 TNG_NON_PARTICLE_BLOCK_DATA,
821 TNG_GZIP_COMPRESSION) != TNG_SUCCESS)
823 gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
826 if (write_data(tng, step, elapsedSeconds,
827 reinterpret_cast<const real *>(&lambda),
828 1, TNG_GMX_LAMBDA, "LAMBDAS",
829 TNG_NON_PARTICLE_BLOCK_DATA,
830 TNG_GZIP_COMPRESSION) != TNG_SUCCESS)
832 gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
835 GMX_UNUSED_VALUE(tng);
836 GMX_UNUSED_VALUE(bUseLossyCompression);
837 GMX_UNUSED_VALUE(step);
838 GMX_UNUSED_VALUE(elapsedPicoSeconds);
839 GMX_UNUSED_VALUE(lambda);
840 GMX_UNUSED_VALUE(box);
841 GMX_UNUSED_VALUE(nAtoms);
848 void fflush_tng(tng_trajectory_t tng)
855 tng_frame_set_premature_write(tng, TNG_USE_HASH);
857 GMX_UNUSED_VALUE(tng);
861 float gmx_tng_get_time_of_final_frame(tng_trajectory_t tng)
868 tng_num_frames_get(tng, &nFrames);
869 tng_util_time_of_frame_get(tng, nFrames - 1, &time);
874 GMX_UNUSED_VALUE(tng);
879 void gmx_prepare_tng_writing(const char *filename,
881 tng_trajectory_t *input,
882 tng_trajectory_t *output,
884 const gmx_mtop_t *mtop,
886 const char *indexGroupName)
889 /* FIXME after 5.0: Currently only standard block types are read */
890 const int defaultNumIds = 5;
891 static gmx_int64_t fallbackIds[defaultNumIds] =
893 TNG_TRAJ_BOX_SHAPE, TNG_TRAJ_POSITIONS,
894 TNG_TRAJ_VELOCITIES, TNG_TRAJ_FORCES,
897 static char fallbackNames[defaultNumIds][32] =
899 "BOX SHAPE", "POSITIONS", "VELOCITIES",
903 typedef tng_function_status (*set_writing_interval_func_pointer)(tng_trajectory_t,
911 set_writing_interval_func_pointer set_writing_interval = tng_util_generic_write_interval_double_set;
913 set_writing_interval_func_pointer set_writing_interval = tng_util_generic_write_interval_set;
916 gmx_tng_open(filename, mode, output);
918 /* Do we have an input file in TNG format? If so, then there's
919 more data we can copy over, rather than having to improvise. */
922 /* Set parameters (compression, time per frame, molecule
923 * information, number of frames per frame set and writing
924 * intervals of positions, box shape and lambdas) of the
925 * output tng container based on their respective values int
926 * the input tng container */
927 double time, compression_precision;
928 gmx_int64_t n_frames_per_frame_set, interval = -1;
930 tng_compression_precision_get(*input, &compression_precision);
931 tng_compression_precision_set(*output, compression_precision);
932 // TODO make this configurable in a future version
933 char compression_type = TNG_TNG_COMPRESSION;
935 tng_molecule_system_copy(*input, *output);
937 tng_time_per_frame_get(*input, &time);
938 tng_time_per_frame_set(*output, time);
940 tng_num_frames_per_frame_set_get(*input, &n_frames_per_frame_set);
941 tng_num_frames_per_frame_set_set(*output, n_frames_per_frame_set);
943 for (int i = 0; i < defaultNumIds; i++)
945 if (tng_data_get_stride_length(*input, fallbackIds[i], -1, &interval)
948 switch (fallbackIds[i])
950 case TNG_TRAJ_POSITIONS:
951 case TNG_TRAJ_VELOCITIES:
952 set_writing_interval(*output, interval, 3, fallbackIds[i],
953 fallbackNames[i], TNG_PARTICLE_BLOCK_DATA,
956 case TNG_TRAJ_FORCES:
957 set_writing_interval(*output, interval, 3, fallbackIds[i],
958 fallbackNames[i], TNG_PARTICLE_BLOCK_DATA,
959 TNG_GZIP_COMPRESSION);
961 case TNG_TRAJ_BOX_SHAPE:
962 set_writing_interval(*output, interval, 9, fallbackIds[i],
963 fallbackNames[i], TNG_NON_PARTICLE_BLOCK_DATA,
964 TNG_GZIP_COMPRESSION);
967 set_writing_interval(*output, interval, 1, fallbackIds[i],
968 fallbackNames[i], TNG_NON_PARTICLE_BLOCK_DATA,
969 TNG_GZIP_COMPRESSION);
980 /* TODO after trjconv is modularized: fix this so the user can
981 change precision when they are doing an operation where
982 this makes sense, and not otherwise.
984 char compression = bUseLossyCompression ? TNG_TNG_COMPRESSION : TNG_GZIP_COMPRESSION;
985 gmx_tng_set_compression_precision(*output, ndec2prec(nDecimalsOfPrecision));
987 gmx_tng_add_mtop(*output, mtop);
988 tng_num_frames_per_frame_set_set(*output, 1);
991 if (index && nAtoms > 0)
993 gmx_tng_setup_atom_subgroup(*output, nAtoms, index, indexGroupName);
996 /* If for some reason there are more requested atoms than there are atoms in the
997 * molecular system create a number of implicit atoms (without atom data) to
998 * compensate for that. */
1001 tng_implicit_num_particles_set(*output, nAtoms);
1004 GMX_UNUSED_VALUE(filename);
1005 GMX_UNUSED_VALUE(mode);
1006 GMX_UNUSED_VALUE(input);
1007 GMX_UNUSED_VALUE(output);
1008 GMX_UNUSED_VALUE(nAtoms);
1009 GMX_UNUSED_VALUE(mtop);
1010 GMX_UNUSED_VALUE(index);
1011 GMX_UNUSED_VALUE(indexGroupName);
1015 void gmx_write_tng_from_trxframe(tng_trajectory_t output,
1016 const t_trxframe *frame,
1020 if (frame->step > 0)
1022 double timePerFrame = frame->time * PICO / frame->step;
1023 tng_time_per_frame_set(output, timePerFrame);
1027 natoms = frame->natoms;
1029 gmx_fwrite_tng(output,
1040 GMX_UNUSED_VALUE(output);
1041 GMX_UNUSED_VALUE(frame);
1042 GMX_UNUSED_VALUE(natoms);
1051 convert_array_to_real_array(void *from,
1056 const char datatype)
1060 const bool useDouble = GMX_DOUBLE;
1063 case TNG_FLOAT_DATA:
1068 memcpy(to, from, nValues * sizeof(real) * nAtoms);
1072 for (i = 0; i < nAtoms; i++)
1074 for (j = 0; j < nValues; j++)
1076 to[i*nValues+j] = reinterpret_cast<float *>(from)[i*nValues+j] * fact;
1083 for (i = 0; i < nAtoms; i++)
1085 for (j = 0; j < nValues; j++)
1087 to[i*nValues+j] = reinterpret_cast<float *>(from)[i*nValues+j] * fact;
1093 for (i = 0; i < nAtoms; i++)
1095 for (j = 0; j < nValues; j++)
1097 to[i*nValues+j] = reinterpret_cast<gmx_int64_t *>(from)[i*nValues+j] * fact;
1101 case TNG_DOUBLE_DATA:
1102 if (sizeof(real) == sizeof(double))
1106 memcpy(to, from, nValues * sizeof(real) * nAtoms);
1110 for (i = 0; i < nAtoms; i++)
1112 for (j = 0; j < nValues; j++)
1114 to[i*nValues+j] = reinterpret_cast<double *>(from)[i*nValues+j] * fact;
1121 for (i = 0; i < nAtoms; i++)
1123 for (j = 0; j < nValues; j++)
1125 to[i*nValues+j] = reinterpret_cast<double *>(from)[i*nValues+j] * fact;
1131 gmx_incons("Illegal datatype when converting values to a real array!");
1137 real getDistanceScaleFactor(tng_trajectory_t in)
1139 gmx_int64_t exp = -1;
1140 real distanceScaleFactor;
1142 // TODO Hopefully, TNG 2.0 will do this kind of thing for us
1143 tng_distance_unit_exponential_get(in, &exp);
1145 // GROMACS expects distances in nm
1149 distanceScaleFactor = NANO/NANO;
1152 distanceScaleFactor = NANO/ANGSTROM;
1155 distanceScaleFactor = pow(10.0, exp + 9.0);
1158 return distanceScaleFactor;
1164 void gmx_tng_setup_atom_subgroup(tng_trajectory_t tng,
1170 gmx_int64_t nAtoms, cnt, nMols;
1171 tng_molecule_t mol, iterMol;
1175 tng_function_status stat;
1177 tng_num_particles_get(tng, &nAtoms);
1184 stat = tng_molecule_find(tng, name, -1, &mol);
1185 if (stat == TNG_SUCCESS)
1187 tng_molecule_num_atoms_get(tng, mol, &nAtoms);
1188 tng_molecule_cnt_get(tng, mol, &cnt);
1198 if (stat == TNG_FAILURE)
1200 /* The indexed atoms are added to one separate molecule. */
1201 tng_molecule_alloc(tng, &mol);
1202 tng_molecule_name_set(tng, mol, name);
1203 tng_molecule_chain_add(tng, mol, "", &chain);
1205 for (int i = 0; i < nind; i++)
1207 char temp_name[256], temp_type[256];
1209 /* Try to retrieve the residue name of the atom */
1210 stat = tng_residue_name_of_particle_nr_get(tng, ind[i], temp_name, 256);
1211 if (stat != TNG_SUCCESS)
1213 temp_name[0] = '\0';
1215 /* Check if the molecule of the selection already contains this residue */
1216 if (tng_chain_residue_find(tng, chain, temp_name, -1, &res)
1219 tng_chain_residue_add(tng, chain, temp_name, &res);
1221 /* Try to find the original name and type of the atom */
1222 stat = tng_atom_name_of_particle_nr_get(tng, ind[i], temp_name, 256);
1223 if (stat != TNG_SUCCESS)
1225 temp_name[0] = '\0';
1227 stat = tng_atom_type_of_particle_nr_get(tng, ind[i], temp_type, 256);
1228 if (stat != TNG_SUCCESS)
1230 temp_type[0] = '\0';
1232 tng_residue_atom_w_id_add(tng, res, temp_name, temp_type, ind[i], &atom);
1234 tng_molecule_existing_add(tng, &mol);
1236 /* Set the count of the molecule containing the selected atoms to 1 and all
1237 * other molecules to 0 */
1238 tng_molecule_cnt_set(tng, mol, 1);
1239 tng_num_molecule_types_get(tng, &nMols);
1240 for (gmx_int64_t k = 0; k < nMols; k++)
1242 tng_molecule_of_index_get(tng, k, &iterMol);
1247 tng_molecule_cnt_set(tng, iterMol, 0);
1250 GMX_UNUSED_VALUE(tng);
1251 GMX_UNUSED_VALUE(nind);
1252 GMX_UNUSED_VALUE(ind);
1253 GMX_UNUSED_VALUE(name);
1257 /* TODO: If/when TNG acquires the ability to copy data blocks without
1258 * uncompressing them, then this implemenation should be reconsidered.
1259 * Ideally, gmx trjconv -f a.tng -o b.tng -b 10 -e 20 would be fast
1260 * and lose no information. */
1261 gmx_bool gmx_read_next_tng_frame(tng_trajectory_t input,
1263 gmx_int64_t *requestedIds,
1264 int numRequestedIds)
1267 gmx_bool bOK = TRUE;
1268 tng_function_status stat;
1269 gmx_int64_t numberOfAtoms = -1, frameNumber = -1;
1270 gmx_int64_t nBlocks, blockId, *blockIds = nullptr, codecId;
1272 void *values = nullptr;
1273 double frameTime = -1.0;
1274 int size, blockDependency;
1276 const int defaultNumIds = 5;
1277 static gmx_int64_t fallbackRequestedIds[defaultNumIds] =
1279 TNG_TRAJ_BOX_SHAPE, TNG_TRAJ_POSITIONS,
1280 TNG_TRAJ_VELOCITIES, TNG_TRAJ_FORCES,
1287 fr->bLambda = FALSE;
1295 /* If no specific IDs were requested read all block types that can
1296 * currently be interpreted */
1297 if (!requestedIds || numRequestedIds == 0)
1299 numRequestedIds = defaultNumIds;
1300 requestedIds = fallbackRequestedIds;
1303 stat = tng_num_particles_get(input, &numberOfAtoms);
1304 if (stat != TNG_SUCCESS)
1306 gmx_file("Cannot determine number of atoms from TNG file.");
1308 fr->natoms = numberOfAtoms;
1310 bool nextFrameExists = gmx_get_tng_data_block_types_of_next_frame(input,
1317 gmx::unique_cptr<gmx_int64_t, gmx::free_wrapper> blockIdsGuard(blockIds);
1318 if (!nextFrameExists)
1328 for (gmx_int64_t i = 0; i < nBlocks; i++)
1330 blockId = blockIds[i];
1331 tng_data_block_dependency_get(input, blockId, &blockDependency);
1332 if (blockDependency & TNG_PARTICLE_DEPENDENT)
1334 stat = tng_util_particle_data_next_frame_read(input,
1343 stat = tng_util_non_particle_data_next_frame_read(input,
1350 if (stat == TNG_CRITICAL)
1352 gmx_file("Cannot read positions from TNG file.");
1355 else if (stat == TNG_FAILURE)
1361 case TNG_TRAJ_BOX_SHAPE:
1365 size = sizeof(gmx_int64_t);
1367 case TNG_FLOAT_DATA:
1368 size = sizeof(float);
1370 case TNG_DOUBLE_DATA:
1371 size = sizeof(double);
1374 gmx_incons("Illegal datatype of box shape values!");
1376 for (int i = 0; i < DIM; i++)
1378 convert_array_to_real_array(reinterpret_cast<char *>(values) + size * i * DIM,
1379 reinterpret_cast<real *>(fr->box[i]),
1380 getDistanceScaleFactor(input),
1387 case TNG_TRAJ_POSITIONS:
1388 srenew(fr->x, fr->natoms);
1389 convert_array_to_real_array(values,
1390 reinterpret_cast<real *>(fr->x),
1391 getDistanceScaleFactor(input),
1396 tng_util_frame_current_compression_get(input, blockId, &codecId, &prec);
1397 /* This must be updated if/when more lossy compression methods are added */
1398 if (codecId == TNG_TNG_COMPRESSION)
1404 case TNG_TRAJ_VELOCITIES:
1405 srenew(fr->v, fr->natoms);
1406 convert_array_to_real_array(values,
1408 getDistanceScaleFactor(input),
1413 tng_util_frame_current_compression_get(input, blockId, &codecId, &prec);
1414 /* This must be updated if/when more lossy compression methods are added */
1415 if (codecId == TNG_TNG_COMPRESSION)
1421 case TNG_TRAJ_FORCES:
1422 srenew(fr->f, fr->natoms);
1423 convert_array_to_real_array(values,
1424 reinterpret_cast<real *>(fr->f),
1425 getDistanceScaleFactor(input),
1431 case TNG_GMX_LAMBDA:
1434 case TNG_FLOAT_DATA:
1435 fr->lambda = *(reinterpret_cast<float *>(values));
1437 case TNG_DOUBLE_DATA:
1438 fr->lambda = *(reinterpret_cast<double *>(values));
1441 gmx_incons("Illegal datatype lambda value!");
1446 gmx_warning("Illegal block type! Currently GROMACS tools can only handle certain data types. Skipping block.");
1448 /* values does not have to be freed before reading next frame. It will
1449 * be reallocated if it is not NULL. */
1452 fr->step = frameNumber;
1454 // Convert the time to ps
1455 fr->time = frameTime / PICO;
1458 // TODO This does not leak, but is not exception safe.
1459 /* values must be freed before leaving this function */
1464 GMX_UNUSED_VALUE(input);
1465 GMX_UNUSED_VALUE(fr);
1466 GMX_UNUSED_VALUE(requestedIds);
1467 GMX_UNUSED_VALUE(numRequestedIds);
1472 void gmx_print_tng_molecule_system(tng_trajectory_t input,
1476 gmx_int64_t nMolecules, nChains, nResidues, nAtoms, *molCntList;
1477 tng_molecule_t molecule;
1479 tng_residue_t residue;
1481 char str[256], varNAtoms;
1483 tng_num_molecule_types_get(input, &nMolecules);
1484 tng_molecule_cnt_list_get(input, &molCntList);
1485 /* Can the number of particles change in the trajectory or is it constant? */
1486 tng_num_particles_variable_get(input, &varNAtoms);
1488 for (gmx_int64_t i = 0; i < nMolecules; i++)
1490 tng_molecule_of_index_get(input, i, &molecule);
1491 tng_molecule_name_get(input, molecule, str, 256);
1492 if (varNAtoms == TNG_CONSTANT_N_ATOMS)
1494 if ((int)molCntList[i] == 0)
1498 fprintf(stream, "Molecule: %s, count: %d\n", str, (int)molCntList[i]);
1502 fprintf(stream, "Molecule: %s\n", str);
1504 tng_molecule_num_chains_get(input, molecule, &nChains);
1507 for (gmx_int64_t j = 0; j < nChains; j++)
1509 tng_molecule_chain_of_index_get(input, molecule, j, &chain);
1510 tng_chain_name_get(input, chain, str, 256);
1511 fprintf(stream, "\tChain: %s\n", str);
1512 tng_chain_num_residues_get(input, chain, &nResidues);
1513 for (gmx_int64_t k = 0; k < nResidues; k++)
1515 tng_chain_residue_of_index_get(input, chain, k, &residue);
1516 tng_residue_name_get(input, residue, str, 256);
1517 fprintf(stream, "\t\tResidue: %s\n", str);
1518 tng_residue_num_atoms_get(input, residue, &nAtoms);
1519 for (gmx_int64_t l = 0; l < nAtoms; l++)
1521 tng_residue_atom_of_index_get(input, residue, l, &atom);
1522 tng_atom_name_get(input, atom, str, 256);
1523 fprintf(stream, "\t\t\tAtom: %s", str);
1524 tng_atom_type_get(input, atom, str, 256);
1525 fprintf(stream, " (%s)\n", str);
1530 /* It is possible to have a molecule without chains, in which case
1531 * residues in the molecule can be iterated through without going
1532 * through chains. */
1535 tng_molecule_num_residues_get(input, molecule, &nResidues);
1538 for (gmx_int64_t k = 0; k < nResidues; k++)
1540 tng_molecule_residue_of_index_get(input, molecule, k, &residue);
1541 tng_residue_name_get(input, residue, str, 256);
1542 fprintf(stream, "\t\tResidue: %s\n", str);
1543 tng_residue_num_atoms_get(input, residue, &nAtoms);
1544 for (gmx_int64_t l = 0; l < nAtoms; l++)
1546 tng_residue_atom_of_index_get(input, residue, l, &atom);
1547 tng_atom_name_get(input, atom, str, 256);
1548 fprintf(stream, "\t\t\tAtom: %s", str);
1549 tng_atom_type_get(input, atom, str, 256);
1550 fprintf(stream, " (%s)\n", str);
1556 tng_molecule_num_atoms_get(input, molecule, &nAtoms);
1557 for (gmx_int64_t l = 0; l < nAtoms; l++)
1559 tng_molecule_atom_of_index_get(input, molecule, l, &atom);
1560 tng_atom_name_get(input, atom, str, 256);
1561 fprintf(stream, "\t\t\tAtom: %s", str);
1562 tng_atom_type_get(input, atom, str, 256);
1563 fprintf(stream, " (%s)\n", str);
1569 GMX_UNUSED_VALUE(input);
1570 GMX_UNUSED_VALUE(stream);
1574 gmx_bool gmx_get_tng_data_block_types_of_next_frame(tng_trajectory_t input,
1577 gmx_int64_t *requestedIds,
1578 gmx_int64_t *nextFrame,
1579 gmx_int64_t *nBlocks,
1580 gmx_int64_t **blockIds)
1583 tng_function_status stat;
1585 stat = tng_util_trajectory_next_frame_present_data_blocks_find(input, frame,
1586 nRequestedIds, requestedIds,
1590 if (stat == TNG_CRITICAL)
1592 gmx_file("Cannot read TNG file. Cannot find data blocks of next frame.");
1594 else if (stat == TNG_FAILURE)
1600 GMX_UNUSED_VALUE(input);
1601 GMX_UNUSED_VALUE(frame);
1602 GMX_UNUSED_VALUE(nRequestedIds);
1603 GMX_UNUSED_VALUE(requestedIds);
1604 GMX_UNUSED_VALUE(nextFrame);
1605 GMX_UNUSED_VALUE(nBlocks);
1606 GMX_UNUSED_VALUE(blockIds);
1611 gmx_bool gmx_get_tng_data_next_frame_of_block_type(tng_trajectory_t input,
1612 gmx_int64_t blockId,
1614 gmx_int64_t *frameNumber,
1616 gmx_int64_t *nValuesPerFrame,
1617 gmx_int64_t *nAtoms,
1624 tng_function_status stat;
1626 gmx_int64_t codecId;
1627 int blockDependency;
1628 void *data = nullptr;
1631 stat = tng_data_block_name_get(input, blockId, name, maxLen);
1632 if (stat != TNG_SUCCESS)
1634 gmx_file("Cannot read next frame of TNG file");
1636 stat = tng_data_block_dependency_get(input, blockId, &blockDependency);
1637 if (stat != TNG_SUCCESS)
1639 gmx_file("Cannot read next frame of TNG file");
1641 if (blockDependency & TNG_PARTICLE_DEPENDENT)
1643 tng_num_particles_get(input, nAtoms);
1644 stat = tng_util_particle_data_next_frame_read(input,
1653 *nAtoms = 1; /* There are not actually any atoms, but it is used for
1654 allocating memory */
1655 stat = tng_util_non_particle_data_next_frame_read(input,
1662 if (stat == TNG_CRITICAL)
1664 gmx_file("Cannot read next frame of TNG file");
1666 if (stat == TNG_FAILURE)
1672 stat = tng_data_block_num_values_per_frame_get(input, blockId, nValuesPerFrame);
1673 if (stat != TNG_SUCCESS)
1675 gmx_file("Cannot read next frame of TNG file");
1677 srenew(*values, sizeof(real) * *nValuesPerFrame * *nAtoms);
1678 convert_array_to_real_array(data,
1680 getDistanceScaleFactor(input),
1685 tng_util_frame_current_compression_get(input, blockId, &codecId, &localPrec);
1687 /* This must be updated if/when more lossy compression methods are added */
1688 if (codecId != TNG_TNG_COMPRESSION)
1700 GMX_UNUSED_VALUE(input);
1701 GMX_UNUSED_VALUE(blockId);
1702 GMX_UNUSED_VALUE(values);
1703 GMX_UNUSED_VALUE(frameNumber);
1704 GMX_UNUSED_VALUE(frameTime);
1705 GMX_UNUSED_VALUE(nValuesPerFrame);
1706 GMX_UNUSED_VALUE(nAtoms);
1707 GMX_UNUSED_VALUE(prec);
1708 GMX_UNUSED_VALUE(name);
1709 GMX_UNUSED_VALUE(maxLen);
1710 GMX_UNUSED_VALUE(bOK);