Fix issues with using int for number of steps
[gromacs/AngularHB.git] / src / gromacs / fileio / tngio.cpp
blob13d9cbd70ed14ce4a7b9be4a3ed84de68641c3bc
1 /*
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.
35 #include "gmxpre.h"
37 #include "tngio.h"
39 #include "config.h"
41 #if GMX_USE_TNG
42 #include "tng/tng_io.h"
43 #endif
45 #include "gromacs/math/units.h"
46 #include "gromacs/math/utilities.h"
47 #include "gromacs/mdtypes/inputrec.h"
48 #include "gromacs/topology/ifunc.h"
49 #include "gromacs/topology/topology.h"
50 #include "gromacs/utility/basedefinitions.h"
51 #include "gromacs/utility/baseversion.h"
52 #include "gromacs/utility/fatalerror.h"
53 #include "gromacs/utility/futil.h"
54 #include "gromacs/utility/gmxassert.h"
55 #include "gromacs/utility/programcontext.h"
56 #include "gromacs/utility/sysinfo.h"
58 static const char *modeToVerb(char mode)
60 const char *p;
61 switch (mode)
63 case 'r':
64 p = "reading";
65 break;
66 case 'w':
67 p = "writing";
68 break;
69 case 'a':
70 p = "appending";
71 break;
72 default:
73 gmx_fatal(FARGS, "Invalid file opening mode %c", mode);
74 p = "";
75 break;
77 return p;
80 void gmx_tng_open(const char *filename,
81 char mode,
82 tng_trajectory_t *tng)
84 #if GMX_USE_TNG
85 /* First check whether we have to make a backup,
86 * only for writing, not for read or append.
88 if (mode == 'w')
90 make_backup(filename);
93 /* tng must not be pointing at already allocated memory.
94 * Memory will be allocated by tng_util_trajectory_open() and must
95 * later on be freed by tng_util_trajectory_close(). */
96 if (TNG_SUCCESS != tng_util_trajectory_open(filename, mode, tng))
98 /* TNG does return more than one degree of error, but there is
99 no use case for GROMACS handling the non-fatal errors
100 gracefully. */
101 gmx_fatal(FARGS,
102 "File I/O error while opening %s for %s",
103 filename,
104 modeToVerb(mode));
107 if (mode == 'w' || mode == 'a')
109 char hostname[256];
110 gmx_gethostname(hostname, 256);
111 if (mode == 'w')
113 tng_first_computer_name_set(*tng, hostname);
115 else
117 tng_last_computer_name_set(*tng, hostname);
120 char programInfo[256];
121 const char *precisionString = "";
122 #if GMX_DOUBLE
123 precisionString = " (double precision)";
124 #endif
125 sprintf(programInfo, "%.100s %.128s%.24s",
126 gmx::getProgramContext().displayName(),
127 gmx_version(), precisionString);
128 if (mode == 'w')
130 tng_first_program_name_set(*tng, programInfo);
132 else
134 tng_last_program_name_set(*tng, programInfo);
137 char username[256];
138 if (!gmx_getusername(username, 256))
140 if (mode == 'w')
142 tng_first_user_name_set(*tng, username);
144 else
146 tng_last_user_name_set(*tng, username);
147 tng_file_headers_write(*tng, TNG_USE_HASH);
151 #else
152 gmx_file("GROMACS was compiled without TNG support, cannot handle this file type");
153 GMX_UNUSED_VALUE(filename);
154 GMX_UNUSED_VALUE(mode);
155 GMX_UNUSED_VALUE(tng);
156 #endif
159 void gmx_tng_close(tng_trajectory_t *tng)
161 /* We have to check that tng is set because
162 * tng_util_trajectory_close wants to return a NULL in it, and
163 * gives a fatal error if it is NULL. */
164 #if GMX_USE_TNG
165 if (tng)
167 tng_util_trajectory_close(tng);
169 #else
170 GMX_UNUSED_VALUE(tng);
171 #endif
174 #if GMX_USE_TNG
175 static void addTngMoleculeFromTopology(tng_trajectory_t tng,
176 const char *moleculeName,
177 const t_atoms *atoms,
178 gmx_int64_t numMolecules,
179 tng_molecule_t *tngMol)
181 tng_chain_t tngChain = NULL;
182 tng_residue_t tngRes = NULL;
184 if (tng_molecule_add(tng, moleculeName, tngMol) != TNG_SUCCESS)
186 gmx_file("Cannot add molecule to TNG molecular system.");
189 /* FIXME: The TNG atoms should contain mass and atomB info (for free
190 * energy calculations), i.e. in when it's available in TNG (2.0). */
191 for (int atomIndex = 0; atomIndex < atoms->nr; atomIndex++)
193 const t_atom *at = &atoms->atom[atomIndex];
194 /* FIXME: Currently the TNG API can only add atoms belonging to a
195 * residue and chain. Wait for TNG 2.0*/
196 if (atoms->nres > 0)
198 const t_resinfo *resInfo = &atoms->resinfo[at->resind];
199 char chainName[2] = {resInfo->chainid, 0};
200 tng_atom_t tngAtom = NULL;
201 t_atom *prevAtom;
203 if (atomIndex > 0)
205 prevAtom = &atoms->atom[atomIndex - 1];
207 else
209 prevAtom = 0;
212 /* If this is the first atom or if the residue changed add the
213 * residue to the TNG molecular system. */
214 if (!prevAtom || resInfo != &atoms->resinfo[prevAtom->resind])
216 /* If this is the first atom or if the chain changed add
217 * the chain to the TNG molecular system. */
218 if (!prevAtom || resInfo->chainid !=
219 atoms->resinfo[prevAtom->resind].chainid)
221 tng_molecule_chain_add(tng, *tngMol, chainName,
222 &tngChain);
224 /* FIXME: When TNG supports both residue index and residue
225 * number the latter should be used. Wait for TNG 2.0*/
226 tng_chain_residue_add(tng, tngChain, *resInfo->name, &tngRes);
228 tng_residue_atom_add(tng, tngRes, *(atoms->atomname[atomIndex]), *(atoms->atomtype[atomIndex]), &tngAtom);
231 tng_molecule_cnt_set(tng, *tngMol, numMolecules);
234 void gmx_tng_add_mtop(tng_trajectory_t tng,
235 const gmx_mtop_t *mtop)
237 int i, j;
238 const t_ilist *ilist;
239 tng_bond_t tngBond;
241 if (!mtop)
243 /* No topology information available to add. */
244 return;
247 for (int molIndex = 0; molIndex < mtop->nmolblock; molIndex++)
249 tng_molecule_t tngMol = NULL;
250 const gmx_moltype_t *molType =
251 &mtop->moltype[mtop->molblock[molIndex].type];
253 /* Add a molecule to the TNG trajectory with the same name as the
254 * current molecule. */
255 addTngMoleculeFromTopology(tng,
256 *(molType->name),
257 &molType->atoms,
258 mtop->molblock[molIndex].nmol,
259 &tngMol);
261 /* Bonds have to be deduced from interactions (constraints etc). Different
262 * interactions have different sets of parameters. */
263 /* Constraints are specified using two atoms */
264 for (i = 0; i < F_NRE; i++)
266 if (IS_CHEMBOND(i))
268 ilist = &molType->ilist[i];
269 if (ilist)
271 j = 1;
272 while (j < ilist->nr)
274 tng_molecule_bond_add(tng, tngMol, ilist->iatoms[j], ilist->iatoms[j+1], &tngBond);
275 j += 3;
280 /* Settle is described using three atoms */
281 ilist = &molType->ilist[F_SETTLE];
282 if (ilist)
284 j = 1;
285 while (j < ilist->nr)
287 tng_molecule_bond_add(tng, tngMol, ilist->iatoms[j], ilist->iatoms[j+1], &tngBond);
288 tng_molecule_bond_add(tng, tngMol, ilist->iatoms[j], ilist->iatoms[j+2], &tngBond);
289 j += 4;
295 /*! \libinternal \brief Compute greatest common divisor of n1 and n2
296 * if they are positive.
298 * If only one of n1 and n2 is positive, then return it.
299 * If neither n1 or n2 is positive, then return -1. */
300 static int
301 greatest_common_divisor_if_positive(int n1, int n2)
303 if (0 >= n1)
305 return (0 >= n2) ? -1 : n2;
307 if (0 >= n2)
309 return n1;
312 /* We have a non-trivial greatest common divisor to compute. */
313 return gmx_greatest_common_divisor(n1, n2);
316 /* By default try to write 100 frames (of actual output) in each frame set.
317 * This number is the number of outputs of the most frequently written data
318 * type per frame set.
319 * TODO for 5.1: Verify that 100 frames per frame set is efficient for most
320 * setups regarding compression efficiency and compression time. Make this
321 * a hidden command-line option? */
322 const int defaultFramesPerFrameSet = 100;
324 /*! \libinternal \brief Set the number of frames per frame
325 * set according to output intervals.
326 * The default is that 100 frames are written of the data
327 * that is written most often. */
328 static void tng_set_frames_per_frame_set(tng_trajectory_t tng,
329 const gmx_bool bUseLossyCompression,
330 const t_inputrec *ir)
332 int gcd = -1;
334 /* Set the number of frames per frame set to contain at least
335 * defaultFramesPerFrameSet of the lowest common denominator of
336 * the writing interval of positions and velocities. */
337 /* FIXME after 5.0: consider nstenergy also? */
338 if (bUseLossyCompression)
340 gcd = ir->nstxout_compressed;
342 else
344 gcd = greatest_common_divisor_if_positive(ir->nstxout, ir->nstvout);
345 gcd = greatest_common_divisor_if_positive(gcd, ir->nstfout);
347 if (0 >= gcd)
349 return;
352 tng_num_frames_per_frame_set_set(tng, gcd * defaultFramesPerFrameSet);
355 /*! \libinternal \brief Set the data-writing intervals, and number of
356 * frames per frame set */
357 static void set_writing_intervals(tng_trajectory_t tng,
358 const gmx_bool bUseLossyCompression,
359 const t_inputrec *ir)
361 /* Define pointers to specific writing functions depending on if we
362 * write float or double data */
363 typedef tng_function_status (*set_writing_interval_func_pointer)(tng_trajectory_t,
364 const gmx_int64_t,
365 const gmx_int64_t,
366 const gmx_int64_t,
367 const char*,
368 const char,
369 const char);
370 #if GMX_DOUBLE
371 set_writing_interval_func_pointer set_writing_interval = tng_util_generic_write_interval_double_set;
372 #else
373 set_writing_interval_func_pointer set_writing_interval = tng_util_generic_write_interval_set;
374 #endif
375 int xout, vout, fout;
376 int gcd = -1, lowest = -1;
377 char compression;
379 tng_set_frames_per_frame_set(tng, bUseLossyCompression, ir);
381 if (bUseLossyCompression)
383 xout = ir->nstxout_compressed;
385 /* If there is no uncompressed coordinate output write forces
386 and velocities to the compressed tng file. */
387 if (ir->nstxout)
389 vout = 0;
390 fout = 0;
392 else
394 vout = ir->nstvout;
395 fout = ir->nstfout;
397 compression = TNG_TNG_COMPRESSION;
399 else
401 xout = ir->nstxout;
402 vout = ir->nstvout;
403 fout = ir->nstfout;
404 compression = TNG_GZIP_COMPRESSION;
406 if (xout)
408 set_writing_interval(tng, xout, 3, TNG_TRAJ_POSITIONS,
409 "POSITIONS", TNG_PARTICLE_BLOCK_DATA,
410 compression);
411 /* TODO: if/when we write energies to TNG also, reconsider how
412 * and when box information is written, because GROMACS
413 * behaviour pre-5.0 was to write the box with every
414 * trajectory frame and every energy frame, and probably
415 * people depend on this. */
417 gcd = greatest_common_divisor_if_positive(gcd, xout);
418 if (lowest < 0 || xout < lowest)
420 lowest = xout;
423 if (vout)
425 set_writing_interval(tng, vout, 3, TNG_TRAJ_VELOCITIES,
426 "VELOCITIES", TNG_PARTICLE_BLOCK_DATA,
427 compression);
429 gcd = greatest_common_divisor_if_positive(gcd, vout);
430 if (lowest < 0 || vout < lowest)
432 lowest = vout;
435 if (fout)
437 set_writing_interval(tng, fout, 3, TNG_TRAJ_FORCES,
438 "FORCES", TNG_PARTICLE_BLOCK_DATA,
439 TNG_GZIP_COMPRESSION);
441 gcd = greatest_common_divisor_if_positive(gcd, fout);
442 if (lowest < 0 || fout < lowest)
444 lowest = fout;
447 if (gcd > 0)
449 /* Lambdas and box shape written at an interval of the lowest common
450 denominator of other output */
451 set_writing_interval(tng, gcd, 1, TNG_GMX_LAMBDA,
452 "LAMBDAS", TNG_NON_PARTICLE_BLOCK_DATA,
453 TNG_GZIP_COMPRESSION);
455 set_writing_interval(tng, gcd, 9, TNG_TRAJ_BOX_SHAPE,
456 "BOX SHAPE", TNG_NON_PARTICLE_BLOCK_DATA,
457 TNG_GZIP_COMPRESSION);
458 if (gcd < lowest / 10)
460 gmx_warning("The lowest common denominator of trajectory output is "
461 "every %d step(s), whereas the shortest output interval "
462 "is every %d steps.", gcd, lowest);
466 #endif
468 void gmx_tng_prepare_md_writing(tng_trajectory_t tng,
469 const gmx_mtop_t *mtop,
470 const t_inputrec *ir)
472 #if GMX_USE_TNG
473 gmx_tng_add_mtop(tng, mtop);
474 set_writing_intervals(tng, FALSE, ir);
475 tng_time_per_frame_set(tng, ir->delta_t * PICO);
476 #else
477 GMX_UNUSED_VALUE(tng);
478 GMX_UNUSED_VALUE(mtop);
479 GMX_UNUSED_VALUE(ir);
480 #endif
483 #if GMX_USE_TNG
484 /* Check if all atoms in the molecule system are selected
485 * by a selection group of type specified by gtype. */
486 static gmx_bool all_atoms_selected(const gmx_mtop_t *mtop,
487 const int gtype)
489 const gmx_moltype_t *molType;
490 const t_atoms *atoms;
492 /* Iterate through all atoms in the molecule system and
493 * check if they belong to a selection group of the
494 * requested type. */
495 for (int molIndex = 0, i = 0; molIndex < mtop->nmoltype; molIndex++)
497 molType = &mtop->moltype[mtop->molblock[molIndex].type];
499 atoms = &molType->atoms;
501 for (int j = 0; j < mtop->molblock[molIndex].nmol; j++)
503 for (int atomIndex = 0; atomIndex < atoms->nr; atomIndex++, i++)
505 if (ggrpnr(&mtop->groups, gtype, i) != 0)
507 return FALSE;
512 return TRUE;
515 /* Create TNG molecules which will represent each of the selection
516 * groups for writing. But do that only if there is actually a
517 * specified selection group and it is not the whole system.
518 * TODO: Currently the only selection that is taken into account
519 * is egcCompressedX, but other selections should be added when
520 * e.g. writing energies is implemented.
522 static void add_selection_groups(tng_trajectory_t tng,
523 const gmx_mtop_t *mtop)
525 const gmx_moltype_t *molType;
526 const t_atoms *atoms;
527 const t_atom *at;
528 const t_resinfo *resInfo;
529 const t_ilist *ilist;
530 int nameIndex;
531 int atom_offset = 0;
532 tng_molecule_t mol, iterMol;
533 tng_chain_t chain;
534 tng_residue_t res;
535 tng_atom_t atom;
536 tng_bond_t tngBond;
537 gmx_int64_t nMols;
538 char *groupName;
540 /* TODO: When the TNG molecules block is more flexible TNG selection
541 * groups should not need all atoms specified. It should be possible
542 * just to specify what molecules are selected (and/or which atoms in
543 * the molecule) and how many (if applicable). */
545 /* If no atoms are selected we do not need to create a
546 * TNG selection group molecule. */
547 if (mtop->groups.ngrpnr[egcCompressedX] == 0)
549 return;
552 /* If all atoms are selected we do not have to create a selection
553 * group molecule in the TNG molecule system. */
554 if (all_atoms_selected(mtop, egcCompressedX))
556 return;
559 /* The name of the TNG molecule containing the selection group is the
560 * same as the name of the selection group. */
561 nameIndex = *mtop->groups.grps[egcCompressedX].nm_ind;
562 groupName = *mtop->groups.grpname[nameIndex];
564 tng_molecule_alloc(tng, &mol);
565 tng_molecule_name_set(tng, mol, groupName);
566 tng_molecule_chain_add(tng, mol, "", &chain);
567 for (int molIndex = 0, i = 0; molIndex < mtop->nmoltype; molIndex++)
569 molType = &mtop->moltype[mtop->molblock[molIndex].type];
571 atoms = &molType->atoms;
573 for (int j = 0; j < mtop->molblock[molIndex].nmol; j++)
575 bool bAtomsAdded = FALSE;
576 for (int atomIndex = 0; atomIndex < atoms->nr; atomIndex++, i++)
578 char *res_name;
579 int res_id;
581 if (ggrpnr(&mtop->groups, egcCompressedX, i) != 0)
583 continue;
585 at = &atoms->atom[atomIndex];
586 if (atoms->nres > 0)
588 resInfo = &atoms->resinfo[at->resind];
589 /* FIXME: When TNG supports both residue index and residue
590 * number the latter should be used. */
591 res_name = *resInfo->name;
592 res_id = at->resind + 1;
594 else
596 res_name = (char *)"";
597 res_id = 0;
599 if (tng_chain_residue_find(tng, chain, res_name, res_id, &res)
600 != TNG_SUCCESS)
602 /* Since there is ONE chain for selection groups do not keep the
603 * original residue IDs - otherwise there might be conflicts. */
604 tng_chain_residue_add(tng, chain, res_name, &res);
606 tng_residue_atom_w_id_add(tng, res, *(atoms->atomname[atomIndex]),
607 *(atoms->atomtype[atomIndex]),
608 atom_offset + atomIndex, &atom);
609 bAtomsAdded = TRUE;
611 /* Add bonds. */
612 if (bAtomsAdded)
614 for (int k = 0; k < F_NRE; k++)
616 if (IS_CHEMBOND(k))
618 ilist = &molType->ilist[k];
619 if (ilist)
621 int l = 1;
622 while (l < ilist->nr)
624 int atom1, atom2;
625 atom1 = ilist->iatoms[l] + atom_offset;
626 atom2 = ilist->iatoms[l+1] + atom_offset;
627 if (ggrpnr(&mtop->groups, egcCompressedX, atom1) == 0 &&
628 ggrpnr(&mtop->groups, egcCompressedX, atom2) == 0)
630 tng_molecule_bond_add(tng, mol, ilist->iatoms[l],
631 ilist->iatoms[l+1], &tngBond);
633 l += 3;
638 /* Settle is described using three atoms */
639 ilist = &molType->ilist[F_SETTLE];
640 if (ilist)
642 int l = 1;
643 while (l < ilist->nr)
645 int atom1, atom2, atom3;
646 atom1 = ilist->iatoms[l] + atom_offset;
647 atom2 = ilist->iatoms[l+1] + atom_offset;
648 atom3 = ilist->iatoms[l+2] + atom_offset;
649 if (ggrpnr(&mtop->groups, egcCompressedX, atom1) == 0)
651 if (ggrpnr(&mtop->groups, egcCompressedX, atom2) == 0)
653 tng_molecule_bond_add(tng, mol, atom1,
654 atom2, &tngBond);
656 if (ggrpnr(&mtop->groups, egcCompressedX, atom3) == 0)
658 tng_molecule_bond_add(tng, mol, atom1,
659 atom3, &tngBond);
662 l += 4;
666 atom_offset += atoms->nr;
669 tng_molecule_existing_add(tng, &mol);
670 tng_molecule_cnt_set(tng, mol, 1);
671 tng_num_molecule_types_get(tng, &nMols);
672 for (gmx_int64_t k = 0; k < nMols; k++)
674 tng_molecule_of_index_get(tng, k, &iterMol);
675 if (iterMol == mol)
677 continue;
679 tng_molecule_cnt_set(tng, iterMol, 0);
682 #endif
684 void gmx_tng_set_compression_precision(tng_trajectory_t tng,
685 real prec)
687 #if GMX_USE_TNG
688 tng_compression_precision_set(tng, prec);
689 #else
690 GMX_UNUSED_VALUE(tng);
691 GMX_UNUSED_VALUE(prec);
692 #endif
695 void gmx_tng_prepare_low_prec_writing(tng_trajectory_t tng,
696 const gmx_mtop_t *mtop,
697 const t_inputrec *ir)
699 #if GMX_USE_TNG
700 gmx_tng_add_mtop(tng, mtop);
701 add_selection_groups(tng, mtop);
702 set_writing_intervals(tng, TRUE, ir);
703 tng_time_per_frame_set(tng, ir->delta_t * PICO);
704 gmx_tng_set_compression_precision(tng, ir->x_compression_precision);
705 #else
706 GMX_UNUSED_VALUE(tng);
707 GMX_UNUSED_VALUE(mtop);
708 GMX_UNUSED_VALUE(ir);
709 #endif
712 void gmx_fwrite_tng(tng_trajectory_t tng,
713 const gmx_bool bUseLossyCompression,
714 gmx_int64_t step,
715 real elapsedPicoSeconds,
716 real lambda,
717 const rvec *box,
718 int nAtoms,
719 const rvec *x,
720 const rvec *v,
721 const rvec *f)
723 #if GMX_USE_TNG
724 typedef tng_function_status (*write_data_func_pointer)(tng_trajectory_t,
725 const gmx_int64_t,
726 const double,
727 const real*,
728 const gmx_int64_t,
729 const gmx_int64_t,
730 const char*,
731 const char,
732 const char);
733 #if GMX_DOUBLE
734 static write_data_func_pointer write_data = tng_util_generic_with_time_double_write;
735 #else
736 static write_data_func_pointer write_data = tng_util_generic_with_time_write;
737 #endif
738 double elapsedSeconds = elapsedPicoSeconds * PICO;
739 gmx_int64_t nParticles;
740 char compression;
743 if (!tng)
745 /* This function might get called when the type of the
746 compressed trajectory is actually XTC. So we exit and move
747 on. */
748 return;
751 tng_num_particles_get(tng, &nParticles);
752 if (nAtoms != (int)nParticles)
754 tng_implicit_num_particles_set(tng, nAtoms);
757 if (bUseLossyCompression)
759 compression = TNG_TNG_COMPRESSION;
761 else
763 compression = TNG_GZIP_COMPRESSION;
766 /* The writing is done using write_data, which writes float or double
767 * depending on the GROMACS compilation. */
768 if (x)
770 GMX_ASSERT(box, "Need a non-NULL box if positions are written");
772 if (write_data(tng, step, elapsedSeconds,
773 reinterpret_cast<const real *>(x),
774 3, TNG_TRAJ_POSITIONS, "POSITIONS",
775 TNG_PARTICLE_BLOCK_DATA,
776 compression) != TNG_SUCCESS)
778 gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
782 if (v)
784 if (write_data(tng, step, elapsedSeconds,
785 reinterpret_cast<const real *>(v),
786 3, TNG_TRAJ_VELOCITIES, "VELOCITIES",
787 TNG_PARTICLE_BLOCK_DATA,
788 compression) != TNG_SUCCESS)
790 gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
794 if (f)
796 /* TNG-MF1 compression only compresses positions and velocities. Use lossless
797 * compression for forces regardless of output mode */
798 if (write_data(tng, step, elapsedSeconds,
799 reinterpret_cast<const real *>(f),
800 3, TNG_TRAJ_FORCES, "FORCES",
801 TNG_PARTICLE_BLOCK_DATA,
802 TNG_GZIP_COMPRESSION) != TNG_SUCCESS)
804 gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
808 /* TNG-MF1 compression only compresses positions and velocities. Use lossless
809 * compression for lambdas and box shape regardless of output mode */
810 if (write_data(tng, step, elapsedSeconds,
811 reinterpret_cast<const real *>(box),
812 9, TNG_TRAJ_BOX_SHAPE, "BOX SHAPE",
813 TNG_NON_PARTICLE_BLOCK_DATA,
814 TNG_GZIP_COMPRESSION) != TNG_SUCCESS)
816 gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
819 if (write_data(tng, step, elapsedSeconds,
820 reinterpret_cast<const real *>(&lambda),
821 1, TNG_GMX_LAMBDA, "LAMBDAS",
822 TNG_NON_PARTICLE_BLOCK_DATA,
823 TNG_GZIP_COMPRESSION) != TNG_SUCCESS)
825 gmx_file("Cannot write TNG trajectory frame; maybe you are out of disk space?");
827 #else
828 GMX_UNUSED_VALUE(tng);
829 GMX_UNUSED_VALUE(bUseLossyCompression);
830 GMX_UNUSED_VALUE(step);
831 GMX_UNUSED_VALUE(elapsedPicoSeconds);
832 GMX_UNUSED_VALUE(lambda);
833 GMX_UNUSED_VALUE(box);
834 GMX_UNUSED_VALUE(nAtoms);
835 GMX_UNUSED_VALUE(x);
836 GMX_UNUSED_VALUE(v);
837 GMX_UNUSED_VALUE(f);
838 #endif
841 void fflush_tng(tng_trajectory_t tng)
843 #if GMX_USE_TNG
844 if (!tng)
846 return;
848 tng_frame_set_premature_write(tng, TNG_USE_HASH);
849 #else
850 GMX_UNUSED_VALUE(tng);
851 #endif
854 float gmx_tng_get_time_of_final_frame(tng_trajectory_t tng)
856 #if GMX_USE_TNG
857 gmx_int64_t nFrames;
858 double time;
859 float fTime;
861 tng_num_frames_get(tng, &nFrames);
862 tng_util_time_of_frame_get(tng, nFrames - 1, &time);
864 fTime = time / PICO;
865 return fTime;
866 #else
867 GMX_UNUSED_VALUE(tng);
868 return -1.0;
869 #endif